about summary refs log tree commit diff
path: root/src/state/queries/profile.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/state/queries/profile.ts')
-rw-r--r--src/state/queries/profile.ts80
1 files changed, 77 insertions, 3 deletions
diff --git a/src/state/queries/profile.ts b/src/state/queries/profile.ts
index affb8295c..e81ea0f3f 100644
--- a/src/state/queries/profile.ts
+++ b/src/state/queries/profile.ts
@@ -4,6 +4,9 @@ import {
   AppBskyActorDefs,
   AppBskyActorProfile,
   AppBskyActorGetProfile,
+  AppBskyFeedDefs,
+  AppBskyEmbedRecord,
+  AppBskyEmbedRecordWithMedia,
 } from '@atproto/api'
 import {
   useQuery,
@@ -23,9 +26,14 @@ import {RQKEY as RQKEY_MY_MUTED} from './my-muted-accounts'
 import {RQKEY as RQKEY_MY_BLOCKED} from './my-blocked-accounts'
 import {STALE} from '#/state/queries'
 import {track} from '#/lib/analytics/analytics'
+import {ThreadNode} from './post-thread'
 
 export const RQKEY = (did: string) => ['profile', did]
 export const profilesQueryKey = (handles: string[]) => ['profiles', handles]
+export const profileBasicQueryKey = (didOrHandle: string) => [
+  'profileBasic',
+  didOrHandle,
+]
 
 export function useProfileQuery({
   did,
@@ -34,18 +42,26 @@ export function useProfileQuery({
   did: string | undefined
   staleTime?: number
 }) {
-  return useQuery({
+  const queryClient = useQueryClient()
+  return useQuery<AppBskyActorDefs.ProfileViewDetailed>({
     // WARNING
     // this staleTime is load-bearing
     // if you remove it, the UI infinite-loops
     // -prf
     staleTime,
     refetchOnWindowFocus: true,
-    queryKey: RQKEY(did || ''),
+    queryKey: RQKEY(did ?? ''),
     queryFn: async () => {
-      const res = await getAgent().getProfile({actor: did || ''})
+      const res = await getAgent().getProfile({actor: did ?? ''})
       return res.data
     },
+    placeholderData: () => {
+      if (!did) return
+
+      return queryClient.getQueryData<AppBskyActorDefs.ProfileViewBasic>(
+        profileBasicQueryKey(did),
+      )
+    },
     enabled: !!did,
   })
 }
@@ -405,6 +421,64 @@ function useProfileUnblockMutation() {
   })
 }
 
+export function precacheProfile(
+  queryClient: QueryClient,
+  profile: AppBskyActorDefs.ProfileViewBasic,
+) {
+  queryClient.setQueryData(profileBasicQueryKey(profile.handle), profile)
+  queryClient.setQueryData(profileBasicQueryKey(profile.did), profile)
+}
+
+export function precacheFeedPostProfiles(
+  queryClient: QueryClient,
+  posts: AppBskyFeedDefs.FeedViewPost[],
+) {
+  for (const post of posts) {
+    // Save the author of the post every time
+    precacheProfile(queryClient, post.post.author)
+    precachePostEmbedProfile(queryClient, post.post.embed)
+
+    // Cache parent author and embeds
+    const parent = post.reply?.parent
+    if (AppBskyFeedDefs.isPostView(parent)) {
+      precacheProfile(queryClient, parent.author)
+      precachePostEmbedProfile(queryClient, parent.embed)
+    }
+  }
+}
+
+function precachePostEmbedProfile(
+  queryClient: QueryClient,
+  embed: AppBskyFeedDefs.PostView['embed'],
+) {
+  if (AppBskyEmbedRecord.isView(embed)) {
+    if (AppBskyEmbedRecord.isViewRecord(embed.record)) {
+      precacheProfile(queryClient, embed.record.author)
+    }
+  } else if (AppBskyEmbedRecordWithMedia.isView(embed)) {
+    if (AppBskyEmbedRecord.isViewRecord(embed.record.record)) {
+      precacheProfile(queryClient, embed.record.record.author)
+    }
+  }
+}
+
+export function precacheThreadPostProfiles(
+  queryClient: QueryClient,
+  node: ThreadNode,
+) {
+  if (node.type === 'post') {
+    precacheProfile(queryClient, node.post.author)
+    if (node.parent) {
+      precacheThreadPostProfiles(queryClient, node.parent)
+    }
+    if (node.replies?.length) {
+      for (const reply of node.replies) {
+        precacheThreadPostProfiles(queryClient, reply)
+      }
+    }
+  }
+}
+
 async function whenAppViewReady(
   actor: string,
   fn: (res: AppBskyActorGetProfile.Response) => boolean,