An ATproto social media client -- with an independent Appview.

Hover card on anchor displayName/handle (#8479)

* add hover to anchor display name / handle

* use newer link component

* Wrap using a single hover element

---------

Co-authored-by: Eric Bailey <git@esb.lol>

authored by samuel.fm

Eric Bailey and committed by
GitHub
ed969151 7cd607f5

+58 -46
-1
src/components/ProfileHoverCard/types.ts
··· 3 3 export type ProfileHoverCardProps = { 4 4 children: React.ReactElement 5 5 did: string 6 - inline?: boolean 7 6 disable?: boolean 8 7 }
+5 -5
src/components/RichText.tsx
··· 1 1 import React from 'react' 2 - import {TextStyle} from 'react-native' 2 + import {type TextStyle} from 'react-native' 3 3 import {AppBskyRichtextFacet, RichText as RichTextAPI} from '@atproto/api' 4 4 5 5 import {toShortUrl} from '#/lib/strings/url-helpers' 6 - import {atoms as a, flatten, TextStyleProp} from '#/alf' 6 + import {atoms as a, flatten, type TextStyleProp} from '#/alf' 7 7 import {isOnlyEmoji} from '#/alf/typography' 8 - import {InlineLinkText, LinkProps} from '#/components/Link' 8 + import {InlineLinkText, type LinkProps} from '#/components/Link' 9 9 import {ProfileHoverCard} from '#/components/ProfileHoverCard' 10 10 import {RichTextTag} from '#/components/RichTextTag' 11 - import {Text, TextProps} from '#/components/Typography' 11 + import {Text, type TextProps} from '#/components/Typography' 12 12 13 13 const WORD_WRAP = {wordWrap: 1} 14 14 ··· 105 105 !disableLinks 106 106 ) { 107 107 els.push( 108 - <ProfileHoverCard key={key} inline did={mention.did}> 108 + <ProfileHoverCard key={key} did={mention.did}> 109 109 <InlineLinkText 110 110 selectable={selectable} 111 111 to={`/profile/${mention.did}`}
+49 -36
src/screens/PostThread/components/ThreadItemAnchor.tsx
··· 17 17 import {sanitizeDisplayName} from '#/lib/strings/display-names' 18 18 import {sanitizeHandle} from '#/lib/strings/handles' 19 19 import {niceDate} from '#/lib/strings/time' 20 - import {s} from '#/lib/styles' 21 20 import {getTranslatorLink, isPostInLanguage} from '#/locale/helpers' 22 21 import {logger} from '#/logger' 23 22 import { ··· 34 33 import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies' 35 34 import {type PostSource} from '#/state/unstable-post-source' 36 35 import {PostThreadFollowBtn} from '#/view/com/post-thread/PostThreadFollowBtn' 37 - import {Link} from '#/view/com/util/Link' 38 36 import {formatCount} from '#/view/com/util/numeric/format' 39 37 import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' 40 38 import { ··· 47 45 import {Button} from '#/components/Button' 48 46 import {CalendarClock_Stroke2_Corner0_Rounded as CalendarClockIcon} from '#/components/icons/CalendarClock' 49 47 import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash' 50 - import {InlineLinkText} from '#/components/Link' 48 + import {InlineLinkText, Link} from '#/components/Link' 51 49 import {ContentHider} from '#/components/moderation/ContentHider' 52 50 import {LabelsOnMyPost} from '#/components/moderation/LabelsOnMe' 53 51 import {PostAlerts} from '#/components/moderation/PostAlerts' 54 52 import {type AppModerationCause} from '#/components/Pills' 55 53 import {Embed, PostEmbedViewContext} from '#/components/Post/Embed' 56 54 import {PostControls} from '#/components/PostControls' 55 + import {ProfileHoverCard} from '#/components/ProfileHoverCard' 57 56 import * as Prompt from '#/components/Prompt' 58 57 import {RichText} from '#/components/RichText' 59 58 import * as Skele from '#/components/Skeleton' ··· 197 196 198 197 const threadRootUri = record.reply?.root?.uri || post.uri 199 198 const authorHref = makeProfileLink(post.author) 200 - const authorTitle = post.author.handle 201 199 const isThreadAuthor = getThreadAuthor(post, record) === currentAccount?.did 202 200 203 201 const likesHref = useMemo(() => { ··· 321 319 live={live} 322 320 onBeforePress={onOpenAuthor} 323 321 /> 324 - <View style={[a.flex_1]}> 325 - <View style={[a.flex_row, a.align_center]}> 326 - <Link 327 - style={[a.flex_shrink]} 328 - href={authorHref} 329 - title={authorTitle} 330 - onBeforePress={onOpenAuthor}> 331 - <Text 332 - emoji 333 - style={[a.text_lg, a.font_bold, a.leading_snug, a.self_start]} 334 - numberOfLines={1}> 335 - {sanitizeDisplayName( 322 + <ProfileHoverCard did={post.author.did}> 323 + <View style={[a.flex_1]}> 324 + <View style={[a.flex_row, a.align_center]}> 325 + <Link 326 + to={authorHref} 327 + style={[a.flex_shrink]} 328 + label={sanitizeDisplayName( 336 329 post.author.displayName || 337 330 sanitizeHandle(post.author.handle), 338 331 moderation.ui('displayName'), 339 332 )} 340 - </Text> 341 - </Link> 333 + onPress={onOpenAuthor}> 334 + <Text 335 + emoji 336 + style={[ 337 + a.text_lg, 338 + a.font_bold, 339 + a.leading_snug, 340 + a.self_start, 341 + ]} 342 + numberOfLines={1}> 343 + {sanitizeDisplayName( 344 + post.author.displayName || 345 + sanitizeHandle(post.author.handle), 346 + moderation.ui('displayName'), 347 + )} 348 + </Text> 349 + </Link> 342 350 343 - <View style={[{paddingLeft: 3, top: -1}]}> 344 - <VerificationCheckButton profile={authorShadow} size="md" /> 351 + <View style={[{paddingLeft: 3, top: -1}]}> 352 + <VerificationCheckButton profile={authorShadow} size="md" /> 353 + </View> 354 + </View> 355 + <View style={[a.align_start]}> 356 + <Link 357 + style={[a.flex_shrink]} 358 + to={authorHref} 359 + label={sanitizeHandle(post.author.handle, '@')}> 360 + <Text 361 + style={[ 362 + a.text_md, 363 + a.leading_snug, 364 + t.atoms.text_contrast_medium, 365 + ]} 366 + numberOfLines={1}> 367 + {sanitizeHandle(post.author.handle, '@')} 368 + </Text> 369 + </Link> 345 370 </View> 346 371 </View> 347 - <Link style={s.flex1} href={authorHref} title={authorTitle}> 348 - <Text 349 - emoji 350 - style={[ 351 - a.text_md, 352 - a.leading_snug, 353 - t.atoms.text_contrast_medium, 354 - ]} 355 - numberOfLines={1}> 356 - {sanitizeHandle(post.author.handle, '@')} 357 - </Text> 358 - </Link> 359 - </View> 372 + </ProfileHoverCard> 360 373 {showFollowButton && ( 361 374 <View> 362 375 <PostThreadFollowBtn did={post.author.did} /> ··· 417 430 t.atoms.border_contrast_low, 418 431 ]}> 419 432 {post.repostCount != null && post.repostCount !== 0 ? ( 420 - <Link href={repostsHref} title={_(msg`Reposts of this post`)}> 433 + <Link to={repostsHref} label={_(msg`Reposts of this post`)}> 421 434 <Text 422 435 testID="repostCount-expanded" 423 436 style={[a.text_md, t.atoms.text_contrast_medium]}> ··· 435 448 {post.quoteCount != null && 436 449 post.quoteCount !== 0 && 437 450 !post.viewer?.embeddingDisabled ? ( 438 - <Link href={quotesHref} title={_(msg`Quotes of this post`)}> 451 + <Link to={quotesHref} label={_(msg`Quotes of this post`)}> 439 452 <Text 440 453 testID="quoteCount-expanded" 441 454 style={[a.text_md, t.atoms.text_contrast_medium]}> ··· 451 464 </Link> 452 465 ) : null} 453 466 {post.likeCount != null && post.likeCount !== 0 ? ( 454 - <Link href={likesHref} title={_(msg`Likes on this post`)}> 467 + <Link to={likesHref} label={_(msg`Likes on this post`)}> 455 468 <Text 456 469 testID="likeCount-expanded" 457 470 style={[a.text_md, t.atoms.text_contrast_medium]}>
+1 -1
src/view/com/post/Post.tsx
··· 204 204 ) : ( 205 205 <Trans context="description"> 206 206 Reply to{' '} 207 - <ProfileHoverCard inline did={replyAuthorDid}> 207 + <ProfileHoverCard did={replyAuthorDid}> 208 208 <UserInfoText 209 209 type="sm" 210 210 did={replyAuthorDid}
+2 -2
src/view/com/posts/PostFeedItem.tsx
··· 368 368 ) : ( 369 369 <Trans> 370 370 Reposted by{' '} 371 - <ProfileHoverCard inline did={reason.by.did}> 371 + <ProfileHoverCard did={reason.by.did}> 372 372 <TextLinkOnWebOnly 373 373 type="sm-bold" 374 374 style={pal.textLight} ··· 607 607 label = ( 608 608 <Trans context="description"> 609 609 Reply to{' '} 610 - <ProfileHoverCard inline did={profile.did}> 610 + <ProfileHoverCard did={profile.did}> 611 611 <TextLinkOnWebOnly 612 612 type="md" 613 613 style={pal.textLight}
+1 -1
src/view/com/util/PostMeta.tsx
··· 82 82 </View> 83 83 )} 84 84 <View style={[a.flex_row, a.align_end, a.flex_shrink]}> 85 - <ProfileHoverCard inline did={author.did}> 85 + <ProfileHoverCard did={author.did}> 86 86 <View style={[a.flex_row, a.align_end, a.flex_shrink]}> 87 87 <WebOnlyInlineLinkText 88 88 emoji