diff options
Diffstat (limited to 'src/state/cache/profile-shadow.ts')
-rw-r--r-- | src/state/cache/profile-shadow.ts | 114 |
1 files changed, 54 insertions, 60 deletions
diff --git a/src/state/cache/profile-shadow.ts b/src/state/cache/profile-shadow.ts index 6ebd39132..f85e1ad8d 100644 --- a/src/state/cache/profile-shadow.ts +++ b/src/state/cache/profile-shadow.ts @@ -1,107 +1,101 @@ -import {useEffect, useState, useMemo, useCallback} from 'react' +import {useEffect, useState, useMemo} from 'react' import EventEmitter from 'eventemitter3' import {AppBskyActorDefs} from '@atproto/api' import {batchedUpdates} from '#/lib/batchedUpdates' +import {findAllProfilesInQueryData as findAllProfilesInListMembersQueryData} from '../queries/list-members' +import {findAllProfilesInQueryData as findAllProfilesInMyBlockedAccountsQueryData} from '../queries/my-blocked-accounts' +import {findAllProfilesInQueryData as findAllProfilesInMyMutedAccountsQueryData} from '../queries/my-muted-accounts' +import {findAllProfilesInQueryData as findAllProfilesInPostLikedByQueryData} from '../queries/post-liked-by' +import {findAllProfilesInQueryData as findAllProfilesInPostRepostedByQueryData} from '../queries/post-reposted-by' +import {findAllProfilesInQueryData as findAllProfilesInProfileQueryData} from '../queries/profile' +import {findAllProfilesInQueryData as findAllProfilesInProfileFollowersQueryData} from '../queries/profile-followers' +import {findAllProfilesInQueryData as findAllProfilesInProfileFollowsQueryData} from '../queries/profile-follows' +import {findAllProfilesInQueryData as findAllProfilesInSuggestedFollowsQueryData} from '../queries/suggested-follows' import {Shadow, castAsShadow} from './types' +import {queryClient} from 'lib/react-query' export type {Shadow} from './types' -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 -const firstSeenMap = new WeakMap<ProfileView, number>() -function getFirstSeenTS(profile: ProfileView): number { - let timeStamp = firstSeenMap.get(profile) - if (timeStamp !== undefined) { - return timeStamp - } - timeStamp = Date.now() - firstSeenMap.set(profile, timeStamp) - return timeStamp -} +const shadows: WeakMap<ProfileView, Partial<ProfileShadow>> = new WeakMap() +const emitter = new EventEmitter() export function useProfileShadow(profile: ProfileView): Shadow<ProfileView> { - const profileSeenTS = getFirstSeenTS(profile) - const [state, setState] = useState<CacheEntry>(() => ({ - ts: profileSeenTS, - value: fromProfile(profile), - })) - - const [prevProfile, setPrevProfile] = useState(profile) - if (profile !== prevProfile) { - // if we got a new prop, assume it's fresher - // than whatever shadow state we accumulated - setPrevProfile(profile) - setState({ - ts: profileSeenTS, - value: fromProfile(profile), - }) + const [shadow, setShadow] = useState(() => shadows.get(profile)) + const [prevPost, setPrevPost] = useState(profile) + if (profile !== prevPost) { + setPrevPost(profile) + setShadow(shadows.get(profile)) } - const onUpdate = useCallback( - (value: Partial<ProfileShadow>) => { - setState(s => ({ts: Date.now(), value: {...s.value, ...value}})) - }, - [setState], - ) - - // react to shadow updates useEffect(() => { + function onUpdate() { + setShadow(shadows.get(profile)) + } emitter.addListener(profile.did, onUpdate) return () => { emitter.removeListener(profile.did, onUpdate) } - }, [profile.did, onUpdate]) + }, [profile]) return useMemo(() => { - return state.ts > profileSeenTS - ? mergeShadow(profile, state.value) - : castAsShadow(profile) - }, [profile, state, profileSeenTS]) + if (shadow) { + return mergeShadow(profile, shadow) + } else { + return castAsShadow(profile) + } + }, [profile, shadow]) } export function updateProfileShadow( - uri: string, + did: string, value: Partial<ProfileShadow>, ) { + const cachedProfiles = findProfilesInCache(did) + for (let post of cachedProfiles) { + shadows.set(post, {...shadows.get(post), ...value}) + } batchedUpdates(() => { - emitter.emit(uri, value) + emitter.emit(did, value) }) } -function fromProfile(profile: ProfileView): ProfileShadow { - return { - followingUri: profile.viewer?.following, - muted: profile.viewer?.muted, - blockingUri: profile.viewer?.blocking, - } -} - function mergeShadow( profile: ProfileView, - shadow: ProfileShadow, + shadow: Partial<ProfileShadow>, ): Shadow<ProfileView> { return castAsShadow({ ...profile, viewer: { ...(profile.viewer || {}), - following: shadow.followingUri, - muted: shadow.muted, - blocking: shadow.blockingUri, + following: + 'followingUri' in shadow + ? shadow.followingUri + : profile.viewer?.following, + muted: 'muted' in shadow ? shadow.muted : profile.viewer?.muted, + blocking: + 'blockingUri' in shadow ? shadow.blockingUri : profile.viewer?.blocking, }, }) } + +function* findProfilesInCache(did: string): Generator<ProfileView, void> { + yield* findAllProfilesInListMembersQueryData(queryClient, did) + yield* findAllProfilesInMyBlockedAccountsQueryData(queryClient, did) + yield* findAllProfilesInMyMutedAccountsQueryData(queryClient, did) + yield* findAllProfilesInPostLikedByQueryData(queryClient, did) + yield* findAllProfilesInPostRepostedByQueryData(queryClient, did) + yield* findAllProfilesInProfileQueryData(queryClient, did) + yield* findAllProfilesInProfileFollowersQueryData(queryClient, did) + yield* findAllProfilesInProfileFollowsQueryData(queryClient, did) + yield* findAllProfilesInSuggestedFollowsQueryData(queryClient, did) +} |