DMs inbox (#7778)
* improve error screen * add chat request prompt * mock up inbox * bigger button * use two-button layout * get inbox working somewhat * fix type errors * fetch both pages for badge * don't include read convos in preview * in-chat ui for non-accepted convos (part 1) * add chatstatusinfo * fix status info not disappearing * get chat status info working * change min item height * move files around * add updated sdk * improve badge behaviour * mock up mark all as read * update sdk to 0.14.4 * hide chat status info if initiating convo * fix unread count for deleted accounts * add toasts after rejection * add prompt to delete * adjust badge on desktop * requests -> chat requests * fix height flicker * add mark as read button to header * add mark all as read APIs * separate avatarstack into two components (#7845) * fix messages being hidden behind chatstatusinfo * show inbox preview on empty state * fix empty state again * Use new convo availability API (#7812) * [Inbox] Accept button on convo screen (#7795) * accept button on convo screen * fix types * fix type error * improve spacing * [DMs] Implement new log types (#7835) * optimise badge state * add read message log * add isLogAcceptConvo * mute/unmute convo logs * use setqueriesdata * always show label on button * optimistically update badge * change incorrect unread count change * Update src/screens/Messages/Inbox.tsx Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/screens/Messages/components/RequestButtons.tsx Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/screens/Messages/components/RequestButtons.tsx Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/screens/Messages/components/RequestListItem.tsx Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * fix race condition with accepting convo * fix back button on web * filter left convos from badge * update atproto to fix CI * Add accept override external to convo (#7891) * Add accept override external to convo * rm log --------- Co-authored-by: Samuel Newman <mozzius@protonmail.com> --------- Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> Co-authored-by: Eric Bailey <git@esb.lol>
This commit is contained in:
@@ -105,6 +105,7 @@ export class Convo {
|
||||
this.ingestFirehose = this.ingestFirehose.bind(this)
|
||||
this.onFirehoseConnect = this.onFirehoseConnect.bind(this)
|
||||
this.onFirehoseError = this.onFirehoseError.bind(this)
|
||||
this.markConvoAccepted = this.markConvoAccepted.bind(this)
|
||||
}
|
||||
|
||||
private commit() {
|
||||
@@ -145,6 +146,7 @@ export class Convo {
|
||||
deleteMessage: undefined,
|
||||
sendMessage: undefined,
|
||||
fetchMessageHistory: undefined,
|
||||
markConvoAccepted: undefined,
|
||||
}
|
||||
}
|
||||
case ConvoStatus.Disabled:
|
||||
@@ -162,6 +164,7 @@ export class Convo {
|
||||
deleteMessage: this.deleteMessage,
|
||||
sendMessage: this.sendMessage,
|
||||
fetchMessageHistory: this.fetchMessageHistory,
|
||||
markConvoAccepted: this.markConvoAccepted,
|
||||
}
|
||||
}
|
||||
case ConvoStatus.Error: {
|
||||
@@ -176,6 +179,7 @@ export class Convo {
|
||||
deleteMessage: undefined,
|
||||
sendMessage: undefined,
|
||||
fetchMessageHistory: undefined,
|
||||
markConvoAccepted: undefined,
|
||||
}
|
||||
}
|
||||
default: {
|
||||
@@ -190,6 +194,7 @@ export class Convo {
|
||||
deleteMessage: undefined,
|
||||
sendMessage: undefined,
|
||||
fetchMessageHistory: undefined,
|
||||
markConvoAccepted: undefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -780,6 +785,12 @@ export class Convo {
|
||||
id: tempId,
|
||||
message,
|
||||
})
|
||||
if (this.convo?.status === 'request') {
|
||||
this.convo = {
|
||||
...this.convo,
|
||||
status: 'accepted',
|
||||
}
|
||||
}
|
||||
this.commit()
|
||||
|
||||
if (!this.isProcessingPendingMessages && !this.pendingMessageFailure) {
|
||||
@@ -787,6 +798,16 @@ export class Convo {
|
||||
}
|
||||
}
|
||||
|
||||
markConvoAccepted() {
|
||||
if (this.convo) {
|
||||
this.convo = {
|
||||
...this.convo,
|
||||
status: 'accepted',
|
||||
}
|
||||
}
|
||||
this.commit()
|
||||
}
|
||||
|
||||
async processPendingMessages() {
|
||||
logger.debug(
|
||||
`Convo: processing messages (${this.pendingMessages.size} remaining)`,
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
RQKEY as getConvoKey,
|
||||
useMarkAsReadMutation,
|
||||
} from '#/state/queries/messages/conversation'
|
||||
import {RQKEY as ListConvosQueryKey} from '#/state/queries/messages/list-conversations'
|
||||
import {RQKEY_ROOT as ListConvosQueryKeyRoot} from '#/state/queries/messages/list-conversations'
|
||||
import {RQKEY as createProfileQueryKey} from '#/state/queries/profile'
|
||||
import {useAgent} from '#/state/session'
|
||||
|
||||
@@ -104,7 +104,7 @@ export function ConvoProvider({
|
||||
})
|
||||
}
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ListConvosQueryKey,
|
||||
queryKey: [ListConvosQueryKeyRoot],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +141,7 @@ type SendMessage = (
|
||||
message: ChatBskyConvoSendMessage.InputSchema['message'],
|
||||
) => void
|
||||
type FetchMessageHistory = () => Promise<void>
|
||||
type MarkConvoAccepted = () => void
|
||||
|
||||
export type ConvoStateUninitialized = {
|
||||
status: ConvoStatus.Uninitialized
|
||||
@@ -153,6 +154,7 @@ export type ConvoStateUninitialized = {
|
||||
deleteMessage: undefined
|
||||
sendMessage: undefined
|
||||
fetchMessageHistory: undefined
|
||||
markConvoAccepted: undefined
|
||||
}
|
||||
export type ConvoStateInitializing = {
|
||||
status: ConvoStatus.Initializing
|
||||
@@ -165,6 +167,7 @@ export type ConvoStateInitializing = {
|
||||
deleteMessage: undefined
|
||||
sendMessage: undefined
|
||||
fetchMessageHistory: undefined
|
||||
markConvoAccepted: undefined
|
||||
}
|
||||
export type ConvoStateReady = {
|
||||
status: ConvoStatus.Ready
|
||||
@@ -177,6 +180,7 @@ export type ConvoStateReady = {
|
||||
deleteMessage: DeleteMessage
|
||||
sendMessage: SendMessage
|
||||
fetchMessageHistory: FetchMessageHistory
|
||||
markConvoAccepted: MarkConvoAccepted
|
||||
}
|
||||
export type ConvoStateBackgrounded = {
|
||||
status: ConvoStatus.Backgrounded
|
||||
@@ -189,6 +193,7 @@ export type ConvoStateBackgrounded = {
|
||||
deleteMessage: DeleteMessage
|
||||
sendMessage: SendMessage
|
||||
fetchMessageHistory: FetchMessageHistory
|
||||
markConvoAccepted: MarkConvoAccepted
|
||||
}
|
||||
export type ConvoStateSuspended = {
|
||||
status: ConvoStatus.Suspended
|
||||
@@ -201,6 +206,7 @@ export type ConvoStateSuspended = {
|
||||
deleteMessage: DeleteMessage
|
||||
sendMessage: SendMessage
|
||||
fetchMessageHistory: FetchMessageHistory
|
||||
markConvoAccepted: MarkConvoAccepted
|
||||
}
|
||||
export type ConvoStateError = {
|
||||
status: ConvoStatus.Error
|
||||
@@ -213,6 +219,7 @@ export type ConvoStateError = {
|
||||
deleteMessage: undefined
|
||||
sendMessage: undefined
|
||||
fetchMessageHistory: undefined
|
||||
markConvoAccepted: undefined
|
||||
}
|
||||
export type ConvoStateDisabled = {
|
||||
status: ConvoStatus.Disabled
|
||||
@@ -225,6 +232,7 @@ export type ConvoStateDisabled = {
|
||||
deleteMessage: DeleteMessage
|
||||
sendMessage: SendMessage
|
||||
fetchMessageHistory: FetchMessageHistory
|
||||
markConvoAccepted: MarkConvoAccepted
|
||||
}
|
||||
export type ConvoState =
|
||||
| ConvoStateUninitialized
|
||||
|
||||
@@ -7,18 +7,22 @@ import {
|
||||
ConvoStatus,
|
||||
} from './types'
|
||||
|
||||
/**
|
||||
* States where the convo is ready to be used - either ready, or backgrounded/suspended
|
||||
* and ready to be resumed
|
||||
*/
|
||||
export type ActiveConvoStates =
|
||||
| ConvoStateReady
|
||||
| ConvoStateBackgrounded
|
||||
| ConvoStateSuspended
|
||||
| ConvoStateDisabled
|
||||
|
||||
/**
|
||||
* Checks if a `Convo` has a `status` that is "active", meaning the chat is
|
||||
* loaded and ready to be used, or its in a suspended or background state, and
|
||||
* ready for resumption.
|
||||
*/
|
||||
export function isConvoActive(
|
||||
convo: ConvoState,
|
||||
): convo is
|
||||
| ConvoStateReady
|
||||
| ConvoStateBackgrounded
|
||||
| ConvoStateSuspended
|
||||
| ConvoStateDisabled {
|
||||
export function isConvoActive(convo: ConvoState): convo is ActiveConvoStates {
|
||||
return (
|
||||
convo.status === ConvoStatus.Ready ||
|
||||
convo.status === ConvoStatus.Backgrounded ||
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
import {ChatBskyConvoAcceptConvo, ChatBskyConvoListConvos} from '@atproto/api'
|
||||
import {useMutation, useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {logger} from '#/logger'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {DM_SERVICE_HEADERS} from './const'
|
||||
import {
|
||||
RQKEY as CONVO_LIST_KEY,
|
||||
RQKEY_ROOT as CONVO_LIST_ROOT_KEY,
|
||||
} from './list-conversations'
|
||||
|
||||
export function useAcceptConversation(
|
||||
convoId: string,
|
||||
{
|
||||
onSuccess,
|
||||
onMutate,
|
||||
onError,
|
||||
}: {
|
||||
onMutate?: () => void
|
||||
onSuccess?: (data: ChatBskyConvoAcceptConvo.OutputSchema) => void
|
||||
onError?: (error: Error) => void
|
||||
},
|
||||
) {
|
||||
const queryClient = useQueryClient()
|
||||
const agent = useAgent()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async () => {
|
||||
const {data} = await agent.chat.bsky.convo.acceptConvo(
|
||||
{convoId},
|
||||
{headers: DM_SERVICE_HEADERS},
|
||||
)
|
||||
|
||||
return data
|
||||
},
|
||||
onMutate: () => {
|
||||
let prevAcceptedPages: ChatBskyConvoListConvos.OutputSchema[] = []
|
||||
let prevInboxPages: ChatBskyConvoListConvos.OutputSchema[] = []
|
||||
let convoBeingAccepted:
|
||||
| ChatBskyConvoListConvos.OutputSchema['convos'][number]
|
||||
| undefined
|
||||
queryClient.setQueryData(
|
||||
CONVO_LIST_KEY('request'),
|
||||
(old?: {
|
||||
pageParams: Array<string | undefined>
|
||||
pages: Array<ChatBskyConvoListConvos.OutputSchema>
|
||||
}) => {
|
||||
if (!old) return old
|
||||
prevInboxPages = old.pages
|
||||
return {
|
||||
...old,
|
||||
pages: old.pages.map(page => {
|
||||
const found = page.convos.find(convo => convo.id === convoId)
|
||||
if (found) {
|
||||
convoBeingAccepted = found
|
||||
return {
|
||||
...page,
|
||||
convos: page.convos.filter(convo => convo.id !== convoId),
|
||||
}
|
||||
}
|
||||
return page
|
||||
}),
|
||||
}
|
||||
},
|
||||
)
|
||||
queryClient.setQueryData(
|
||||
CONVO_LIST_KEY('accepted'),
|
||||
(old?: {
|
||||
pageParams: Array<string | undefined>
|
||||
pages: Array<ChatBskyConvoListConvos.OutputSchema>
|
||||
}) => {
|
||||
if (!old) return old
|
||||
prevAcceptedPages = old.pages
|
||||
if (convoBeingAccepted) {
|
||||
return {
|
||||
...old,
|
||||
pages: [
|
||||
{
|
||||
...old.pages[0],
|
||||
convos: [
|
||||
{
|
||||
...convoBeingAccepted,
|
||||
status: 'accepted',
|
||||
},
|
||||
...old.pages[0].convos,
|
||||
],
|
||||
},
|
||||
...old.pages.slice(1),
|
||||
],
|
||||
}
|
||||
} else {
|
||||
return old
|
||||
}
|
||||
},
|
||||
)
|
||||
onMutate?.()
|
||||
return {prevAcceptedPages, prevInboxPages}
|
||||
},
|
||||
onSuccess: data => {
|
||||
queryClient.invalidateQueries({queryKey: [CONVO_LIST_KEY]})
|
||||
onSuccess?.(data)
|
||||
},
|
||||
onError: (error, _, context) => {
|
||||
logger.error(error)
|
||||
queryClient.setQueryData(
|
||||
CONVO_LIST_KEY('accepted'),
|
||||
(old?: {
|
||||
pageParams: Array<string | undefined>
|
||||
pages: Array<ChatBskyConvoListConvos.OutputSchema>
|
||||
}) => {
|
||||
if (!old) return old
|
||||
return {
|
||||
...old,
|
||||
pages: context?.prevAcceptedPages || old.pages,
|
||||
}
|
||||
},
|
||||
)
|
||||
queryClient.setQueryData(
|
||||
CONVO_LIST_KEY('request'),
|
||||
(old?: {
|
||||
pageParams: Array<string | undefined>
|
||||
pages: Array<ChatBskyConvoListConvos.OutputSchema>
|
||||
}) => {
|
||||
if (!old) return old
|
||||
return {
|
||||
...old,
|
||||
pages: context?.prevInboxPages || old.pages,
|
||||
}
|
||||
},
|
||||
)
|
||||
queryClient.invalidateQueries({queryKey: [CONVO_LIST_ROOT_KEY]})
|
||||
onError?.(error)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import {useAgent} from '#/state/session'
|
||||
import {
|
||||
ConvoListQueryData,
|
||||
getConvoFromQueryData,
|
||||
RQKEY as LIST_CONVOS_KEY,
|
||||
RQKEY_ROOT as LIST_CONVOS_KEY,
|
||||
} from './list-conversations'
|
||||
|
||||
const RQKEY_ROOT = 'convo'
|
||||
@@ -76,34 +76,37 @@ export function useMarkAsReadMutation() {
|
||||
onSuccess(_, {convoId}) {
|
||||
if (!convoId) return
|
||||
|
||||
queryClient.setQueryData(LIST_CONVOS_KEY, (old: ConvoListQueryData) => {
|
||||
if (!old) return old
|
||||
queryClient.setQueriesData(
|
||||
{queryKey: [LIST_CONVOS_KEY]},
|
||||
(old?: ConvoListQueryData) => {
|
||||
if (!old) return old
|
||||
|
||||
const existingConvo = getConvoFromQueryData(convoId, old)
|
||||
const existingConvo = getConvoFromQueryData(convoId, old)
|
||||
|
||||
if (existingConvo) {
|
||||
return {
|
||||
...old,
|
||||
pages: old.pages.map(page => {
|
||||
return {
|
||||
...page,
|
||||
convos: page.convos.map(convo => {
|
||||
if (convo.id === convoId) {
|
||||
return {
|
||||
...convo,
|
||||
unreadCount: 0,
|
||||
if (existingConvo) {
|
||||
return {
|
||||
...old,
|
||||
pages: old.pages.map(page => {
|
||||
return {
|
||||
...page,
|
||||
convos: page.convos.map(convo => {
|
||||
if (convo.id === convoId) {
|
||||
return {
|
||||
...convo,
|
||||
unreadCount: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
return convo
|
||||
}),
|
||||
}
|
||||
}),
|
||||
return convo
|
||||
}),
|
||||
}
|
||||
}),
|
||||
}
|
||||
} else {
|
||||
// If we somehow marked a convo as read that doesn't exist in the
|
||||
// list, then we don't need to do anything.
|
||||
}
|
||||
} else {
|
||||
// If we somehow marked a convo as read that doesn't exist in the
|
||||
// list, then we don't need to do anything.
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import {useQuery} from '@tanstack/react-query'
|
||||
|
||||
import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {STALE} from '..'
|
||||
|
||||
const RQKEY_ROOT = 'convo-availability'
|
||||
export const RQKEY = (did: string) => [RQKEY_ROOT, did]
|
||||
|
||||
export function useGetConvoAvailabilityQuery(did: string) {
|
||||
const agent = useAgent()
|
||||
|
||||
return useQuery({
|
||||
queryKey: RQKEY(did),
|
||||
queryFn: async () => {
|
||||
const {data} = await agent.chat.bsky.convo.getConvoAvailability(
|
||||
{members: [did]},
|
||||
{headers: DM_SERVICE_HEADERS},
|
||||
)
|
||||
|
||||
return data
|
||||
},
|
||||
staleTime: STALE.INFINITY,
|
||||
})
|
||||
}
|
||||
@@ -1,14 +1,10 @@
|
||||
import {ChatBskyConvoGetConvoForMembers} from '@atproto/api'
|
||||
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
|
||||
import {useMutation, useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {logger} from '#/logger'
|
||||
import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {STALE} from '..'
|
||||
import {RQKEY as CONVO_KEY} from './conversation'
|
||||
|
||||
const RQKEY_ROOT = 'convo-for-user'
|
||||
export const RQKEY = (did: string) => [RQKEY_ROOT, did]
|
||||
import {precacheConvoQuery} from './conversation'
|
||||
|
||||
export function useGetConvoForMembers({
|
||||
onSuccess,
|
||||
@@ -22,7 +18,7 @@ export function useGetConvoForMembers({
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (members: string[]) => {
|
||||
const {data} = await agent.api.chat.bsky.convo.getConvoForMembers(
|
||||
const {data} = await agent.chat.bsky.convo.getConvoForMembers(
|
||||
{members: members},
|
||||
{headers: DM_SERVICE_HEADERS},
|
||||
)
|
||||
@@ -30,7 +26,7 @@ export function useGetConvoForMembers({
|
||||
return data
|
||||
},
|
||||
onSuccess: data => {
|
||||
queryClient.setQueryData(CONVO_KEY(data.convo.id), data.convo)
|
||||
precacheConvoQuery(queryClient, data.convo)
|
||||
onSuccess?.(data)
|
||||
},
|
||||
onError: error => {
|
||||
@@ -39,26 +35,3 @@ export function useGetConvoForMembers({
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the conversation ID for a given DID. Returns null if it's not possible to message them.
|
||||
*/
|
||||
export function useMaybeConvoForUser(did: string) {
|
||||
const agent = useAgent()
|
||||
|
||||
return useQuery({
|
||||
queryKey: RQKEY(did),
|
||||
queryFn: async () => {
|
||||
const convo = await agent.api.chat.bsky.convo
|
||||
.getConvoForMembers({members: [did]}, {headers: DM_SERVICE_HEADERS})
|
||||
.catch(() => ({success: null}))
|
||||
|
||||
if (convo.success) {
|
||||
return convo.data.convo
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
staleTime: STALE.INFINITY,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {useMemo} from 'react'
|
||||
import {ChatBskyConvoLeaveConvo, ChatBskyConvoListConvos} from '@atproto/api'
|
||||
import {
|
||||
useMutation,
|
||||
@@ -8,7 +9,7 @@ import {
|
||||
import {logger} from '#/logger'
|
||||
import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {RQKEY as CONVO_LIST_KEY} from './list-conversations'
|
||||
import {RQKEY_ROOT as CONVO_LIST_KEY} from './list-conversations'
|
||||
|
||||
const RQKEY_ROOT = 'leave-convo'
|
||||
export function RQKEY(convoId: string | undefined) {
|
||||
@@ -35,7 +36,7 @@ export function useLeaveConvo(
|
||||
mutationFn: async () => {
|
||||
if (!convoId) throw new Error('No convoId provided')
|
||||
|
||||
const {data} = await agent.api.chat.bsky.convo.leaveConvo(
|
||||
const {data} = await agent.chat.bsky.convo.leaveConvo(
|
||||
{convoId},
|
||||
{headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
|
||||
)
|
||||
@@ -45,7 +46,7 @@ export function useLeaveConvo(
|
||||
onMutate: () => {
|
||||
let prevPages: ChatBskyConvoListConvos.OutputSchema[] = []
|
||||
queryClient.setQueryData(
|
||||
CONVO_LIST_KEY,
|
||||
[CONVO_LIST_KEY],
|
||||
(old?: {
|
||||
pageParams: Array<string | undefined>
|
||||
pages: Array<ChatBskyConvoListConvos.OutputSchema>
|
||||
@@ -67,13 +68,13 @@ export function useLeaveConvo(
|
||||
return {prevPages}
|
||||
},
|
||||
onSuccess: data => {
|
||||
queryClient.invalidateQueries({queryKey: CONVO_LIST_KEY})
|
||||
queryClient.invalidateQueries({queryKey: [CONVO_LIST_KEY]})
|
||||
onSuccess?.(data)
|
||||
},
|
||||
onError: (error, _, context) => {
|
||||
logger.error(error)
|
||||
queryClient.setQueryData(
|
||||
CONVO_LIST_KEY,
|
||||
[CONVO_LIST_KEY],
|
||||
(old?: {
|
||||
pageParams: Array<string | undefined>
|
||||
pages: Array<ChatBskyConvoListConvos.OutputSchema>
|
||||
@@ -85,7 +86,7 @@ export function useLeaveConvo(
|
||||
}
|
||||
},
|
||||
)
|
||||
queryClient.invalidateQueries({queryKey: CONVO_LIST_KEY})
|
||||
queryClient.invalidateQueries({queryKey: [CONVO_LIST_KEY]})
|
||||
onError?.(error)
|
||||
},
|
||||
})
|
||||
@@ -105,5 +106,8 @@ export function useLeftConvos() {
|
||||
filters: {mutationKey: [RQKEY_ROOT], status: 'success'},
|
||||
select: mutation => mutation.options.mutationKey?.[1] as string | undefined,
|
||||
})
|
||||
return [...pending, ...success].filter(id => id !== undefined)
|
||||
return useMemo(
|
||||
() => [...pending, ...success].filter(id => id !== undefined),
|
||||
[pending, success],
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
ChatBskyConvoDefs,
|
||||
ChatBskyConvoListConvos,
|
||||
moderateProfile,
|
||||
ModerationOpts,
|
||||
} from '@atproto/api'
|
||||
import {
|
||||
InfiniteData,
|
||||
@@ -23,26 +24,39 @@ import {useMessagesEventBus} from '#/state/messages/events'
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
|
||||
import {useAgent, useSession} from '#/state/session'
|
||||
import {useLeftConvos} from './leave-conversation'
|
||||
|
||||
export const RQKEY = ['convo-list']
|
||||
export const RQKEY_ROOT = 'convo-list'
|
||||
export const RQKEY = (
|
||||
status: 'accepted' | 'request' | 'all',
|
||||
readState: 'all' | 'unread' = 'all',
|
||||
) => [RQKEY_ROOT, status, readState]
|
||||
type RQPageParam = string | undefined
|
||||
|
||||
export function useListConvosQuery({
|
||||
enabled,
|
||||
status,
|
||||
readState = 'all',
|
||||
}: {
|
||||
enabled?: boolean
|
||||
status?: 'request' | 'accepted'
|
||||
readState?: 'all' | 'unread'
|
||||
} = {}) {
|
||||
const agent = useAgent()
|
||||
|
||||
return useInfiniteQuery({
|
||||
enabled,
|
||||
queryKey: RQKEY,
|
||||
queryKey: RQKEY(status ?? 'all', readState),
|
||||
queryFn: async ({pageParam}) => {
|
||||
const {data} = await agent.api.chat.bsky.convo.listConvos(
|
||||
{cursor: pageParam, limit: 20},
|
||||
const {data} = await agent.chat.bsky.convo.listConvos(
|
||||
{
|
||||
limit: 20,
|
||||
cursor: pageParam,
|
||||
readState: readState === 'unread' ? 'unread' : undefined,
|
||||
status,
|
||||
},
|
||||
{headers: DM_SERVICE_HEADERS},
|
||||
)
|
||||
|
||||
return data
|
||||
},
|
||||
initialPageParam: undefined as RQPageParam,
|
||||
@@ -50,9 +64,10 @@ export function useListConvosQuery({
|
||||
})
|
||||
}
|
||||
|
||||
const ListConvosContext = createContext<ChatBskyConvoDefs.ConvoView[] | null>(
|
||||
null,
|
||||
)
|
||||
const ListConvosContext = createContext<{
|
||||
accepted: ChatBskyConvoDefs.ConvoView[]
|
||||
request: ChatBskyConvoDefs.ConvoView[]
|
||||
} | null>(null)
|
||||
|
||||
export function useListConvos() {
|
||||
const ctx = useContext(ListConvosContext)
|
||||
@@ -62,12 +77,13 @@ export function useListConvos() {
|
||||
return ctx
|
||||
}
|
||||
|
||||
const empty = {accepted: [], request: []}
|
||||
export function ListConvosProvider({children}: {children: React.ReactNode}) {
|
||||
const {hasSession} = useSession()
|
||||
|
||||
if (!hasSession) {
|
||||
return (
|
||||
<ListConvosContext.Provider value={[]}>
|
||||
<ListConvosContext.Provider value={empty}>
|
||||
{children}
|
||||
</ListConvosContext.Provider>
|
||||
)
|
||||
@@ -81,20 +97,23 @@ export function ListConvosProviderInner({
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const {refetch, data} = useListConvosQuery()
|
||||
const {refetch, data} = useListConvosQuery({readState: 'unread'})
|
||||
const messagesBus = useMessagesEventBus()
|
||||
const queryClient = useQueryClient()
|
||||
const {currentConvoId} = useCurrentConvoId()
|
||||
const {currentAccount} = useSession()
|
||||
const leftConvos = useLeftConvos()
|
||||
|
||||
const debouncedRefetch = useMemo(
|
||||
() =>
|
||||
throttle(refetch, 500, {
|
||||
leading: true,
|
||||
trailing: true,
|
||||
}),
|
||||
[refetch],
|
||||
)
|
||||
const debouncedRefetch = useMemo(() => {
|
||||
const refetchAndInvalidate = () => {
|
||||
refetch()
|
||||
queryClient.invalidateQueries({queryKey: [RQKEY_ROOT]})
|
||||
}
|
||||
return throttle(refetchAndInvalidate, 500, {
|
||||
leading: true,
|
||||
trailing: true,
|
||||
})
|
||||
}, [refetch, queryClient])
|
||||
|
||||
useEffect(() => {
|
||||
const unsub = messagesBus.on(
|
||||
@@ -105,69 +124,159 @@ export function ListConvosProviderInner({
|
||||
if (ChatBskyConvoDefs.isLogBeginConvo(log)) {
|
||||
debouncedRefetch()
|
||||
} else if (ChatBskyConvoDefs.isLogLeaveConvo(log)) {
|
||||
queryClient.setQueryData(RQKEY, (old: ConvoListQueryData) =>
|
||||
optimisticDelete(log.convoId, old),
|
||||
queryClient.setQueriesData(
|
||||
{queryKey: [RQKEY_ROOT]},
|
||||
(old?: ConvoListQueryData) => optimisticDelete(log.convoId, old),
|
||||
)
|
||||
} else if (ChatBskyConvoDefs.isLogDeleteMessage(log)) {
|
||||
queryClient.setQueryData(RQKEY, (old: ConvoListQueryData) =>
|
||||
optimisticUpdate(log.convoId, old, convo => {
|
||||
if (
|
||||
(ChatBskyConvoDefs.isDeletedMessageView(log.message) ||
|
||||
ChatBskyConvoDefs.isMessageView(log.message)) &&
|
||||
(ChatBskyConvoDefs.isDeletedMessageView(convo.lastMessage) ||
|
||||
ChatBskyConvoDefs.isMessageView(convo.lastMessage))
|
||||
) {
|
||||
return log.message.id === convo.lastMessage.id
|
||||
? {
|
||||
...convo,
|
||||
rev: log.rev,
|
||||
lastMessage: log.message,
|
||||
}
|
||||
: convo
|
||||
} else {
|
||||
return convo
|
||||
}
|
||||
}),
|
||||
queryClient.setQueriesData(
|
||||
{queryKey: [RQKEY_ROOT]},
|
||||
(old?: ConvoListQueryData) =>
|
||||
optimisticUpdate(log.convoId, old, convo => {
|
||||
if (
|
||||
(ChatBskyConvoDefs.isDeletedMessageView(log.message) ||
|
||||
ChatBskyConvoDefs.isMessageView(log.message)) &&
|
||||
(ChatBskyConvoDefs.isDeletedMessageView(
|
||||
convo.lastMessage,
|
||||
) ||
|
||||
ChatBskyConvoDefs.isMessageView(convo.lastMessage))
|
||||
) {
|
||||
return log.message.id === convo.lastMessage.id
|
||||
? {
|
||||
...convo,
|
||||
rev: log.rev,
|
||||
lastMessage: log.message,
|
||||
}
|
||||
: convo
|
||||
} else {
|
||||
return convo
|
||||
}
|
||||
}),
|
||||
)
|
||||
} else if (ChatBskyConvoDefs.isLogCreateMessage(log)) {
|
||||
// Store in a new var to avoid TS errors due to closures.
|
||||
const logRef: ChatBskyConvoDefs.LogCreateMessage = log
|
||||
|
||||
queryClient.setQueryData(RQKEY, (old: ConvoListQueryData) => {
|
||||
if (!old) return old
|
||||
// Get all matching queries
|
||||
const queries = queryClient.getQueriesData<ConvoListQueryData>({
|
||||
queryKey: [RQKEY_ROOT],
|
||||
})
|
||||
|
||||
function updateConvo(convo: ChatBskyConvoDefs.ConvoView) {
|
||||
let unreadCount = convo.unreadCount
|
||||
if (convo.id !== currentConvoId) {
|
||||
if (
|
||||
ChatBskyConvoDefs.isMessageView(logRef.message) ||
|
||||
ChatBskyConvoDefs.isDeletedMessageView(logRef.message)
|
||||
) {
|
||||
if (logRef.message.sender.did !== currentAccount?.did) {
|
||||
unreadCount++
|
||||
// Check if convo exists in any query
|
||||
let foundConvo: ChatBskyConvoDefs.ConvoView | null = null
|
||||
for (const [_key, query] of queries) {
|
||||
if (!query) continue
|
||||
const convo = getConvoFromQueryData(logRef.convoId, query)
|
||||
if (convo) {
|
||||
foundConvo = convo
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundConvo) {
|
||||
// Convo not found, trigger refetch
|
||||
debouncedRefetch()
|
||||
return
|
||||
}
|
||||
|
||||
// Update the convo
|
||||
const updatedConvo = {
|
||||
...foundConvo,
|
||||
rev: logRef.rev,
|
||||
lastMessage: logRef.message,
|
||||
unreadCount:
|
||||
foundConvo.id !== currentConvoId
|
||||
? (ChatBskyConvoDefs.isMessageView(logRef.message) ||
|
||||
ChatBskyConvoDefs.isDeletedMessageView(logRef.message)) &&
|
||||
logRef.message.sender.did !== currentAccount?.did
|
||||
? foundConvo.unreadCount + 1
|
||||
: foundConvo.unreadCount
|
||||
: 0,
|
||||
}
|
||||
|
||||
function filterConvoFromPage(convo: ChatBskyConvoDefs.ConvoView[]) {
|
||||
return convo.filter(c => c.id !== logRef.convoId)
|
||||
}
|
||||
|
||||
// Update all matching queries
|
||||
function updateFn(old?: ConvoListQueryData) {
|
||||
if (!old) return old
|
||||
return {
|
||||
...old,
|
||||
pages: old.pages.map((page, i) => {
|
||||
if (i === 0) {
|
||||
return {
|
||||
...page,
|
||||
convos: [
|
||||
updatedConvo,
|
||||
...filterConvoFromPage(page.convos),
|
||||
],
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unreadCount = 0
|
||||
}
|
||||
|
||||
return {
|
||||
return {
|
||||
...page,
|
||||
convos: filterConvoFromPage(page.convos),
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
// always update the unread one
|
||||
queryClient.setQueriesData(
|
||||
{queryKey: RQKEY('all', 'unread')},
|
||||
(old?: ConvoListQueryData) =>
|
||||
old
|
||||
? updateFn(old)
|
||||
: ({
|
||||
pageParams: [undefined],
|
||||
pages: [{convos: [updatedConvo], cursor: undefined}],
|
||||
} satisfies ConvoListQueryData),
|
||||
)
|
||||
// update the other ones based on status of the incoming message
|
||||
if (updatedConvo.status === 'accepted') {
|
||||
queryClient.setQueriesData(
|
||||
{queryKey: RQKEY('accepted')},
|
||||
updateFn,
|
||||
)
|
||||
} else if (updatedConvo.status === 'request') {
|
||||
queryClient.setQueriesData({queryKey: RQKEY('request')}, updateFn)
|
||||
}
|
||||
} else if (ChatBskyConvoDefs.isLogReadMessage(log)) {
|
||||
const logRef: ChatBskyConvoDefs.LogReadMessage = log
|
||||
queryClient.setQueriesData(
|
||||
{queryKey: [RQKEY_ROOT]},
|
||||
(old?: ConvoListQueryData) =>
|
||||
optimisticUpdate(logRef.convoId, old, convo => ({
|
||||
...convo,
|
||||
unreadCount: 0,
|
||||
rev: logRef.rev,
|
||||
lastMessage: logRef.message,
|
||||
unreadCount,
|
||||
})),
|
||||
)
|
||||
} else if (ChatBskyConvoDefs.isLogAcceptConvo(log)) {
|
||||
const logRef: ChatBskyConvoDefs.LogAcceptConvo = log
|
||||
const requests = queryClient.getQueryData<ConvoListQueryData>(
|
||||
RQKEY('request'),
|
||||
)
|
||||
if (!requests) {
|
||||
debouncedRefetch()
|
||||
return
|
||||
}
|
||||
const acceptedConvo = getConvoFromQueryData(log.convoId, requests)
|
||||
if (!acceptedConvo) {
|
||||
debouncedRefetch()
|
||||
return
|
||||
}
|
||||
queryClient.setQueryData(
|
||||
RQKEY('request'),
|
||||
(old?: ConvoListQueryData) =>
|
||||
optimisticDelete(logRef.convoId, old),
|
||||
)
|
||||
queryClient.setQueriesData(
|
||||
{queryKey: RQKEY('accepted')},
|
||||
(old?: ConvoListQueryData) => {
|
||||
if (!old) {
|
||||
debouncedRefetch()
|
||||
return old
|
||||
}
|
||||
}
|
||||
|
||||
function filterConvoFromPage(
|
||||
convo: ChatBskyConvoDefs.ConvoView[],
|
||||
) {
|
||||
return convo.filter(c => c.id !== logRef.convoId)
|
||||
}
|
||||
|
||||
const existingConvo = getConvoFromQueryData(logRef.convoId, old)
|
||||
|
||||
if (existingConvo) {
|
||||
return {
|
||||
...old,
|
||||
pages: old.pages.map((page, i) => {
|
||||
@@ -175,26 +284,38 @@ export function ListConvosProviderInner({
|
||||
return {
|
||||
...page,
|
||||
convos: [
|
||||
updateConvo(existingConvo),
|
||||
...filterConvoFromPage(page.convos),
|
||||
{...acceptedConvo, status: 'accepted'},
|
||||
...page.convos,
|
||||
],
|
||||
}
|
||||
}
|
||||
return {
|
||||
...page,
|
||||
convos: filterConvoFromPage(page.convos),
|
||||
}
|
||||
return page
|
||||
}),
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* We received a message from an conversation old enough that
|
||||
* it doesn't exist in the query cache, meaning we need to
|
||||
* refetch and bump the old convo to the top.
|
||||
*/
|
||||
debouncedRefetch()
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
} else if (ChatBskyConvoDefs.isLogMuteConvo(log)) {
|
||||
const logRef: ChatBskyConvoDefs.LogMuteConvo = log
|
||||
queryClient.setQueriesData(
|
||||
{queryKey: [RQKEY_ROOT]},
|
||||
(old?: ConvoListQueryData) =>
|
||||
optimisticUpdate(logRef.convoId, old, convo => ({
|
||||
...convo,
|
||||
muted: true,
|
||||
rev: logRef.rev,
|
||||
})),
|
||||
)
|
||||
} else if (ChatBskyConvoDefs.isLogUnmuteConvo(log)) {
|
||||
const logRef: ChatBskyConvoDefs.LogUnmuteConvo = log
|
||||
queryClient.setQueriesData(
|
||||
{queryKey: [RQKEY_ROOT]},
|
||||
(old?: ConvoListQueryData) =>
|
||||
optimisticUpdate(logRef.convoId, old, convo => ({
|
||||
...convo,
|
||||
muted: false,
|
||||
rev: logRef.rev,
|
||||
})),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -208,15 +329,21 @@ export function ListConvosProviderInner({
|
||||
}, [
|
||||
messagesBus,
|
||||
currentConvoId,
|
||||
refetch,
|
||||
queryClient,
|
||||
currentAccount?.did,
|
||||
debouncedRefetch,
|
||||
])
|
||||
|
||||
const ctx = useMemo(() => {
|
||||
return data?.pages.flatMap(page => page.convos) ?? []
|
||||
}, [data])
|
||||
const convos =
|
||||
data?.pages
|
||||
.flatMap(page => page.convos)
|
||||
.filter(convo => !leftConvos.includes(convo.id)) ?? []
|
||||
return {
|
||||
accepted: convos.filter(conv => conv.status === 'accepted'),
|
||||
request: convos.filter(conv => conv.status === 'request'),
|
||||
}
|
||||
}, [data, leftConvos])
|
||||
|
||||
return (
|
||||
<ListConvosContext.Provider value={ctx}>
|
||||
@@ -228,38 +355,76 @@ export function ListConvosProviderInner({
|
||||
export function useUnreadMessageCount() {
|
||||
const {currentConvoId} = useCurrentConvoId()
|
||||
const {currentAccount} = useSession()
|
||||
const convos = useListConvos()
|
||||
const {accepted, request} = useListConvos()
|
||||
const moderationOpts = useModerationOpts()
|
||||
|
||||
const count = useMemo(() => {
|
||||
return (
|
||||
convos
|
||||
.filter(convo => convo.id !== currentConvoId)
|
||||
.reduce((acc, convo) => {
|
||||
const otherMember = convo.members.find(
|
||||
member => member.did !== currentAccount?.did,
|
||||
)
|
||||
|
||||
if (!otherMember || !moderationOpts) return acc
|
||||
|
||||
const moderation = moderateProfile(otherMember, moderationOpts)
|
||||
const shouldIgnore =
|
||||
convo.muted ||
|
||||
moderation.blocked ||
|
||||
otherMember.did === 'missing.invalid'
|
||||
const unreadCount = !shouldIgnore && convo.unreadCount > 0 ? 1 : 0
|
||||
|
||||
return acc + unreadCount
|
||||
}, 0) ?? 0
|
||||
return useMemo<{
|
||||
count: number
|
||||
numUnread?: string
|
||||
hasNew: boolean
|
||||
}>(() => {
|
||||
const acceptedCount = calculateCount(
|
||||
accepted,
|
||||
currentAccount?.did,
|
||||
currentConvoId,
|
||||
moderationOpts,
|
||||
)
|
||||
}, [convos, currentAccount?.did, currentConvoId, moderationOpts])
|
||||
|
||||
return useMemo(() => {
|
||||
return {
|
||||
count,
|
||||
numUnread: count > 0 ? (count > 10 ? '10+' : String(count)) : undefined,
|
||||
const requestCount = calculateCount(
|
||||
request,
|
||||
currentAccount?.did,
|
||||
currentConvoId,
|
||||
moderationOpts,
|
||||
)
|
||||
if (acceptedCount > 0) {
|
||||
const total = acceptedCount + Math.min(requestCount, 1)
|
||||
return {
|
||||
count: total,
|
||||
numUnread: total > 10 ? '10+' : String(total),
|
||||
// only needed when numUnread is undefined
|
||||
hasNew: false,
|
||||
}
|
||||
} else if (requestCount > 0) {
|
||||
return {
|
||||
count: 1,
|
||||
numUnread: undefined,
|
||||
hasNew: true,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
count: 0,
|
||||
numUnread: undefined,
|
||||
hasNew: false,
|
||||
}
|
||||
}
|
||||
}, [count])
|
||||
}, [accepted, request, currentAccount?.did, currentConvoId, moderationOpts])
|
||||
}
|
||||
|
||||
function calculateCount(
|
||||
convos: ChatBskyConvoDefs.ConvoView[],
|
||||
currentAccountDid: string | undefined,
|
||||
currentConvoId: string | undefined,
|
||||
moderationOpts: ModerationOpts | undefined,
|
||||
) {
|
||||
return (
|
||||
convos
|
||||
.filter(convo => convo.id !== currentConvoId)
|
||||
.reduce((acc, convo) => {
|
||||
const otherMember = convo.members.find(
|
||||
member => member.did !== currentAccountDid,
|
||||
)
|
||||
|
||||
if (!otherMember || !moderationOpts) return acc
|
||||
|
||||
const moderation = moderateProfile(otherMember, moderationOpts)
|
||||
const shouldIgnore =
|
||||
convo.muted ||
|
||||
moderation.blocked ||
|
||||
otherMember.handle === 'missing.invalid'
|
||||
const unreadCount = !shouldIgnore && convo.unreadCount > 0 ? 1 : 0
|
||||
|
||||
return acc + unreadCount
|
||||
}, 0) ?? 0
|
||||
)
|
||||
}
|
||||
|
||||
export type ConvoListQueryData = {
|
||||
@@ -272,12 +437,16 @@ export function useOnMarkAsRead() {
|
||||
|
||||
return useCallback(
|
||||
(chatId: string) => {
|
||||
queryClient.setQueryData(RQKEY, (old: ConvoListQueryData) => {
|
||||
return optimisticUpdate(chatId, old, convo => ({
|
||||
...convo,
|
||||
unreadCount: 0,
|
||||
}))
|
||||
})
|
||||
queryClient.setQueriesData(
|
||||
{queryKey: [RQKEY_ROOT]},
|
||||
(old?: ConvoListQueryData) => {
|
||||
if (!old) return old
|
||||
return optimisticUpdate(chatId, old, convo => ({
|
||||
...convo,
|
||||
unreadCount: 0,
|
||||
}))
|
||||
},
|
||||
)
|
||||
},
|
||||
[queryClient],
|
||||
)
|
||||
@@ -285,10 +454,12 @@ export function useOnMarkAsRead() {
|
||||
|
||||
function optimisticUpdate(
|
||||
chatId: string,
|
||||
old: ConvoListQueryData,
|
||||
updateFn: (convo: ChatBskyConvoDefs.ConvoView) => ChatBskyConvoDefs.ConvoView,
|
||||
old?: ConvoListQueryData,
|
||||
updateFn?: (
|
||||
convo: ChatBskyConvoDefs.ConvoView,
|
||||
) => ChatBskyConvoDefs.ConvoView,
|
||||
) {
|
||||
if (!old) return old
|
||||
if (!old || !updateFn) return old
|
||||
|
||||
return {
|
||||
...old,
|
||||
@@ -301,7 +472,7 @@ function optimisticUpdate(
|
||||
}
|
||||
}
|
||||
|
||||
function optimisticDelete(chatId: string, old: ConvoListQueryData) {
|
||||
function optimisticDelete(chatId: string, old?: ConvoListQueryData) {
|
||||
if (!old) return old
|
||||
|
||||
return {
|
||||
@@ -331,7 +502,7 @@ export function* findAllProfilesInQueryData(
|
||||
const queryDatas = queryClient.getQueriesData<
|
||||
InfiniteData<ChatBskyConvoListConvos.OutputSchema>
|
||||
>({
|
||||
queryKey: RQKEY,
|
||||
queryKey: [RQKEY_ROOT],
|
||||
})
|
||||
for (const [_queryKey, queryData] of queryDatas) {
|
||||
if (!queryData?.pages) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import {InfiniteData, useMutation, useQueryClient} from '@tanstack/react-query'
|
||||
import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {RQKEY as CONVO_KEY} from './conversation'
|
||||
import {RQKEY as CONVO_LIST_KEY} from './list-conversations'
|
||||
import {RQKEY_ROOT as CONVO_LIST_KEY} from './list-conversations'
|
||||
|
||||
export function useMuteConvo(
|
||||
convoId: string | undefined,
|
||||
@@ -53,7 +53,7 @@ export function useMuteConvo(
|
||||
)
|
||||
queryClient.setQueryData<
|
||||
InfiniteData<ChatBskyConvoListConvos.OutputSchema>
|
||||
>(CONVO_LIST_KEY, prev => {
|
||||
>([CONVO_LIST_KEY], prev => {
|
||||
if (!prev?.pages) return
|
||||
return {
|
||||
...prev,
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import {ChatBskyConvoListConvos} from '@atproto/api'
|
||||
import {useMutation, useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {logger} from '#/logger'
|
||||
import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {RQKEY as CONVO_LIST_KEY} from './list-conversations'
|
||||
|
||||
export function useUpdateAllRead(
|
||||
status: 'accepted' | 'request',
|
||||
{
|
||||
onSuccess,
|
||||
onMutate,
|
||||
onError,
|
||||
}: {
|
||||
onMutate?: () => void
|
||||
onSuccess?: () => void
|
||||
onError?: (error: Error) => void
|
||||
},
|
||||
) {
|
||||
const queryClient = useQueryClient()
|
||||
const agent = useAgent()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async () => {
|
||||
const {data} = await agent.chat.bsky.convo.updateAllRead(
|
||||
{status},
|
||||
{headers: DM_SERVICE_HEADERS, encoding: 'application/json'},
|
||||
)
|
||||
|
||||
return data
|
||||
},
|
||||
onMutate: () => {
|
||||
let prevPages: ChatBskyConvoListConvos.OutputSchema[] = []
|
||||
queryClient.setQueryData(
|
||||
CONVO_LIST_KEY(status),
|
||||
(old?: {
|
||||
pageParams: Array<string | undefined>
|
||||
pages: Array<ChatBskyConvoListConvos.OutputSchema>
|
||||
}) => {
|
||||
if (!old) return old
|
||||
prevPages = old.pages
|
||||
return {
|
||||
...old,
|
||||
pages: old.pages.map(page => {
|
||||
return {
|
||||
...page,
|
||||
convos: page.convos.map(convo => {
|
||||
return {
|
||||
...convo,
|
||||
unreadCount: 0,
|
||||
}
|
||||
}),
|
||||
}
|
||||
}),
|
||||
}
|
||||
},
|
||||
)
|
||||
// remove unread convos from the badge query
|
||||
queryClient.setQueryData(
|
||||
CONVO_LIST_KEY('all', 'unread'),
|
||||
(old?: {
|
||||
pageParams: Array<string | undefined>
|
||||
pages: Array<ChatBskyConvoListConvos.OutputSchema>
|
||||
}) => {
|
||||
if (!old) return old
|
||||
return {
|
||||
...old,
|
||||
pages: old.pages.map(page => {
|
||||
return {
|
||||
...page,
|
||||
convos: page.convos.filter(convo => convo.status !== status),
|
||||
}
|
||||
}),
|
||||
}
|
||||
},
|
||||
)
|
||||
onMutate?.()
|
||||
return {prevPages}
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({queryKey: CONVO_LIST_KEY(status)})
|
||||
onSuccess?.()
|
||||
},
|
||||
onError: (error, _, context) => {
|
||||
logger.error(error)
|
||||
queryClient.setQueryData(
|
||||
CONVO_LIST_KEY(status),
|
||||
(old?: {
|
||||
pageParams: Array<string | undefined>
|
||||
pages: Array<ChatBskyConvoListConvos.OutputSchema>
|
||||
}) => {
|
||||
if (!old) return old
|
||||
return {
|
||||
...old,
|
||||
pages: context?.prevPages || old.pages,
|
||||
}
|
||||
},
|
||||
)
|
||||
queryClient.invalidateQueries({queryKey: CONVO_LIST_KEY(status)})
|
||||
queryClient.invalidateQueries({queryKey: CONVO_LIST_KEY('all', 'unread')})
|
||||
onError?.(error)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -37,7 +37,7 @@ import {
|
||||
ProgressGuideAction,
|
||||
useProgressGuideControls,
|
||||
} from '../shell/progress-guide'
|
||||
import {RQKEY as RQKEY_LIST_CONVOS} from './messages/list-conversations'
|
||||
import {RQKEY_ROOT as RQKEY_LIST_CONVOS} from './messages/list-conversations'
|
||||
import {RQKEY as RQKEY_MY_BLOCKED} from './my-blocked-accounts'
|
||||
import {RQKEY as RQKEY_MY_MUTED} from './my-muted-accounts'
|
||||
|
||||
@@ -456,7 +456,7 @@ export function useProfileBlockMutationQueue(
|
||||
updateProfileShadow(queryClient, did, {
|
||||
blockingUri: finalBlockingUri,
|
||||
})
|
||||
queryClient.invalidateQueries({queryKey: RQKEY_LIST_CONVOS})
|
||||
queryClient.invalidateQueries({queryKey: [RQKEY_LIST_CONVOS]})
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user