Optimistically update chat invite button status (#10870)
This commit is contained in:
@@ -20,6 +20,7 @@ import {logger} from '#/logger'
|
|||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
import {
|
import {
|
||||||
invalidateJoinLinkPreviewsForCode,
|
invalidateJoinLinkPreviewsForCode,
|
||||||
|
setJoinLinkPreviewRequestedForCode,
|
||||||
useJoinLinkPreviewsQuery,
|
useJoinLinkPreviewsQuery,
|
||||||
} from '#/state/queries/join-links'
|
} from '#/state/queries/join-links'
|
||||||
import {useRequestJoinGroupChat} from '#/state/queries/messages/request-join-group-chat'
|
import {useRequestJoinGroupChat} from '#/state/queries/messages/request-join-group-chat'
|
||||||
@@ -104,9 +105,14 @@ function GroupChatJoinDialogContent({code}: {code?: string}) {
|
|||||||
const {mutate: joinGroupChat, isPending: isJoinPending} =
|
const {mutate: joinGroupChat, isPending: isJoinPending} =
|
||||||
useRequestJoinGroupChat({
|
useRequestJoinGroupChat({
|
||||||
onSuccess: data => {
|
onSuccess: data => {
|
||||||
if (code) void invalidateJoinLinkPreviewsForCode(queryClient, code)
|
|
||||||
switch (data.status) {
|
switch (data.status) {
|
||||||
case 'pending':
|
case 'pending':
|
||||||
|
// Optimistically mark the link as requested so any invite cards
|
||||||
|
// backed by the preview cache (e.g. the DM embed) flip to
|
||||||
|
// "Requested" right away, rather than waiting on a server refetch
|
||||||
|
// that can lag behind the write.
|
||||||
|
if (code)
|
||||||
|
setJoinLinkPreviewRequestedForCode(queryClient, code, true)
|
||||||
ax.metric('groupchat:inviteLink:redeem', {})
|
ax.metric('groupchat:inviteLink:redeem', {})
|
||||||
control.close(() => {
|
control.close(() => {
|
||||||
Toast.show(
|
Toast.show(
|
||||||
@@ -116,6 +122,10 @@ function GroupChatJoinDialogContent({code}: {code?: string}) {
|
|||||||
break
|
break
|
||||||
case 'joined': {
|
case 'joined': {
|
||||||
if (data.convo && data.convo.id) {
|
if (data.convo && data.convo.id) {
|
||||||
|
// Refetch so invite cards pick up the resolved convo and switch
|
||||||
|
// their action to "Open chat".
|
||||||
|
if (code)
|
||||||
|
void invalidateJoinLinkPreviewsForCode(queryClient, code)
|
||||||
ax.metric('groupchat:inviteLink:redeem', {})
|
ax.metric('groupchat:inviteLink:redeem', {})
|
||||||
control.close(() => {
|
control.close(() => {
|
||||||
Toast.show(l`Successfully joined the group chat!`)
|
Toast.show(l`Successfully joined the group chat!`)
|
||||||
@@ -172,6 +182,9 @@ function GroupChatJoinDialogContent({code}: {code?: string}) {
|
|||||||
const {mutate: withdrawRequest, isPending: isWithdrawPending} =
|
const {mutate: withdrawRequest, isPending: isWithdrawPending} =
|
||||||
useWithdrawJoinGroupChatRequest({
|
useWithdrawJoinGroupChatRequest({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
|
// Optimistically clear the requested state so invite cards backed by
|
||||||
|
// the preview cache flip back to "Request to join" right away.
|
||||||
|
if (code) setJoinLinkPreviewRequestedForCode(queryClient, code, false)
|
||||||
control.close(() => {
|
control.close(() => {
|
||||||
Toast.show(l`Join request rescinded.`)
|
Toast.show(l`Join request rescinded.`)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -76,6 +76,55 @@ export function invalidateJoinLinkPreviewsForCode(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optimistically set whether the viewer has requested to join the link with the
|
||||||
|
* given code, across any cached join link preview queries. Used right after a
|
||||||
|
* successful join request (requested = true) or withdrawal (requested = false)
|
||||||
|
* so the UI ("Requested" vs "Request to join") updates immediately, without
|
||||||
|
* waiting on a server refetch that can lag behind the write.
|
||||||
|
*/
|
||||||
|
export function setJoinLinkPreviewRequestedForCode(
|
||||||
|
queryClient: QueryClient,
|
||||||
|
code: string,
|
||||||
|
requested: boolean,
|
||||||
|
) {
|
||||||
|
queryClient.setQueriesData<ChatBskyGroupGetJoinLinkPreviews.OutputSchema>(
|
||||||
|
{
|
||||||
|
predicate: query => {
|
||||||
|
const [root, args] = query.queryKey as Partial<
|
||||||
|
StructuredQueryKey<{codes?: string[]}>
|
||||||
|
>
|
||||||
|
return (
|
||||||
|
root === joinLinkPreviewQueryKeyRoot &&
|
||||||
|
Array.isArray(args?.codes) &&
|
||||||
|
args.codes.includes(code)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
old => {
|
||||||
|
if (!old) return old
|
||||||
|
return {
|
||||||
|
...old,
|
||||||
|
joinLinkPreviews: old.joinLinkPreviews.map(preview => {
|
||||||
|
if (
|
||||||
|
ChatBskyGroupDefs.isJoinLinkPreviewView(preview) &&
|
||||||
|
preview.code === code
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
...preview,
|
||||||
|
viewer: {
|
||||||
|
...preview.viewer,
|
||||||
|
requestedAt: requested ? new Date().toISOString() : undefined,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return preview
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalidate any join link preview queries that resolved to the given convo.
|
* Invalidate any join link preview queries that resolved to the given convo.
|
||||||
* The code isn't always known to the viewer (e.g. when they're a regular
|
* The code isn't always known to the viewer (e.g. when they're a regular
|
||||||
|
|||||||
Reference in New Issue
Block a user