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