diff --git a/src/components/dms/dialogs/NewChatDialog.tsx b/src/components/dms/dialogs/NewChatDialog.tsx
index 4cca14bd0a..7ee103ef9b 100644
--- a/src/components/dms/dialogs/NewChatDialog.tsx
+++ b/src/components/dms/dialogs/NewChatDialog.tsx
@@ -1,5 +1,4 @@
import {useCallback} from 'react'
-import {ChatBskyGroupCreateGroup} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro'
import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification'
@@ -88,29 +87,27 @@ export function NewChat({
let errorMessage = l`An issue occurred starting the group chat, please try again.`
if (isNetworkError(error)) {
errorMessage = l`A network error occurred. Please check your internet connection.`
- } else if (
- error instanceof ChatBskyGroupCreateGroup.AccountSuspendedError
- ) {
- errorMessage = l`Suspended accounts cannot participate in a group chat.`
- } else if (error instanceof ChatBskyGroupCreateGroup.BlockedActorError) {
- errorMessage = l`One of the selected recipients has blocked you and cannot be messaged.`
- } else if (
- error instanceof
- ChatBskyGroupCreateGroup.NewAccountCannotCreateGroupError
- ) {
- errorMessage = l`You cannot create a group chat yet.`
- } else if (
- error instanceof ChatBskyGroupCreateGroup.NotFollowedBySenderError
- ) {
- errorMessage = l`A selected recipient is not followed by the sender.`
- } else if (
- error instanceof ChatBskyGroupCreateGroup.RecipientNotFoundError
- ) {
- errorMessage = l`Unable to find a selected recipient.`
- } else if (
- error instanceof ChatBskyGroupCreateGroup.UserForbidsGroupsError
- ) {
- errorMessage = l`One of the selected recipients does not allow group chats.`
+ } else {
+ switch (matchXrpcError(error, chat.bsky.group.createGroup)) {
+ case 'AccountSuspended':
+ errorMessage = l`Suspended accounts cannot participate in a group chat.`
+ break
+ case 'BlockedActor':
+ errorMessage = l`One of the selected recipients has blocked you and cannot be messaged.`
+ break
+ case 'NewAccountCannotCreateGroup':
+ errorMessage = l`You cannot create a group chat yet.`
+ break
+ case 'NotFollowedBySender':
+ errorMessage = l`A selected recipient is not followed by the sender.`
+ break
+ case 'RecipientNotFound':
+ errorMessage = l`Unable to find a selected recipient.`
+ break
+ case 'UserForbidsGroups':
+ errorMessage = l`One of the selected recipients does not allow group chats.`
+ break
+ }
}
Toast.show(errorMessage, {
type: 'error',
diff --git a/src/components/dms/dialogs/ShareViaChatDialog.tsx b/src/components/dms/dialogs/ShareViaChatDialog.tsx
index 6dfb6145f7..f26dd47a57 100644
--- a/src/components/dms/dialogs/ShareViaChatDialog.tsx
+++ b/src/components/dms/dialogs/ShareViaChatDialog.tsx
@@ -1,5 +1,4 @@
import {useCallback, useState} from 'react'
-import {ChatBskyGroupCreateGroup} from '@atproto/api'
import {useLingui} from '@lingui/react/macro'
import {isNetworkError} from '#/lib/strings/errors'
@@ -104,29 +103,27 @@ function SendViaChatDialogInner({
let errorMessage = l`An issue occurred starting the group chat, please try again.`
if (isNetworkError(error)) {
errorMessage = l`A network error occurred. Please check your internet connection.`
- } else if (
- error instanceof ChatBskyGroupCreateGroup.AccountSuspendedError
- ) {
- errorMessage = l`Suspended accounts cannot participate in a group chat.`
- } else if (error instanceof ChatBskyGroupCreateGroup.BlockedActorError) {
- errorMessage = l`One of the selected recipients has blocked you and cannot be messaged.`
- } else if (
- error instanceof
- ChatBskyGroupCreateGroup.NewAccountCannotCreateGroupError
- ) {
- errorMessage = l`You cannot create a group chat yet.`
- } else if (
- error instanceof ChatBskyGroupCreateGroup.NotFollowedBySenderError
- ) {
- errorMessage = l`A selected recipient is not followed by the sender.`
- } else if (
- error instanceof ChatBskyGroupCreateGroup.RecipientNotFoundError
- ) {
- errorMessage = l`Unable to find a selected recipient.`
- } else if (
- error instanceof ChatBskyGroupCreateGroup.UserForbidsGroupsError
- ) {
- errorMessage = l`One of the selected recipients does not allow group chats.`
+ } else {
+ switch (matchXrpcError(error, chat.bsky.group.createGroup)) {
+ case 'AccountSuspended':
+ errorMessage = l`Suspended accounts cannot participate in a group chat.`
+ break
+ case 'BlockedActor':
+ errorMessage = l`One of the selected recipients has blocked you and cannot be messaged.`
+ break
+ case 'NewAccountCannotCreateGroup':
+ errorMessage = l`You cannot create a group chat yet.`
+ break
+ case 'NotFollowedBySender':
+ errorMessage = l`A selected recipient is not followed by the sender.`
+ break
+ case 'RecipientNotFound':
+ errorMessage = l`Unable to find a selected recipient.`
+ break
+ case 'UserForbidsGroups':
+ errorMessage = l`One of the selected recipients does not allow group chats.`
+ break
+ }
}
Toast.show(errorMessage, {
type: 'error',
diff --git a/src/components/intents/GroupChatJoinDialog.tsx b/src/components/intents/GroupChatJoinDialog.tsx
index 7c506a634d..53782ae271 100644
--- a/src/components/intents/GroupChatJoinDialog.tsx
+++ b/src/components/intents/GroupChatJoinDialog.tsx
@@ -1,11 +1,6 @@
import {useEffect} from 'react'
import {View} from 'react-native'
-import {
- ChatBskyGroupDefs,
- ChatBskyGroupRequestJoin,
- ChatBskyGroupWithdrawJoinRequest,
- moderateProfile,
-} from '@atproto/api'
+import {ChatBskyGroupDefs, moderateProfile} from '@atproto/api'
import {Plural, Trans, useLingui} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query'
@@ -16,6 +11,7 @@ import {makeProfileLink} from '#/lib/routes/links'
import {type NavigationProp} from '#/lib/routes/types'
import {isNetworkError} from '#/lib/strings/errors'
import {sanitizeHandle} from '#/lib/strings/handles'
+import {matchXrpcError} from '#/lib/xrpc-error'
import {logger} from '#/logger'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {
@@ -49,6 +45,7 @@ import {Loader} from '#/components/Loader'
import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics'
+import {chat} from '#/lexicons'
import {ProfileBadges} from '../ProfileBadges'
export function GroupChatJoinDialog() {
@@ -147,33 +144,37 @@ function GroupChatJoinDialogContent({code}: {code?: string}) {
let errorMessage = l`Failed to join the group chat. Please try again.`
if (isNetworkError(error)) {
errorMessage = l`There was a problem with your internet connection, please try again`
- } else if (error instanceof ChatBskyGroupRequestJoin.ConvoLockedError) {
- errorMessage = l`This conversation is locked.`
- } else if (
- error instanceof ChatBskyGroupRequestJoin.FollowRequiredError
- ) {
- errorMessage = l`Only followers can join this group chat.`
- } else if (error instanceof ChatBskyGroupRequestJoin.InvalidCodeError) {
- errorMessage = l`Invalid group chat code.`
- } else if (
- error instanceof ChatBskyGroupRequestJoin.LinkDisabledError
- ) {
- errorMessage = l`This invite link has been disabled.`
- } else if (
- error instanceof ChatBskyGroupRequestJoin.MemberLimitReachedError
- ) {
- errorMessage = l`The member limit has been reached.`
- const preview = data?.joinLinkPreviews[0]
- if (
- ChatBskyGroupDefs.isJoinLinkPreviewView(preview) &&
- preview.convo?.id
- ) {
- ax.metric('groupchat:join:memberLimitReached', {
- convoId: preview.convo.id,
- })
+ } else {
+ switch (matchXrpcError(error, chat.bsky.group.requestJoin)) {
+ case 'ConvoLocked':
+ errorMessage = l`This conversation is locked.`
+ break
+ case 'FollowRequired':
+ errorMessage = l`Only followers can join this group chat.`
+ break
+ case 'InvalidCode':
+ errorMessage = l`Invalid group chat code.`
+ break
+ case 'LinkDisabled':
+ errorMessage = l`This invite link has been disabled.`
+ break
+ case 'MemberLimitReached': {
+ errorMessage = l`The member limit has been reached.`
+ const preview = data?.joinLinkPreviews[0]
+ if (
+ ChatBskyGroupDefs.isJoinLinkPreviewView(preview) &&
+ preview.convo?.id
+ ) {
+ ax.metric('groupchat:join:memberLimitReached', {
+ convoId: preview.convo.id,
+ })
+ }
+ break
+ }
+ case 'UserKicked':
+ errorMessage = l`You have been previously removed from this group and can’t join it using this link.`
+ break
}
- } else if (error instanceof ChatBskyGroupRequestJoin.UserKickedError) {
- errorMessage = l`You have been previously removed from this group and can’t join it using this link.`
}
Toast.show(errorMessage)
},
@@ -194,8 +195,8 @@ function GroupChatJoinDialogContent({code}: {code?: string}) {
if (isNetworkError(error)) {
errorMessage = l`There was a problem with your internet connection, please try again`
} else if (
- error instanceof
- ChatBskyGroupWithdrawJoinRequest.InvalidJoinRequestError
+ matchXrpcError(error, chat.bsky.group.withdrawJoinRequest) ===
+ 'InvalidJoinRequest'
) {
errorMessage = l`Invalid rescind request.`
}
diff --git a/src/components/moderation/BlockDialog.tsx b/src/components/moderation/BlockDialog.tsx
index 5ec9e4d9e4..d576e071d7 100644
--- a/src/components/moderation/BlockDialog.tsx
+++ b/src/components/moderation/BlockDialog.tsx
@@ -1,6 +1,6 @@
import {useState} from 'react'
import {View} from 'react-native'
-import {type ChatBskyConvoDefs, ChatBskyGroupRemoveMembers} from '@atproto/api'
+import {type ChatBskyConvoDefs} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro'
import {useQueryClient} from '@tanstack/react-query'
@@ -309,14 +309,15 @@ function MutualGroupChat({
let errorMessage = l`Could not remove member.`
if (isNetworkError(error)) {
errorMessage = l`A network error occurred. Please check your internet connection.`
- } else if (
- error instanceof ChatBskyGroupRemoveMembers.InvalidConvoError
- ) {
- errorMessage = l`Chat not found.`
- } else if (
- error instanceof ChatBskyGroupRemoveMembers.InsufficientRoleError
- ) {
- errorMessage = l`You must be a chat owner to remove a member.`
+ } else {
+ switch (matchXrpcError(error, chat.bsky.group.removeMembers)) {
+ case 'InvalidConvo':
+ errorMessage = l`Chat not found.`
+ break
+ case 'InsufficientRole':
+ errorMessage = l`You must be a chat owner to remove a member.`
+ break
+ }
}
Toast.show(errorMessage, {type: 'error'})
},
diff --git a/src/screens/Messages/JoinRequests.tsx b/src/screens/Messages/JoinRequests.tsx
index ffe3fb219e..d8d6ff9b06 100644
--- a/src/screens/Messages/JoinRequests.tsx
+++ b/src/screens/Messages/JoinRequests.tsx
@@ -1,10 +1,6 @@
import {useState} from 'react'
import {View} from 'react-native'
-import {
- ChatBskyGroupApproveJoinRequest,
- type ChatBskyGroupListJoinRequests,
- ChatBskyGroupRejectJoinRequest,
-} from '@atproto/api'
+import {type ChatBskyGroupListJoinRequests} from '@atproto/api'
import {Plural, Trans, useLingui} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native'
import {type InfiniteData, useQueryClient} from '@tanstack/react-query'
@@ -16,6 +12,7 @@ import {
type NativeStackScreenProps,
type NavigationProp,
} from '#/lib/routes/types'
+import {matchXrpcError} from '#/lib/xrpc-error'
import {logger} from '#/logger'
import {ConvoProvider, useConvo} from '#/state/messages/convo'
import {ConvoStatus} from '#/state/messages/convo/types'
@@ -43,6 +40,7 @@ import * as ProfileCard from '#/components/ProfileCard'
import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics'
+import {chat} from '#/lexicons'
import type * as bsky from '#/types/bsky'
import {InviteLinkDialog} from './components/InviteLinkDialog'
@@ -190,19 +188,18 @@ function JoinRequestsList({
let errorMessage = l`Failed to accept join request`
if (isNetworkError(error)) {
errorMessage = l`A network error occurred. Please check your internet connection.`
- } else if (
- error instanceof ChatBskyGroupApproveJoinRequest.InvalidConvoError
- ) {
- errorMessage = l`Conversation not found.`
- } else if (
- error instanceof ChatBskyGroupApproveJoinRequest.InsufficientRoleError
- ) {
- errorMessage = l`Only admins can accept join requests.`
- } else if (
- error instanceof
- ChatBskyGroupApproveJoinRequest.MemberLimitReachedError
- ) {
- errorMessage = l`The member limit has been reached.`
+ } else {
+ switch (matchXrpcError(error, chat.bsky.group.approveJoinRequest)) {
+ case 'InvalidConvo':
+ errorMessage = l`Conversation not found.`
+ break
+ case 'InsufficientRole':
+ errorMessage = l`Only admins can accept join requests.`
+ break
+ case 'MemberLimitReached':
+ errorMessage = l`The member limit has been reached.`
+ break
+ }
}
Toast.show(errorMessage, {type: 'error'})
},
@@ -223,14 +220,15 @@ function JoinRequestsList({
let errorMessage = l`Failed to reject join request`
if (isNetworkError(error)) {
errorMessage = l`A network error occurred. Please check your internet connection.`
- } else if (
- error instanceof ChatBskyGroupRejectJoinRequest.InvalidConvoError
- ) {
- errorMessage = l`Conversation not found.`
- } else if (
- error instanceof ChatBskyGroupRejectJoinRequest.InsufficientRoleError
- ) {
- errorMessage = l`Only admins can reject join requests.`
+ } else {
+ switch (matchXrpcError(error, chat.bsky.group.rejectJoinRequest)) {
+ case 'InvalidConvo':
+ errorMessage = l`Conversation not found.`
+ break
+ case 'InsufficientRole':
+ errorMessage = l`Only admins can reject join requests.`
+ break
+ }
}
Toast.show(errorMessage, {type: 'error'})
},
diff --git a/src/screens/Messages/components/OutgoingRequestListItem.tsx b/src/screens/Messages/components/OutgoingRequestListItem.tsx
index b5dcdf08df..eb62f24b81 100644
--- a/src/screens/Messages/components/OutgoingRequestListItem.tsx
+++ b/src/screens/Messages/components/OutgoingRequestListItem.tsx
@@ -1,11 +1,9 @@
import {View} from 'react-native'
-import {
- type ChatBskyGroupDefs,
- ChatBskyGroupWithdrawJoinRequest,
-} from '@atproto/api'
+import {type ChatBskyGroupDefs} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro'
import {isNetworkError} from '#/lib/strings/errors'
+import {matchXrpcError} from '#/lib/xrpc-error'
import {useWithdrawJoinGroupChatRequest} from '#/state/queries/messages/withdraw-join-group-chat'
import {TimeElapsed} from '#/view/com/util/TimeElapsed'
import {atoms as a, useTheme, web} from '#/alf'
@@ -14,6 +12,7 @@ import {createStaticClick, Link} from '#/components/Link'
import * as Prompt from '#/components/Prompt'
import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography'
+import {chat} from '#/lexicons'
export function OutgoingRequestListItem({
convo: convoView,
@@ -35,8 +34,8 @@ export function OutgoingRequestListItem({
if (isNetworkError(error)) {
errorMessage = l`There was a problem with your internet connection, please try again`
} else if (
- error instanceof
- ChatBskyGroupWithdrawJoinRequest.InvalidJoinRequestError
+ matchXrpcError(error, chat.bsky.group.withdrawJoinRequest) ===
+ 'InvalidJoinRequest'
) {
errorMessage = l`Invalid rescind request.`
}
diff --git a/src/state/queries/join-links.ts b/src/state/queries/join-links.ts
index ca1db05558..e5dbbc9bed 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,39 @@ 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.
+ *
+ * Unlike the public appview client this does NOT wrap `networkAwareFetch`,
+ * matching the plain-fetch behavior of the ad-hoc agent it replaces: a
+ * logged-out link resolution failing says nothing useful about the session's
+ * reachability, so it should not move the app-wide network signal.
+ */
+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 +206,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 +226,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 +244,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 +259,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 +269,6 @@ export function useGetJoinLinkPreview() {
return undefined
}
},
- [agent, queryClient],
+ [client, queryClient],
)
}
diff --git a/src/state/queries/messages/add-group-members.ts b/src/state/queries/messages/add-group-members.ts
index b5ddacbbff..bf4f8a8f4e 100644
--- a/src/state/queries/messages/add-group-members.ts
+++ b/src/state/queries/messages/add-group-members.ts
@@ -4,16 +4,17 @@ import {
type ChatBskyConvoListConvos,
type ChatBskyGroupAddMembers,
} 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 {useProfileQuery} from '#/state/queries/profile'
-import {useAgent, useSession} from '#/state/session'
+import {useChatClient, useSession} from '#/state/session'
+import {chat} from '#/lexicons'
import type * as bsky from '#/types/bsky'
import {RQKEY as CONVO_KEY} from './conversation'
import {RQKEY_ROOT as CONVO_LIST_KEY} from './list-conversations'
@@ -30,7 +31,7 @@ export function useAddGroupMembers(
},
) {
const queryClient = useQueryClient()
- const agent = useAgent()
+ const client = useChatClient()
const {currentAccount} = useSession()
const {data: myProfile} = useProfileQuery({did: currentAccount?.did})
@@ -42,11 +43,11 @@ export function useAddGroupMembers(
profiles: bsky.profile.AnyProfileView[]
}) => {
if (!convoId) throw new Error('No convoId provided')
- const {data} = await agent.chat.bsky.group.addMembers(
- {convoId, members},
- {headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
- )
- return data
+ return await client.call(chat.bsky.group.addMembers, {
+ convoId,
+ // callers pass already-resolved actor dids
+ members: members as DidString[],
+ })
},
onMutate: ({profiles}) => {
if (!convoId) return
diff --git a/src/state/queries/messages/create-group-chat.ts b/src/state/queries/messages/create-group-chat.ts
index 9f8aadc7d0..4a78a2be6a 100644
--- a/src/state/queries/messages/create-group-chat.ts
+++ b/src/state/queries/messages/create-group-chat.ts
@@ -1,9 +1,10 @@
import {type ChatBskyGroupCreateGroup} from '@atproto/api'
+import {type DidString} from '@atproto/syntax'
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 {precacheConvoQuery} from './conversation'
export function useCreateGroupChat({
@@ -14,16 +15,15 @@ export function useCreateGroupChat({
onError?: (error: Error) => void
}) {
const queryClient = useQueryClient()
- const agent = useAgent()
+ const client = useChatClient()
return useMutation({
mutationFn: async ({name, members}: {name: string; members: string[]}) => {
- const {data} = await agent.chat.bsky.group.createGroup(
- {name, members},
- {headers: DM_SERVICE_HEADERS},
- )
-
- return data
+ return await client.call(chat.bsky.group.createGroup, {
+ name,
+ // callers pass already-resolved actor dids
+ members: members as DidString[],
+ })
},
onSuccess: data => {
precacheConvoQuery(queryClient, data.convo)
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-group-chat-name.ts b/src/state/queries/messages/edit-group-chat-name.ts
index ef2a72f4ec..ecc73fd3d3 100644
--- a/src/state/queries/messages/edit-group-chat-name.ts
+++ b/src/state/queries/messages/edit-group-chat-name.ts
@@ -1,9 +1,9 @@
import {ChatBskyConvoDefs, type ChatBskyGroupEditGroup} 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,
@@ -20,16 +20,15 @@ export function useEditGroupChatName(
},
) {
const queryClient = useQueryClient()
- const agent = useAgent()
+ const client = useChatClient()
return useMutation({
mutationFn: async ({name: groupName}: {name: string}) => {
if (!convoId) throw new Error('No convoId provided')
- const {data} = await agent.chat.bsky.group.editGroup(
- {convoId, name: groupName},
- {headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
- )
- return data
+ return await client.call(chat.bsky.group.editGroup, {
+ convoId,
+ name: groupName,
+ })
},
onMutate: ({name: groupName}) => {
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/list-mutual-groups.ts b/src/state/queries/messages/list-mutual-groups.ts
index 9ff4a1da09..65743a45e7 100644
--- a/src/state/queries/messages/list-mutual-groups.ts
+++ b/src/state/queries/messages/list-mutual-groups.ts
@@ -1,8 +1,9 @@
+import {type DidString} from '@atproto/syntax'
import {useInfiniteQuery} from '@tanstack/react-query'
-import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {createQueryKey} from '#/state/queries/util'
-import {useAgent} from '#/state/session'
+import {useChatClient} from '#/state/session'
+import {chat} from '#/lexicons'
const listMutualGroupsQueryKeyRoot = 'list-mutual-groups'
@@ -18,7 +19,7 @@ export function useListMutualGroupsQuery({
enabled?: boolean
limit?: number
}) {
- const agent = useAgent()
+ const client = useChatClient()
const isEnabled = enabled !== false && !!subject
return useInfiniteQuery({
@@ -27,11 +28,12 @@ export function useListMutualGroupsQuery({
enabled: isEnabled,
queryKey: createListMutualGroupsQueryKey({subject: subject ?? ''}),
queryFn: async ({pageParam}) => {
- const {data} = await agent.chat.bsky.group.listMutualGroups(
- {subject: subject!, cursor: pageParam, limit},
- {headers: DM_SERVICE_HEADERS},
- )
- return data
+ return await client.call(chat.bsky.group.listMutualGroups, {
+ // guarded by `enabled`, and callers pass a resolved actor did
+ subject: subject as DidString,
+ cursor: pageParam,
+ limit,
+ })
},
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/remove-from-group.ts b/src/state/queries/messages/remove-from-group.ts
index 95f246be5a..10437ad7e9 100644
--- a/src/state/queries/messages/remove-from-group.ts
+++ b/src/state/queries/messages/remove-from-group.ts
@@ -4,15 +4,16 @@ import {
type ChatBskyConvoListConvos,
type ChatBskyGroupRemoveMembers,
} 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 {RQKEY as CONVO_KEY} from './conversation'
import {RQKEY_ROOT as CONVO_LIST_KEY} from './list-conversations'
import {listConvoMembersQueryKey} from './list-convo-members'
@@ -28,16 +29,16 @@ export function useRemoveFromGroupChat(
},
) {
const queryClient = useQueryClient()
- const agent = useAgent()
+ const client = useChatClient()
return useMutation({
mutationFn: async ({members}: {members: string[]}) => {
if (!convoId) throw new Error('No convoId provided')
- const {data} = await agent.chat.bsky.group.removeMembers(
- {convoId, members},
- {headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
- )
- return data
+ return await client.call(chat.bsky.group.removeMembers, {
+ convoId,
+ // callers pass already-resolved actor dids
+ members: members as DidString[],
+ })
},
onMutate: ({members}) => {
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(