Block posting of invalid chat invites (#10757)
Co-authored-by: Samuel Newman <mozzius@protonmail.com>
This commit is contained in:
@@ -10,7 +10,7 @@ import {type ButtonColor} from '#/components/Button'
|
|||||||
import {ArrowRight_Stroke2_Corner0_Rounded as ArrowRightIcon} from '#/components/icons/Arrow'
|
import {ArrowRight_Stroke2_Corner0_Rounded as ArrowRightIcon} from '#/components/icons/Arrow'
|
||||||
import {ArrowBoxRight_Stroke2_Corner3_Rounded as JoinIcon} from '#/components/icons/ArrowBoxRight'
|
import {ArrowBoxRight_Stroke2_Corner3_Rounded as JoinIcon} from '#/components/icons/ArrowBoxRight'
|
||||||
import {ChainLink_Stroke2_Corner0_Rounded as LinkIcon} from '#/components/icons/ChainLink'
|
import {ChainLink_Stroke2_Corner0_Rounded as LinkIcon} from '#/components/icons/ChainLink'
|
||||||
import {CheckThick_Stroke2_Corner0_Rounded as CheckIcon} from '#/components/icons/Check'
|
import {Check_Stroke2_Corner0_Rounded as CheckIcon} from '#/components/icons/Check'
|
||||||
import {type Props as SVGIconProps} from '#/components/icons/common'
|
import {type Props as SVGIconProps} from '#/components/icons/common'
|
||||||
import {RaisingHand4Finger_Stroke2_Corner2_Rounded as HandIcon} from '#/components/icons/RaisingHand'
|
import {RaisingHand4Finger_Stroke2_Corner2_Rounded as HandIcon} from '#/components/icons/RaisingHand'
|
||||||
import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning'
|
import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning'
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {type BskyAgent} from '@atproto/api'
|
import {type AtpAgent} from '@atproto/api'
|
||||||
import {type QueryClient, useQuery} from '@tanstack/react-query'
|
import {type QueryClient, queryOptions, useQuery} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {type ResolvedLink, resolveGif, resolveLink} from '#/lib/api/resolve'
|
import {type ResolvedLink, resolveGif, resolveLink} from '#/lib/api/resolve'
|
||||||
import {STALE} from '#/state/queries/index'
|
import {STALE} from '#/state/queries/index'
|
||||||
@@ -12,29 +12,24 @@ export const RQKEY_LINK = (url: string) => [RQKEY_LINK_ROOT, url]
|
|||||||
export const RQKEY_GIF_ROOT = 'resolve-gif'
|
export const RQKEY_GIF_ROOT = 'resolve-gif'
|
||||||
export const RQKEY_GIF = (url: string) => [RQKEY_GIF_ROOT, url]
|
export const RQKEY_GIF = (url: string) => [RQKEY_GIF_ROOT, url]
|
||||||
|
|
||||||
export function useResolveLinkQuery(url: string) {
|
export function resolveLinkQueryOptions(agent: AtpAgent, url: string) {
|
||||||
const agent = useAgent()
|
return queryOptions({
|
||||||
|
|
||||||
return useQuery({
|
|
||||||
staleTime: STALE.HOURS.ONE,
|
staleTime: STALE.HOURS.ONE,
|
||||||
queryKey: RQKEY_LINK(url),
|
queryKey: RQKEY_LINK(url),
|
||||||
queryFn: async () => {
|
queryFn: () => resolveLink(agent, url),
|
||||||
return await resolveLink(agent, url)
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useResolveLinkQuery(url: string) {
|
||||||
|
const agent = useAgent()
|
||||||
|
return useQuery(resolveLinkQueryOptions(agent, url))
|
||||||
|
}
|
||||||
export function fetchResolveLinkQuery(
|
export function fetchResolveLinkQuery(
|
||||||
queryClient: QueryClient,
|
queryClient: QueryClient,
|
||||||
agent: BskyAgent,
|
agent: AtpAgent,
|
||||||
url: string,
|
url: string,
|
||||||
) {
|
) {
|
||||||
return queryClient.fetchQuery({
|
return queryClient.fetchQuery(resolveLinkQueryOptions(agent, url))
|
||||||
staleTime: STALE.HOURS.ONE,
|
|
||||||
queryKey: RQKEY_LINK(url),
|
|
||||||
queryFn: async () => {
|
|
||||||
return await resolveLink(agent, url)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
export function precacheResolveLinkQuery(
|
export function precacheResolveLinkQuery(
|
||||||
queryClient: QueryClient,
|
queryClient: QueryClient,
|
||||||
@@ -56,7 +51,7 @@ export function useResolveGifQuery(gif: Gif) {
|
|||||||
}
|
}
|
||||||
export function fetchResolveGifQuery(
|
export function fetchResolveGifQuery(
|
||||||
queryClient: QueryClient,
|
queryClient: QueryClient,
|
||||||
agent: BskyAgent,
|
agent: AtpAgent,
|
||||||
gif: Gif,
|
gif: Gif,
|
||||||
) {
|
) {
|
||||||
return queryClient.fetchQuery({
|
return queryClient.fetchQuery({
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ import {
|
|||||||
import {plural} from '@lingui/core/macro'
|
import {plural} from '@lingui/core/macro'
|
||||||
import {Trans, useLingui} from '@lingui/react/macro'
|
import {Trans, useLingui} from '@lingui/react/macro'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
import {useQueryClient} from '@tanstack/react-query'
|
import {useQueries, useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
import * as apilib from '#/lib/api/index'
|
import * as apilib from '#/lib/api/index'
|
||||||
import {EmbeddingDisabledError} from '#/lib/api/resolve'
|
import {EmbeddingDisabledError} from '#/lib/api/resolve'
|
||||||
@@ -95,6 +95,7 @@ import {
|
|||||||
} from '#/state/preferences/languages'
|
} from '#/state/preferences/languages'
|
||||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||||
import {useProfileQuery} from '#/state/queries/profile'
|
import {useProfileQuery} from '#/state/queries/profile'
|
||||||
|
import {resolveLinkQueryOptions} from '#/state/queries/resolve-link'
|
||||||
import {useAgent, useSession} from '#/state/session'
|
import {useAgent, useSession} from '#/state/session'
|
||||||
import {useComposerControls} from '#/state/shell/composer'
|
import {useComposerControls} from '#/state/shell/composer'
|
||||||
import {type ComposerOpts, type OnPostSuccessData} from '#/state/shell/composer'
|
import {type ComposerOpts, type OnPostSuccessData} from '#/state/shell/composer'
|
||||||
@@ -865,8 +866,25 @@ export const ComposePost = ({
|
|||||||
}
|
}
|
||||||
}, [thread, requireAltTextEnabled, l])
|
}, [thread, requireAltTextEnabled, l])
|
||||||
|
|
||||||
|
// Subscribe to the resolve-link cache for any link URIs in the thread so we
|
||||||
|
// can detect chat invites that resolved to no preview (revoked/expired) and
|
||||||
|
// block publishing - otherwise the post would go out without the embed.
|
||||||
|
const linkUris = thread.posts
|
||||||
|
.filter(post => post.embed.link)
|
||||||
|
.map(post => post.embed.link!.uri)
|
||||||
|
const linkQueries = useQueries({
|
||||||
|
queries: linkUris.map(uri => ({
|
||||||
|
...resolveLinkQueryOptions(agent, uri),
|
||||||
|
enabled: false,
|
||||||
|
})),
|
||||||
|
})
|
||||||
|
const hasUnavailableChatInvite = linkQueries.some(
|
||||||
|
q => q.data?.type === 'chat-invite' && !q.data.view,
|
||||||
|
)
|
||||||
|
|
||||||
const canPost =
|
const canPost =
|
||||||
!missingAltError &&
|
!missingAltError &&
|
||||||
|
!hasUnavailableChatInvite &&
|
||||||
thread.posts.some(post => !isEmptyPost(post)) &&
|
thread.posts.some(post => !isEmptyPost(post)) &&
|
||||||
thread.posts.every(
|
thread.posts.every(
|
||||||
post =>
|
post =>
|
||||||
|
|||||||
Reference in New Issue
Block a user