Enable profile hover card on quoted post authors (#9896)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-02-18 14:35:38 +00:00
committed by GitHub
parent c188fd4f04
commit b12d703353
3 changed files with 28 additions and 25 deletions
+16 -12
View File
@@ -12,7 +12,7 @@ import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {sanitizeHandle} from '#/lib/strings/handles'
import {niceDate} from '#/lib/strings/time'
import {useProfileShadow} from '#/state/cache/profile-shadow'
import {precacheProfile} from '#/state/queries/profile'
import {unstableCacheProfileView} from '#/state/queries/profile'
import {atoms as a, platform, useTheme, web} from '#/alf'
import {WebOnlyInlineLinkText} from '#/components/Link'
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
@@ -29,6 +29,7 @@ interface PostMetaOpts {
moderation: ModerationDecision | undefined
postHref: string
timestamp: string
linkDisabled?: boolean
showAvatar?: boolean
avatarSize?: number
onOpenAuthor?: () => void
@@ -46,17 +47,19 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
const queryClient = useQueryClient()
const onOpenAuthor = opts.onOpenAuthor
const onBeforePressAuthor = useCallback(() => {
precacheProfile(queryClient, author)
unstableCacheProfileView(queryClient, author)
onOpenAuthor?.()
}, [queryClient, author, onOpenAuthor])
const onBeforePressPost = useCallback(() => {
precacheProfile(queryClient, author)
unstableCacheProfileView(queryClient, author)
}, [queryClient, author])
const timestampLabel = niceDate(i18n, opts.timestamp)
const verification = useSimpleVerificationState({profile: author})
const {isActive: live} = useActorStatus(author)
const MaybeLinkText = opts.linkDisabled ? Text : WebOnlyInlineLinkText
return (
<View
style={[
@@ -77,19 +80,20 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
type={author.associated?.labeler ? 'labeler' : 'user'}
live={live}
hideLiveBadge
disableNavigation={opts.linkDisabled}
/>
</View>
)}
<View style={[a.flex_row, a.align_end, a.flex_shrink]}>
<ProfileHoverCard did={author.did}>
<View style={[a.flex_row, a.align_end, a.flex_shrink]}>
<WebOnlyInlineLinkText
<MaybeLinkText
emoji
numberOfLines={1}
to={profileLink}
label={_(msg`View profile`)}
disableMismatchWarning
onPress={onBeforePressAuthor}
onPress={opts.linkDisabled ? undefined : onBeforePressAuthor}
style={[
a.text_md,
a.font_semi_bold,
@@ -104,7 +108,7 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
opts.moderation?.ui('displayName'),
),
)}
</WebOnlyInlineLinkText>
</MaybeLinkText>
{verification.showBadge && (
<View
style={[
@@ -120,14 +124,14 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
/>
</View>
)}
<WebOnlyInlineLinkText
<MaybeLinkText
emoji
numberOfLines={1}
to={profileLink}
label={_(msg`View profile`)}
disableMismatchWarning
disableUnderline
onPress={onBeforePressAuthor}
onPress={opts.linkDisabled ? undefined : onBeforePressAuthor}
style={[
a.text_md,
t.atoms.text_contrast_medium,
@@ -135,19 +139,19 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
{flexShrink: 10},
]}>
{NON_BREAKING_SPACE + sanitizeHandle(handle, '@')}
</WebOnlyInlineLinkText>
</MaybeLinkText>
</View>
</ProfileHoverCard>
<TimeElapsed timestamp={opts.timestamp}>
{({timeElapsed}) => (
<WebOnlyInlineLinkText
<MaybeLinkText
to={opts.postHref}
label={timestampLabel}
title={timestampLabel}
disableMismatchWarning
disableUnderline
onPress={onBeforePressPost}
onPress={opts.linkDisabled ? undefined : onBeforePressPost}
style={[
a.pl_xs,
a.text_md,
@@ -171,7 +175,7 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
</Text>
)}
{timeElapsed}
</WebOnlyInlineLinkText>
</MaybeLinkText>
)}
</TimeElapsed>
</View>
+4 -4
View File
@@ -1,4 +1,4 @@
import React, {type JSX} from 'react'
import {useState} from 'react'
import {type I18n} from '@lingui/core'
import {useLingui} from '@lingui/react'
@@ -11,17 +11,17 @@ export function TimeElapsed({
timeToString,
}: {
timestamp: string
children: ({timeElapsed}: {timeElapsed: string}) => JSX.Element
children: ({timeElapsed}: {timeElapsed: string}) => React.ReactElement
timeToString?: (i18n: I18n, timeElapsed: string) => string
}) {
const {i18n} = useLingui()
const ago = useGetTimeAgo()
const tick = useTickEveryMinute()
const [timeElapsed, setTimeAgo] = React.useState(() =>
const [timeElapsed, setTimeAgo] = useState(() =>
timeToString ? timeToString(i18n, timestamp) : ago(timestamp, tick),
)
const [prevTick, setPrevTick] = React.useState(tick)
const [prevTick, setPrevTick] = useState(tick)
if (prevTick !== tick) {
setPrevTick(tick)
setTimeAgo(