Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

authored by

Samuel Newman
Claude Opus 4.6
and committed by
GitHub
b12d7033 c188fd4f

+28 -25
+8 -9
src/components/Post/Embed/index.tsx
··· 285 285 286 286 const contents = ( 287 287 <> 288 - <View pointerEvents="none"> 289 - <PostMeta 290 - author={quote.author} 291 - moderation={moderation} 292 - showAvatar 293 - postHref={itemHref} 294 - timestamp={quote.indexedAt} 295 - /> 296 - </View> 288 + <PostMeta 289 + author={quote.author} 290 + moderation={moderation} 291 + showAvatar 292 + postHref={itemHref} 293 + timestamp={quote.indexedAt} 294 + linkDisabled 295 + /> 297 296 {moderation ? ( 298 297 <PostAlerts modui={moderation.ui('contentView')} style={[a.py_xs]} /> 299 298 ) : null}
+16 -12
src/view/com/util/PostMeta.tsx
··· 12 12 import {sanitizeHandle} from '#/lib/strings/handles' 13 13 import {niceDate} from '#/lib/strings/time' 14 14 import {useProfileShadow} from '#/state/cache/profile-shadow' 15 - import {precacheProfile} from '#/state/queries/profile' 15 + import {unstableCacheProfileView} from '#/state/queries/profile' 16 16 import {atoms as a, platform, useTheme, web} from '#/alf' 17 17 import {WebOnlyInlineLinkText} from '#/components/Link' 18 18 import {ProfileHoverCard} from '#/components/ProfileHoverCard' ··· 29 29 moderation: ModerationDecision | undefined 30 30 postHref: string 31 31 timestamp: string 32 + linkDisabled?: boolean 32 33 showAvatar?: boolean 33 34 avatarSize?: number 34 35 onOpenAuthor?: () => void ··· 46 47 const queryClient = useQueryClient() 47 48 const onOpenAuthor = opts.onOpenAuthor 48 49 const onBeforePressAuthor = useCallback(() => { 49 - precacheProfile(queryClient, author) 50 + unstableCacheProfileView(queryClient, author) 50 51 onOpenAuthor?.() 51 52 }, [queryClient, author, onOpenAuthor]) 52 53 const onBeforePressPost = useCallback(() => { 53 - precacheProfile(queryClient, author) 54 + unstableCacheProfileView(queryClient, author) 54 55 }, [queryClient, author]) 55 56 56 57 const timestampLabel = niceDate(i18n, opts.timestamp) 57 58 const verification = useSimpleVerificationState({profile: author}) 58 59 const {isActive: live} = useActorStatus(author) 59 60 61 + const MaybeLinkText = opts.linkDisabled ? Text : WebOnlyInlineLinkText 62 + 60 63 return ( 61 64 <View 62 65 style={[ ··· 77 80 type={author.associated?.labeler ? 'labeler' : 'user'} 78 81 live={live} 79 82 hideLiveBadge 83 + disableNavigation={opts.linkDisabled} 80 84 /> 81 85 </View> 82 86 )} 83 87 <View style={[a.flex_row, a.align_end, a.flex_shrink]}> 84 88 <ProfileHoverCard did={author.did}> 85 89 <View style={[a.flex_row, a.align_end, a.flex_shrink]}> 86 - <WebOnlyInlineLinkText 90 + <MaybeLinkText 87 91 emoji 88 92 numberOfLines={1} 89 93 to={profileLink} 90 94 label={_(msg`View profile`)} 91 95 disableMismatchWarning 92 - onPress={onBeforePressAuthor} 96 + onPress={opts.linkDisabled ? undefined : onBeforePressAuthor} 93 97 style={[ 94 98 a.text_md, 95 99 a.font_semi_bold, ··· 104 108 opts.moderation?.ui('displayName'), 105 109 ), 106 110 )} 107 - </WebOnlyInlineLinkText> 111 + </MaybeLinkText> 108 112 {verification.showBadge && ( 109 113 <View 110 114 style={[ ··· 120 124 /> 121 125 </View> 122 126 )} 123 - <WebOnlyInlineLinkText 127 + <MaybeLinkText 124 128 emoji 125 129 numberOfLines={1} 126 130 to={profileLink} 127 131 label={_(msg`View profile`)} 128 132 disableMismatchWarning 129 133 disableUnderline 130 - onPress={onBeforePressAuthor} 134 + onPress={opts.linkDisabled ? undefined : onBeforePressAuthor} 131 135 style={[ 132 136 a.text_md, 133 137 t.atoms.text_contrast_medium, ··· 135 139 {flexShrink: 10}, 136 140 ]}> 137 141 {NON_BREAKING_SPACE + sanitizeHandle(handle, '@')} 138 - </WebOnlyInlineLinkText> 142 + </MaybeLinkText> 139 143 </View> 140 144 </ProfileHoverCard> 141 145 142 146 <TimeElapsed timestamp={opts.timestamp}> 143 147 {({timeElapsed}) => ( 144 - <WebOnlyInlineLinkText 148 + <MaybeLinkText 145 149 to={opts.postHref} 146 150 label={timestampLabel} 147 151 title={timestampLabel} 148 152 disableMismatchWarning 149 153 disableUnderline 150 - onPress={onBeforePressPost} 154 + onPress={opts.linkDisabled ? undefined : onBeforePressPost} 151 155 style={[ 152 156 a.pl_xs, 153 157 a.text_md, ··· 171 175 </Text> 172 176 )} 173 177 {timeElapsed} 174 - </WebOnlyInlineLinkText> 178 + </MaybeLinkText> 175 179 )} 176 180 </TimeElapsed> 177 181 </View>
+4 -4
src/view/com/util/TimeElapsed.tsx
··· 1 - import React, {type JSX} from 'react' 1 + import {useState} from 'react' 2 2 import {type I18n} from '@lingui/core' 3 3 import {useLingui} from '@lingui/react' 4 4 ··· 11 11 timeToString, 12 12 }: { 13 13 timestamp: string 14 - children: ({timeElapsed}: {timeElapsed: string}) => JSX.Element 14 + children: ({timeElapsed}: {timeElapsed: string}) => React.ReactElement 15 15 timeToString?: (i18n: I18n, timeElapsed: string) => string 16 16 }) { 17 17 const {i18n} = useLingui() 18 18 const ago = useGetTimeAgo() 19 19 const tick = useTickEveryMinute() 20 - const [timeElapsed, setTimeAgo] = React.useState(() => 20 + const [timeElapsed, setTimeAgo] = useState(() => 21 21 timeToString ? timeToString(i18n, timestamp) : ago(timestamp, tick), 22 22 ) 23 23 24 - const [prevTick, setPrevTick] = React.useState(tick) 24 + const [prevTick, setPrevTick] = useState(tick) 25 25 if (prevTick !== tick) { 26 26 setPrevTick(tick) 27 27 setTimeAgo(