diff options
Diffstat (limited to 'src/view/com/profile/ProfileCard.tsx')
-rw-r--r-- | src/view/com/profile/ProfileCard.tsx | 311 |
1 files changed, 20 insertions, 291 deletions
diff --git a/src/view/com/profile/ProfileCard.tsx b/src/view/com/profile/ProfileCard.tsx index bd09d6514..cee950703 100644 --- a/src/view/com/profile/ProfileCard.tsx +++ b/src/view/com/profile/ProfileCard.tsx @@ -1,307 +1,36 @@ -import React from 'react' -import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' -import { - AppBskyActorDefs, - moderateProfile, - ModerationDecision, -} from '@atproto/api' -import {useQueryClient} from '@tanstack/react-query' +import {View} from 'react-native' +import {type AppBskyActorDefs} from '@atproto/api' -import {usePalette} from '#/lib/hooks/usePalette' -import {getModerationCauseKey, isJustAMute} from '#/lib/moderation' -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 {useProfileShadow} from '#/state/cache/profile-shadow' -import {Shadow} from '#/state/cache/types' import {useModerationOpts} from '#/state/preferences/moderation-opts' -import {precacheProfile} from '#/state/queries/profile' -import {useSession} from '#/state/session' -import {atoms as a} from '#/alf' -import { - KnownFollowers, - shouldShowKnownFollowers, -} from '#/components/KnownFollowers' -import * as Pills from '#/components/Pills' -import * as bsky from '#/types/bsky' -import {Link} from '../util/Link' -import {Text} from '../util/text/Text' -import {PreviewableUserAvatar} from '../util/UserAvatar' -import {FollowButton} from './FollowButton' - -export function ProfileCard({ - testID, - profile: profileUnshadowed, - noModFilter, - noBg, - noBorder, - renderButton, - onPress, - style, - showKnownFollowers, -}: { - testID?: string - profile: bsky.profile.AnyProfileView - noModFilter?: boolean - noBg?: boolean - noBorder?: boolean - renderButton?: ( - profile: Shadow<bsky.profile.AnyProfileView>, - ) => React.ReactNode - onPress?: () => void - style?: StyleProp<ViewStyle> - showKnownFollowers?: boolean -}) { - const queryClient = useQueryClient() - const pal = usePalette('default') - const profile = useProfileShadow(profileUnshadowed) - const moderationOpts = useModerationOpts() - const isLabeler = profile?.associated?.labeler - - const onBeforePress = React.useCallback(() => { - onPress?.() - precacheProfile(queryClient, profile) - }, [onPress, profile, queryClient]) - - if (!moderationOpts) { - return null - } - const moderation = moderateProfile(profile, moderationOpts) - const modui = moderation.ui('profileList') - if (!noModFilter && modui.filter && !isJustAMute(modui)) { - return null - } - - const knownFollowersVisible = - showKnownFollowers && - shouldShowKnownFollowers(profile.viewer?.knownFollowers) && - moderationOpts - const hasDescription = 'description' in profile - - return ( - <Link - testID={testID} - style={[ - styles.outer, - pal.border, - noBorder && styles.outerNoBorder, - !noBg && pal.view, - style, - ]} - href={makeProfileLink(profile)} - title={profile.handle} - asAnchor - onBeforePress={onBeforePress} - anchorNoUnderline> - <View style={styles.layout}> - <View style={styles.layoutAvi}> - <PreviewableUserAvatar - size={40} - profile={profile} - moderation={moderation.ui('avatar')} - type={isLabeler ? 'labeler' : 'user'} - /> - </View> - <View style={styles.layoutContent}> - <Text - emoji - type="lg" - style={[s.bold, pal.text, a.self_start]} - numberOfLines={1} - lineHeight={1.2}> - {sanitizeDisplayName( - profile.displayName || sanitizeHandle(profile.handle), - moderation.ui('displayName'), - )} - </Text> - <Text emoji type="md" style={[pal.textLight]} numberOfLines={1}> - {sanitizeHandle(profile.handle, '@')} - </Text> - <ProfileCardPills - followedBy={!!profile.viewer?.followedBy} - moderation={moderation} - /> - {!!profile.viewer?.followedBy && <View style={s.flexRow} />} - </View> - {renderButton && !isLabeler ? ( - <View style={styles.layoutButton}>{renderButton(profile)}</View> - ) : undefined} - </View> - {hasDescription || knownFollowersVisible ? ( - <View style={styles.details}> - {hasDescription && profile.description ? ( - <Text emoji style={pal.text} numberOfLines={4}> - {profile.description as string} - </Text> - ) : null} - {knownFollowersVisible ? ( - <View - style={[ - a.flex_row, - a.align_center, - a.gap_sm, - !!hasDescription && a.mt_md, - ]}> - <KnownFollowers - minimal - profile={profile} - moderationOpts={moderationOpts} - /> - </View> - ) : null} - </View> - ) : null} - </Link> - ) -} - -export function ProfileCardPills({ - followedBy, - moderation, -}: { - followedBy: boolean - moderation: ModerationDecision -}) { - const modui = moderation.ui('profileList') - if (!followedBy && !modui.inform && !modui.alert) { - return null - } - - return ( - <Pills.Row style={[a.pt_xs]}> - {followedBy && <Pills.FollowsYou />} - {modui.alerts.map(alert => ( - <Pills.Label key={getModerationCauseKey(alert)} cause={alert} /> - ))} - {modui.informs.map(inform => ( - <Pills.Label key={getModerationCauseKey(inform)} cause={inform} /> - ))} - </Pills.Row> - ) -} +import {atoms as a, useTheme} from '#/alf' +import * as ProfileCard from '#/components/ProfileCard' export function ProfileCardWithFollowBtn({ profile, - noBg, noBorder, - onPress, - onFollow, logContext = 'ProfileCard', - showKnownFollowers, }: { profile: AppBskyActorDefs.ProfileView - noBg?: boolean noBorder?: boolean - onPress?: () => void - onFollow?: () => void logContext?: 'ProfileCard' | 'StarterPackProfilesList' - showKnownFollowers?: boolean }) { - const {currentAccount} = useSession() - const isMe = profile.did === currentAccount?.did + const t = useTheme() + const moderationOpts = useModerationOpts() + + if (!moderationOpts) return null return ( - <ProfileCard - profile={profile} - noBg={noBg} - noBorder={noBorder} - renderButton={ - isMe - ? undefined - : profileShadow => ( - <FollowButton - profile={profileShadow} - logContext={logContext} - onFollow={onFollow} - /> - ) - } - onPress={onPress} - showKnownFollowers={!isMe && showKnownFollowers} - /> + <View + style={[ + a.py_md, + a.px_xl, + !noBorder && [a.border_t, t.atoms.border_contrast_low], + ]}> + <ProfileCard.Default + profile={profile} + moderationOpts={moderationOpts} + logContext={logContext} + /> + </View> ) } - -const styles = StyleSheet.create({ - outer: { - borderTopWidth: StyleSheet.hairlineWidth, - paddingHorizontal: 6, - paddingVertical: 4, - }, - outerNoBorder: { - borderTopWidth: 0, - }, - layout: { - flexDirection: 'row', - alignItems: 'center', - }, - layoutAvi: { - alignSelf: 'flex-start', - width: 54, - paddingLeft: 4, - paddingTop: 10, - }, - avi: { - width: 40, - height: 40, - borderRadius: 20, - resizeMode: 'cover', - }, - layoutContent: { - flex: 1, - paddingRight: 10, - paddingTop: 10, - paddingBottom: 10, - }, - layoutButton: { - paddingRight: 10, - }, - details: { - justifyContent: 'center', - paddingLeft: 54, - paddingRight: 10, - paddingBottom: 10, - }, - pills: { - alignItems: 'flex-start', - flexDirection: 'row', - flexWrap: 'wrap', - columnGap: 6, - rowGap: 2, - }, - pill: { - borderRadius: 4, - paddingHorizontal: 6, - paddingVertical: 2, - justifyContent: 'center', - }, - btn: { - paddingVertical: 7, - borderRadius: 50, - marginLeft: 6, - paddingHorizontal: 14, - }, - - followedBy: { - flexDirection: 'row', - paddingLeft: 54, - paddingRight: 20, - marginBottom: 10, - marginTop: -6, - }, - followedByAviContainer: { - width: 24, - height: 36, - }, - followedByAvi: { - width: 36, - height: 36, - borderRadius: 18, - padding: 2, - }, - followsByDesc: { - flex: 1, - paddingRight: 10, - }, -}) |