about summary refs log tree commit diff
path: root/src/state/cache/profile-shadow.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/state/cache/profile-shadow.ts')
-rw-r--r--src/state/cache/profile-shadow.ts114
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)
+}