From 87e63fe6ece54aa97c53c23a1f164f5432cb1b78 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Thu, 27 Jun 2024 11:09:06 -0500 Subject: [PATCH] Break things up a bit more --- src/components/ProfileCard.tsx | 240 ++++++++++++++++++++++++++------- 1 file changed, 193 insertions(+), 47 deletions(-) diff --git a/src/components/ProfileCard.tsx b/src/components/ProfileCard.tsx index 73bde261a9..015afb996a 100644 --- a/src/components/ProfileCard.tsx +++ b/src/components/ProfileCard.tsx @@ -1,16 +1,28 @@ import React from 'react' -import {View} from 'react-native' -import {AppBskyActorDefs, moderateProfile, ModerationOpts} from '@atproto/api' +import {GestureResponderEvent, View} from 'react-native' +import { + AppBskyActorDefs, + moderateProfile, + ModerationOpts, + RichText as RichTextApi, +} from '@atproto/api' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' -import {createSanitizedDisplayName} from 'lib/moderation/create-sanitized-display-name' +import {sanitizeDisplayName} from '#/lib/strings/display-names' +import {useProfileFollowMutationQueue} from '#/state/queries/profile' import {sanitizeHandle} from 'lib/strings/handles' import {useProfileShadow} from 'state/cache/profile-shadow' import {useSession} from 'state/session' -import {FollowButton} from 'view/com/profile/FollowButton' +import * as Toast from '#/view/com/util/Toast' import {ProfileCardPills} from 'view/com/profile/ProfileCard' import {UserAvatar} from 'view/com/util/UserAvatar' import {atoms as a, useTheme} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' +import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' import {Link as InternalLink, LinkProps} from '#/components/Link' +import {RichText} from '#/components/RichText' import {Text} from '#/components/Typography' export function Default({ @@ -34,7 +46,7 @@ export function Default({ } export function Card({ - profile: profileUnshadowed, + profile, moderationOpts, logContext = 'ProfileCard', }: { @@ -42,62 +54,36 @@ export function Card({ moderationOpts: ModerationOpts logContext?: 'ProfileCard' | 'StarterPackProfilesList' }) { - const t = useTheme() - const {currentAccount, hasSession} = useSession() - - const profile = useProfileShadow(profileUnshadowed) - const name = createSanitizedDisplayName(profile) - const handle = `@${sanitizeHandle(profile.handle)}` const moderation = moderateProfile(profile, moderationOpts) return ( - - - - - {name} - - - {handle} - - - {hasSession && profile.did !== currentAccount?.did && ( - - - - )} - +
+ + + +
+ - {profile.description && ( - - {profile.description} - - )} + + {profile.description && }
) } +export function Header({ + children, +}: { + children: React.ReactElement | React.ReactElement[] +}) { + return {children} +} + export function Link({did, children}: {did: string} & Omit) { return ( ) { ) } + +export function Avatar({ + profile, + moderationOpts, +}: { + profile: AppBskyActorDefs.ProfileViewDetailed + moderationOpts: ModerationOpts +}) { + const moderation = moderateProfile(profile, moderationOpts) + + return ( + + ) +} + +export function NameAndHandle({ + profile, + moderationOpts, +}: { + profile: AppBskyActorDefs.ProfileViewDetailed + moderationOpts: ModerationOpts +}) { + const t = useTheme() + const moderation = moderateProfile(profile, moderationOpts) + const name = sanitizeDisplayName( + profile.displayName || sanitizeHandle(profile.handle), + moderation.ui('displayName'), + ) + const handle = sanitizeHandle(profile.handle, '@') + + return ( + + + {name} + + + {handle} + + + ) +} + +export function Description({description}: {description?: string}) { + const rt = React.useMemo(() => { + if (!description) return + const rt = new RichTextApi({text: description || ''}) + rt.detectFacetsWithoutResolution() + return rt + }, [description]) + if (!rt) return null + return ( + + ) +} + +export function FollowButton({ + profile, + logContext, +}: { + profile: AppBskyActorDefs.ProfileViewBasic + logContext: 'ProfileCard' | 'StarterPackProfilesList' +}) { + const {currentAccount, hasSession} = useSession() + const isMe = profile.did === currentAccount?.did + return hasSession && !isMe ? ( + + ) : null +} + +export function FollowButtonInner({ + profile: profileUnshadowed, + logContext, +}: { + profile: AppBskyActorDefs.ProfileViewBasic + logContext: 'ProfileCard' | 'StarterPackProfilesList' +}) { + const {_} = useLingui() + const profile = useProfileShadow(profileUnshadowed) + const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue( + profile, + logContext, + ) + + const onPressFollow = async (e: GestureResponderEvent) => { + e.preventDefault() + e.stopPropagation() + try { + await queueFollow() + } catch (e: any) { + if (e?.name !== 'AbortError') { + Toast.show(_(msg`An issue occurred, please try again.`)) + } + } + } + + const onPressUnfollow = async (e: GestureResponderEvent) => { + e.preventDefault() + e.stopPropagation() + try { + await queueUnfollow() + } catch (e: any) { + if (e?.name !== 'AbortError') { + Toast.show(_(msg`An issue occurred, please try again.`)) + } + } + } + + const unfollowLabel = _( + msg({ + message: 'Following', + comment: 'User is following this account, click to unfollow', + }), + ) + const followLabel = _( + msg({ + message: 'Follow', + comment: 'User is not following this account, click to follow', + }), + ) + + if (!profile.viewer) return null + + return ( + + {profile.viewer.following ? ( + + ) : ( + + )} + + ) +}