about summary refs log tree commit diff
path: root/src/view/com
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com')
-rw-r--r--src/view/com/composer/ComposerReplyTo.tsx4
-rw-r--r--src/view/com/notifications/FeedItem.tsx70
-rw-r--r--src/view/com/post-thread/PostThreadItem.tsx11
-rw-r--r--src/view/com/post/Post.tsx10
-rw-r--r--src/view/com/posts/FeedItem.tsx14
-rw-r--r--src/view/com/profile/ProfileCard.tsx15
-rw-r--r--src/view/com/profile/ProfileHeaderSuggestedFollows.tsx3
-rw-r--r--src/view/com/util/Link.tsx7
-rw-r--r--src/view/com/util/PostMeta.tsx17
-rw-r--r--src/view/com/util/TimeElapsed.tsx5
-rw-r--r--src/view/com/util/UserAvatar.tsx32
-rw-r--r--src/view/com/util/post-embeds/QuoteEmbed.tsx6
12 files changed, 101 insertions, 93 deletions
diff --git a/src/view/com/composer/ComposerReplyTo.tsx b/src/view/com/composer/ComposerReplyTo.tsx
index 24a2373f5..7dc17fd4a 100644
--- a/src/view/com/composer/ComposerReplyTo.tsx
+++ b/src/view/com/composer/ComposerReplyTo.tsx
@@ -86,9 +86,7 @@ export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) {
       )}>
       <PreviewableUserAvatar
         size={50}
-        did={replyTo.author.did}
-        handle={replyTo.author.handle}
-        avatar={replyTo.author.avatar}
+        profile={replyTo.author}
         moderation={replyTo.moderation?.ui('avatar')}
         type={replyTo.author.associated?.labeler ? 'labeler' : 'user'}
       />
diff --git a/src/view/com/notifications/FeedItem.tsx b/src/view/com/notifications/FeedItem.tsx
index 3c9c64061..94844cb1a 100644
--- a/src/view/com/notifications/FeedItem.tsx
+++ b/src/view/com/notifications/FeedItem.tsx
@@ -24,6 +24,7 @@ import {
 } from '@fortawesome/react-native-fontawesome'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
+import {useQueryClient} from '@tanstack/react-query'
 
 import {FeedNotification} from '#/state/queries/notifications/feed'
 import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
@@ -36,6 +37,7 @@ import {pluralize} from 'lib/strings/helpers'
 import {niceDate} from 'lib/strings/time'
 import {colors, s} from 'lib/styles'
 import {isWeb} from 'platform/detection'
+import {precacheProfile} from 'state/queries/profile'
 import {Link as NewLink} from '#/components/Link'
 import {ProfileHoverCard} from '#/components/ProfileHoverCard'
 import {FeedSourceCard} from '../feeds/FeedSourceCard'
@@ -52,13 +54,9 @@ const MAX_AUTHORS = 5
 const EXPANDED_AUTHOR_EL_HEIGHT = 35
 
 interface Author {
+  profile: AppBskyActorDefs.ProfileViewBasic
   href: string
-  did: string
-  handle: string
-  displayName?: string
-  avatar?: string
   moderation: ModerationDecision
-  associated?: AppBskyActorDefs.ProfileAssociated
 }
 
 let FeedItem = ({
@@ -68,6 +66,7 @@ let FeedItem = ({
   item: FeedNotification
   moderationOpts: ModerationOpts
 }): React.ReactNode => {
+  const queryClient = useQueryClient()
   const pal = usePalette('default')
   const {_} = useLingui()
   const [isAuthorsExpanded, setAuthorsExpanded] = useState<boolean>(false)
@@ -95,28 +94,22 @@ let FeedItem = ({
     setAuthorsExpanded(currentlyExpanded => !currentlyExpanded)
   }
 
+  const onBeforePress = React.useCallback(() => {
+    precacheProfile(queryClient, item.notification.author)
+  }, [queryClient, item.notification.author])
+
   const authors: Author[] = useMemo(() => {
     return [
       {
+        profile: item.notification.author,
         href: makeProfileLink(item.notification.author),
-        did: item.notification.author.did,
-        handle: item.notification.author.handle,
-        displayName: item.notification.author.displayName,
-        avatar: item.notification.author.avatar,
         moderation: moderateProfile(item.notification.author, moderationOpts),
-        associated: item.notification.author.associated,
       },
-      ...(item.additional?.map(({author}) => {
-        return {
-          href: makeProfileLink(author),
-          did: author.did,
-          handle: author.handle,
-          displayName: author.displayName,
-          avatar: author.avatar,
-          moderation: moderateProfile(author, moderationOpts),
-          associated: author.associated,
-        }
-      }) || []),
+      ...(item.additional?.map(({author}) => ({
+        profile: author,
+        href: makeProfileLink(author),
+        moderation: moderateProfile(author, moderationOpts),
+      })) || []),
     ]
   }, [item, moderationOpts])
 
@@ -201,7 +194,8 @@ let FeedItem = ({
       accessible={
         (item.type === 'post-like' && authors.length === 1) ||
         item.type === 'repost'
-      }>
+      }
+      onBeforePress={onBeforePress}>
       <View style={styles.layoutIcon}>
         {/* TODO: Prevent conditional rendering and move toward composable
         notifications for clearer accessibility labeling */}
@@ -231,7 +225,7 @@ let FeedItem = ({
               style={[pal.text, s.bold]}
               href={authors[0].href}
               text={sanitizeDisplayName(
-                authors[0].displayName || authors[0].handle,
+                authors[0].profile.displayName || authors[0].profile.handle,
               )}
               disableMismatchWarning
             />
@@ -339,11 +333,9 @@ function CondensedAuthorsList({
       <View style={styles.avis}>
         <PreviewableUserAvatar
           size={35}
-          did={authors[0].did}
-          handle={authors[0].handle}
-          avatar={authors[0].avatar}
+          profile={authors[0].profile}
           moderation={authors[0].moderation.ui('avatar')}
-          type={authors[0].associated?.labeler ? 'labeler' : 'user'}
+          type={authors[0].profile.associated?.labeler ? 'labeler' : 'user'}
         />
       </View>
     )
@@ -360,11 +352,9 @@ function CondensedAuthorsList({
           <View key={author.href} style={s.mr5}>
             <PreviewableUserAvatar
               size={35}
-              did={author.did}
-              handle={author.handle}
-              avatar={author.avatar}
+              profile={author.profile}
               moderation={author.moderation.ui('avatar')}
-              type={author.associated?.labeler ? 'labeler' : 'user'}
+              type={author.profile.associated?.labeler ? 'labeler' : 'user'}
             />
           </View>
         ))}
@@ -415,20 +405,20 @@ function ExpandedAuthorsList({
       ]}>
       {authors.map(author => (
         <NewLink
-          key={author.did}
+          key={author.profile.did}
           label={_(msg`See profile`)}
           to={makeProfileLink({
-            did: author.did,
-            handle: author.handle,
+            did: author.profile.did,
+            handle: author.profile.handle,
           })}
           style={styles.expandedAuthor}>
           <View style={styles.expandedAuthorAvi}>
-            <ProfileHoverCard did={author.did}>
+            <ProfileHoverCard did={author.profile.did}>
               <UserAvatar
                 size={35}
-                avatar={author.avatar}
+                avatar={author.profile.avatar}
                 moderation={author.moderation.ui('avatar')}
-                type={author.associated?.labeler ? 'labeler' : 'user'}
+                type={author.profile.associated?.labeler ? 'labeler' : 'user'}
               />
             </ProfileHoverCard>
           </View>
@@ -438,10 +428,12 @@ function ExpandedAuthorsList({
               numberOfLines={1}
               style={pal.text}
               lineHeight={1.2}>
-              {sanitizeDisplayName(author.displayName || author.handle)}
+              {sanitizeDisplayName(
+                author.profile.displayName || author.profile.handle,
+              )}
               &nbsp;
               <Text style={[pal.textLight]} lineHeight={1.2}>
-                {sanitizeHandle(author.handle)}
+                {sanitizeHandle(author.profile.handle)}
               </Text>
             </Text>
           </View>
diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx
index 4c11fdff3..564e37e7a 100644
--- a/src/view/com/post-thread/PostThreadItem.tsx
+++ b/src/view/com/post-thread/PostThreadItem.tsx
@@ -249,9 +249,7 @@ let PostThreadItemLoaded = ({
             <View style={[styles.layoutAvi, {paddingBottom: 8}]}>
               <PreviewableUserAvatar
                 size={42}
-                did={post.author.did}
-                handle={post.author.handle}
-                avatar={post.author.avatar}
+                profile={post.author}
                 moderation={moderation.ui('avatar')}
                 type={post.author.associated?.labeler ? 'labeler' : 'user'}
               />
@@ -399,7 +397,8 @@ let PostThreadItemLoaded = ({
               isThreadedChild
                 ? {marginRight: 4}
                 : {marginLeft: 2, marginRight: 2}
-            }>
+            }
+            profile={post.author}>
             <View
               style={{
                 flexDirection: 'row',
@@ -440,9 +439,7 @@ let PostThreadItemLoaded = ({
                 <View style={styles.layoutAvi}>
                   <PreviewableUserAvatar
                     size={38}
-                    did={post.author.did}
-                    handle={post.author.handle}
-                    avatar={post.author.avatar}
+                    profile={post.author}
                     moderation={moderation.ui('avatar')}
                     type={post.author.associated?.labeler ? 'labeler' : 'user'}
                   />
diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx
index b46586941..546eb2821 100644
--- a/src/view/com/post/Post.tsx
+++ b/src/view/com/post/Post.tsx
@@ -21,7 +21,7 @@ import {usePalette} from 'lib/hooks/usePalette'
 import {makeProfileLink} from 'lib/routes/links'
 import {countLines} from 'lib/strings/helpers'
 import {colors, s} from 'lib/styles'
-import {RQKEY as RQKEY_URI} from 'state/queries/resolve-uri'
+import {precacheProfile} from 'state/queries/profile'
 import {atoms as a} from '#/alf'
 import {ProfileHoverCard} from '#/components/ProfileHoverCard'
 import {RichText} from '#/components/RichText'
@@ -135,8 +135,8 @@ function PostInner({
   }, [setLimitLines])
 
   const onBeforePress = React.useCallback(() => {
-    queryClient.setQueryData(RQKEY_URI(post.author.handle), post.author.did)
-  }, [queryClient, post.author.handle, post.author.did])
+    precacheProfile(queryClient, post.author)
+  }, [queryClient, post.author])
 
   return (
     <Link
@@ -148,9 +148,7 @@ function PostInner({
         <View style={styles.layoutAvi}>
           <PreviewableUserAvatar
             size={52}
-            did={post.author.did}
-            handle={post.author.handle}
-            avatar={post.author.avatar}
+            profile={post.author}
             moderation={moderation.ui('avatar')}
             type={post.author.associated?.labeler ? 'labeler' : 'user'}
           />
diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx
index 7694b5024..605dffde9 100644
--- a/src/view/com/posts/FeedItem.tsx
+++ b/src/view/com/posts/FeedItem.tsx
@@ -13,6 +13,7 @@ import {
 } from '@fortawesome/react-native-fontawesome'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
+import {useQueryClient} from '@tanstack/react-query'
 
 import {POST_TOMBSTONE, Shadow, usePostShadow} from '#/state/cache/post-shadow'
 import {useComposerControls} from '#/state/shell/composer'
@@ -24,6 +25,7 @@ import {sanitizeDisplayName} from 'lib/strings/display-names'
 import {sanitizeHandle} from 'lib/strings/handles'
 import {countLines} from 'lib/strings/helpers'
 import {s} from 'lib/styles'
+import {precacheProfile} from 'state/queries/profile'
 import {atoms as a} from '#/alf'
 import {ContentHider} from '#/components/moderation/ContentHider'
 import {ProfileHoverCard} from '#/components/ProfileHoverCard'
@@ -106,6 +108,7 @@ let FeedItemInner = ({
   isThreadLastChild?: boolean
   isThreadParent?: boolean
 }): React.ReactNode => {
+  const queryClient = useQueryClient()
   const {openComposer} = useComposerControls()
   const pal = usePalette('default')
   const {_} = useLingui()
@@ -135,6 +138,10 @@ let FeedItemInner = ({
     })
   }, [post, record, openComposer, moderation])
 
+  const onBeforePress = React.useCallback(() => {
+    precacheProfile(queryClient, post.author)
+  }, [queryClient, post.author])
+
   const outerStyles = [
     styles.outer,
     {
@@ -153,7 +160,8 @@ let FeedItemInner = ({
       style={outerStyles}
       href={href}
       noFeedback
-      accessible={false}>
+      accessible={false}
+      onBeforePress={onBeforePress}>
       <View style={{flexDirection: 'row', gap: 10, paddingLeft: 8}}>
         <View style={{width: 52}}>
           {isThreadChild && (
@@ -240,9 +248,7 @@ let FeedItemInner = ({
         <View style={styles.layoutAvi}>
           <PreviewableUserAvatar
             size={52}
-            did={post.author.did}
-            handle={post.author.handle}
-            avatar={post.author.avatar}
+            profile={post.author}
             moderation={moderation.ui('avatar')}
             type={post.author.associated?.labeler ? 'labeler' : 'user'}
           />
diff --git a/src/view/com/profile/ProfileCard.tsx b/src/view/com/profile/ProfileCard.tsx
index b52573a01..90ab9b738 100644
--- a/src/view/com/profile/ProfileCard.tsx
+++ b/src/view/com/profile/ProfileCard.tsx
@@ -20,8 +20,7 @@ import {makeProfileLink} from 'lib/routes/links'
 import {sanitizeDisplayName} from 'lib/strings/display-names'
 import {sanitizeHandle} from 'lib/strings/handles'
 import {s} from 'lib/styles'
-import {profileBasicQueryKey as RQKEY_PROFILE_BASIC} from 'state/queries/profile'
-import {RQKEY as RQKEY_URI} from 'state/queries/resolve-uri'
+import {precacheProfile} from 'state/queries/profile'
 import {Link} from '../util/Link'
 import {Text} from '../util/text/Text'
 import {PreviewableUserAvatar} from '../util/UserAvatar'
@@ -58,9 +57,7 @@ export function ProfileCard({
 
   const onBeforePress = React.useCallback(() => {
     onPress?.()
-
-    queryClient.setQueryData(RQKEY_URI(profile.handle), profile.did)
-    queryClient.setQueryData(RQKEY_PROFILE_BASIC(profile.did), profile)
+    precacheProfile(queryClient, profile)
   }, [onPress, profile, queryClient])
 
   if (!moderationOpts) {
@@ -91,9 +88,7 @@ export function ProfileCard({
         <View style={styles.layoutAvi}>
           <PreviewableUserAvatar
             size={40}
-            did={profile.did}
-            handle={profile.handle}
-            avatar={profile.avatar}
+            profile={profile}
             moderation={moderation.ui('avatar')}
             type={isLabeler ? 'labeler' : 'user'}
           />
@@ -238,9 +233,7 @@ function FollowersList({
           <View style={[styles.followedByAvi, pal.view]}>
             <PreviewableUserAvatar
               size={32}
-              did={f.did}
-              handle={f.handle}
-              avatar={f.avatar}
+              profile={f}
               moderation={mod.ui('avatar')}
               type={f.associated?.labeler ? 'labeler' : 'user'}
             />
diff --git a/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx b/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx
index cf35885cd..4c9d164f7 100644
--- a/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx
+++ b/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx
@@ -220,8 +220,7 @@ function SuggestedFollow({
         ]}>
         <PreviewableUserAvatar
           size={60}
-          did={profile.did}
-          handle={profile.handle}
+          profile={profile}
           avatar={profile.avatar}
           moderation={moderation.ui('avatar')}
         />
diff --git a/src/view/com/util/Link.tsx b/src/view/com/util/Link.tsx
index d35d0fcc6..78d995ee8 100644
--- a/src/view/com/util/Link.tsx
+++ b/src/view/com/util/Link.tsx
@@ -148,6 +148,7 @@ export const TextLink = memo(function TextLink({
   dataSet,
   title,
   onPress,
+  onBeforePress,
   disableMismatchWarning,
   navigationAction,
   anchorNoUnderline,
@@ -165,6 +166,7 @@ export const TextLink = memo(function TextLink({
   disableMismatchWarning?: boolean
   navigationAction?: 'push' | 'replace' | 'navigate'
   anchorNoUnderline?: boolean
+  onBeforePress?: () => void
 } & TextProps) {
   const {...props} = useLinkProps({to: sanitizeUrl(href)})
   const navigation = useNavigationDeduped()
@@ -202,6 +204,7 @@ export const TextLink = memo(function TextLink({
         // Let the browser handle opening in new tab etc.
         return
       }
+      onBeforePress?.()
       if (onPress) {
         e?.preventDefault?.()
         // @ts-ignore function signature differs by platform -prf
@@ -226,6 +229,7 @@ export const TextLink = memo(function TextLink({
       disableMismatchWarning,
       navigationAction,
       openLink,
+      onBeforePress,
     ],
   )
   const hrefAttrs = useMemo(() => {
@@ -274,6 +278,7 @@ interface TextLinkOnWebOnlyProps extends TextProps {
   title?: string
   navigationAction?: 'push' | 'replace' | 'navigate'
   disableMismatchWarning?: boolean
+  onBeforePress?: () => void
   onPointerEnter?: () => void
   anchorNoUnderline?: boolean
 }
@@ -287,6 +292,7 @@ export const TextLinkOnWebOnly = memo(function DesktopWebTextLink({
   lineHeight,
   navigationAction,
   disableMismatchWarning,
+  onBeforePress,
   ...props
 }: TextLinkOnWebOnlyProps) {
   if (isWeb) {
@@ -302,6 +308,7 @@ export const TextLinkOnWebOnly = memo(function DesktopWebTextLink({
         title={props.title}
         navigationAction={navigationAction}
         disableMismatchWarning={disableMismatchWarning}
+        onBeforePress={onBeforePress}
         {...props}
       />
     )
diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx
index ed3d3e5b0..db16ff066 100644
--- a/src/view/com/util/PostMeta.tsx
+++ b/src/view/com/util/PostMeta.tsx
@@ -1,8 +1,9 @@
-import React, {memo} from 'react'
+import React, {memo, useCallback} from 'react'
 import {StyleProp, StyleSheet, TextStyle, View, ViewStyle} from 'react-native'
 import {AppBskyActorDefs, ModerationDecision, ModerationUI} from '@atproto/api'
+import {useQueryClient} from '@tanstack/react-query'
 
-import {usePrefetchProfileQuery} from '#/state/queries/profile'
+import {precacheProfile, usePrefetchProfileQuery} from '#/state/queries/profile'
 import {usePalette} from 'lib/hooks/usePalette'
 import {makeProfileLink} from 'lib/routes/links'
 import {sanitizeDisplayName} from 'lib/strings/display-names'
@@ -40,15 +41,18 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
     ? () => prefetchProfileQuery(opts.author.did)
     : undefined
 
+  const queryClient = useQueryClient()
+  const onBeforePress = useCallback(() => {
+    precacheProfile(queryClient, opts.author)
+  }, [queryClient, opts.author])
+
   return (
     <View style={[styles.container, opts.style]}>
       {opts.showAvatar && (
         <View style={styles.avatar}>
           <PreviewableUserAvatar
             size={opts.avatarSize || 16}
-            did={opts.author.did}
-            handle={opts.author.handle}
-            avatar={opts.author.avatar}
+            profile={opts.author}
             moderation={opts.avatarModeration}
             type={opts.author.associated?.labeler ? 'labeler' : 'user'}
           />
@@ -71,6 +75,7 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
             </>
           }
           href={profileLink}
+          onBeforePress={onBeforePress}
           onPointerEnter={onPointerEnter}
         />
         <TextLinkOnWebOnly
@@ -79,6 +84,7 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
           style={[pal.textLight, {flexShrink: 4}]}
           text={'\xa0' + sanitizeHandle(handle, '@')}
           href={profileLink}
+          onBeforePress={onBeforePress}
           onPointerEnter={onPointerEnter}
           anchorNoUnderline
         />
@@ -103,6 +109,7 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
             title={niceDate(opts.timestamp)}
             accessibilityHint=""
             href={opts.postHref}
+            onBeforePress={onBeforePress}
           />
         )}
       </TimeElapsed>
diff --git a/src/view/com/util/TimeElapsed.tsx b/src/view/com/util/TimeElapsed.tsx
index aa3a09223..6ea41b82b 100644
--- a/src/view/com/util/TimeElapsed.tsx
+++ b/src/view/com/util/TimeElapsed.tsx
@@ -1,6 +1,7 @@
 import React from 'react'
-import {ago} from 'lib/strings/time'
+
 import {useTickEveryMinute} from '#/state/shell'
+import {ago} from 'lib/strings/time'
 
 // FIXME(dan): Figure out why the false positives
 
@@ -12,7 +13,7 @@ export function TimeElapsed({
   children: ({timeElapsed}: {timeElapsed: string}) => JSX.Element
 }) {
   const tick = useTickEveryMinute()
-  const [timeElapsed, setTimeAgo] = React.useState(ago(timestamp))
+  const [timeElapsed, setTimeAgo] = React.useState(() => ago(timestamp))
 
   React.useEffect(() => {
     setTimeAgo(ago(timestamp))
diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx
index 89aa56b73..118e2ce2b 100644
--- a/src/view/com/util/UserAvatar.tsx
+++ b/src/view/com/util/UserAvatar.tsx
@@ -2,10 +2,11 @@ import React, {memo, useMemo} from 'react'
 import {Image, StyleSheet, TouchableOpacity, View} from 'react-native'
 import {Image as RNImage} from 'react-native-image-crop-picker'
 import Svg, {Circle, Path, Rect} from 'react-native-svg'
-import {ModerationUI} from '@atproto/api'
+import {AppBskyActorDefs, ModerationUI} from '@atproto/api'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
+import {useQueryClient} from '@tanstack/react-query'
 
 import {usePalette} from 'lib/hooks/usePalette'
 import {
@@ -15,6 +16,7 @@ import {
 import {makeProfileLink} from 'lib/routes/links'
 import {colors} from 'lib/styles'
 import {isAndroid, isNative, isWeb} from 'platform/detection'
+import {precacheProfile} from 'state/queries/profile'
 import {HighPriorityImage} from 'view/com/util/images/Image'
 import {tokens, useTheme} from '#/alf'
 import {
@@ -47,8 +49,7 @@ interface EditableUserAvatarProps extends BaseUserAvatarProps {
 
 interface PreviewableUserAvatarProps extends BaseUserAvatarProps {
   moderation?: ModerationUI
-  did: string
-  handle: string
+  profile: AppBskyActorDefs.ProfileViewBasic
 }
 
 const BLUR_AMOUNT = isWeb ? 5 : 100
@@ -371,19 +372,28 @@ let EditableUserAvatar = ({
 EditableUserAvatar = memo(EditableUserAvatar)
 export {EditableUserAvatar}
 
-let PreviewableUserAvatar = (
-  props: PreviewableUserAvatarProps,
-): React.ReactNode => {
+let PreviewableUserAvatar = ({
+  moderation,
+  profile,
+  ...rest
+}: PreviewableUserAvatarProps): React.ReactNode => {
   const {_} = useLingui()
+  const queryClient = useQueryClient()
+
+  const onPress = React.useCallback(() => {
+    precacheProfile(queryClient, profile)
+  }, [profile, queryClient])
+
   return (
-    <ProfileHoverCard did={props.did}>
+    <ProfileHoverCard did={profile.did}>
       <Link
         label={_(msg`See profile`)}
         to={makeProfileLink({
-          did: props.did,
-          handle: props.handle,
-        })}>
-        <UserAvatar {...props} />
+          did: profile.did,
+          handle: profile.handle,
+        })}
+        onPress={onPress}>
+        <UserAvatar avatar={profile.avatar} moderation={moderation} {...rest} />
       </Link>
     </ProfileHoverCard>
   )
diff --git a/src/view/com/util/post-embeds/QuoteEmbed.tsx b/src/view/com/util/post-embeds/QuoteEmbed.tsx
index 935696ab7..e0178f34b 100644
--- a/src/view/com/util/post-embeds/QuoteEmbed.tsx
+++ b/src/view/com/util/post-embeds/QuoteEmbed.tsx
@@ -26,10 +26,10 @@ import {useQueryClient} from '@tanstack/react-query'
 import {HITSLOP_20} from '#/lib/constants'
 import {s} from '#/lib/styles'
 import {useModerationOpts} from '#/state/queries/preferences'
-import {RQKEY as RQKEY_URI} from '#/state/queries/resolve-uri'
 import {usePalette} from 'lib/hooks/usePalette'
 import {InfoCircleIcon} from 'lib/icons'
 import {makeProfileLink} from 'lib/routes/links'
+import {precacheProfile} from 'state/queries/profile'
 import {ComposerOptsQuote} from 'state/shell/composer'
 import {atoms as a} from '#/alf'
 import {RichText} from '#/components/RichText'
@@ -149,8 +149,8 @@ export function QuoteEmbed({
   }, [quote.embeds])
 
   const onBeforePress = React.useCallback(() => {
-    queryClient.setQueryData(RQKEY_URI(quote.author.handle), quote.author.did)
-  }, [queryClient, quote.author.did, quote.author.handle])
+    precacheProfile(queryClient, quote.author)
+  }, [queryClient, quote.author])
 
   return (
     <ContentHider modui={moderation?.ui('contentList')}>