POC cache mutator
This commit is contained in:
@@ -162,7 +162,7 @@ function ThreadItemAnchorParentReplyLine({isRoot}: {isRoot: boolean}) {
|
||||
const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
|
||||
item,
|
||||
isRoot,
|
||||
postShadow,
|
||||
// postShadow,
|
||||
onPostSuccess,
|
||||
threadgateRecord,
|
||||
postSource,
|
||||
@@ -488,7 +488,8 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
|
||||
<FeedFeedbackProvider value={feedFeedback}>
|
||||
<PostControls
|
||||
big
|
||||
post={postShadow}
|
||||
// @ts-expect-error TODO no more shadow here
|
||||
post={post}
|
||||
record={record}
|
||||
richText={richText}
|
||||
onPressReply={onPressReply}
|
||||
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
import {type QueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {mutatePost} from '#/state/queries/cache/mutations'
|
||||
import {type PostMutations} from '#/state/queries/cache/types'
|
||||
import {applyPostCacheMutator as applyPostCacheMutator_usePostThread} from '#/state/queries/usePostThread/queryCache'
|
||||
|
||||
/**
|
||||
* Applies mutations to a post (identified by URI) in all active query caches
|
||||
*/
|
||||
export function applyPostCacheMutations(
|
||||
qc: QueryClient,
|
||||
uri: string,
|
||||
mutations: Partial<PostMutations>,
|
||||
) {
|
||||
applyPostCacheMutator_usePostThread({
|
||||
qc,
|
||||
uri,
|
||||
mutator: post => mutatePost(post, mutations),
|
||||
})
|
||||
|
||||
// ... add all other queries below
|
||||
}
|
||||
Vendored
+72
@@ -0,0 +1,72 @@
|
||||
import {
|
||||
AppBskyEmbedRecord,
|
||||
AppBskyEmbedRecordWithMedia,
|
||||
type AppBskyFeedDefs,
|
||||
} from '@atproto/api'
|
||||
|
||||
import {type PostMutations} from '#/state/queries/cache/types'
|
||||
import {DELETED_POST} from '#/state/queries/cache/util'
|
||||
|
||||
/**
|
||||
* Applies mutations to a post in the cache. If the post is deleted, returns a
|
||||
* symbol to force individual cache mutators to handle the unknown value
|
||||
* appropriately.
|
||||
*/
|
||||
export function mutatePost(
|
||||
post: AppBskyFeedDefs.PostView,
|
||||
mutations: Partial<PostMutations>,
|
||||
): AppBskyFeedDefs.PostView | typeof DELETED_POST {
|
||||
if (mutations.isDeleted) {
|
||||
return DELETED_POST
|
||||
}
|
||||
|
||||
let likeCount = post.likeCount ?? 0
|
||||
if ('likeUri' in mutations) {
|
||||
const wasLiked = !!post.viewer?.like
|
||||
const isLiked = !!mutations.likeUri
|
||||
if (wasLiked && !isLiked) {
|
||||
likeCount--
|
||||
} else if (!wasLiked && isLiked) {
|
||||
likeCount++
|
||||
}
|
||||
likeCount = Math.max(0, likeCount)
|
||||
}
|
||||
|
||||
let repostCount = post.repostCount ?? 0
|
||||
if ('repostUri' in mutations) {
|
||||
const wasReposted = !!post.viewer?.repost
|
||||
const isReposted = !!mutations.repostUri
|
||||
if (wasReposted && !isReposted) {
|
||||
repostCount--
|
||||
} else if (!wasReposted && isReposted) {
|
||||
repostCount++
|
||||
}
|
||||
repostCount = Math.max(0, repostCount)
|
||||
}
|
||||
|
||||
let embed: typeof post.embed
|
||||
if ('embed' in mutations) {
|
||||
if (
|
||||
(AppBskyEmbedRecord.isView(post.embed) &&
|
||||
AppBskyEmbedRecord.isView(mutations.embed)) ||
|
||||
(AppBskyEmbedRecordWithMedia.isView(post.embed) &&
|
||||
AppBskyEmbedRecordWithMedia.isView(mutations.embed))
|
||||
) {
|
||||
embed = mutations.embed
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...post,
|
||||
embed: embed || post.embed,
|
||||
likeCount: likeCount,
|
||||
repostCount: repostCount,
|
||||
viewer: {
|
||||
...(post.viewer || {}),
|
||||
like: 'likeUri' in mutations ? mutations.likeUri : post.viewer?.like,
|
||||
repost:
|
||||
'repostUri' in mutations ? mutations.repostUri : post.viewer?.repost,
|
||||
pinned: 'pinned' in mutations ? mutations.pinned : post.viewer?.pinned,
|
||||
},
|
||||
}
|
||||
}
|
||||
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
type AppBskyEmbedRecord,
|
||||
type AppBskyEmbedRecordWithMedia,
|
||||
type AppBskyFeedDefs,
|
||||
} from '@atproto/api'
|
||||
import {type QueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {type DELETED_POST} from '#/state/queries/cache/util'
|
||||
|
||||
/**
|
||||
* Available mutations for a post.
|
||||
*/
|
||||
export type PostMutations = {
|
||||
likeUri: string | undefined
|
||||
repostUri: string | undefined
|
||||
isDeleted: boolean
|
||||
embed: AppBskyEmbedRecord.View | AppBskyEmbedRecordWithMedia.View | undefined
|
||||
pinned: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies mutations to a post.
|
||||
*/
|
||||
export type ApplyPostCacheMutator = (params: {
|
||||
/**
|
||||
* The query client to use for cache updates.
|
||||
*/
|
||||
qc: QueryClient
|
||||
/**
|
||||
* The URI of the post to mutate.
|
||||
*/
|
||||
uri: string
|
||||
/**
|
||||
* The mutation function that applies changes to the post.
|
||||
*/
|
||||
mutator: (
|
||||
post: AppBskyFeedDefs.PostView,
|
||||
) => AppBskyFeedDefs.PostView | typeof DELETED_POST
|
||||
}) => void
|
||||
Vendored
+34
@@ -0,0 +1,34 @@
|
||||
import {AtUri} from '@atproto/api'
|
||||
|
||||
import {type ApplyPostCacheMutator} from '#/state/queries/cache/types'
|
||||
|
||||
/**
|
||||
* Symbol used to indicate that a value has been deleted.
|
||||
*/
|
||||
export const DELETED_POST = Symbol('DELETED_POST')
|
||||
|
||||
/**
|
||||
* Sugar for creating a post cache mutator, for use alongside the definitions
|
||||
* for all post queries.
|
||||
*/
|
||||
export function createApplyPostCacheMutator(
|
||||
applyPostCacheMutator: ApplyPostCacheMutator,
|
||||
) {
|
||||
return applyPostCacheMutator
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a source URI matches a given URI, or the same URI with handle in
|
||||
* place of a DID.
|
||||
*/
|
||||
export function uriMatches(
|
||||
sourceUri: string,
|
||||
matchUri: string,
|
||||
matchHandle?: string,
|
||||
) {
|
||||
const source = new AtUri(sourceUri)
|
||||
if (source.host.startsWith('did:')) return source.toString() === matchUri
|
||||
if (!matchHandle) return false
|
||||
source.host = matchHandle
|
||||
return source.toString() === matchUri
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import {type LogEvents, toClout} from '#/lib/statsig/statsig'
|
||||
import {logger} from '#/logger'
|
||||
import {updatePostShadow} from '#/state/cache/post-shadow'
|
||||
import {type Shadow} from '#/state/cache/types'
|
||||
import {applyPostCacheMutations} from '#/state/queries/cache'
|
||||
import {useAgent, useSession} from '#/state/session'
|
||||
import * as userActionHistory from '#/state/userActionHistory'
|
||||
import {useIsThreadMuted, useSetThreadMute} from '../cache/thread-mutes'
|
||||
@@ -135,7 +136,7 @@ export function usePostLikeMutationQueue(
|
||||
},
|
||||
onSuccess(finalLikeUri) {
|
||||
// finalize
|
||||
updatePostShadow(queryClient, postUri, {
|
||||
applyPostCacheMutations(queryClient, postUri, {
|
||||
likeUri: finalLikeUri,
|
||||
})
|
||||
},
|
||||
@@ -143,7 +144,7 @@ export function usePostLikeMutationQueue(
|
||||
|
||||
const queueLike = useCallback(() => {
|
||||
// optimistically update
|
||||
updatePostShadow(queryClient, postUri, {
|
||||
applyPostCacheMutations(queryClient, postUri, {
|
||||
likeUri: 'pending',
|
||||
})
|
||||
return queueToggle(true)
|
||||
@@ -151,7 +152,7 @@ export function usePostLikeMutationQueue(
|
||||
|
||||
const queueUnlike = useCallback(() => {
|
||||
// optimistically update
|
||||
updatePostShadow(queryClient, postUri, {
|
||||
applyPostCacheMutations(queryClient, postUri, {
|
||||
likeUri: undefined,
|
||||
})
|
||||
return queueToggle(false)
|
||||
|
||||
@@ -9,6 +9,11 @@ import {
|
||||
} from '@atproto/api'
|
||||
import {type QueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {
|
||||
createApplyPostCacheMutator,
|
||||
DELETED_POST,
|
||||
uriMatches,
|
||||
} from '#/state/queries/cache/util'
|
||||
import {findAllPostsInQueryData as findAllPostsInExploreFeedPreviewsQueryData} from '#/state/queries/explore-feed-previews'
|
||||
import {findAllPostsInQueryData as findAllPostsInNotifsQueryData} from '#/state/queries/notifications/feed'
|
||||
import {findAllPostsInQueryData as findAllPostsInFeedQueryData} from '#/state/queries/post-feed'
|
||||
@@ -298,3 +303,42 @@ export function* findAllProfilesInQueryData(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const applyPostCacheMutator = createApplyPostCacheMutator(
|
||||
({qc, uri, mutator}) => {
|
||||
qc.setQueriesData<AppBskyUnspeccedGetPostThreadV2.OutputSchema>(
|
||||
{queryKey: [postThreadQueryKeyRoot]},
|
||||
data => {
|
||||
if (!data) return
|
||||
|
||||
const thread = []
|
||||
|
||||
for (const item of data.thread) {
|
||||
if (
|
||||
AppBskyUnspeccedDefs.isThreadItemPost(item.value) &&
|
||||
uriMatches(uri, item.value.post.uri, item.value.post.author.handle)
|
||||
) {
|
||||
const post = mutator(item.value.post)
|
||||
|
||||
if (post !== DELETED_POST) {
|
||||
thread.push({
|
||||
...item,
|
||||
value: {
|
||||
...item.value,
|
||||
post,
|
||||
},
|
||||
})
|
||||
}
|
||||
} else {
|
||||
thread.push(item)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...data,
|
||||
thread,
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user