[SDK] Migrate chat convo queries to the chat client (#11357)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Matthieu Sieben <matthieusieben@users.noreply.github.com>
This commit is contained in:
Samuel Newman
2026-08-13 22:26:15 +03:00
committed by GitHub
parent 0912119c77
commit 0e8e0b2ab4
19 changed files with 171 additions and 211 deletions
+9 -5
View File
@@ -1,14 +1,15 @@
import {ChatBskyConvoLeaveConvo} from '@atproto/api'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {StackActions, useNavigation} from '@react-navigation/native' import {StackActions, useNavigation} from '@react-navigation/native'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import {isNetworkError} from '#/lib/strings/errors' import {isNetworkError} from '#/lib/strings/errors'
import {matchXrpcError} from '#/lib/xrpc-error'
import {useLeaveConvo} from '#/state/queries/messages/leave-conversation' import {useLeaveConvo} from '#/state/queries/messages/leave-conversation'
import {type DialogOuterProps} from '#/components/Dialog' import {type DialogOuterProps} from '#/components/Dialog'
import * as Prompt from '#/components/Prompt' import * as Prompt from '#/components/Prompt'
import * as Toast from '#/components/Toast' import * as Toast from '#/components/Toast'
import {IS_NATIVE} from '#/env' import {IS_NATIVE} from '#/env'
import {chat} from '#/lexicons'
export function LeaveConvoPrompt({ export function LeaveConvoPrompt({
control, control,
@@ -36,12 +37,15 @@ export function LeaveConvoPrompt({
let errorMessage = l`Could not leave chat` let errorMessage = l`Could not leave chat`
if (isNetworkError(error)) { if (isNetworkError(error)) {
errorMessage = l`A network error occurred. Please check your internet connection.` errorMessage = l`A network error occurred. Please check your internet connection.`
} else if (error instanceof ChatBskyConvoLeaveConvo.InvalidConvoError) { } else {
switch (matchXrpcError(error, chat.bsky.convo.leaveConvo)) {
case 'InvalidConvo':
errorMessage = l`Conversation not found.` errorMessage = l`Conversation not found.`
} else if ( break
error instanceof ChatBskyConvoLeaveConvo.OwnerCannotLeaveError case 'OwnerCannotLeave':
) {
errorMessage = l`Owner must lock the group before leaving.` errorMessage = l`Owner must lock the group before leaving.`
break
}
} }
Toast.show(errorMessage, {type: 'error'}) Toast.show(errorMessage, {type: 'error'})
}, },
+16 -20
View File
@@ -1,12 +1,10 @@
import {useCallback} from 'react' import {useCallback} from 'react'
import { import {ChatBskyGroupCreateGroup} from '@atproto/api'
ChatBskyConvoGetConvoForMembers,
ChatBskyGroupCreateGroup,
} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification'
import {isNetworkError} from '#/lib/strings/errors' import {isNetworkError} from '#/lib/strings/errors'
import {matchXrpcError} from '#/lib/xrpc-error'
import {logger} from '#/logger' import {logger} from '#/logger'
import {useCreateGroupChat} from '#/state/queries/messages/create-group-chat' import {useCreateGroupChat} from '#/state/queries/messages/create-group-chat'
import {useGetConvoForMembers} from '#/state/queries/messages/get-convo-for-members' 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 {MessagePlus_Stroke2_Corner0_Rounded as NewChatIcon} from '#/components/icons/Message'
import * as Toast from '#/components/Toast' import * as Toast from '#/components/Toast'
import {useAnalytics} from '#/analytics' import {useAnalytics} from '#/analytics'
import {chat} from '#/lexicons'
export function NewChat({ export function NewChat({
control, control,
@@ -54,27 +53,24 @@ export function NewChat({
let errorMessage = l`An issue occurred starting the chat, please try again.` let errorMessage = l`An issue occurred starting the chat, please try again.`
if (isNetworkError(error)) { if (isNetworkError(error)) {
errorMessage = l`A network error occurred. Please check your internet connection.` errorMessage = l`A network error occurred. Please check your internet connection.`
} else if ( } else {
error instanceof ChatBskyConvoGetConvoForMembers.AccountSuspendedError switch (matchXrpcError(error, chat.bsky.convo.getConvoForMembers)) {
) { case 'AccountSuspended':
errorMessage = l`Suspended accounts cannot participate in chat.` errorMessage = l`Suspended accounts cannot participate in chat.`
} else if ( break
error instanceof ChatBskyConvoGetConvoForMembers.BlockedActorError case 'BlockedActor':
) {
errorMessage = l`This user has blocked you and cannot be messaged.` errorMessage = l`This user has blocked you and cannot be messaged.`
} else if ( break
error instanceof ChatBskyConvoGetConvoForMembers.MessagesDisabledError case 'MessagesDisabled':
) {
errorMessage = l`This user has disabled chat and cannot be messaged.` errorMessage = l`This user has disabled chat and cannot be messaged.`
} else if ( break
error instanceof case 'NotFollowedBySender':
ChatBskyConvoGetConvoForMembers.NotFollowedBySenderError
) {
errorMessage = l`Chat recipient is not followed by the sender.` errorMessage = l`Chat recipient is not followed by the sender.`
} else if ( break
error instanceof ChatBskyConvoGetConvoForMembers.RecipientNotFoundError case 'RecipientNotFound':
) {
errorMessage = l`Unable to find the selected recipient.` errorMessage = l`Unable to find the selected recipient.`
break
}
} }
Toast.show(errorMessage, { Toast.show(errorMessage, {
type: 'error', type: 'error',
@@ -1,11 +1,9 @@
import {useCallback, useState} from 'react' import {useCallback, useState} from 'react'
import { import {ChatBskyGroupCreateGroup} from '@atproto/api'
ChatBskyConvoGetConvoForMembers,
ChatBskyGroupCreateGroup,
} from '@atproto/api'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {isNetworkError} from '#/lib/strings/errors' import {isNetworkError} from '#/lib/strings/errors'
import {matchXrpcError} from '#/lib/xrpc-error'
import {logger} from '#/logger' import {logger} from '#/logger'
import {useCreateGroupChat} from '#/state/queries/messages/create-group-chat' import {useCreateGroupChat} from '#/state/queries/messages/create-group-chat'
import {useGetConvoForMembers} from '#/state/queries/messages/get-convo-for-members' 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 {InitiateChatFlow} from '#/components/dms/InitiateChatFlow'
import * as Toast from '#/components/Toast' import * as Toast from '#/components/Toast'
import {useAnalytics} from '#/analytics' import {useAnalytics} from '#/analytics'
import {chat} from '#/lexicons'
export function SendViaChatDialog({ export function SendViaChatDialog({
control, control,
@@ -69,27 +68,24 @@ function SendViaChatDialogInner({
let errorMessage = l`An issue occurred starting the chat, please try again.` let errorMessage = l`An issue occurred starting the chat, please try again.`
if (isNetworkError(error)) { if (isNetworkError(error)) {
errorMessage = l`A network error occurred. Please check your internet connection.` errorMessage = l`A network error occurred. Please check your internet connection.`
} else if ( } else {
error instanceof ChatBskyConvoGetConvoForMembers.AccountSuspendedError switch (matchXrpcError(error, chat.bsky.convo.getConvoForMembers)) {
) { case 'AccountSuspended':
errorMessage = l`Suspended accounts cannot participate in chat.` errorMessage = l`Suspended accounts cannot participate in chat.`
} else if ( break
error instanceof ChatBskyConvoGetConvoForMembers.BlockedActorError case 'BlockedActor':
) {
errorMessage = l`This user has blocked you and cannot be messaged.` errorMessage = l`This user has blocked you and cannot be messaged.`
} else if ( break
error instanceof ChatBskyConvoGetConvoForMembers.MessagesDisabledError case 'MessagesDisabled':
) {
errorMessage = l`This user has disabled chat and cannot be messaged.` errorMessage = l`This user has disabled chat and cannot be messaged.`
} else if ( break
error instanceof case 'NotFollowedBySender':
ChatBskyConvoGetConvoForMembers.NotFollowedBySenderError
) {
errorMessage = l`Chat recipient is not followed by the sender.` errorMessage = l`Chat recipient is not followed by the sender.`
} else if ( break
error instanceof ChatBskyConvoGetConvoForMembers.RecipientNotFoundError case 'RecipientNotFound':
) {
errorMessage = l`Unable to find the selected recipient.` errorMessage = l`Unable to find the selected recipient.`
break
}
} }
Toast.show(errorMessage, { Toast.show(errorMessage, {
type: 'error', type: 'error',
+10 -9
View File
@@ -1,14 +1,11 @@
import {useState} from 'react' import {useState} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import { import {type ChatBskyConvoDefs, ChatBskyGroupRemoveMembers} from '@atproto/api'
type ChatBskyConvoDefs,
ChatBskyConvoLeaveConvo,
ChatBskyGroupRemoveMembers,
} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import {isNetworkError} from '#/lib/strings/errors' import {isNetworkError} from '#/lib/strings/errors'
import {matchXrpcError} from '#/lib/xrpc-error'
import {logger} from '#/logger' import {logger} from '#/logger'
import {type Shadow} from '#/state/cache/types' import {type Shadow} from '#/state/cache/types'
import {useLeaveConvo} from '#/state/queries/messages/leave-conversation' import {useLeaveConvo} from '#/state/queries/messages/leave-conversation'
@@ -27,6 +24,7 @@ import {parseConvoView} from '#/components/dms/util'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import * as Toast from '#/components/Toast' import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {chat} from '#/lexicons'
import {type AnyProfileView} from '#/types/bsky/profile' import {type AnyProfileView} from '#/types/bsky/profile'
type Item = ChatBskyConvoDefs.ConvoView type Item = ChatBskyConvoDefs.ConvoView
@@ -282,12 +280,15 @@ function MutualGroupChat({
let errorMessage = l`Could not leave chat.` let errorMessage = l`Could not leave chat.`
if (isNetworkError(error)) { if (isNetworkError(error)) {
errorMessage = l`A network error occurred. Please check your internet connection.` errorMessage = l`A network error occurred. Please check your internet connection.`
} else if (error instanceof ChatBskyConvoLeaveConvo.InvalidConvoError) { } else {
switch (matchXrpcError(error, chat.bsky.convo.leaveConvo)) {
case 'InvalidConvo':
errorMessage = l`Chat not found.` errorMessage = l`Chat not found.`
} else if ( break
error instanceof ChatBskyConvoLeaveConvo.OwnerCannotLeaveError case 'OwnerCannotLeave':
) {
errorMessage = l`Chat owners cannot leave a group chat.` errorMessage = l`Chat owners cannot leave a group chat.`
break
}
} }
Toast.show(errorMessage, {type: 'error'}) Toast.show(errorMessage, {type: 'error'})
}, },
@@ -3,7 +3,6 @@ import {Pressable, View} from 'react-native'
import { import {
ChatBskyActorDefs, ChatBskyActorDefs,
ChatBskyConvoDefs, ChatBskyConvoDefs,
ChatBskyConvoUnlockConvo,
type ModerationOpts, type ModerationOpts,
} from '@atproto/api' } from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
@@ -19,6 +18,7 @@ import {
type NativeStackScreenProps, type NativeStackScreenProps,
type NavigationProp, type NavigationProp,
} from '#/lib/routes/types' } from '#/lib/routes/types'
import {matchXrpcError} from '#/lib/xrpc-error'
import {logger} from '#/logger' import {logger} from '#/logger'
import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useConvoQuery} from '#/state/queries/messages/conversation' import {useConvoQuery} from '#/state/queries/messages/conversation'
@@ -59,6 +59,7 @@ import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics' import {useAnalytics} from '#/analytics'
import {IS_WEB} from '#/env' import {IS_WEB} from '#/env'
import {chat} from '#/lexicons'
import * as bsky from '#/types/bsky' import * as bsky from '#/types/bsky'
import {InviteLinkDialog} from '../components/InviteLinkDialog' import {InviteLinkDialog} from '../components/InviteLinkDialog'
import {AddMembersLink} from './AddMembersLink' import {AddMembersLink} from './AddMembersLink'
@@ -432,7 +433,8 @@ function SettingsHeader({
logger.error('Failed to lock group chat', {message: e}) logger.error('Failed to lock group chat', {message: e})
Toast.show(l`Failed to lock group chat`, {type: 'error'}) Toast.show(l`Failed to lock group chat`, {type: 'error'})
} else if ( } else if (
e instanceof ChatBskyConvoUnlockConvo.ConvoLockedByModerationError matchXrpcError(e, chat.bsky.convo.unlockConvo) ===
'ConvoLockedByModeration'
) { ) {
Toast.show(l`This chat is locked by a moderation action`, { Toast.show(l`This chat is locked by a moderation action`, {
type: 'error', type: 'error',
@@ -1,10 +1,10 @@
import {Pressable} from 'react-native' import {Pressable} from 'react-native'
import {ChatBskyConvoUnlockConvo} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {HITSLOP_10} from '#/lib/constants' import {HITSLOP_10} from '#/lib/constants'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import {matchXrpcError} from '#/lib/xrpc-error'
import {logger} from '#/logger' import {logger} from '#/logger'
import {useLeaveConvo} from '#/state/queries/messages/leave-conversation' import {useLeaveConvo} from '#/state/queries/messages/leave-conversation'
import {useLockConvo} from '#/state/queries/messages/lock-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 Prompt from '#/components/Prompt'
import * as Toast from '#/components/Toast' import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {chat} from '#/lexicons'
import {LeaveChatPrompt} from '../ConversationSettings/prompts' import {LeaveChatPrompt} from '../ConversationSettings/prompts'
import {ChatFooter} from './ChatFooter' import {ChatFooter} from './ChatFooter'
@@ -41,7 +42,10 @@ export function ChatLocked({
Toast.show(l({message: 'Group chat unlocked', context: 'toast'})) Toast.show(l({message: 'Group chat unlocked', context: 'toast'}))
}, },
onError: e => { 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`, { Toast.show(l`This chat is locked by a moderation action`, {
type: 'error', type: 'error',
}) })
@@ -4,9 +4,9 @@ import {
} from '@atproto/api' } from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {logger} from '#/logger' import {logger} from '#/logger'
import {useAgent} from '#/state/session' import {useChatClient} from '#/state/session'
import {chat} from '#/lexicons'
import { import {
type ConvoRequestListQueryData, type ConvoRequestListQueryData,
optimisticDelete as optimisticDeleteRequest, optimisticDelete as optimisticDeleteRequest,
@@ -34,16 +34,11 @@ export function useAcceptConversation(
}, },
) { ) {
const queryClient = useQueryClient() const queryClient = useQueryClient()
const agent = useAgent() const client = useChatClient()
return useMutation({ return useMutation({
mutationFn: async () => { mutationFn: async () => {
const {data} = await agent.chat.bsky.convo.acceptConvo( return await client.call(chat.bsky.convo.acceptConvo, {convoId})
{convoId},
{headers: DM_SERVICE_HEADERS},
)
return data
}, },
onMutate: () => { onMutate: () => {
// snapshot every convo-list cache up front so onError can restore them // snapshot every convo-list cache up front so onError can restore them
+7 -16
View File
@@ -11,10 +11,10 @@ import {
useQueryClient, useQueryClient,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {STALE} from '#/state/queries' import {STALE} from '#/state/queries'
import {useOnMarkAsRead} from '#/state/queries/messages/list-conversations' import {useOnMarkAsRead} from '#/state/queries/messages/list-conversations'
import {useAgent} from '#/state/session' import {useChatClient} from '#/state/session'
import {chat} from '#/lexicons'
import { import {
RQKEY_PARTIAL as UNREAD_COUNTS_PARTIAL_KEY, RQKEY_PARTIAL as UNREAD_COUNTS_PARTIAL_KEY,
UNREAD_ACCEPTED_CAP, UNREAD_ACCEPTED_CAP,
@@ -30,15 +30,12 @@ export const RQKEY_ROOT = 'convo'
export const RQKEY = (convoId: string) => [RQKEY_ROOT, convoId] export const RQKEY = (convoId: string) => [RQKEY_ROOT, convoId]
export function useConvoQuery({convoId}: {convoId: string}) { export function useConvoQuery({convoId}: {convoId: string}) {
const agent = useAgent() const client = useChatClient()
return useQuery({ return useQuery({
queryKey: RQKEY(convoId), queryKey: RQKEY(convoId),
queryFn: async () => { queryFn: async () => {
const {data} = await agent.chat.bsky.convo.getConvo( const data = await client.call(chat.bsky.convo.getConvo, {convoId})
{convoId},
{headers: DM_SERVICE_HEADERS},
)
return data.convo return data.convo
}, },
staleTime: STALE.INFINITY, staleTime: STALE.INFINITY,
@@ -55,7 +52,7 @@ export function precacheConvoQuery(
export function useMarkAsReadMutation() { export function useMarkAsReadMutation() {
const optimisticUpdate = useOnMarkAsRead() const optimisticUpdate = useOnMarkAsRead()
const queryClient = useQueryClient() const queryClient = useQueryClient()
const agent = useAgent() const client = useChatClient()
return useMutation({ return useMutation({
mutationFn: async ({ mutationFn: async ({
@@ -67,16 +64,10 @@ export function useMarkAsReadMutation() {
}) => { }) => {
if (!convoId) throw new Error('No convoId provided') if (!convoId) throw new Error('No convoId provided')
await agent.chat.bsky.convo.updateRead( await client.call(chat.bsky.convo.updateRead, {
{
convoId, convoId,
messageId, messageId,
}, })
{
encoding: 'application/json',
headers: DM_SERVICE_HEADERS,
},
)
}, },
onMutate({convoId}) { onMutate({convoId}) {
if (!convoId) throw new Error('No convoId provided') if (!convoId) throw new Error('No convoId provided')
@@ -1,7 +1,8 @@
import {type DidString} from '@atproto/syntax'
import {useQuery} from '@tanstack/react-query' import {useQuery} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants' import {useChatClient, useSession} from '#/state/session'
import {useAgent} from '#/state/session' import {chat} from '#/lexicons'
import {STALE} from '..' import {STALE} from '..'
const RQKEY_ROOT = 'convo-availability' const RQKEY_ROOT = 'convo-availability'
@@ -11,19 +12,18 @@ export function useGetConvoAvailabilityQuery(
did: string, did: string,
{enabled = true}: {enabled?: boolean} = {}, {enabled = true}: {enabled?: boolean} = {},
) { ) {
const agent = useAgent() const client = useChatClient()
const {hasSession} = useSession()
return useQuery({ return useQuery({
queryKey: RQKEY(did), queryKey: RQKEY(did),
queryFn: async () => { queryFn: async () => {
const {data} = await agent.chat.bsky.convo.getConvoAvailability( return client.call(chat.bsky.convo.getConvoAvailability, {
{members: [did]}, // callers pass an already-resolved actor did
{headers: DM_SERVICE_HEADERS}, members: [did as DidString],
) })
return data
}, },
staleTime: STALE.INFINITY, staleTime: STALE.INFINITY,
enabled, enabled: enabled && hasSession,
}) })
} }
@@ -1,9 +1,10 @@
import {type ChatBskyConvoGetConvoForMembers} from '@atproto/api' import {type ChatBskyConvoGetConvoForMembers} from '@atproto/api'
import {type DidString} from '@atproto/syntax'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {logger} from '#/logger' import {logger} from '#/logger'
import {useAgent} from '#/state/session' import {useChatClient} from '#/state/session'
import {chat} from '#/lexicons'
import {precacheConvoQuery} from './conversation' import {precacheConvoQuery} from './conversation'
export function useGetConvoForMembers({ export function useGetConvoForMembers({
@@ -14,16 +15,14 @@ export function useGetConvoForMembers({
onError?: (error: Error) => void onError?: (error: Error) => void
}) { }) {
const queryClient = useQueryClient() const queryClient = useQueryClient()
const agent = useAgent() const client = useChatClient()
return useMutation({ return useMutation({
mutationFn: async (members: string[]) => { mutationFn: async (members: string[]) => {
const {data} = await agent.chat.bsky.convo.getConvoForMembers( return await client.call(chat.bsky.convo.getConvoForMembers, {
{members: members}, // callers pass already-resolved actor dids
{headers: DM_SERVICE_HEADERS}, members: members as DidString[],
) })
return data
}, },
onSuccess: data => { onSuccess: data => {
precacheConvoQuery(queryClient, data.convo) precacheConvoQuery(queryClient, data.convo)
+6 -9
View File
@@ -1,7 +1,7 @@
import {useQuery} from '@tanstack/react-query' import {useQuery} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants' import {useChatClient, useSession} from '#/state/session'
import {useAgent} from '#/state/session' import {chat} from '#/lexicons'
import {STALE} from '..' import {STALE} from '..'
import {createQueryKey} from '../util' import {createQueryKey} from '../util'
@@ -9,19 +9,16 @@ const chatActorStatusQueryKey = () =>
createQueryKey('chat-actor-status', {}, {persistedVersion: 1}) createQueryKey('chat-actor-status', {}, {persistedVersion: 1})
export function useChatActorStatusQuery() { export function useChatActorStatusQuery() {
const agent = useAgent() const client = useChatClient()
const {hasSession} = useSession()
return useQuery({ return useQuery({
gcTime: STALE.INFINITY, gcTime: STALE.INFINITY,
staleTime: STALE.SECONDS.FIFTEEN, staleTime: STALE.SECONDS.FIFTEEN,
queryKey: chatActorStatusQueryKey(), queryKey: chatActorStatusQueryKey(),
queryFn: async () => { queryFn: async () => {
const {data} = await agent.chat.bsky.actor.getStatus( return await client.call(chat.bsky.actor.getStatus)
{},
{headers: DM_SERVICE_HEADERS},
)
return data
}, },
enabled: hasSession,
}) })
} }
@@ -1,8 +1,8 @@
import {useQuery} from '@tanstack/react-query' import {useQuery} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants' import {useChatClient, useSession} from '#/state/session'
import {useAgent, useSession} from '#/state/session'
import {useAgeAssurance} from '#/ageAssurance' import {useAgeAssurance} from '#/ageAssurance'
import {chat} from '#/lexicons'
import {STALE} from '..' import {STALE} from '..'
const RQKEY_ROOT = 'convo-unread-counts' const RQKEY_ROOT = 'convo-unread-counts'
@@ -18,7 +18,7 @@ export const UNREAD_ACCEPTED_CAP = 100
export const UNREAD_REQUEST_CAP = 100 export const UNREAD_REQUEST_CAP = 100
export function useUnreadCountsQuery() { export function useUnreadCountsQuery() {
const agent = useAgent() const client = useChatClient()
const {hasSession} = useSession() const {hasSession} = useSession()
const aa = useAgeAssurance() const aa = useAgeAssurance()
const includeGroupChats = !aa.flags.groupChatDisabled const includeGroupChats = !aa.flags.groupChatDisabled
@@ -26,11 +26,9 @@ export function useUnreadCountsQuery() {
return useQuery({ return useQuery({
queryKey: RQKEY(includeGroupChats), queryKey: RQKEY(includeGroupChats),
queryFn: async () => { queryFn: async () => {
const {data} = await agent.chat.bsky.convo.getUnreadCounts( return await client.call(chat.bsky.convo.getUnreadCounts, {
{includeGroupChats}, includeGroupChats,
{headers: DM_SERVICE_HEADERS}, })
)
return data
}, },
staleTime: STALE.SECONDS.FIFTEEN, staleTime: STALE.SECONDS.FIFTEEN,
enabled: hasSession, enabled: hasSession,
@@ -4,10 +4,10 @@ import {
} from '@atproto/api' } from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {logger} from '#/logger' import {logger} from '#/logger'
import {invalidateJoinLinkPreviewsForConvo} from '#/state/queries/join-links' import {invalidateJoinLinkPreviewsForConvo} from '#/state/queries/join-links'
import {useAgent} from '#/state/session' import {useChatClient} from '#/state/session'
import {chat} from '#/lexicons'
import { import {
type ConvoRequestListQueryData, type ConvoRequestListQueryData,
optimisticDelete as optimisticDeleteRequest, optimisticDelete as optimisticDeleteRequest,
@@ -38,19 +38,14 @@ export function useLeaveConvo(
}, },
) { ) {
const queryClient = useQueryClient() const queryClient = useQueryClient()
const agent = useAgent() const client = useChatClient()
return useMutation({ return useMutation({
mutationKey: RQKEY(convoId), mutationKey: RQKEY(convoId),
mutationFn: async () => { mutationFn: async () => {
if (!convoId) throw new Error('No convoId provided') if (!convoId) throw new Error('No convoId provided')
const {data} = await agent.chat.bsky.convo.leaveConvo( return await client.call(chat.bsky.convo.leaveConvo, {convoId})
{convoId},
{headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
)
return data
}, },
onMutate: () => { onMutate: () => {
const prevConvoListQueries = const prevConvoListQueries =
@@ -9,8 +9,8 @@ import {
useInfiniteQuery, useInfiniteQuery,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants' import {useChatClient} from '#/state/session'
import {useAgent} from '#/state/session' import {chat} from '#/lexicons'
const DEFAULT_LIMIT = 10 const DEFAULT_LIMIT = 10
@@ -26,17 +26,16 @@ export function useListConvoRequests({
enabled?: boolean enabled?: boolean
limit?: number limit?: number
} = {}) { } = {}) {
const agent = useAgent() const client = useChatClient()
return useInfiniteQuery({ return useInfiniteQuery({
enabled, enabled,
queryKey: RQKEY(limit), queryKey: RQKEY(limit),
queryFn: async ({pageParam}) => { queryFn: async ({pageParam}) => {
const {data} = await agent.chat.bsky.convo.listConvoRequests( return await client.call(chat.bsky.convo.listConvoRequests, {
{limit, cursor: pageParam}, limit,
{headers: DM_SERVICE_HEADERS}, cursor: pageParam,
) })
return data
}, },
initialPageParam: undefined as RQPageParam, initialPageParam: undefined as RQPageParam,
getNextPageParam: lastPage => lastPage.cursor, getNextPageParam: lastPage => lastPage.cursor,
@@ -14,11 +14,11 @@ import {
} from '@tanstack/react-query' } from '@tanstack/react-query'
import throttle from 'lodash.throttle' import throttle from 'lodash.throttle'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {useCurrentConvoId} from '#/state/messages/current-convo-id' import {useCurrentConvoId} from '#/state/messages/current-convo-id'
import {useMessagesEventBus} from '#/state/messages/events' import {useMessagesEventBus} from '#/state/messages/events'
import {invalidateJoinLinkPreviewsForConvo} from '#/state/queries/join-links' 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 * as bsky from '#/types/bsky'
import {RQKEY as CONVO_KEY} from './conversation' import {RQKEY as CONVO_KEY} from './conversation'
import { import {
@@ -119,24 +119,20 @@ export function useListConvosQuery({
limit?: number limit?: number
lockStatus?: 'unlocked' | 'locked' | 'locked-permanently' lockStatus?: 'unlocked' | 'locked' | 'locked-permanently'
} = {}) { } = {}) {
const agent = useAgent() const client = useChatClient()
return useInfiniteQuery({ return useInfiniteQuery({
enabled, enabled,
queryKey: RQKEY(status ?? 'all', readState, kind, lockStatus, limit), queryKey: RQKEY(status ?? 'all', readState, kind, lockStatus, limit),
queryFn: async ({pageParam}) => { queryFn: async ({pageParam}) => {
const {data} = await agent.chat.bsky.convo.listConvos( return await client.call(chat.bsky.convo.listConvos, {
{
limit, limit,
cursor: pageParam, cursor: pageParam,
readState: readState === 'unread' ? 'unread' : undefined, readState: readState === 'unread' ? 'unread' : undefined,
kind: kind === 'all' ? undefined : kind, kind: kind === 'all' ? undefined : kind,
lockStatus, lockStatus,
status, status,
}, })
{headers: DM_SERVICE_HEADERS},
)
return data
}, },
initialPageParam: undefined as RQPageParam, initialPageParam: undefined as RQPageParam,
getNextPageParam: lastPage => lastPage.cursor, getNextPageParam: lastPage => lastPage.cursor,
@@ -1,10 +1,10 @@
import {type ChatBskyActorDefs} from '@atproto/api' import {type ChatBskyActorDefs} from '@atproto/api'
import {type QueryClient, useQuery} from '@tanstack/react-query' import {type QueryClient, useQuery} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {STALE} from '#/state/queries' import {STALE} from '#/state/queries'
import {createQueryKey} from '#/state/queries/util' import {createQueryKey} from '#/state/queries/util'
import {useAgent} from '#/state/session' import {useChatClient} from '#/state/session'
import {chat} from '#/lexicons'
const RQKEY_ROOT = 'listConvoMembers' const RQKEY_ROOT = 'listConvoMembers'
export const listConvoMembersQueryKey = (convoId: string) => export const listConvoMembersQueryKey = (convoId: string) =>
@@ -20,19 +20,27 @@ export function useListConvoMembersQuery({
convoId: string convoId: string
placeholderData?: ChatBskyActorDefs.ProfileViewBasic[] placeholderData?: ChatBskyActorDefs.ProfileViewBasic[]
}) { }) {
const agent = useAgent() const client = useChatClient()
return useQuery({ return useQuery({
queryKey: listConvoMembersQueryKey(convoId), queryKey: listConvoMembersQueryKey(convoId),
queryFn: async () => { 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 { do {
const {data} = await agent.chat.bsky.convo.getConvoMembers( const data = await client.call(chat.bsky.convo.getConvoMembers, {
{convoId, cursor, limit: LIMIT}, convoId,
{headers: DM_SERVICE_HEADERS}, cursor,
) limit: LIMIT,
})
members.push(...data.members) members.push(...data.members)
cursor = data.cursor cursor = data.cursor
} while (cursor) } while (cursor)
@@ -1,8 +1,8 @@
import {ChatBskyConvoDefs, type ChatBskyConvoLockConvo} from '@atproto/api' import {ChatBskyConvoDefs, type ChatBskyConvoLockConvo} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants' import {useChatClient} from '#/state/session'
import {useAgent} from '#/state/session' import {chat} from '#/lexicons'
import { import {
rollbackConvoOptimistic, rollbackConvoOptimistic,
updateConvoOptimistic, updateConvoOptimistic,
@@ -25,23 +25,15 @@ export function useLockConvo(
}, },
) { ) {
const queryClient = useQueryClient() const queryClient = useQueryClient()
const agent = useAgent() const client = useChatClient()
return useMutation({ return useMutation({
mutationFn: async ({lock}: {lock: boolean; silent?: boolean}) => { mutationFn: async ({lock}: {lock: boolean; silent?: boolean}) => {
if (!convoId) throw new Error('No convoId provided') if (!convoId) throw new Error('No convoId provided')
if (lock) { if (lock) {
const {data} = await agent.chat.bsky.convo.lockConvo( return await client.call(chat.bsky.convo.lockConvo, {convoId})
{convoId},
{headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
)
return data
} else { } else {
const {data} = await agent.chat.bsky.convo.unlockConvo( return await client.call(chat.bsky.convo.unlockConvo, {convoId})
{convoId},
{headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
)
return data
} }
}, },
onMutate: ({lock}) => { onMutate: ({lock}) => {
@@ -1,8 +1,8 @@
import {type ChatBskyConvoMuteConvo} from '@atproto/api' import {type ChatBskyConvoMuteConvo} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants' import {useChatClient} from '#/state/session'
import {useAgent} from '#/state/session' import {chat} from '#/lexicons'
import { import {
rollbackConvoOptimistic, rollbackConvoOptimistic,
updateConvoOptimistic, updateConvoOptimistic,
@@ -19,23 +19,15 @@ export function useMuteConvo(
}, },
) { ) {
const queryClient = useQueryClient() const queryClient = useQueryClient()
const agent = useAgent() const client = useChatClient()
return useMutation({ return useMutation({
mutationFn: async ({mute}: {mute: boolean}) => { mutationFn: async ({mute}: {mute: boolean}) => {
if (!convoId) throw new Error('No convoId provided') if (!convoId) throw new Error('No convoId provided')
if (mute) { if (mute) {
const {data} = await agent.chat.bsky.convo.muteConvo( return await client.call(chat.bsky.convo.muteConvo, {convoId})
{convoId},
{headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
)
return data
} else { } else {
const {data} = await agent.chat.bsky.convo.unmuteConvo( return await client.call(chat.bsky.convo.unmuteConvo, {convoId})
{convoId},
{headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
)
return data
} }
}, },
onMutate: ({mute}) => { onMutate: ({mute}) => {
@@ -1,9 +1,9 @@
import {type ChatBskyConvoGetUnreadCounts} from '@atproto/api' import {type ChatBskyConvoGetUnreadCounts} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {logger} from '#/logger' 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 {RQKEY_PARTIAL as UNREAD_COUNTS_PARTIAL_KEY} from './get-unread-counts'
import { import {
type ConvoRequestListQueryData, type ConvoRequestListQueryData,
@@ -29,16 +29,11 @@ export function useUpdateAllRead(
}, },
) { ) {
const queryClient = useQueryClient() const queryClient = useQueryClient()
const agent = useAgent() const client = useChatClient()
return useMutation({ return useMutation({
mutationFn: async () => { mutationFn: async () => {
const {data} = await agent.chat.bsky.convo.updateAllRead( return await client.call(chat.bsky.convo.updateAllRead, {status})
{status},
{headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
)
return data
}, },
onMutate: () => { onMutate: () => {
// snapshot every convo-list cache up front so onError can restore them // snapshot every convo-list cache up front so onError can restore them