[Chat] revalidate join links on leave (#10833)

This commit is contained in:
Samuel Newman
2026-06-10 11:33:21 +03:00
committed by GitHub
parent 8720d05242
commit d6a14ade54
4 changed files with 46 additions and 1 deletions
@@ -7,6 +7,7 @@ import {
} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {makeProfileLink} from '#/lib/routes/links'
@@ -15,7 +16,10 @@ import {isNetworkError} from '#/lib/strings/errors'
import {sanitizeHandle} from '#/lib/strings/handles'
import {logger} from '#/logger'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useJoinLinkPreviewsQuery} from '#/state/queries/join-links'
import {
invalidateJoinLinkPreviewsForCode,
useJoinLinkPreviewsQuery,
} from '#/state/queries/join-links'
import {useRequestJoinGroupChat} from '#/state/queries/messages/request-join-group-chat'
import {useWithdrawJoinGroupChatRequest} from '#/state/queries/messages/withdraw-join-group-chat'
import {useSession} from '#/state/session'
@@ -78,6 +82,7 @@ function GroupChatJoinDialogContent({code}: {code?: string}) {
const {hasSession} = useSession()
const moderationOpts = useModerationOpts()
const navigation = useNavigation<NavigationProp>()
const queryClient = useQueryClient()
const {data, error, isLoading} = useJoinLinkPreviewsQuery({
codes: code ? [code] : undefined,
@@ -88,6 +93,7 @@ function GroupChatJoinDialogContent({code}: {code?: string}) {
const {mutate: joinGroupChat, isPending: isJoinPending} =
useRequestJoinGroupChat({
onSuccess: data => {
if (code) void invalidateJoinLinkPreviewsForCode(queryClient, code)
switch (data.status) {
case 'pending':
control.close(() => {
+29
View File
@@ -76,6 +76,35 @@ export function invalidateJoinLinkPreviewsForCode(
})
}
/**
* Invalidate any join link preview queries that resolved to the given convo.
* The code isn't always known to the viewer (e.g. when they're a regular
* member), so we match on the convoId carried by the resolved preview instead.
* Use this when the viewer's membership changes (e.g. they leave or are removed)
* so cached previews refetch and reflect their new viewer state.
*/
export function invalidateJoinLinkPreviewsForConvo(
queryClient: QueryClient,
convoId: string,
) {
return queryClient.invalidateQueries({
predicate: query => {
const [root] = query.queryKey
if (root !== joinLinkPreviewQueryKeyRoot) return false
const data = query.state.data as
| ChatBskyGroupGetJoinLinkPreviews.OutputSchema
| undefined
return (
data?.joinLinkPreviews.some(
preview =>
ChatBskyGroupDefs.isJoinLinkPreviewView(preview) &&
preview.convoId === convoId,
) ?? false
)
},
})
}
async function fetchJoinLinkPreviews({
agent,
codes,
@@ -11,6 +11,7 @@ import {
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {logger} from '#/logger'
import {invalidateJoinLinkPreviewsForConvo} from '#/state/queries/join-links'
import {useAgent} from '#/state/session'
import {RQKEY_ROOT as CONVO_LIST_KEY} from './list-conversations'
@@ -72,6 +73,9 @@ export function useLeaveConvo(
},
onSuccess: data => {
void queryClient.invalidateQueries({queryKey: [CONVO_LIST_KEY]})
if (convoId) {
void invalidateJoinLinkPreviewsForConvo(queryClient, convoId)
}
onSuccess?.(data)
},
onError: (error, _, context) => {
@@ -18,6 +18,7 @@ import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {useCurrentConvoId} from '#/state/messages/current-convo-id'
import {useMessagesEventBus} from '#/state/messages/events'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {invalidateJoinLinkPreviewsForConvo} from '#/state/queries/join-links'
import {useAgent, useSession} from '#/state/session'
import {parseConvoView} from '#/components/dms/util'
import {useAgeAssurance} from '#/ageAssurance'
@@ -257,6 +258,11 @@ export function ListConvosProviderInner({
debouncedRefetch()
} else if (ChatBskyConvoDefs.isLogLeaveConvo(log)) {
deleteConvoFromAllLists(log.convoId)
// The viewer is no longer in this convo (they left on another
// device, or were removed - removed members receive a
// logLeaveConvo, not a logRemoveMember). Refetch any cached join
// link preview so its viewer state reflects the lost membership.
void invalidateJoinLinkPreviewsForConvo(queryClient, log.convoId)
} else if (ChatBskyConvoDefs.isLogDeleteMessage(log)) {
updateConvoInAllLists(log.convoId, convo => {
if (