From 23a4bbf6082a7d02c7dbf0af74f81e2200327eb0 Mon Sep 17 00:00:00 2001
From: dan <dan.abramov@gmail.com>
Date: Fri, 26 Jan 2024 05:23:56 +0000
Subject: [PATCH] Prefetch profile links on web (#2634)

---
 src/state/queries/profile.ts          | 17 +++++++++++++++++
 src/view/com/util/Link.tsx            |  2 ++
 src/view/com/util/PostMeta.tsx        |  9 ++++++++-
 src/view/com/util/UserPreviewLink.tsx |  7 +++++++
 4 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/src/state/queries/profile.ts b/src/state/queries/profile.ts
index e6203550..74be9933 100644
--- a/src/state/queries/profile.ts
+++ b/src/state/queries/profile.ts
@@ -57,6 +57,23 @@ export function useProfilesQuery({handles}: {handles: string[]}) {
   })
 }
 
+export function usePrefetchProfileQuery() {
+  const queryClient = useQueryClient()
+  const prefetchProfileQuery = useCallback(
+    (did: string) => {
+      queryClient.prefetchQuery({
+        queryKey: RQKEY(did),
+        queryFn: async () => {
+          const res = await getAgent().getProfile({actor: did || ''})
+          return res.data
+        },
+      })
+    },
+    [queryClient],
+  )
+  return prefetchProfileQuery
+}
+
 interface ProfileUpdateParams {
   profile: AppBskyActorDefs.ProfileView
   updates:
diff --git a/src/view/com/util/Link.tsx b/src/view/com/util/Link.tsx
index db26258d..a517ba43 100644
--- a/src/view/com/util/Link.tsx
+++ b/src/view/com/util/Link.tsx
@@ -47,6 +47,7 @@ interface Props extends ComponentProps<typeof TouchableOpacity> {
   asAnchor?: boolean
   anchorNoUnderline?: boolean
   navigationAction?: 'push' | 'replace' | 'navigate'
+  onPointerEnter?: () => void
 }
 
 export const Link = memo(function Link({
@@ -264,6 +265,7 @@ interface TextLinkOnWebOnlyProps extends TextProps {
   accessibilityHint?: string
   title?: string
   navigationAction?: 'push' | 'replace' | 'navigate'
+  onPointerEnter?: () => void
 }
 export const TextLinkOnWebOnly = memo(function DesktopWebTextLink({
   testID,
diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx
index b9c3842b..4cd574d5 100644
--- a/src/view/com/util/PostMeta.tsx
+++ b/src/view/com/util/PostMeta.tsx
@@ -8,10 +8,11 @@ import {TypographyVariant} from 'lib/ThemeContext'
 import {UserAvatar} from './UserAvatar'
 import {sanitizeDisplayName} from 'lib/strings/display-names'
 import {sanitizeHandle} from 'lib/strings/handles'
-import {isAndroid} from 'platform/detection'
+import {isAndroid, isWeb} from 'platform/detection'
 import {TimeElapsed} from './TimeElapsed'
 import {makeProfileLink} from 'lib/routes/links'
 import {ModerationUI} from '@atproto/api'
+import {usePrefetchProfileQuery} from '#/state/queries/profile'
 
 interface PostMetaOpts {
   author: {
@@ -35,6 +36,7 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
   const pal = usePalette('default')
   const displayName = opts.author.displayName || opts.author.handle
   const handle = opts.author.handle
+  const prefetchProfileQuery = usePrefetchProfileQuery()
 
   return (
     <View style={[styles.container, opts.style]}>
@@ -66,6 +68,11 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
             </>
           }
           href={makeProfileLink(opts.author)}
+          onPointerEnter={() => {
+            if (isWeb) {
+              prefetchProfileQuery(opts.author.did)
+            }
+          }}
         />
       </View>
       {!isAndroid && (
diff --git a/src/view/com/util/UserPreviewLink.tsx b/src/view/com/util/UserPreviewLink.tsx
index a8985ce4..2f257bb5 100644
--- a/src/view/com/util/UserPreviewLink.tsx
+++ b/src/view/com/util/UserPreviewLink.tsx
@@ -4,6 +4,7 @@ import {Link} from './Link'
 import {isAndroid, isWeb} from 'platform/detection'
 import {makeProfileLink} from 'lib/routes/links'
 import {useModalControls} from '#/state/modals'
+import {usePrefetchProfileQuery} from '#/state/queries/profile'
 
 interface UserPreviewLinkProps {
   did: string
@@ -14,10 +15,16 @@ export function UserPreviewLink(
   props: React.PropsWithChildren<UserPreviewLinkProps>,
 ) {
   const {openModal} = useModalControls()
+  const prefetchProfileQuery = usePrefetchProfileQuery()
 
   if (isWeb || isAndroid) {
     return (
       <Link
+        onPointerEnter={() => {
+          if (isWeb) {
+            prefetchProfileQuery(props.did)
+          }
+        }}
         href={makeProfileLink(props)}
         title={props.handle}
         asAnchor