diff --git a/src/state/cache/post-shadow.ts b/src/state/cache/post-shadow.ts index 3f9644879b..90fddda2bd 100644 --- a/src/state/cache/post-shadow.ts +++ b/src/state/cache/post-shadow.ts @@ -14,6 +14,7 @@ import {findAllPostsInQueryData as findAllPostsInFeedQueryData} from '#/state/qu import {findAllPostsInQueryData as findAllPostsInQuoteQueryData} from '#/state/queries/post-quotes' import {findAllPostsInQueryData as findAllPostsInThreadQueryData} from '#/state/queries/post-thread' import {findAllPostsInQueryData as findAllPostsInSearchQueryData} from '#/state/queries/search-posts' +import {findAllPostsInQueryData as findAllPostsInThreadV2QueryData} from '#/state/queries/usePostThread/queryCache' import {useProfileShadow} from './profile-shadow' import {castAsShadow, type Shadow} from './types' export type {Shadow} from './types' @@ -157,6 +158,9 @@ function* findPostsInCache( yield node.post } } + for (let post of findAllPostsInThreadV2QueryData(queryClient, uri)) { + yield post + } for (let post of findAllPostsInSearchQueryData(queryClient, uri)) { yield post } diff --git a/src/state/queries/usePostThread/index.ts b/src/state/queries/usePostThread/index.ts index 8b9f786cc0..194a85e264 100644 --- a/src/state/queries/usePostThread/index.ts +++ b/src/state/queries/usePostThread/index.ts @@ -74,7 +74,6 @@ export function usePostThread({ const items = flatten( sort(query.data?.thread || [], { - view: params.view, threadgateHiddenReplies: mergeThreadgateHiddenReplies( query.data?.threadgate?.record, ), diff --git a/src/state/queries/usePostThread/queryCache.ts b/src/state/queries/usePostThread/queryCache.ts index 3fc4e160bf..17cec359d1 100644 --- a/src/state/queries/usePostThread/queryCache.ts +++ b/src/state/queries/usePostThread/queryCache.ts @@ -1,4 +1,9 @@ -import {type $Typed, AppBskyUnspeccedGetPostThreadV2, AtUri} from '@atproto/api' +import { + type $Typed, + type AppBskyFeedDefs, + AppBskyUnspeccedGetPostThreadV2, + AtUri, +} from '@atproto/api' import {type QueryClient} from '@tanstack/react-query' import {findAllPostsInQueryData as findAllPostsInExploreFeedPreviewsQueryData} from '#/state/queries/explore-feed-previews' @@ -13,11 +18,9 @@ import { postThreadQueryKeyRoot, } from '#/state/queries/usePostThread/types' import {getRootPostAtUri} from '#/state/queries/usePostThread/utils' -import { - embedViewToThreadPlaceholder, - postViewToThreadPlaceholder, -} from '#/state/queries/usePostThread/views' +import {postViewToThreadPlaceholder} from '#/state/queries/usePostThread/views' import {didOrHandleUriMatches, getEmbeddedPost} from '#/state/queries/util' +import {embedViewRecordToPostView} from '#/state/queries/util' export function createCacheMutator({ params, @@ -113,7 +116,37 @@ export function createCacheMutator({ }, ) }, - deletePost(_post: AppBskyUnspeccedGetPostThreadV2.ThreadItem) {}, + /** + * Unused atm, post shadow does the trick, but it would be nice to clean up + * the whole sub-tree on deletes. + */ + deletePost(post: AppBskyUnspeccedGetPostThreadV2.ThreadItem) { + queryClient.setQueryData( + queryKey, + queryData => { + if (!queryData) return + + const thread = [...queryData.thread] + + for (let i = 0; i < thread.length; i++) { + const existingPost = thread[i] + if (!AppBskyUnspeccedGetPostThreadV2.isThreadItemPost(post.value)) + continue + + if (existingPost.uri === post.uri) { + const branch = getBranch(thread, i, existingPost.depth) + thread.splice(branch.start, branch.length) + break + } + } + + return { + ...queryData, + thread, + } + }, + ) + }, } } @@ -153,38 +186,11 @@ export function* getThreadPlaceholderCandidates( >, void > { - const atUri = new AtUri(uri) - /* - * Check this thread in the cache first. - * TODO extract just this for shadowing + * Check post thread queries first */ - const queryDatas = - queryClient.getQueriesData({ - queryKey: [postThreadQueryKeyRoot], - }) - for (const [_queryKey, queryData] of queryDatas) { - if (!queryData) continue - - const {thread} = queryData - - for (const item of thread) { - if (AppBskyUnspeccedGetPostThreadV2.isThreadItemPost(item.value)) { - if (didOrHandleUriMatches(atUri, item.value.post)) { - yield { - $type: 'app.bsky.unspecced.getPostThreadV2#threadItem', - ...item, - depth: 0, - value: item.value, - } - } - - const qp = getEmbeddedPost(item.value.post.embed) - if (qp && didOrHandleUriMatches(atUri, qp)) { - yield embedViewToThreadPlaceholder(qp) - } - } - } + for (const post of findAllPostsInQueryData(queryClient, uri)) { + yield postViewToThreadPlaceholder(post) } /* @@ -212,3 +218,33 @@ export function* getThreadPlaceholderCandidates( yield postViewToThreadPlaceholder(post) } } + +export function* findAllPostsInQueryData( + queryClient: QueryClient, + uri: string, +): Generator { + const atUri = new AtUri(uri) + const queryDatas = + queryClient.getQueriesData({ + queryKey: [postThreadQueryKeyRoot], + }) + + for (const [_queryKey, queryData] of queryDatas) { + if (!queryData) continue + + const {thread} = queryData + + for (const item of thread) { + if (AppBskyUnspeccedGetPostThreadV2.isThreadItemPost(item.value)) { + if (didOrHandleUriMatches(atUri, item.value.post)) { + yield item.value.post + } + + const qp = getEmbeddedPost(item.value.post.embed) + if (qp && didOrHandleUriMatches(atUri, qp)) { + yield embedViewRecordToPostView(qp) + } + } + } + } +} diff --git a/src/state/queries/usePostThread/views.ts b/src/state/queries/usePostThread/views.ts index 7450e6d83b..4af5b27c03 100644 --- a/src/state/queries/usePostThread/views.ts +++ b/src/state/queries/usePostThread/views.ts @@ -1,6 +1,5 @@ import { type $Typed, - type AppBskyEmbedRecord, type AppBskyFeedDefs, type AppBskyFeedPost, type AppBskyUnspeccedGetPostThreadV2, @@ -9,7 +8,6 @@ import { } from '@atproto/api' import {type Slice} from '#/state/queries/usePostThread/types' -import {embedViewRecordToPostView} from '#/state/queries/util' export function threadPostNoUnauthenticated({ uri, @@ -84,9 +82,12 @@ export function threadPost({ depth, value: { ...value, - post: { - ...value.post, - record: value.post.record as AppBskyFeedPost.Record, + /* + * Do not spread anything here, load bearing for post shadow strict + * equality checks. + */ + post: value.post as Omit & { + record: AppBskyFeedPost.Record }, }, moderation: moderatePost(value.post, moderationOpts), @@ -123,27 +124,3 @@ export function postViewToThreadPlaceholder( }, } } - -export function embedViewToThreadPlaceholder( - record: AppBskyEmbedRecord.ViewRecord, -): $Typed< - Omit & { - value: $Typed - } -> { - return { - $type: 'app.bsky.unspecced.getPostThreadV2#threadItem', - uri: record.uri, - depth: 0, // reset to 0 for highlighted post - value: { - $type: 'app.bsky.unspecced.getPostThreadV2#threadItemPost', - post: embedViewRecordToPostView(record), - isOPThread: false, // unknown - hasOPLike: false, // unknown - // @ts-expect-error - hasUnhydratedReplies: false, // unknown - // TODO test - hasUnhydratedParents: !!(record.value as AppBskyFeedPost.Record).reply, // unknown - }, - } -}