diff options
author | Paul Frazee <pfrazee@gmail.com> | 2023-11-09 15:35:25 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-09 15:35:25 -0800 |
commit | fb4f5709c43c070653c917e3196b9b1c120418a6 (patch) | |
tree | 74e6ff954441b6da3044853e16ebf5dd12213c87 /src/state/cache | |
parent | 625cbc435f15bc0d611661b44dbf8add990dff7d (diff) | |
download | voidsky-fb4f5709c43c070653c917e3196b9b1c120418a6.tar.zst |
Refactor post threads to use react query (#1851)
* Add post and post-thread queries * Update PostThread components to use new queries * Move from normalized cache to shadow cache model * Merge post shadow into the post automatically * Remove dead code * Remove old temporary session * Fix: set agent on session creation * Temporarily double-login * Handle post-thread uri resolution errors
Diffstat (limited to 'src/state/cache')
-rw-r--r-- | src/state/cache/post-shadow.ts | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/src/state/cache/post-shadow.ts b/src/state/cache/post-shadow.ts new file mode 100644 index 000000000..c06ed60c4 --- /dev/null +++ b/src/state/cache/post-shadow.ts @@ -0,0 +1,90 @@ +import {useEffect, useState, useCallback, useRef} from 'react' +import EventEmitter from 'eventemitter3' +import {AppBskyFeedDefs} from '@atproto/api' + +const emitter = new EventEmitter() + +export interface PostShadow { + likeUri: string | undefined + likeCount: number | undefined + repostUri: string | undefined + repostCount: number | undefined + isDeleted: boolean +} + +export const POST_TOMBSTONE = Symbol('PostTombstone') + +interface CacheEntry { + ts: number + value: PostShadow +} + +export function usePostShadow( + post: AppBskyFeedDefs.PostView, + ifAfterTS: number, +): AppBskyFeedDefs.PostView | typeof POST_TOMBSTONE { + const [state, setState] = useState<CacheEntry>({ + ts: Date.now(), + value: fromPost(post), + }) + const firstRun = useRef(true) + + const onUpdate = useCallback( + (value: Partial<PostShadow>) => { + setState(s => ({ts: Date.now(), value: {...s.value, ...value}})) + }, + [setState], + ) + + // react to shadow updates + useEffect(() => { + emitter.addListener(post.uri, onUpdate) + return () => { + emitter.removeListener(post.uri, onUpdate) + } + }, [post.uri, onUpdate]) + + // react to post updates + useEffect(() => { + // dont fire on first run to avoid needless re-renders + if (!firstRun.current) { + setState({ts: Date.now(), value: fromPost(post)}) + } + firstRun.current = false + }, [post]) + + return state.ts > ifAfterTS ? mergeShadow(post, state.value) : post +} + +export function updatePostShadow(uri: string, value: Partial<PostShadow>) { + emitter.emit(uri, value) +} + +function fromPost(post: AppBskyFeedDefs.PostView): PostShadow { + return { + likeUri: post.viewer?.like, + likeCount: post.likeCount, + repostUri: post.viewer?.repost, + repostCount: post.repostCount, + isDeleted: false, + } +} + +function mergeShadow( + post: AppBskyFeedDefs.PostView, + shadow: PostShadow, +): AppBskyFeedDefs.PostView | typeof POST_TOMBSTONE { + if (shadow.isDeleted) { + return POST_TOMBSTONE + } + return { + ...post, + likeCount: shadow.likeCount, + repostCount: shadow.repostCount, + viewer: { + ...(post.viewer || {}), + like: shadow.likeUri, + repost: shadow.repostUri, + }, + } +} |