this repo has no description
0
fork

Configure Feed

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

at main 219 lines 6.7 kB view raw
1import {useMemo} from 'react' 2import {View} from 'react-native' 3import { 4 type AppBskyActorDefs, 5 type ModerationCause, 6 type ModerationDecision, 7} from '@atproto/api' 8import {msg} from '@lingui/core/macro' 9import {useLingui} from '@lingui/react' 10 11import {makeProfileLink} from '#/lib/routes/links' 12import {sanitizeDisplayName} from '#/lib/strings/display-names' 13import {type Shadow} from '#/state/cache/profile-shadow' 14import {isConvoActive, useConvo} from '#/state/messages/convo' 15import {type ConvoItem} from '#/state/messages/convo/types' 16import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' 17import {atoms as a, useTheme, web} from '#/alf' 18import {ConvoMenu} from '#/components/dms/ConvoMenu' 19import {Bell2Off_Filled_Corner0_Rounded as BellStroke} from '#/components/icons/Bell2' 20import * as Layout from '#/components/Layout' 21import {Link} from '#/components/Link' 22import {PostAlerts} from '#/components/moderation/PostAlerts' 23import {PdsBadge} from '#/components/PdsBadge' 24import {ProfileBadges} from '#/components/ProfileBadges' 25import {Text} from '#/components/Typography' 26import {IS_WEB} from '#/env' 27 28const PFP_SIZE = IS_WEB ? 40 : Layout.HEADER_SLOT_SIZE 29 30export function MessagesListHeader({ 31 profile, 32 moderation, 33}: { 34 profile?: Shadow<AppBskyActorDefs.ProfileViewDetailed> 35 moderation?: ModerationDecision 36}) { 37 const t = useTheme() 38 39 const blockInfo = useMemo(() => { 40 if (!moderation) return 41 const modui = moderation.ui('profileView') 42 const blocks = modui.alerts.filter(alert => alert.type === 'blocking') 43 const listBlocks = blocks.filter(alert => alert.source.type === 'list') 44 const userBlock = blocks.find(alert => alert.source.type === 'user') 45 return { 46 listBlocks, 47 userBlock, 48 } 49 }, [moderation]) 50 51 return ( 52 <Layout.Header.Outer> 53 <View style={[a.w_full, a.flex_row, a.gap_xs, a.align_start]}> 54 <View style={[{minHeight: PFP_SIZE}, a.justify_center]}> 55 <Layout.Header.BackButton /> 56 </View> 57 {profile && moderation && blockInfo ? ( 58 <HeaderReady 59 profile={profile} 60 moderation={moderation} 61 blockInfo={blockInfo} 62 /> 63 ) : ( 64 <> 65 <View style={[a.flex_row, a.align_center, a.gap_md, a.flex_1]}> 66 <View 67 style={[ 68 {width: PFP_SIZE, height: PFP_SIZE}, 69 a.rounded_full, 70 t.atoms.bg_contrast_25, 71 ]} 72 /> 73 <View style={a.gap_xs}> 74 <View 75 style={[ 76 {width: 120, height: 16}, 77 a.rounded_xs, 78 t.atoms.bg_contrast_25, 79 a.mt_xs, 80 ]} 81 /> 82 <View 83 style={[ 84 {width: 175, height: 12}, 85 a.rounded_xs, 86 t.atoms.bg_contrast_25, 87 ]} 88 /> 89 </View> 90 </View> 91 92 <Layout.Header.Slot /> 93 </> 94 )} 95 </View> 96 </Layout.Header.Outer> 97 ) 98} 99 100function HeaderReady({ 101 profile, 102 moderation, 103 blockInfo, 104}: { 105 profile: Shadow<AppBskyActorDefs.ProfileViewDetailed> 106 moderation: ModerationDecision 107 blockInfo: { 108 listBlocks: ModerationCause[] 109 userBlock?: ModerationCause 110 } 111}) { 112 const {_} = useLingui() 113 const t = useTheme() 114 const convoState = useConvo() 115 116 const isDeletedAccount = profile?.handle === 'missing.invalid' 117 const displayName = isDeletedAccount 118 ? _(msg`Deleted Account`) 119 : sanitizeDisplayName( 120 profile.displayName || profile.handle, 121 moderation.ui('displayName'), 122 ) 123 124 // @ts-ignore findLast is polyfilled - esb 125 const latestMessageFromOther = convoState.items.findLast( 126 (item: ConvoItem) => 127 item.type === 'message' && item.message.sender.did === profile.did, 128 ) 129 130 const latestReportableMessage = 131 latestMessageFromOther?.type === 'message' 132 ? latestMessageFromOther.message 133 : undefined 134 135 return ( 136 <View style={[a.flex_1]}> 137 <View style={[a.w_full, a.flex_row, a.align_center, a.justify_between]}> 138 <Link 139 label={_(msg`View ${displayName}'s profile`)} 140 style={[a.flex_row, a.align_start, a.gap_md, a.flex_1, a.pr_md]} 141 to={makeProfileLink(profile)}> 142 <PreviewableUserAvatar 143 size={PFP_SIZE} 144 profile={profile} 145 moderation={moderation.ui('avatar')} 146 disableHoverCard={moderation.blocked} 147 /> 148 <View style={[a.flex_1]}> 149 <View style={[a.flex_row, a.align_center]}> 150 <Text 151 emoji 152 style={[ 153 a.text_md, 154 a.font_semi_bold, 155 a.self_start, 156 web(a.leading_normal), 157 ]} 158 numberOfLines={1}> 159 {displayName} 160 </Text> 161 <View style={[a.pl_xs]}> 162 <PdsBadge did={profile.did} size="sm" /> 163 </View> 164 <ProfileBadges profile={profile} size="md" style={[a.pl_xs]} /> 165 </View> 166 {!isDeletedAccount && ( 167 <Text 168 style={[ 169 t.atoms.text_contrast_medium, 170 a.text_xs, 171 web([a.leading_normal, {marginTop: -2}]), 172 ]} 173 numberOfLines={1}> 174 @{profile.handle} 175 {convoState.convo?.muted && ( 176 <> 177 {' '} 178 &middot;{' '} 179 <BellStroke 180 size="xs" 181 style={t.atoms.text_contrast_medium} 182 /> 183 </> 184 )} 185 </Text> 186 )} 187 </View> 188 </Link> 189 190 <View style={[{minHeight: PFP_SIZE}, a.justify_center]}> 191 <Layout.Header.Slot> 192 {isConvoActive(convoState) && ( 193 <ConvoMenu 194 convo={convoState.convo} 195 profile={profile} 196 currentScreen="conversation" 197 blockInfo={blockInfo} 198 latestReportableMessage={latestReportableMessage} 199 /> 200 )} 201 </Layout.Header.Slot> 202 </View> 203 </View> 204 205 <View 206 style={[ 207 { 208 paddingLeft: PFP_SIZE + a.gap_md.gap, 209 }, 210 ]}> 211 <PostAlerts 212 modui={moderation.ui('contentList')} 213 size="lg" 214 style={[a.pt_xs]} 215 /> 216 </View> 217 </View> 218 ) 219}