diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/state/cache/post-shadow.ts | 13 | ||||
-rw-r--r-- | src/state/queries/post-thread.ts | 16 | ||||
-rw-r--r-- | src/state/queries/threadgate/index.ts | 19 | ||||
-rw-r--r-- | src/state/queries/threadgate/util.ts | 20 | ||||
-rw-r--r-- | src/state/threadgate-hidden-replies.tsx | 16 | ||||
-rw-r--r-- | src/view/com/post-thread/PostThread.tsx | 41 | ||||
-rw-r--r-- | src/view/com/post-thread/PostThreadItem.tsx | 19 | ||||
-rw-r--r-- | src/view/com/posts/FeedItem.tsx | 43 | ||||
-rw-r--r-- | src/view/com/util/forms/PostDropdownBtn.tsx | 12 |
9 files changed, 70 insertions, 129 deletions
diff --git a/src/state/cache/post-shadow.ts b/src/state/cache/post-shadow.ts index 4d848ccc4..65300a8ef 100644 --- a/src/state/cache/post-shadow.ts +++ b/src/state/cache/post-shadow.ts @@ -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, }) } diff --git a/src/state/queries/post-thread.ts b/src/state/queries/post-thread.ts index 2c4a36c01..9d650024a 100644 --- a/src/state/queries/post-thread.ts +++ b/src/state/queries/post-thread.ts @@ -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 }, diff --git a/src/state/queries/threadgate/index.ts b/src/state/queries/threadgate/index.ts index faa166e2c..8aa932081 100644 --- a/src/state/queries/threadgate/index.ts +++ b/src/state/queries/threadgate/index.ts @@ -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, - }) }, }) } diff --git a/src/state/queries/threadgate/util.ts b/src/state/queries/threadgate/util.ts index 35c33875e..09ae0a0c1 100644 --- a/src/state/queries/threadgate/util.ts +++ b/src/state/queries/threadgate/util.ts @@ -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, - } -} diff --git a/src/state/threadgate-hidden-replies.tsx b/src/state/threadgate-hidden-replies.tsx index 06fc22366..60806f570 100644 --- a/src/state/threadgate-hidden-replies.tsx +++ b/src/state/threadgate-hidden-replies.tsx @@ -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]) +} diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx index b3196f9ba..d5740f870 100644 --- a/src/view/com/post-thread/PostThread.tsx +++ b/src/view/com/post-thread/PostThread.tsx @@ -3,12 +3,7 @@ import {StyleSheet, useWindowDimensions, View} from 'react-native' import {runOnJS} from 'react-native-reanimated' import Animated from 'react-native-reanimated' import {useSafeAreaInsets} from 'react-native-safe-area-context' -import { - AppBskyFeedDefs, - AppBskyFeedPost, - AppBskyFeedThreadgate, - AtUri, -} from '@atproto/api' +import {AppBskyFeedDefs, AppBskyFeedThreadgate} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -28,9 +23,9 @@ import { usePostThreadQuery, } from '#/state/queries/post-thread' import {usePreferencesQuery} from '#/state/queries/preferences' -import {useThreadgateRecordQuery} from '#/state/queries/threadgate' import {useSession} from '#/state/session' import {useComposerControls} from '#/state/shell' +import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies' import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender' import {useMinimalShellFabTransform} from 'lib/hooks/useMinimalShellTransform' import {useSetTitle} from 'lib/hooks/useSetTitle' @@ -108,7 +103,7 @@ export function PostThread({uri}: {uri: string | undefined}) { isError: isThreadError, error: threadError, refetch, - data: thread, + data: {thread, threadgate} = {}, } = usePostThreadQuery(uri) const treeView = React.useMemo( @@ -119,26 +114,11 @@ export function PostThread({uri}: {uri: string | undefined}) { ) const rootPost = thread?.type === 'post' ? thread.post : undefined const rootPostRecord = thread?.type === 'post' ? thread.record : undefined - const replyRef = - rootPostRecord && AppBskyFeedPost.isRecord(rootPostRecord) - ? rootPostRecord.reply - : undefined - const rootPostUri = replyRef ? replyRef.root.uri : rootPost?.uri - - const isOP = - currentAccount && - rootPostUri && - currentAccount?.did === new AtUri(rootPostUri).host - const initialThreadgateRecord = rootPost?.threadgate?.record as + const threadgateRecord = threadgate?.record as | AppBskyFeedThreadgate.Record | undefined - const {data: threadgateRecord} = useThreadgateRecordQuery({ - /** - * If the user is the OP and we have a root post, fetch the threadgate. - */ - enabled: Boolean(isOP && rootPostUri), - postUri: rootPostUri, - initialData: initialThreadgateRecord, + const threadgateHiddenReplies = useMergedThreadgateHiddenReplies({ + threadgateRecord, }) const moderationOpts = useModerationOpts() @@ -194,9 +174,6 @@ export function PostThread({uri}: {uri: string | undefined}) { const skeleton = React.useMemo(() => { const threadViewPrefs = preferences?.threadViewPrefs if (!threadViewPrefs || !thread) return null - const threadgateRecordHiddenReplies = new Set<string>( - threadgateRecord?.hiddenReplies || [], - ) return createThreadSkeleton( sortThread( @@ -205,13 +182,13 @@ export function PostThread({uri}: {uri: string | undefined}) { threadModerationCache, currentDid, justPostedUris, - threadgateRecordHiddenReplies, + threadgateHiddenReplies, ), currentDid, treeView, threadModerationCache, hiddenRepliesState !== HiddenRepliesState.Hide, - threadgateRecordHiddenReplies, + threadgateHiddenReplies, ) }, [ thread, @@ -221,7 +198,7 @@ export function PostThread({uri}: {uri: string | undefined}) { threadModerationCache, hiddenRepliesState, justPostedUris, - threadgateRecord, + threadgateHiddenReplies, ]) const error = React.useMemo(() => { diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index f2cd8e85a..f2a8be598 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -17,6 +17,7 @@ import {useLanguagePrefs} from '#/state/preferences' import {useOpenLink} from '#/state/preferences/in-app-browser' import {ThreadPost} from '#/state/queries/post-thread' import {useComposerControls} from '#/state/shell/composer' +import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies' import {MAX_POST_LINES} from 'lib/constants' import {usePalette} from 'lib/hooks/usePalette' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' @@ -206,24 +207,22 @@ let PostThreadItemLoaded = ({ return makeProfileLink(post.author, 'post', urip.rkey, 'reposted-by') }, [post.uri, post.author]) const repostsTitle = _(msg`Reposts of this post`) + const threadgateHiddenReplies = useMergedThreadgateHiddenReplies({ + threadgateRecord, + }) const additionalPostAlerts: AppModerationCause[] = React.useMemo(() => { - const isPostHiddenByThreadgate = threadgateRecord?.hiddenReplies?.includes( - post.uri, - ) - const isControlledByViewer = - threadgateRecord && - new AtUri(threadgateRecord.post).host === currentAccount?.did - if (!isControlledByViewer) return [] - return threadgateRecord && isPostHiddenByThreadgate + const isPostHiddenByThreadgate = threadgateHiddenReplies.has(post.uri) + const isControlledByViewer = new AtUri(rootUri).host === currentAccount?.did + return isControlledByViewer && isPostHiddenByThreadgate ? [ { type: 'reply-hidden', - source: {type: 'user', did: new AtUri(threadgateRecord.post).host}, + source: {type: 'user', did: currentAccount?.did}, priority: 6, }, ] : [] - }, [post, threadgateRecord, currentAccount?.did]) + }, [post, currentAccount?.did, threadgateHiddenReplies, rootUri]) const quotesHref = React.useMemo(() => { const urip = new AtUri(post.uri) return makeProfileLink(post.author, 'post', urip.rkey, 'quotes') diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx index e90e8b885..a5714fafe 100644 --- a/src/view/com/posts/FeedItem.tsx +++ b/src/view/com/posts/FeedItem.tsx @@ -22,7 +22,7 @@ import {POST_TOMBSTONE, Shadow, usePostShadow} from '#/state/cache/post-shadow' import {useFeedFeedbackContext} from '#/state/feed-feedback' import {useSession} from '#/state/session' import {useComposerControls} from '#/state/shell/composer' -import {useThreadgateHiddenReplyUris} from '#/state/threadgate-hidden-replies' +import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies' import {isReasonFeedSource, ReasonFeedSource} from 'lib/api/feed/types' import {MAX_POST_LINES} from 'lib/constants' import {usePalette} from 'lib/hooks/usePalette' @@ -227,6 +227,10 @@ let FeedItemInner = ({ AppBskyFeedDefs.isReasonRepost(reason) && reason.by.did === currentAccount?.did + /** + * If `post[0]` in this slice is the actual root post (not an orphan thread), + * then we may have a threadgate record to reference + */ const threadgateRecord = AppBskyFeedThreadgate.isRecord( rootPost.threadgate?.record, ) @@ -422,41 +426,26 @@ let PostContent = ({ const [limitLines, setLimitLines] = useState( () => countLines(richText.text) >= MAX_POST_LINES, ) - const {uris: hiddenReplyUris, recentlyUnhiddenUris} = - useThreadgateHiddenReplyUris() + const threadgateHiddenReplies = useMergedThreadgateHiddenReplies({ + threadgateRecord, + }) const additionalPostAlerts: AppModerationCause[] = React.useMemo(() => { - const isPostHiddenByHiddenReplyCache = hiddenReplyUris.has(post.uri) - const isPostHiddenByThreadgate = - !recentlyUnhiddenUris.has(post.uri) && - !!threadgateRecord?.hiddenReplies?.includes(post.uri) - const isHidden = isPostHiddenByHiddenReplyCache || isPostHiddenByThreadgate + const isPostHiddenByThreadgate = threadgateHiddenReplies.has(post.uri) + const rootPostUri = AppBskyFeedPost.isRecord(post.record) + ? post.record?.reply?.root?.uri || post.uri + : undefined const isControlledByViewer = - isPostHiddenByHiddenReplyCache || - (threadgateRecord && - new AtUri(threadgateRecord.post).host === currentAccount?.did) - if (!isControlledByViewer) return [] - const alertSource = - threadgateRecord && isPostHiddenByThreadgate - ? new AtUri(threadgateRecord.post).host - : isPostHiddenByHiddenReplyCache - ? currentAccount?.did - : undefined - return isHidden && alertSource + rootPostUri && new AtUri(rootPostUri).host === currentAccount?.did + return isControlledByViewer && isPostHiddenByThreadgate ? [ { type: 'reply-hidden', - source: {type: 'user', did: alertSource}, + source: {type: 'user', did: currentAccount?.did}, priority: 6, }, ] : [] - }, [ - post, - hiddenReplyUris, - recentlyUnhiddenUris, - threadgateRecord, - currentAccount?.did, - ]) + }, [post, currentAccount?.did, threadgateHiddenReplies]) const onPressShowMore = React.useCallback(() => { setLimitLines(false) diff --git a/src/view/com/util/forms/PostDropdownBtn.tsx b/src/view/com/util/forms/PostDropdownBtn.tsx index b293b0dff..03b6dd233 100644 --- a/src/view/com/util/forms/PostDropdownBtn.tsx +++ b/src/view/com/util/forms/PostDropdownBtn.tsx @@ -37,7 +37,7 @@ import {useToggleQuoteDetachmentMutation} from '#/state/queries/postgate' import {getMaybeDetachedQuoteEmbed} from '#/state/queries/postgate/util' import {useToggleReplyVisibilityMutation} from '#/state/queries/threadgate' import {useSession} from '#/state/session' -import {useThreadgateHiddenReplyUris} from '#/state/threadgate-hidden-replies' +import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies' import {getCurrentRoute} from 'lib/routes/helpers' import {shareUrl} from 'lib/sharing' import {toShareUrl} from 'lib/strings/url-helpers' @@ -124,8 +124,6 @@ let PostDropdownBtn = ({ const hideReplyConfirmControl = useDialogControl() const {mutateAsync: toggleReplyVisibility} = useToggleReplyVisibilityMutation() - const {uris: hiddenReplies, recentlyUnhiddenUris} = - useThreadgateHiddenReplyUris() const postUri = post.uri const postCid = post.cid @@ -147,10 +145,10 @@ let PostDropdownBtn = ({ const isPostHidden = hiddenPosts && hiddenPosts.includes(postUri) const isAuthor = postAuthor.did === currentAccount?.did const isRootPostAuthor = new AtUri(rootUri).host === currentAccount?.did - const isReplyHiddenByThreadgate = - hiddenReplies.has(postUri) || - (!recentlyUnhiddenUris.has(postUri) && - threadgateRecord?.hiddenReplies?.includes(postUri)) + const threadgateHiddenReplies = useMergedThreadgateHiddenReplies({ + threadgateRecord, + }) + const isReplyHiddenByThreadgate = threadgateHiddenReplies.has(postUri) const {mutateAsync: toggleQuoteDetachment, isPending} = useToggleQuoteDetachmentMutation() |