diff --git a/src/state/queries/join-links.ts b/src/state/queries/join-links.ts
index ca1db05558..e53edc0291 100644
--- a/src/state/queries/join-links.ts
+++ b/src/state/queries/join-links.ts
@@ -1,17 +1,19 @@
import {useCallback} from 'react'
import {
type $Typed,
- AtpAgent,
ChatBskyGroupDefs,
type ChatBskyGroupGetJoinLinkPreviews,
} from '@atproto/api'
+import {type Client} from '@atproto/lex'
import {type QueryClient, useQuery, useQueryClient} from '@tanstack/react-query'
-import {CHAT_SERVICE, DM_SERVICE_HEADERS} from '#/lib/constants'
+import {CHAT_SERVICE} from '#/lib/constants'
+import {createLexClient} from '#/lib/lexClient'
import {logger} from '#/logger'
import {STALE} from '#/state/queries/index'
import {createQueryKey, type StructuredQueryKey} from '#/state/queries/util'
-import {useAgent} from '#/state/session'
+import {useChatClient} from '#/state/session'
+import {chat} from '#/lexicons'
/**
* The three preview shapes we currently support. Excludes the `{$type: string}`
@@ -154,23 +156,34 @@ export function invalidateJoinLinkPreviewsForConvo(
})
}
+let publicChatClient: Client | undefined
+
+/**
+ * The unauthenticated {@link Client} for the chat service, used to resolve join
+ * links before the viewer has a session.
+ *
+ * It talks to {@link CHAT_SERVICE} directly rather than proxying through a PDS,
+ * so no `atproto-proxy` header is involved. A single module-level instance,
+ * because there is no session to scope it to and reconstructing it per call
+ * would be waste.
+ */
+function getPublicChatClient(): Client {
+ return (publicChatClient ??= createLexClient({service: CHAT_SERVICE}))
+}
+
async function fetchJoinLinkPreviews({
- agent,
+ client,
codes,
hasSession,
}: {
- agent: AtpAgent
+ client: Client
codes: string[]
hasSession: boolean
}) {
- const previewAgent = new AtpAgent({service: CHAT_SERVICE})
- const res = hasSession
- ? await agent.chat.bsky.group.getJoinLinkPreviews(
- {codes},
- {headers: DM_SERVICE_HEADERS},
- )
- : await previewAgent.chat.bsky.group.getJoinLinkPreviews({codes})
- return res.data
+ return await (hasSession ? client : getPublicChatClient()).call(
+ chat.bsky.group.getJoinLinkPreviews,
+ {codes},
+ )
}
export function useJoinLinkPreviewsQuery({
@@ -188,14 +201,14 @@ export function useJoinLinkPreviewsQuery({
*/
initialData?: ChatBskyGroupGetJoinLinkPreviews.OutputSchema
}) {
- const agent = useAgent()
+ const client = useChatClient()
return useQuery({
queryKey: createJoinLinkPreviewQueryKey({codes: codes ?? [], hasSession}),
queryFn: async () => {
if (!codes) throw new Error('No invite code')
try {
- return await fetchJoinLinkPreviews({agent, codes, hasSession})
+ return await fetchJoinLinkPreviews({client, codes, hasSession})
} catch (error) {
logger.error('Failed to fetch join link preview', {safeMessage: error})
throw error
@@ -208,13 +221,13 @@ export function useJoinLinkPreviewsQuery({
}
export function usePrefetchJoinLinkPreviews() {
- const agent = useAgent()
+ const client = useChatClient()
const queryClient = useQueryClient()
return ({codes, hasSession}: {codes: string[]; hasSession: boolean}) => {
return queryClient.prefetchQuery({
queryKey: createJoinLinkPreviewQueryKey({codes, hasSession}),
- queryFn: () => fetchJoinLinkPreviews({agent, codes, hasSession}),
+ queryFn: () => fetchJoinLinkPreviews({client, codes, hasSession}),
staleTime: STALE.SECONDS.FIFTEEN,
})
}
@@ -226,7 +239,7 @@ export function usePrefetchJoinLinkPreviews() {
* Returns undefined if the preview can't be resolved.
*/
export function useGetJoinLinkPreview() {
- const agent = useAgent()
+ const client = useChatClient()
const queryClient = useQueryClient()
return useCallback(
@@ -241,7 +254,7 @@ export function useGetJoinLinkPreview() {
const data = await queryClient.fetchQuery({
queryKey: createJoinLinkPreviewQueryKey({codes: [code], hasSession}),
queryFn: () =>
- fetchJoinLinkPreviews({agent, codes: [code], hasSession}),
+ fetchJoinLinkPreviews({client, codes: [code], hasSession}),
staleTime: STALE.SECONDS.FIFTEEN,
})
const found = data.joinLinkPreviews[0]
@@ -251,6 +264,6 @@ export function useGetJoinLinkPreview() {
return undefined
}
},
- [agent, queryClient],
+ [client, queryClient],
)
}
diff --git a/src/state/queries/messages/create-join-link.ts b/src/state/queries/messages/create-join-link.ts
index fbac855466..57269b93e3 100644
--- a/src/state/queries/messages/create-join-link.ts
+++ b/src/state/queries/messages/create-join-link.ts
@@ -5,9 +5,9 @@ import {
} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query'
-import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {logger} from '#/logger'
-import {useAgent} from '#/state/session'
+import {useChatClient} from '#/state/session'
+import {chat} from '#/lexicons'
import {
rollbackConvoOptimistic,
updateConvoOptimistic,
@@ -24,7 +24,7 @@ export function useCreateJoinLink(
},
) {
const queryClient = useQueryClient()
- const agent = useAgent()
+ const client = useChatClient()
return useMutation({
mutationFn: async ({
@@ -35,11 +35,11 @@ export function useCreateJoinLink(
requireApproval: boolean
}) => {
if (!convoId) throw new Error('No convoId provided')
- const {data} = await agent.chat.bsky.group.createJoinLink(
- {convoId, joinRule, requireApproval},
- {headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
- )
- return data
+ return await client.call(chat.bsky.group.createJoinLink, {
+ convoId,
+ joinRule,
+ requireApproval,
+ })
},
onMutate: ({joinRule, requireApproval}) => {
if (!convoId) return
diff --git a/src/state/queries/messages/disable-join-link.ts b/src/state/queries/messages/disable-join-link.ts
index c143feb6cc..b995dbcc6f 100644
--- a/src/state/queries/messages/disable-join-link.ts
+++ b/src/state/queries/messages/disable-join-link.ts
@@ -4,10 +4,10 @@ import {
} from '@atproto/api'
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 {useChatClient} from '#/state/session'
+import {chat} from '#/lexicons'
import {
rollbackConvoOptimistic,
updateConvoOptimistic,
@@ -24,16 +24,12 @@ export function useDisableJoinLink(
},
) {
const queryClient = useQueryClient()
- const agent = useAgent()
+ const client = useChatClient()
return useMutation({
mutationFn: async () => {
if (!convoId) throw new Error('No convoId provided')
- const {data} = await agent.chat.bsky.group.disableJoinLink(
- {convoId},
- {headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
- )
- return data
+ return await client.call(chat.bsky.group.disableJoinLink, {convoId})
},
onMutate: () => {
if (!convoId) return
diff --git a/src/state/queries/messages/edit-join-link.ts b/src/state/queries/messages/edit-join-link.ts
index 7e34ce88d9..7870c1f7e3 100644
--- a/src/state/queries/messages/edit-join-link.ts
+++ b/src/state/queries/messages/edit-join-link.ts
@@ -5,9 +5,9 @@ import {
} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query'
-import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {logger} from '#/logger'
-import {useAgent} from '#/state/session'
+import {useChatClient} from '#/state/session'
+import {chat} from '#/lexicons'
import {
rollbackConvoOptimistic,
updateConvoOptimistic,
@@ -24,7 +24,7 @@ export function useEditJoinLink(
},
) {
const queryClient = useQueryClient()
- const agent = useAgent()
+ const client = useChatClient()
return useMutation({
mutationFn: async ({
@@ -35,11 +35,11 @@ export function useEditJoinLink(
requireApproval: boolean
}) => {
if (!convoId) throw new Error('No convoId provided')
- const {data} = await agent.chat.bsky.group.editJoinLink(
- {convoId, joinRule, requireApproval},
- {headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
- )
- return data
+ return await client.call(chat.bsky.group.editJoinLink, {
+ convoId,
+ joinRule,
+ requireApproval,
+ })
},
onMutate: ({joinRule, requireApproval}) => {
if (!convoId) return
diff --git a/src/state/queries/messages/enable-join-link.ts b/src/state/queries/messages/enable-join-link.ts
index 4febc85441..ef3b168068 100644
--- a/src/state/queries/messages/enable-join-link.ts
+++ b/src/state/queries/messages/enable-join-link.ts
@@ -1,10 +1,10 @@
import {ChatBskyConvoDefs, type ChatBskyGroupEnableJoinLink} from '@atproto/api'
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 {useChatClient} from '#/state/session'
+import {chat} from '#/lexicons'
import {
rollbackConvoOptimistic,
updateConvoOptimistic,
@@ -21,16 +21,12 @@ export function useEnableJoinLink(
},
) {
const queryClient = useQueryClient()
- const agent = useAgent()
+ const client = useChatClient()
return useMutation({
mutationFn: async () => {
if (!convoId) throw new Error('No convoId provided')
- const {data} = await agent.chat.bsky.group.enableJoinLink(
- {convoId},
- {headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
- )
- return data
+ return await client.call(chat.bsky.group.enableJoinLink, {convoId})
},
onMutate: () => {
if (!convoId) return
diff --git a/src/state/queries/messages/join-requests.ts b/src/state/queries/messages/join-requests.ts
index 179dfafaaf..d36ebd60ae 100644
--- a/src/state/queries/messages/join-requests.ts
+++ b/src/state/queries/messages/join-requests.ts
@@ -4,15 +4,16 @@ import {
type ChatBskyGroupListJoinRequests,
type ChatBskyGroupRejectJoinRequest,
} from '@atproto/api'
+import {type DidString} from '@atproto/syntax'
import {
type InfiniteData,
useMutation,
useQueryClient,
} from '@tanstack/react-query'
-import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {logger} from '#/logger'
-import {useAgent} from '#/state/session'
+import {useChatClient} from '#/state/session'
+import {chat} from '#/lexicons'
import {listConvoMembersQueryKey} from './list-convo-members'
import {createListJoinRequestsQueryKey} from './list-join-requests'
@@ -34,21 +35,28 @@ export function useJoinRequestMutation(
},
) {
const queryClient = useQueryClient()
- const agent = useAgent()
+ const client = useChatClient()
return useMutation({
mutationFn: async ({member}: {member: string}) => {
if (!convoId) throw new Error('No convoId provided')
- const {data} =
+ // callers pass an already-resolved actor did
+ const memberDid = member as DidString
+ const data =
action === 'approve'
- ? await agent.chat.bsky.group.approveJoinRequest(
- {convoId, member},
- {headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
- )
- : await agent.chat.bsky.group.rejectJoinRequest(
- {convoId, member},
- {headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
- )
+ ? await client.call(chat.bsky.group.approveJoinRequest, {
+ convoId,
+ member: memberDid,
+ })
+ : await client.call(chat.bsky.group.rejectJoinRequest, {
+ convoId,
+ member: memberDid,
+ })
+ /*
+ * The two branches have different output schemas, so the union cannot be
+ * narrowed from the `action` value alone - the cast carries the generic's
+ * mapping through, exactly as the pre-migration code did.
+ */
return data as JoinRequestOutput
},
onMutate: ({member}) => {
diff --git a/src/state/queries/messages/list-join-requests.ts b/src/state/queries/messages/list-join-requests.ts
index 64a999c118..bbb82eeca5 100644
--- a/src/state/queries/messages/list-join-requests.ts
+++ b/src/state/queries/messages/list-join-requests.ts
@@ -2,10 +2,10 @@ import {useEffect} from 'react'
import {ChatBskyConvoDefs} from '@atproto/api'
import {useInfiniteQuery, useQueryClient} from '@tanstack/react-query'
-import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {useMessagesEventBus} from '#/state/messages/events'
import {createQueryKey} from '#/state/queries/util'
-import {useAgent} from '#/state/session'
+import {useChatClient} from '#/state/session'
+import {chat} from '#/lexicons'
import {STALE} from '..'
export const JOIN_REQUESTS_THRESHOLD = 20
@@ -22,7 +22,7 @@ export function useListJoinRequestsQuery({
convoId: string | undefined
enabled?: boolean
}) {
- const agent = useAgent()
+ const client = useChatClient()
const queryClient = useQueryClient()
const messagesBus = useMessagesEventBus()
const isEnabled = enabled !== false && !!convoId
@@ -54,11 +54,12 @@ export function useListJoinRequestsQuery({
enabled: isEnabled,
queryKey: createListJoinRequestsQueryKey({convoId: convoId ?? ''}),
queryFn: async ({pageParam}) => {
- const {data} = await agent.chat.bsky.group.listJoinRequests(
- {convoId: convoId!, cursor: pageParam, limit: JOIN_REQUESTS_THRESHOLD},
- {headers: DM_SERVICE_HEADERS},
- )
- return data
+ return await client.call(chat.bsky.group.listJoinRequests, {
+ // guarded by `isEnabled`
+ convoId: convoId!,
+ cursor: pageParam,
+ limit: JOIN_REQUESTS_THRESHOLD,
+ })
},
initialPageParam: undefined as string | undefined,
getNextPageParam: page => page.cursor,
diff --git a/src/state/queries/messages/mark-join-request-read.ts b/src/state/queries/messages/mark-join-request-read.ts
index ae6d7e88a7..3dfd995af0 100644
--- a/src/state/queries/messages/mark-join-request-read.ts
+++ b/src/state/queries/messages/mark-join-request-read.ts
@@ -1,9 +1,9 @@
import {ChatBskyConvoDefs} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query'
-import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {logger} from '#/logger'
-import {useAgent} from '#/state/session'
+import {useChatClient} from '#/state/session'
+import {chat} from '#/lexicons'
import {RQKEY as CONVO_KEY} from './conversation'
import {
type ConvoListQueryData,
@@ -12,15 +12,12 @@ import {
export function useMarkJoinRequestsRead(convoId: string | undefined) {
const queryClient = useQueryClient()
- const agent = useAgent()
+ const client = useChatClient()
return useMutation({
mutationFn: async () => {
if (!convoId) throw new Error('No convoId provided')
- await agent.chat.bsky.group.updateJoinRequestsRead(
- {convoId},
- {headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
- )
+ await client.call(chat.bsky.group.updateJoinRequestsRead, {convoId})
},
onMutate: () => {
if (!convoId) return
diff --git a/src/state/queries/messages/request-join-group-chat.ts b/src/state/queries/messages/request-join-group-chat.ts
index f4d5650346..b0a1922007 100644
--- a/src/state/queries/messages/request-join-group-chat.ts
+++ b/src/state/queries/messages/request-join-group-chat.ts
@@ -1,9 +1,9 @@
import {type ChatBskyGroupRequestJoin} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query'
-import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {logger} from '#/logger'
-import {useAgent, useSession} from '#/state/session'
+import {useChatClient, useSession} from '#/state/session'
+import {chat} from '#/lexicons'
import {RQKEY_ROOT as REQUESTS_RQKEY_ROOT} from './list-conversation-requests'
export function useRequestJoinGroupChat({
@@ -13,7 +13,7 @@ export function useRequestJoinGroupChat({
onSuccess?: (data: ChatBskyGroupRequestJoin.OutputSchema) => void
onError?: (error: Error) => void
} = {}) {
- const agent = useAgent()
+ const client = useChatClient()
const queryClient = useQueryClient()
const {hasSession} = useSession()
@@ -22,11 +22,7 @@ export function useRequestJoinGroupChat({
if (!hasSession) throw new Error('Must be logged in to join')
if (!code) throw new Error('No invite code')
- const res = await agent.chat.bsky.group.requestJoin(
- {code},
- {headers: DM_SERVICE_HEADERS},
- )
- return res.data
+ return await client.call(chat.bsky.group.requestJoin, {code})
},
onSuccess: data => {
void queryClient.invalidateQueries({queryKey: [REQUESTS_RQKEY_ROOT]})
diff --git a/src/state/queries/messages/withdraw-join-group-chat.ts b/src/state/queries/messages/withdraw-join-group-chat.ts
index 2ede8c941d..736128ee25 100644
--- a/src/state/queries/messages/withdraw-join-group-chat.ts
+++ b/src/state/queries/messages/withdraw-join-group-chat.ts
@@ -1,9 +1,9 @@
import {type ChatBskyGroupWithdrawJoinRequest} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query'
-import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {logger} from '#/logger'
-import {useAgent, useSession} from '#/state/session'
+import {useChatClient, useSession} from '#/state/session'
+import {chat} from '#/lexicons'
import {
type ConvoRequestListQueryData,
optimisticDeleteJoinRequest,
@@ -17,7 +17,7 @@ export function useWithdrawJoinGroupChatRequest({
onSuccess?: (data: ChatBskyGroupWithdrawJoinRequest.OutputSchema) => void
onError?: (error: Error) => void
} = {}) {
- const agent = useAgent()
+ const client = useChatClient()
const queryClient = useQueryClient()
const {hasSession} = useSession()
@@ -27,11 +27,7 @@ export function useWithdrawJoinGroupChatRequest({
throw new Error('Must be logged in to withdraw a join request')
if (!convoId) throw new Error('No convoId provided')
- const res = await agent.chat.bsky.group.withdrawJoinRequest(
- {convoId},
- {headers: DM_SERVICE_HEADERS},
- )
- return res.data
+ return await client.call(chat.bsky.group.withdrawJoinRequest, {convoId})
},
onSuccess: (data, {convoId}) => {
queryClient.setQueriesData(