Optimistic hidden replies (#4977)

This commit is contained in:
Eric Bailey 2024-08-23 14:35:48 -05:00 committed by GitHub
parent 5ec8761b29
commit 425dd5f27f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 70 additions and 129 deletions

View file

@ -21,7 +21,6 @@ export interface PostShadow {
repostUri: string | undefined
isDeleted: boolean
embed: AppBskyEmbedRecord.View | AppBskyEmbedRecordWithMedia.View | undefined
threadgateView: AppBskyFeedDefs.ThreadgateView | undefined
}
export const POST_TOMBSTONE = Symbol('PostTombstone')
@ -105,16 +104,6 @@ function mergeShadow(
}
}
let threadgateView: typeof post.threadgate
if ('threadgateView' in shadow && !post.threadgate) {
if (
AppBskyFeedDefs.isThreadgateView(shadow.threadgateView) ||
shadow.threadgateView === undefined
) {
threadgateView = shadow.threadgateView
}
}
return castAsShadow({
...post,
embed: embed || post.embed,
@ -125,8 +114,6 @@ function mergeShadow(
like: 'likeUri' in shadow ? shadow.likeUri : post.viewer?.like,
repost: 'repostUri' in shadow ? shadow.repostUri : post.viewer?.repost,
},
// always prefer real post data
threadgate: post.threadgate || threadgateView,
})
}

View file

@ -88,7 +88,10 @@ export type ThreadModerationCache = WeakMap<ThreadNode, ModerationDecision>
export function usePostThreadQuery(uri: string | undefined) {
const queryClient = useQueryClient()
const agent = useAgent()
return useQuery<ThreadNode, Error>({
return useQuery<
{thread: ThreadNode; threadgate?: AppBskyFeedDefs.ThreadgateView},
Error
>({
gcTime: 0,
queryKey: RQKEY(uri || ''),
async queryFn() {
@ -99,16 +102,21 @@ export function usePostThreadQuery(uri: string | undefined) {
if (res.success) {
const thread = responseToThreadNodes(res.data.thread)
annotateSelfThread(thread)
return thread
return {
thread,
threadgate: res.data.threadgate as
| AppBskyFeedDefs.ThreadgateView
| undefined,
}
}
return {type: 'unknown', uri: uri!}
return {thread: {type: 'unknown', uri: uri!}}
},
enabled: !!uri,
placeholderData: () => {
if (!uri) return
const post = findPostInQueryData(queryClient, uri)
if (post) {
return post
return {thread: post}
}
return undefined
},

View file

@ -9,12 +9,10 @@ import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
import {networkRetry, retry} from '#/lib/async/retry'
import {until} from '#/lib/async/until'
import {updatePostShadow} from '#/state/cache/post-shadow'
import {STALE} from '#/state/queries'
import {RQKEY_ROOT as postThreadQueryKeyRoot} from '#/state/queries/post-thread'
import {ThreadgateAllowUISetting} from '#/state/queries/threadgate/types'
import {
createTempThreadgateView,
createThreadgateRecord,
mergeThreadgateRecords,
threadgateAllowUISettingToAllowRecordValue,
@ -33,18 +31,16 @@ export const createThreadgateRecordQueryKey = (uri: string) => [
]
export function useThreadgateRecordQuery({
enabled,
postUri,
initialData,
}: {
enabled?: boolean
postUri?: string
initialData?: AppBskyFeedThreadgate.Record
} = {}) {
const agent = useAgent()
return useQuery({
enabled: enabled ?? !!postUri,
enabled: !!postUri,
queryKey: createThreadgateRecordQueryKey(postUri || ''),
placeholderData: initialData,
staleTime: STALE.MINUTES.ONE,
@ -344,26 +340,17 @@ export function useToggleReplyVisibilityMutation() {
}
})
},
onSuccess(_, {postUri, replyUri}) {
updatePostShadow(queryClient, postUri, {
threadgateView: createTempThreadgateView({
postUri,
hiddenReplies: [replyUri],
}),
})
onSuccess() {
queryClient.invalidateQueries({
queryKey: [threadgateRecordQueryKeyRoot],
})
},
onError(_, {postUri, replyUri, action}) {
onError(_, {replyUri, action}) {
if (action === 'hide') {
hiddenReplies.removeHiddenReplyUri(replyUri)
} else if (action === 'show') {
hiddenReplies.addHiddenReplyUri(replyUri)
}
updatePostShadow(queryClient, postUri, {
threadgateView: undefined,
})
},
})
}

View file

@ -139,23 +139,3 @@ export function createThreadgateRecord(
hiddenReplies: threadgate.hiddenReplies || [],
}
}
export function createTempThreadgateView({
postUri,
hiddenReplies,
}: Pick<AppBskyFeedThreadgate.Record, 'hiddenReplies'> & {
postUri: string
}): AppBskyFeedDefs.ThreadgateView {
const record: AppBskyFeedThreadgate.Record = {
$type: 'app.bsky.feed.threadgate',
post: postUri,
allow: undefined,
hiddenReplies,
createdAt: new Date().toISOString(),
}
return {
$type: 'app.bsky.feed.defs#threadgateView',
uri: postUri,
record,
}
}

View file

@ -1,4 +1,5 @@
import React from 'react'
import {AppBskyFeedThreadgate} from '@atproto/api'
type StateContext = {
uris: Set<string>
@ -67,3 +68,18 @@ export function useThreadgateHiddenReplyUris() {
export function useThreadgateHiddenReplyUrisAPI() {
return React.useContext(ApiContext)
}
export function useMergedThreadgateHiddenReplies({
threadgateRecord,
}: {
threadgateRecord?: AppBskyFeedThreadgate.Record
}) {
const {uris, recentlyUnhiddenUris} = useThreadgateHiddenReplyUris()
return React.useMemo(() => {
const set = new Set([...(threadgateRecord?.hiddenReplies || []), ...uris])
for (const uri of recentlyUnhiddenUris) {
set.delete(uri)
}
return set
}, [uris, recentlyUnhiddenUris, threadgateRecord])
}