Remove leave-convo suppression, use real optimistic update (#10834)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-06-10 11:58:16 +03:00
committed by GitHub
parent f321122656
commit 9a2e636c17
4 changed files with 48 additions and 77 deletions
@@ -1,18 +1,18 @@
import {useMemo} from 'react'
import {
type ChatBskyConvoLeaveConvo,
type ChatBskyConvoListConvos,
} from '@atproto/api'
import {
useMutation,
useMutationState,
useQueryClient,
} from '@tanstack/react-query'
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 {
type ConvoRequestListQueryData,
optimisticDelete as optimisticDeleteRequest,
RQKEY_ROOT as REQUESTS_RQKEY_ROOT,
} from './list-conversation-requests'
import {RQKEY_ROOT as CONVO_LIST_KEY} from './list-conversations'
const RQKEY_ROOT = 'leave-convo'
@@ -20,6 +20,11 @@ export function RQKEY(convoId: string | undefined) {
return [RQKEY_ROOT, convoId]
}
type ConvoListQueryData = {
pageParams: Array<string | undefined>
pages: Array<ChatBskyConvoListConvos.OutputSchema>
}
export function useLeaveConvo(
convoId: string | undefined,
{
@@ -48,31 +53,37 @@ export function useLeaveConvo(
return data
},
onMutate: () => {
let prevPages: ChatBskyConvoListConvos.OutputSchema[] = []
queryClient.setQueryData(
[CONVO_LIST_KEY],
(old?: {
pageParams: Array<string | undefined>
pages: Array<ChatBskyConvoListConvos.OutputSchema>
}) => {
const prevConvoListQueries =
queryClient.getQueriesData<ConvoListQueryData>({
queryKey: [CONVO_LIST_KEY],
})
queryClient.setQueriesData<ConvoListQueryData>(
{queryKey: [CONVO_LIST_KEY]},
old => {
if (!old) return old
prevPages = old.pages
return {
...old,
pages: old.pages.map(page => {
return {
...page,
convos: page.convos.filter(convo => convo.id !== convoId),
}
}),
pages: old.pages.map(page => ({
...page,
convos: page.convos.filter(convo => convo.id !== convoId),
})),
}
},
)
const prevRequestsQueries =
queryClient.getQueriesData<ConvoRequestListQueryData>({
queryKey: [REQUESTS_RQKEY_ROOT],
})
queryClient.setQueriesData<ConvoRequestListQueryData>(
{queryKey: [REQUESTS_RQKEY_ROOT]},
old => (convoId ? optimisticDeleteRequest(convoId, old) : old),
)
onMutate?.()
return {prevPages}
return {prevConvoListQueries, prevRequestsQueries}
},
onSuccess: data => {
void queryClient.invalidateQueries({queryKey: [CONVO_LIST_KEY]})
void queryClient.invalidateQueries({queryKey: [REQUESTS_RQKEY_ROOT]})
if (convoId) {
void invalidateJoinLinkPreviewsForConvo(queryClient, convoId)
}
@@ -80,41 +91,19 @@ export function useLeaveConvo(
},
onError: (error, _, context) => {
logger.error(error)
queryClient.setQueryData(
[CONVO_LIST_KEY],
(old?: {
pageParams: Array<string | undefined>
pages: Array<ChatBskyConvoListConvos.OutputSchema>
}) => {
if (!old) return old
return {
...old,
pages: context?.prevPages || old.pages,
}
},
)
if (context?.prevConvoListQueries) {
for (const [queryKey, prevData] of context.prevConvoListQueries) {
queryClient.setQueryData(queryKey, prevData)
}
}
if (context?.prevRequestsQueries) {
for (const [queryKey, prevData] of context.prevRequestsQueries) {
queryClient.setQueryData(queryKey, prevData)
}
}
void queryClient.invalidateQueries({queryKey: [CONVO_LIST_KEY]})
void queryClient.invalidateQueries({queryKey: [REQUESTS_RQKEY_ROOT]})
onError?.(error)
},
})
}
/**
* Gets currently pending and successful leave convo mutations
*
* @returns Array of `convoId`
*/
export function useLeftConvos() {
const pending = useMutationState({
filters: {mutationKey: [RQKEY_ROOT], status: 'pending'},
select: mutation => mutation.options.mutationKey?.[1] as string | undefined,
})
const success = useMutationState({
filters: {mutationKey: [RQKEY_ROOT], status: 'success'},
select: mutation => mutation.options.mutationKey?.[1] as string | undefined,
})
return useMemo(
() => [...pending, ...success].filter(id => id !== undefined),
[pending, success],
)
}
@@ -25,7 +25,6 @@ import {useAgeAssurance} from '#/ageAssurance'
import {type AgeAssuranceFlags} from '#/ageAssurance/types'
import * as bsky from '#/types/bsky'
import {RQKEY as CONVO_KEY} from './conversation'
import {useLeftConvos} from './leave-conversation'
import {
type ConvoRequestListQueryData,
optimisticDelete as optimisticDeleteRequest,
@@ -136,7 +135,6 @@ export function ListConvosProviderInner({
const queryClient = useQueryClient()
const {currentConvoId} = useCurrentConvoId()
const {currentAccount} = useSession()
const leftConvos = useLeftConvos()
const debouncedRefetch = useMemo(() => {
const refetchAndInvalidate = () => {
@@ -664,15 +662,12 @@ export function ListConvosProviderInner({
])
const ctx = useMemo(() => {
const convos =
data?.pages
.flatMap(page => page.convos)
.filter(convo => !leftConvos.includes(convo.id)) ?? []
const convos = data?.pages.flatMap(page => page.convos) ?? []
return {
accepted: convos.filter(conv => conv.status === 'accepted'),
request: convos.filter(conv => conv.status === 'request'),
}
}, [data, leftConvos])
}, [data])
return (
<ListConvosContext.Provider value={ctx}>