migrate link resolution to the appview and chat clients

`resolveLink` took an agent and used it for four appview reads plus the chat
invite preview, so it now takes both clients as a `LinkResolvers` pair - the
caller cannot know which branch a URL will take until it is parsed. That was
the last DM_SERVICE_HEADERS site, so the constant is deleted.

`resolveGif` never touched the agent at all (it is pure URL metadata work on
what the picker already returned), so its parameter is dropped rather than
replaced, along with the one on `fetchResolveGifQuery`.

With the resolvers on clients, `apilib.post` loses the agent parameter the
previous slice kept solely for them, and `composerStateToDraft` takes the
resolver pair instead of minting a throwaway public agent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-08-04 03:23:37 +03:00
parent 9bbc3befdd
commit 92408c62a6
7 changed files with 132 additions and 101 deletions
+21 -17
View File
@@ -1,9 +1,13 @@
import {type AtpAgent} from '@atproto/api'
import {type QueryClient, queryOptions, useQuery} from '@tanstack/react-query'
import {type ResolvedLink, resolveGif, resolveLink} from '#/lib/api/resolve'
import {
type LinkResolvers,
type ResolvedLink,
resolveGif,
resolveLink,
} from '#/lib/api/resolve'
import {STALE} from '#/state/queries/index'
import {useAgent} from '#/state/session'
import {useAppviewClient, useChatClient} from '#/state/session'
import {type Gif} from '#/features/gifPicker/types'
export const RQKEY_LINK_ROOT = 'resolve-link'
@@ -12,24 +16,25 @@ export const RQKEY_LINK = (url: string) => [RQKEY_LINK_ROOT, url]
export const RQKEY_GIF_ROOT = 'resolve-gif'
export const RQKEY_GIF = (url: string) => [RQKEY_GIF_ROOT, url]
export function resolveLinkQueryOptions(agent: AtpAgent, url: string) {
export function resolveLinkQueryOptions(clients: LinkResolvers, url: string) {
return queryOptions({
staleTime: STALE.HOURS.ONE,
queryKey: RQKEY_LINK(url),
queryFn: () => resolveLink(agent, url),
queryFn: () => resolveLink(clients, url),
})
}
export function useResolveLinkQuery(url: string) {
const agent = useAgent()
return useQuery(resolveLinkQueryOptions(agent, url))
const appviewClient = useAppviewClient()
const chatClient = useChatClient()
return useQuery(resolveLinkQueryOptions({appviewClient, chatClient}, url))
}
export function fetchResolveLinkQuery(
queryClient: QueryClient,
agent: AtpAgent,
clients: LinkResolvers,
url: string,
) {
return queryClient.fetchQuery(resolveLinkQueryOptions(agent, url))
return queryClient.fetchQuery(resolveLinkQueryOptions(clients, url))
}
export function precacheResolveLinkQuery(
queryClient: QueryClient,
@@ -39,26 +44,25 @@ export function precacheResolveLinkQuery(
queryClient.setQueryData(RQKEY_LINK(url), resolvedLink)
}
/*
* GIF resolution is pure metadata work on a URL the picker already returned -
* it makes no atproto request - so it takes no client.
*/
export function useResolveGifQuery(gif: Gif) {
const agent = useAgent()
return useQuery({
staleTime: STALE.HOURS.ONE,
queryKey: RQKEY_GIF(gif.url),
queryFn: async () => {
return await resolveGif(agent, gif)
return await resolveGif(gif)
},
})
}
export function fetchResolveGifQuery(
queryClient: QueryClient,
agent: AtpAgent,
gif: Gif,
) {
export function fetchResolveGifQuery(queryClient: QueryClient, gif: Gif) {
return queryClient.fetchQuery({
staleTime: STALE.HOURS.ONE,
queryKey: RQKEY_GIF(gif.url),
queryFn: async () => {
return await resolveGif(agent, gif)
return await resolveGif(gif)
},
})
}