From 0e8e0b2ab4755caebc5be7133f90db950dcdb9b9 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Thu, 13 Aug 2026 22:26:15 +0300 Subject: [PATCH] [SDK] Migrate chat convo queries to the chat client (#11357) Co-authored-by: Claude Fable 5 Co-authored-by: Matthieu Sieben --- src/components/dms/LeaveConvoPrompt.tsx | 18 +++++--- src/components/dms/dialogs/NewChatDialog.tsx | 46 +++++++++---------- .../dms/dialogs/ShareViaChatDialog.tsx | 46 +++++++++---------- src/components/moderation/BlockDialog.tsx | 23 +++++----- .../Messages/ConversationSettings/index.tsx | 6 ++- .../Messages/components/ChatLocked.tsx | 8 +++- .../queries/messages/accept-conversation.ts | 13 ++---- src/state/queries/messages/conversation.ts | 27 ++++------- .../messages/get-convo-availability.ts | 20 ++++---- .../queries/messages/get-convo-for-members.ts | 17 ++++--- src/state/queries/messages/get-status.ts | 15 +++--- .../queries/messages/get-unread-counts.ts | 14 +++--- .../queries/messages/leave-conversation.ts | 13 ++---- .../messages/list-conversation-requests.tsx | 15 +++--- .../queries/messages/list-conversations.tsx | 26 +++++------ .../queries/messages/list-convo-members.ts | 26 +++++++---- .../queries/messages/lock-conversation.ts | 18 ++------ .../queries/messages/mute-conversation.ts | 18 ++------ src/state/queries/messages/update-all-read.ts | 13 ++---- 19 files changed, 171 insertions(+), 211 deletions(-) diff --git a/src/components/dms/LeaveConvoPrompt.tsx b/src/components/dms/LeaveConvoPrompt.tsx index 5e9943e53f..a69dfcf845 100644 --- a/src/components/dms/LeaveConvoPrompt.tsx +++ b/src/components/dms/LeaveConvoPrompt.tsx @@ -1,14 +1,15 @@ -import {ChatBskyConvoLeaveConvo} from '@atproto/api' import {useLingui} from '@lingui/react/macro' import {StackActions, useNavigation} from '@react-navigation/native' import {type NavigationProp} from '#/lib/routes/types' import {isNetworkError} from '#/lib/strings/errors' +import {matchXrpcError} from '#/lib/xrpc-error' import {useLeaveConvo} from '#/state/queries/messages/leave-conversation' import {type DialogOuterProps} from '#/components/Dialog' import * as Prompt from '#/components/Prompt' import * as Toast from '#/components/Toast' import {IS_NATIVE} from '#/env' +import {chat} from '#/lexicons' export function LeaveConvoPrompt({ control, @@ -36,12 +37,15 @@ export function LeaveConvoPrompt({ let errorMessage = l`Could not leave chat` if (isNetworkError(error)) { errorMessage = l`A network error occurred. Please check your internet connection.` - } else if (error instanceof ChatBskyConvoLeaveConvo.InvalidConvoError) { - errorMessage = l`Conversation not found.` - } else if ( - error instanceof ChatBskyConvoLeaveConvo.OwnerCannotLeaveError - ) { - errorMessage = l`Owner must lock the group before leaving.` + } else { + switch (matchXrpcError(error, chat.bsky.convo.leaveConvo)) { + case 'InvalidConvo': + errorMessage = l`Conversation not found.` + break + case 'OwnerCannotLeave': + errorMessage = l`Owner must lock the group before leaving.` + break + } } Toast.show(errorMessage, {type: 'error'}) }, diff --git a/src/components/dms/dialogs/NewChatDialog.tsx b/src/components/dms/dialogs/NewChatDialog.tsx index fd9eaa8880..4cca14bd0a 100644 --- a/src/components/dms/dialogs/NewChatDialog.tsx +++ b/src/components/dms/dialogs/NewChatDialog.tsx @@ -1,12 +1,10 @@ import {useCallback} from 'react' -import { - ChatBskyConvoGetConvoForMembers, - ChatBskyGroupCreateGroup, -} from '@atproto/api' +import {ChatBskyGroupCreateGroup} from '@atproto/api' import {Trans, useLingui} from '@lingui/react/macro' import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' import {isNetworkError} from '#/lib/strings/errors' +import {matchXrpcError} from '#/lib/xrpc-error' import {logger} from '#/logger' import {useCreateGroupChat} from '#/state/queries/messages/create-group-chat' import {useGetConvoForMembers} from '#/state/queries/messages/get-convo-for-members' @@ -19,6 +17,7 @@ import {InitiateChatFlow} from '#/components/dms/InitiateChatFlow' import {MessagePlus_Stroke2_Corner0_Rounded as NewChatIcon} from '#/components/icons/Message' import * as Toast from '#/components/Toast' import {useAnalytics} from '#/analytics' +import {chat} from '#/lexicons' export function NewChat({ control, @@ -54,27 +53,24 @@ export function NewChat({ let errorMessage = l`An issue occurred starting the chat, please try again.` if (isNetworkError(error)) { errorMessage = l`A network error occurred. Please check your internet connection.` - } else if ( - error instanceof ChatBskyConvoGetConvoForMembers.AccountSuspendedError - ) { - errorMessage = l`Suspended accounts cannot participate in chat.` - } else if ( - error instanceof ChatBskyConvoGetConvoForMembers.BlockedActorError - ) { - errorMessage = l`This user has blocked you and cannot be messaged.` - } else if ( - error instanceof ChatBskyConvoGetConvoForMembers.MessagesDisabledError - ) { - errorMessage = l`This user has disabled chat and cannot be messaged.` - } else if ( - error instanceof - ChatBskyConvoGetConvoForMembers.NotFollowedBySenderError - ) { - errorMessage = l`Chat recipient is not followed by the sender.` - } else if ( - error instanceof ChatBskyConvoGetConvoForMembers.RecipientNotFoundError - ) { - errorMessage = l`Unable to find the selected recipient.` + } else { + switch (matchXrpcError(error, chat.bsky.convo.getConvoForMembers)) { + case 'AccountSuspended': + errorMessage = l`Suspended accounts cannot participate in chat.` + break + case 'BlockedActor': + errorMessage = l`This user has blocked you and cannot be messaged.` + break + case 'MessagesDisabled': + errorMessage = l`This user has disabled chat and cannot be messaged.` + break + case 'NotFollowedBySender': + errorMessage = l`Chat recipient is not followed by the sender.` + break + case 'RecipientNotFound': + errorMessage = l`Unable to find the selected recipient.` + break + } } Toast.show(errorMessage, { type: 'error', diff --git a/src/components/dms/dialogs/ShareViaChatDialog.tsx b/src/components/dms/dialogs/ShareViaChatDialog.tsx index 0463cc694d..6dfb6145f7 100644 --- a/src/components/dms/dialogs/ShareViaChatDialog.tsx +++ b/src/components/dms/dialogs/ShareViaChatDialog.tsx @@ -1,11 +1,9 @@ import {useCallback, useState} from 'react' -import { - ChatBskyConvoGetConvoForMembers, - ChatBskyGroupCreateGroup, -} from '@atproto/api' +import {ChatBskyGroupCreateGroup} from '@atproto/api' import {useLingui} from '@lingui/react/macro' import {isNetworkError} from '#/lib/strings/errors' +import {matchXrpcError} from '#/lib/xrpc-error' import {logger} from '#/logger' import {useCreateGroupChat} from '#/state/queries/messages/create-group-chat' import {useGetConvoForMembers} from '#/state/queries/messages/get-convo-for-members' @@ -14,6 +12,7 @@ import {SearchablePeopleList} from '#/components/dialogs/SearchablePeopleList' import {InitiateChatFlow} from '#/components/dms/InitiateChatFlow' import * as Toast from '#/components/Toast' import {useAnalytics} from '#/analytics' +import {chat} from '#/lexicons' export function SendViaChatDialog({ control, @@ -69,27 +68,24 @@ function SendViaChatDialogInner({ let errorMessage = l`An issue occurred starting the chat, please try again.` if (isNetworkError(error)) { errorMessage = l`A network error occurred. Please check your internet connection.` - } else if ( - error instanceof ChatBskyConvoGetConvoForMembers.AccountSuspendedError - ) { - errorMessage = l`Suspended accounts cannot participate in chat.` - } else if ( - error instanceof ChatBskyConvoGetConvoForMembers.BlockedActorError - ) { - errorMessage = l`This user has blocked you and cannot be messaged.` - } else if ( - error instanceof ChatBskyConvoGetConvoForMembers.MessagesDisabledError - ) { - errorMessage = l`This user has disabled chat and cannot be messaged.` - } else if ( - error instanceof - ChatBskyConvoGetConvoForMembers.NotFollowedBySenderError - ) { - errorMessage = l`Chat recipient is not followed by the sender.` - } else if ( - error instanceof ChatBskyConvoGetConvoForMembers.RecipientNotFoundError - ) { - errorMessage = l`Unable to find the selected recipient.` + } else { + switch (matchXrpcError(error, chat.bsky.convo.getConvoForMembers)) { + case 'AccountSuspended': + errorMessage = l`Suspended accounts cannot participate in chat.` + break + case 'BlockedActor': + errorMessage = l`This user has blocked you and cannot be messaged.` + break + case 'MessagesDisabled': + errorMessage = l`This user has disabled chat and cannot be messaged.` + break + case 'NotFollowedBySender': + errorMessage = l`Chat recipient is not followed by the sender.` + break + case 'RecipientNotFound': + errorMessage = l`Unable to find the selected recipient.` + break + } } Toast.show(errorMessage, { type: 'error', diff --git a/src/components/moderation/BlockDialog.tsx b/src/components/moderation/BlockDialog.tsx index a575cd7433..5ec9e4d9e4 100644 --- a/src/components/moderation/BlockDialog.tsx +++ b/src/components/moderation/BlockDialog.tsx @@ -1,14 +1,11 @@ import {useState} from 'react' import {View} from 'react-native' -import { - type ChatBskyConvoDefs, - ChatBskyConvoLeaveConvo, - ChatBskyGroupRemoveMembers, -} from '@atproto/api' +import {type ChatBskyConvoDefs, ChatBskyGroupRemoveMembers} from '@atproto/api' import {Trans, useLingui} from '@lingui/react/macro' import {useQueryClient} from '@tanstack/react-query' import {isNetworkError} from '#/lib/strings/errors' +import {matchXrpcError} from '#/lib/xrpc-error' import {logger} from '#/logger' import {type Shadow} from '#/state/cache/types' import {useLeaveConvo} from '#/state/queries/messages/leave-conversation' @@ -27,6 +24,7 @@ import {parseConvoView} from '#/components/dms/util' import {Loader} from '#/components/Loader' import * as Toast from '#/components/Toast' import {Text} from '#/components/Typography' +import {chat} from '#/lexicons' import {type AnyProfileView} from '#/types/bsky/profile' type Item = ChatBskyConvoDefs.ConvoView @@ -282,12 +280,15 @@ function MutualGroupChat({ let errorMessage = l`Could not leave chat.` if (isNetworkError(error)) { errorMessage = l`A network error occurred. Please check your internet connection.` - } else if (error instanceof ChatBskyConvoLeaveConvo.InvalidConvoError) { - errorMessage = l`Chat not found.` - } else if ( - error instanceof ChatBskyConvoLeaveConvo.OwnerCannotLeaveError - ) { - errorMessage = l`Chat owners cannot leave a group chat.` + } else { + switch (matchXrpcError(error, chat.bsky.convo.leaveConvo)) { + case 'InvalidConvo': + errorMessage = l`Chat not found.` + break + case 'OwnerCannotLeave': + errorMessage = l`Chat owners cannot leave a group chat.` + break + } } Toast.show(errorMessage, {type: 'error'}) }, diff --git a/src/screens/Messages/ConversationSettings/index.tsx b/src/screens/Messages/ConversationSettings/index.tsx index 5576f28674..5f76a3f565 100644 --- a/src/screens/Messages/ConversationSettings/index.tsx +++ b/src/screens/Messages/ConversationSettings/index.tsx @@ -3,7 +3,6 @@ import {Pressable, View} from 'react-native' import { ChatBskyActorDefs, ChatBskyConvoDefs, - ChatBskyConvoUnlockConvo, type ModerationOpts, } from '@atproto/api' import {Trans, useLingui} from '@lingui/react/macro' @@ -19,6 +18,7 @@ import { type NativeStackScreenProps, type NavigationProp, } from '#/lib/routes/types' +import {matchXrpcError} from '#/lib/xrpc-error' import {logger} from '#/logger' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useConvoQuery} from '#/state/queries/messages/conversation' @@ -59,6 +59,7 @@ import * as Toast from '#/components/Toast' import {Text} from '#/components/Typography' import {useAnalytics} from '#/analytics' import {IS_WEB} from '#/env' +import {chat} from '#/lexicons' import * as bsky from '#/types/bsky' import {InviteLinkDialog} from '../components/InviteLinkDialog' import {AddMembersLink} from './AddMembersLink' @@ -432,7 +433,8 @@ function SettingsHeader({ logger.error('Failed to lock group chat', {message: e}) Toast.show(l`Failed to lock group chat`, {type: 'error'}) } else if ( - e instanceof ChatBskyConvoUnlockConvo.ConvoLockedByModerationError + matchXrpcError(e, chat.bsky.convo.unlockConvo) === + 'ConvoLockedByModeration' ) { Toast.show(l`This chat is locked by a moderation action`, { type: 'error', diff --git a/src/screens/Messages/components/ChatLocked.tsx b/src/screens/Messages/components/ChatLocked.tsx index 8718667e9c..c9dcd93c3d 100644 --- a/src/screens/Messages/components/ChatLocked.tsx +++ b/src/screens/Messages/components/ChatLocked.tsx @@ -1,10 +1,10 @@ import {Pressable} from 'react-native' -import {ChatBskyConvoUnlockConvo} from '@atproto/api' import {Trans, useLingui} from '@lingui/react/macro' import {useNavigation} from '@react-navigation/native' import {HITSLOP_10} from '#/lib/constants' import {type NavigationProp} from '#/lib/routes/types' +import {matchXrpcError} from '#/lib/xrpc-error' import {logger} from '#/logger' import {useLeaveConvo} from '#/state/queries/messages/leave-conversation' import {useLockConvo} from '#/state/queries/messages/lock-conversation' @@ -15,6 +15,7 @@ import {Lock_Stroke2_Corner0_Rounded as LockIcon} from '#/components/icons/Lock' import * as Prompt from '#/components/Prompt' import * as Toast from '#/components/Toast' import {Text} from '#/components/Typography' +import {chat} from '#/lexicons' import {LeaveChatPrompt} from '../ConversationSettings/prompts' import {ChatFooter} from './ChatFooter' @@ -41,7 +42,10 @@ export function ChatLocked({ Toast.show(l({message: 'Group chat unlocked', context: 'toast'})) }, onError: e => { - if (e instanceof ChatBskyConvoUnlockConvo.ConvoLockedByModerationError) { + if ( + matchXrpcError(e, chat.bsky.convo.unlockConvo) === + 'ConvoLockedByModeration' + ) { Toast.show(l`This chat is locked by a moderation action`, { type: 'error', }) diff --git a/src/state/queries/messages/accept-conversation.ts b/src/state/queries/messages/accept-conversation.ts index 668c7decc3..391f3e49f5 100644 --- a/src/state/queries/messages/accept-conversation.ts +++ b/src/state/queries/messages/accept-conversation.ts @@ -4,9 +4,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 { type ConvoRequestListQueryData, optimisticDelete as optimisticDeleteRequest, @@ -34,16 +34,11 @@ export function useAcceptConversation( }, ) { const queryClient = useQueryClient() - const agent = useAgent() + const client = useChatClient() return useMutation({ mutationFn: async () => { - const {data} = await agent.chat.bsky.convo.acceptConvo( - {convoId}, - {headers: DM_SERVICE_HEADERS}, - ) - - return data + return await client.call(chat.bsky.convo.acceptConvo, {convoId}) }, onMutate: () => { // snapshot every convo-list cache up front so onError can restore them diff --git a/src/state/queries/messages/conversation.ts b/src/state/queries/messages/conversation.ts index 1b7699c0ef..c0bf8f3551 100644 --- a/src/state/queries/messages/conversation.ts +++ b/src/state/queries/messages/conversation.ts @@ -11,10 +11,10 @@ import { useQueryClient, } from '@tanstack/react-query' -import {DM_SERVICE_HEADERS} from '#/lib/constants' import {STALE} from '#/state/queries' import {useOnMarkAsRead} from '#/state/queries/messages/list-conversations' -import {useAgent} from '#/state/session' +import {useChatClient} from '#/state/session' +import {chat} from '#/lexicons' import { RQKEY_PARTIAL as UNREAD_COUNTS_PARTIAL_KEY, UNREAD_ACCEPTED_CAP, @@ -30,15 +30,12 @@ export const RQKEY_ROOT = 'convo' export const RQKEY = (convoId: string) => [RQKEY_ROOT, convoId] export function useConvoQuery({convoId}: {convoId: string}) { - const agent = useAgent() + const client = useChatClient() return useQuery({ queryKey: RQKEY(convoId), queryFn: async () => { - const {data} = await agent.chat.bsky.convo.getConvo( - {convoId}, - {headers: DM_SERVICE_HEADERS}, - ) + const data = await client.call(chat.bsky.convo.getConvo, {convoId}) return data.convo }, staleTime: STALE.INFINITY, @@ -55,7 +52,7 @@ export function precacheConvoQuery( export function useMarkAsReadMutation() { const optimisticUpdate = useOnMarkAsRead() const queryClient = useQueryClient() - const agent = useAgent() + const client = useChatClient() return useMutation({ mutationFn: async ({ @@ -67,16 +64,10 @@ export function useMarkAsReadMutation() { }) => { if (!convoId) throw new Error('No convoId provided') - await agent.chat.bsky.convo.updateRead( - { - convoId, - messageId, - }, - { - encoding: 'application/json', - headers: DM_SERVICE_HEADERS, - }, - ) + await client.call(chat.bsky.convo.updateRead, { + convoId, + messageId, + }) }, onMutate({convoId}) { if (!convoId) throw new Error('No convoId provided') diff --git a/src/state/queries/messages/get-convo-availability.ts b/src/state/queries/messages/get-convo-availability.ts index b73efe7953..31b4bb4166 100644 --- a/src/state/queries/messages/get-convo-availability.ts +++ b/src/state/queries/messages/get-convo-availability.ts @@ -1,7 +1,8 @@ +import {type DidString} from '@atproto/syntax' import {useQuery} from '@tanstack/react-query' -import {DM_SERVICE_HEADERS} from '#/lib/constants' -import {useAgent} from '#/state/session' +import {useChatClient, useSession} from '#/state/session' +import {chat} from '#/lexicons' import {STALE} from '..' const RQKEY_ROOT = 'convo-availability' @@ -11,19 +12,18 @@ export function useGetConvoAvailabilityQuery( did: string, {enabled = true}: {enabled?: boolean} = {}, ) { - const agent = useAgent() + const client = useChatClient() + const {hasSession} = useSession() return useQuery({ queryKey: RQKEY(did), queryFn: async () => { - const {data} = await agent.chat.bsky.convo.getConvoAvailability( - {members: [did]}, - {headers: DM_SERVICE_HEADERS}, - ) - - return data + return client.call(chat.bsky.convo.getConvoAvailability, { + // callers pass an already-resolved actor did + members: [did as DidString], + }) }, staleTime: STALE.INFINITY, - enabled, + enabled: enabled && hasSession, }) } diff --git a/src/state/queries/messages/get-convo-for-members.ts b/src/state/queries/messages/get-convo-for-members.ts index 58c1ab524a..20f5380126 100644 --- a/src/state/queries/messages/get-convo-for-members.ts +++ b/src/state/queries/messages/get-convo-for-members.ts @@ -1,9 +1,10 @@ import {type ChatBskyConvoGetConvoForMembers} 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 useGetConvoForMembers({ @@ -14,16 +15,14 @@ export function useGetConvoForMembers({ onError?: (error: Error) => void }) { const queryClient = useQueryClient() - const agent = useAgent() + const client = useChatClient() return useMutation({ mutationFn: async (members: string[]) => { - const {data} = await agent.chat.bsky.convo.getConvoForMembers( - {members: members}, - {headers: DM_SERVICE_HEADERS}, - ) - - return data + return await client.call(chat.bsky.convo.getConvoForMembers, { + // callers pass already-resolved actor dids + members: members as DidString[], + }) }, onSuccess: data => { precacheConvoQuery(queryClient, data.convo) diff --git a/src/state/queries/messages/get-status.ts b/src/state/queries/messages/get-status.ts index 89625dca24..465d442cb6 100644 --- a/src/state/queries/messages/get-status.ts +++ b/src/state/queries/messages/get-status.ts @@ -1,7 +1,7 @@ import {useQuery} from '@tanstack/react-query' -import {DM_SERVICE_HEADERS} from '#/lib/constants' -import {useAgent} from '#/state/session' +import {useChatClient, useSession} from '#/state/session' +import {chat} from '#/lexicons' import {STALE} from '..' import {createQueryKey} from '../util' @@ -9,19 +9,16 @@ const chatActorStatusQueryKey = () => createQueryKey('chat-actor-status', {}, {persistedVersion: 1}) export function useChatActorStatusQuery() { - const agent = useAgent() + const client = useChatClient() + const {hasSession} = useSession() return useQuery({ gcTime: STALE.INFINITY, staleTime: STALE.SECONDS.FIFTEEN, queryKey: chatActorStatusQueryKey(), queryFn: async () => { - const {data} = await agent.chat.bsky.actor.getStatus( - {}, - {headers: DM_SERVICE_HEADERS}, - ) - - return data + return await client.call(chat.bsky.actor.getStatus) }, + enabled: hasSession, }) } diff --git a/src/state/queries/messages/get-unread-counts.ts b/src/state/queries/messages/get-unread-counts.ts index 8c276d67f7..5ec532d6ed 100644 --- a/src/state/queries/messages/get-unread-counts.ts +++ b/src/state/queries/messages/get-unread-counts.ts @@ -1,8 +1,8 @@ import {useQuery} from '@tanstack/react-query' -import {DM_SERVICE_HEADERS} from '#/lib/constants' -import {useAgent, useSession} from '#/state/session' +import {useChatClient, useSession} from '#/state/session' import {useAgeAssurance} from '#/ageAssurance' +import {chat} from '#/lexicons' import {STALE} from '..' const RQKEY_ROOT = 'convo-unread-counts' @@ -18,7 +18,7 @@ export const UNREAD_ACCEPTED_CAP = 100 export const UNREAD_REQUEST_CAP = 100 export function useUnreadCountsQuery() { - const agent = useAgent() + const client = useChatClient() const {hasSession} = useSession() const aa = useAgeAssurance() const includeGroupChats = !aa.flags.groupChatDisabled @@ -26,11 +26,9 @@ export function useUnreadCountsQuery() { return useQuery({ queryKey: RQKEY(includeGroupChats), queryFn: async () => { - const {data} = await agent.chat.bsky.convo.getUnreadCounts( - {includeGroupChats}, - {headers: DM_SERVICE_HEADERS}, - ) - return data + return await client.call(chat.bsky.convo.getUnreadCounts, { + includeGroupChats, + }) }, staleTime: STALE.SECONDS.FIFTEEN, enabled: hasSession, diff --git a/src/state/queries/messages/leave-conversation.ts b/src/state/queries/messages/leave-conversation.ts index 51b51bde80..72379d8b30 100644 --- a/src/state/queries/messages/leave-conversation.ts +++ b/src/state/queries/messages/leave-conversation.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 {invalidateJoinLinkPreviewsForConvo} from '#/state/queries/join-links' -import {useAgent} from '#/state/session' +import {useChatClient} from '#/state/session' +import {chat} from '#/lexicons' import { type ConvoRequestListQueryData, optimisticDelete as optimisticDeleteRequest, @@ -38,19 +38,14 @@ export function useLeaveConvo( }, ) { const queryClient = useQueryClient() - const agent = useAgent() + const client = useChatClient() return useMutation({ mutationKey: RQKEY(convoId), mutationFn: async () => { if (!convoId) throw new Error('No convoId provided') - const {data} = await agent.chat.bsky.convo.leaveConvo( - {convoId}, - {headers: DM_SERVICE_HEADERS, encoding: 'application/json'}, - ) - - return data + return await client.call(chat.bsky.convo.leaveConvo, {convoId}) }, onMutate: () => { const prevConvoListQueries = diff --git a/src/state/queries/messages/list-conversation-requests.tsx b/src/state/queries/messages/list-conversation-requests.tsx index 10023db4eb..38ae8e6c3d 100644 --- a/src/state/queries/messages/list-conversation-requests.tsx +++ b/src/state/queries/messages/list-conversation-requests.tsx @@ -9,8 +9,8 @@ import { useInfiniteQuery, } from '@tanstack/react-query' -import {DM_SERVICE_HEADERS} from '#/lib/constants' -import {useAgent} from '#/state/session' +import {useChatClient} from '#/state/session' +import {chat} from '#/lexicons' const DEFAULT_LIMIT = 10 @@ -26,17 +26,16 @@ export function useListConvoRequests({ enabled?: boolean limit?: number } = {}) { - const agent = useAgent() + const client = useChatClient() return useInfiniteQuery({ enabled, queryKey: RQKEY(limit), queryFn: async ({pageParam}) => { - const {data} = await agent.chat.bsky.convo.listConvoRequests( - {limit, cursor: pageParam}, - {headers: DM_SERVICE_HEADERS}, - ) - return data + return await client.call(chat.bsky.convo.listConvoRequests, { + limit, + cursor: pageParam, + }) }, initialPageParam: undefined as RQPageParam, getNextPageParam: lastPage => lastPage.cursor, diff --git a/src/state/queries/messages/list-conversations.tsx b/src/state/queries/messages/list-conversations.tsx index 1f73c720bd..6d81a96ecc 100644 --- a/src/state/queries/messages/list-conversations.tsx +++ b/src/state/queries/messages/list-conversations.tsx @@ -14,11 +14,11 @@ import { } from '@tanstack/react-query' import throttle from 'lodash.throttle' -import {DM_SERVICE_HEADERS} from '#/lib/constants' import {useCurrentConvoId} from '#/state/messages/current-convo-id' import {useMessagesEventBus} from '#/state/messages/events' import {invalidateJoinLinkPreviewsForConvo} from '#/state/queries/join-links' -import {useAgent, useSession} from '#/state/session' +import {useChatClient, useSession} from '#/state/session' +import {chat} from '#/lexicons' import * as bsky from '#/types/bsky' import {RQKEY as CONVO_KEY} from './conversation' import { @@ -119,24 +119,20 @@ export function useListConvosQuery({ limit?: number lockStatus?: 'unlocked' | 'locked' | 'locked-permanently' } = {}) { - const agent = useAgent() + const client = useChatClient() return useInfiniteQuery({ enabled, queryKey: RQKEY(status ?? 'all', readState, kind, lockStatus, limit), queryFn: async ({pageParam}) => { - const {data} = await agent.chat.bsky.convo.listConvos( - { - limit, - cursor: pageParam, - readState: readState === 'unread' ? 'unread' : undefined, - kind: kind === 'all' ? undefined : kind, - lockStatus, - status, - }, - {headers: DM_SERVICE_HEADERS}, - ) - return data + return await client.call(chat.bsky.convo.listConvos, { + limit, + cursor: pageParam, + readState: readState === 'unread' ? 'unread' : undefined, + kind: kind === 'all' ? undefined : kind, + lockStatus, + status, + }) }, initialPageParam: undefined as RQPageParam, getNextPageParam: lastPage => lastPage.cursor, diff --git a/src/state/queries/messages/list-convo-members.ts b/src/state/queries/messages/list-convo-members.ts index d2ff52dd5c..386d82ca82 100644 --- a/src/state/queries/messages/list-convo-members.ts +++ b/src/state/queries/messages/list-convo-members.ts @@ -1,10 +1,10 @@ import {type ChatBskyActorDefs} from '@atproto/api' import {type QueryClient, useQuery} from '@tanstack/react-query' -import {DM_SERVICE_HEADERS} from '#/lib/constants' import {STALE} from '#/state/queries' import {createQueryKey} from '#/state/queries/util' -import {useAgent} from '#/state/session' +import {useChatClient} from '#/state/session' +import {chat} from '#/lexicons' const RQKEY_ROOT = 'listConvoMembers' export const listConvoMembersQueryKey = (convoId: string) => @@ -20,19 +20,27 @@ export function useListConvoMembersQuery({ convoId: string placeholderData?: ChatBskyActorDefs.ProfileViewBasic[] }) { - const agent = useAgent() + const client = useChatClient() return useQuery({ queryKey: listConvoMembersQueryKey(convoId), queryFn: async () => { - const members = [] - let cursor + /* + * Both locals are annotated because the loop is self-referential: `data` + * is inferred from a call whose params include `cursor`, so leaving + * `cursor` to be inferred from `data.cursor` is circular. Annotating + * `members` with the exported profile type also keeps the hook's result + * type unchanged for consumers. + */ + const members: ChatBskyActorDefs.ProfileViewBasic[] = [] + let cursor: string | undefined do { - const {data} = await agent.chat.bsky.convo.getConvoMembers( - {convoId, cursor, limit: LIMIT}, - {headers: DM_SERVICE_HEADERS}, - ) + const data = await client.call(chat.bsky.convo.getConvoMembers, { + convoId, + cursor, + limit: LIMIT, + }) members.push(...data.members) cursor = data.cursor } while (cursor) diff --git a/src/state/queries/messages/lock-conversation.ts b/src/state/queries/messages/lock-conversation.ts index 122c0633b4..13242d59db 100644 --- a/src/state/queries/messages/lock-conversation.ts +++ b/src/state/queries/messages/lock-conversation.ts @@ -1,8 +1,8 @@ import {ChatBskyConvoDefs, type ChatBskyConvoLockConvo} from '@atproto/api' import {useMutation, useQueryClient} from '@tanstack/react-query' -import {DM_SERVICE_HEADERS} from '#/lib/constants' -import {useAgent} from '#/state/session' +import {useChatClient} from '#/state/session' +import {chat} from '#/lexicons' import { rollbackConvoOptimistic, updateConvoOptimistic, @@ -25,23 +25,15 @@ export function useLockConvo( }, ) { const queryClient = useQueryClient() - const agent = useAgent() + const client = useChatClient() return useMutation({ mutationFn: async ({lock}: {lock: boolean; silent?: boolean}) => { if (!convoId) throw new Error('No convoId provided') if (lock) { - const {data} = await agent.chat.bsky.convo.lockConvo( - {convoId}, - {headers: DM_SERVICE_HEADERS, encoding: 'application/json'}, - ) - return data + return await client.call(chat.bsky.convo.lockConvo, {convoId}) } else { - const {data} = await agent.chat.bsky.convo.unlockConvo( - {convoId}, - {headers: DM_SERVICE_HEADERS, encoding: 'application/json'}, - ) - return data + return await client.call(chat.bsky.convo.unlockConvo, {convoId}) } }, onMutate: ({lock}) => { diff --git a/src/state/queries/messages/mute-conversation.ts b/src/state/queries/messages/mute-conversation.ts index 03a9ab0b4a..2e9e911b5e 100644 --- a/src/state/queries/messages/mute-conversation.ts +++ b/src/state/queries/messages/mute-conversation.ts @@ -1,8 +1,8 @@ import {type ChatBskyConvoMuteConvo} from '@atproto/api' import {useMutation, useQueryClient} from '@tanstack/react-query' -import {DM_SERVICE_HEADERS} from '#/lib/constants' -import {useAgent} from '#/state/session' +import {useChatClient} from '#/state/session' +import {chat} from '#/lexicons' import { rollbackConvoOptimistic, updateConvoOptimistic, @@ -19,23 +19,15 @@ export function useMuteConvo( }, ) { const queryClient = useQueryClient() - const agent = useAgent() + const client = useChatClient() return useMutation({ mutationFn: async ({mute}: {mute: boolean}) => { if (!convoId) throw new Error('No convoId provided') if (mute) { - const {data} = await agent.chat.bsky.convo.muteConvo( - {convoId}, - {headers: DM_SERVICE_HEADERS, encoding: 'application/json'}, - ) - return data + return await client.call(chat.bsky.convo.muteConvo, {convoId}) } else { - const {data} = await agent.chat.bsky.convo.unmuteConvo( - {convoId}, - {headers: DM_SERVICE_HEADERS, encoding: 'application/json'}, - ) - return data + return await client.call(chat.bsky.convo.unmuteConvo, {convoId}) } }, onMutate: ({mute}) => { diff --git a/src/state/queries/messages/update-all-read.ts b/src/state/queries/messages/update-all-read.ts index 6fbffc58cd..afcb062d16 100644 --- a/src/state/queries/messages/update-all-read.ts +++ b/src/state/queries/messages/update-all-read.ts @@ -1,9 +1,9 @@ import {type ChatBskyConvoGetUnreadCounts} 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_PARTIAL as UNREAD_COUNTS_PARTIAL_KEY} from './get-unread-counts' import { type ConvoRequestListQueryData, @@ -29,16 +29,11 @@ export function useUpdateAllRead( }, ) { const queryClient = useQueryClient() - const agent = useAgent() + const client = useChatClient() return useMutation({ mutationFn: async () => { - const {data} = await agent.chat.bsky.convo.updateAllRead( - {status}, - {headers: DM_SERVICE_HEADERS, encoding: 'application/json'}, - ) - - return data + return await client.call(chat.bsky.convo.updateAllRead, {status}) }, onMutate: () => { // snapshot every convo-list cache up front so onError can restore them