Update handling of join link previews (#10798)

Co-authored-by: Samuel Newman <mozzius@protonmail.com>
This commit is contained in:
DS Boyce
2026-06-09 07:23:33 -07:00
committed by GitHub
parent 4c4ebd0510
commit c52231a449
24 changed files with 192 additions and 103 deletions
+60 -5
View File
@@ -1,17 +1,48 @@
import {useCallback} from 'react'
import {
type $Typed,
AtpAgent,
type ChatBskyGroupDefs,
ChatBskyGroupDefs,
type ChatBskyGroupGetJoinLinkPreviews,
} from '@atproto/api'
import {useQuery, useQueryClient} from '@tanstack/react-query'
import {type QueryClient, useQuery, useQueryClient} from '@tanstack/react-query'
import {CHAT_SERVICE, DM_SERVICE_HEADERS} from '#/lib/constants'
import {logger} from '#/logger'
import {STALE} from '#/state/queries/index'
import {createQueryKey} from '#/state/queries/util'
import {createQueryKey, type StructuredQueryKey} from '#/state/queries/util'
import {useAgent} from '#/state/session'
/**
* The three preview shapes we currently support. Excludes the `{$type: string}`
* open-union fallback for unrecognized future variants - use
* `ChatInvitePreview` for that.
*/
export type KnownChatInvitePreview =
| $Typed<ChatBskyGroupDefs.JoinLinkPreviewView>
| $Typed<ChatBskyGroupDefs.DisabledJoinLinkPreviewView>
| $Typed<ChatBskyGroupDefs.InvalidJoinLinkPreviewView>
/**
* The full open-union shape, including the `{$type: string}` fallback for
* future variants.
*/
export type ChatInvitePreview = KnownChatInvitePreview | {$type: string}
/**
* Narrows a preview to one of the three known variants, filtering out the
* `{$type: string}` open-union fallback for unrecognized future shapes.
*/
export function isKnownJoinLinkPreview(
preview: unknown,
): preview is KnownChatInvitePreview {
return (
ChatBskyGroupDefs.isJoinLinkPreviewView(preview) ||
ChatBskyGroupDefs.isDisabledJoinLinkPreviewView(preview) ||
ChatBskyGroupDefs.isInvalidJoinLinkPreviewView(preview)
)
}
const joinLinkPreviewQueryKeyRoot = 'join-link-preview'
export const createJoinLinkPreviewQueryKey = (args: {
@@ -22,6 +53,29 @@ export const createJoinLinkPreviewQueryKey = (args: {
persistedVersion: 1,
})
/**
* Invalidate any join link preview queries whose `codes` include the given
* code. Use this when a link's state changes (e.g. it's disabled) so cached
* previews refetch and reflect the new state.
*/
export function invalidateJoinLinkPreviewsForCode(
queryClient: QueryClient,
code: string,
) {
return queryClient.invalidateQueries({
predicate: query => {
const [root, args] = query.queryKey as Partial<
StructuredQueryKey<{codes?: string[]}>
>
return (
root === joinLinkPreviewQueryKeyRoot &&
Array.isArray(args?.codes) &&
args.codes.includes(code)
)
},
})
}
async function fetchJoinLinkPreviews({
agent,
codes,
@@ -104,7 +158,7 @@ export function useGetJoinLinkPreview() {
}: {
code: string
hasSession: boolean
}): Promise<ChatBskyGroupDefs.JoinLinkPreviewView | undefined> => {
}): Promise<KnownChatInvitePreview | undefined> => {
try {
const data = await queryClient.fetchQuery({
queryKey: createJoinLinkPreviewQueryKey({codes: [code], hasSession}),
@@ -112,7 +166,8 @@ export function useGetJoinLinkPreview() {
fetchJoinLinkPreviews({agent, codes: [code], hasSession}),
staleTime: STALE.SECONDS.FIFTEEN,
})
return data.joinLinkPreviews[0]
const found = data.joinLinkPreviews[0]
return isKnownJoinLinkPreview(found) ? found : undefined
} catch (error) {
logger.error('Failed to fetch join link preview', {safeMessage: error})
return undefined
@@ -6,6 +6,7 @@ import {useMutation, useQueryClient} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {logger} from '#/logger'
import {invalidateJoinLinkPreviewsForCode} from '#/state/queries/join-links'
import {useAgent} from '#/state/session'
import {
rollbackConvoOptimistic,
@@ -59,6 +60,7 @@ export function useDisableJoinLink(
}
})
}
void invalidateJoinLinkPreviewsForCode(queryClient, data.joinLink.code)
onSuccess?.(data)
},
onError: (e, _variables, context) => {
@@ -3,6 +3,7 @@ import {useMutation, useQueryClient} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {logger} from '#/logger'
import {invalidateJoinLinkPreviewsForCode} from '#/state/queries/join-links'
import {useAgent} from '#/state/session'
import {
rollbackConvoOptimistic,
@@ -56,6 +57,7 @@ export function useEnableJoinLink(
}
})
}
void invalidateJoinLinkPreviewsForCode(queryClient, data.joinLink.code)
onSuccess?.(data)
},
onError: (e, _variables, context) => {