diff options
author | dan <dan.abramov@gmail.com> | 2023-11-13 18:35:15 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-13 10:35:15 -0800 |
commit | e1938931e028f4486cce8cd9da39ffd940c10ec2 (patch) | |
tree | d7d22d93a95503350ab4d2d0ace409cd873815cb /src/state/cache | |
parent | c3edde8ac6f9c65eac1004cd8e2fc14b0493cba8 (diff) | |
download | voidsky-e1938931e028f4486cce8cd9da39ffd940c10ec2.tar.zst |
Refactor profile screen to use new pager and react-query (#1870)
* Profile tabs WIP * Refactor the profile screen to use react-query (WIP) * Add the profile shadow and get follow, mute, and block working * Cleanup --------- Co-authored-by: Paul Frazee <pfrazee@gmail.com>
Diffstat (limited to 'src/state/cache')
-rw-r--r-- | src/state/cache/profile-shadow.ts | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/src/state/cache/profile-shadow.ts b/src/state/cache/profile-shadow.ts new file mode 100644 index 000000000..a1cf59954 --- /dev/null +++ b/src/state/cache/profile-shadow.ts @@ -0,0 +1,88 @@ +import {useEffect, useState, useCallback, useRef} from 'react' +import EventEmitter from 'eventemitter3' +import {AppBskyActorDefs} from '@atproto/api' + +const emitter = new EventEmitter() + +export interface ProfileShadow { + followingUri: string | undefined + muted: boolean | undefined + blockingUri: string | undefined +} + +interface CacheEntry { + ts: number + value: ProfileShadow +} + +type ProfileView = + | AppBskyActorDefs.ProfileView + | AppBskyActorDefs.ProfileViewBasic + | AppBskyActorDefs.ProfileViewDetailed + +export function useProfileShadow<T extends ProfileView>( + profile: T, + ifAfterTS: number, +): T { + const [state, setState] = useState<CacheEntry>({ + ts: Date.now(), + value: fromProfile(profile), + }) + const firstRun = useRef(true) + + const onUpdate = useCallback( + (value: Partial<ProfileShadow>) => { + setState(s => ({ts: Date.now(), value: {...s.value, ...value}})) + }, + [setState], + ) + + // react to shadow updates + useEffect(() => { + emitter.addListener(profile.did, onUpdate) + return () => { + emitter.removeListener(profile.did, onUpdate) + } + }, [profile.did, onUpdate]) + + // react to profile updates + useEffect(() => { + // dont fire on first run to avoid needless re-renders + if (!firstRun.current) { + setState({ts: Date.now(), value: fromProfile(profile)}) + } + firstRun.current = false + }, [profile]) + + return state.ts > ifAfterTS ? mergeShadow(profile, state.value) : profile +} + +export function updateProfileShadow( + uri: string, + value: Partial<ProfileShadow>, +) { + emitter.emit(uri, value) +} + +function fromProfile(profile: ProfileView): ProfileShadow { + return { + followingUri: profile.viewer?.following, + muted: profile.viewer?.muted, + blockingUri: profile.viewer?.blocking, + } +} + +function mergeShadow<T extends ProfileView>( + profile: T, + shadow: ProfileShadow, +): T { + return { + ...profile, + viewer: { + ...(profile.viewer || {}), + following: shadow.followingUri, + muted: shadow.muted, + blocking: shadow.blockingUri, + }, + } +} |