mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React from 'react'
2import {Pressable, View} from 'react-native'
3import {MeasuredDimensions, runOnJS, runOnUI} from 'react-native-reanimated'
4import {AppBskyGraphDefs} from '@atproto/api'
5import {msg, Trans} from '@lingui/macro'
6import {useLingui} from '@lingui/react'
7import {useNavigation} from '@react-navigation/native'
8
9import {measureHandle, useHandleRef} from '#/lib/hooks/useHandleRef'
10import {usePalette} from '#/lib/hooks/usePalette'
11import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
12import {makeProfileLink} from '#/lib/routes/links'
13import {NavigationProp} from '#/lib/routes/types'
14import {sanitizeHandle} from '#/lib/strings/handles'
15import {emitSoftReset} from '#/state/events'
16import {useLightboxControls} from '#/state/lightbox'
17import {TextLink} from '#/view/com/util/Link'
18import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
19import {Text} from '#/view/com/util/text/Text'
20import {UserAvatar, UserAvatarType} from '#/view/com/util/UserAvatar'
21import {StarterPack} from '#/components/icons/StarterPack'
22import * as Layout from '#/components/Layout'
23
24export function ProfileSubpageHeader({
25 isLoading,
26 href,
27 title,
28 avatar,
29 isOwner,
30 purpose,
31 creator,
32 avatarType,
33 children,
34}: React.PropsWithChildren<{
35 isLoading?: boolean
36 href: string
37 title: string | undefined
38 avatar: string | undefined
39 isOwner: boolean | undefined
40 purpose: AppBskyGraphDefs.ListPurpose | undefined
41 creator:
42 | {
43 did: string
44 handle: string
45 }
46 | undefined
47 avatarType: UserAvatarType | 'starter-pack'
48}>) {
49 const navigation = useNavigation<NavigationProp>()
50 const {_} = useLingui()
51 const {isMobile} = useWebMediaQueries()
52 const {openLightbox} = useLightboxControls()
53 const pal = usePalette('default')
54 const canGoBack = navigation.canGoBack()
55 const aviRef = useHandleRef()
56
57 const _openLightbox = React.useCallback(
58 (uri: string, thumbRect: MeasuredDimensions | null) => {
59 openLightbox({
60 images: [
61 {
62 uri,
63 thumbUri: uri,
64 thumbRect,
65 dimensions: {
66 // It's fine if it's actually smaller but we know it's 1:1.
67 height: 1000,
68 width: 1000,
69 },
70 thumbDimensions: null,
71 type: 'rect-avi',
72 },
73 ],
74 index: 0,
75 })
76 },
77 [openLightbox],
78 )
79
80 const onPressAvi = React.useCallback(() => {
81 if (
82 avatar // TODO && !(view.moderation.avatar.blur && view.moderation.avatar.noOverride)
83 ) {
84 const aviHandle = aviRef.current
85 runOnUI(() => {
86 'worklet'
87 const rect = measureHandle(aviHandle)
88 runOnJS(_openLightbox)(avatar, rect)
89 })()
90 }
91 }, [_openLightbox, avatar, aviRef])
92
93 return (
94 <>
95 <Layout.Header.Outer>
96 {canGoBack ? (
97 <Layout.Header.BackButton />
98 ) : (
99 <Layout.Header.MenuButton />
100 )}
101 <Layout.Header.Content />
102 {children}
103 </Layout.Header.Outer>
104
105 <View
106 style={{
107 flexDirection: 'row',
108 alignItems: 'flex-start',
109 gap: 10,
110 paddingTop: 14,
111 paddingBottom: 14,
112 paddingHorizontal: isMobile ? 12 : 14,
113 }}>
114 <View ref={aviRef} collapsable={false}>
115 <Pressable
116 testID="headerAviButton"
117 onPress={onPressAvi}
118 accessibilityRole="image"
119 accessibilityLabel={_(msg`View the avatar`)}
120 accessibilityHint=""
121 style={{width: 58}}>
122 {avatarType === 'starter-pack' ? (
123 <StarterPack width={58} gradient="sky" />
124 ) : (
125 <UserAvatar type={avatarType} size={58} avatar={avatar} />
126 )}
127 </Pressable>
128 </View>
129 <View style={{flex: 1, gap: 4}}>
130 {isLoading ? (
131 <LoadingPlaceholder
132 width={200}
133 height={32}
134 style={{marginVertical: 6}}
135 />
136 ) : (
137 <TextLink
138 testID="headerTitle"
139 type="title-xl"
140 href={href}
141 style={[pal.text, {fontWeight: '600'}]}
142 text={title || ''}
143 onPress={emitSoftReset}
144 numberOfLines={4}
145 />
146 )}
147
148 {isLoading || !creator ? (
149 <LoadingPlaceholder width={50} height={8} />
150 ) : (
151 <Text type="lg" style={[pal.textLight]} numberOfLines={1}>
152 {purpose === 'app.bsky.graph.defs#curatelist' ? (
153 isOwner ? (
154 <Trans>List by you</Trans>
155 ) : (
156 <Trans>
157 List by{' '}
158 <TextLink
159 text={sanitizeHandle(creator.handle || '', '@')}
160 href={makeProfileLink(creator)}
161 style={pal.textLight}
162 />
163 </Trans>
164 )
165 ) : purpose === 'app.bsky.graph.defs#modlist' ? (
166 isOwner ? (
167 <Trans>Moderation list by you</Trans>
168 ) : (
169 <Trans>
170 Moderation list by{' '}
171 <TextLink
172 text={sanitizeHandle(creator.handle || '', '@')}
173 href={makeProfileLink(creator)}
174 style={pal.textLight}
175 />
176 </Trans>
177 )
178 ) : purpose === 'app.bsky.graph.defs#referencelist' ? (
179 isOwner ? (
180 <Trans>Starter pack by you</Trans>
181 ) : (
182 <Trans>
183 Starter pack by{' '}
184 <TextLink
185 text={sanitizeHandle(creator.handle || '', '@')}
186 href={makeProfileLink(creator)}
187 style={pal.textLight}
188 />
189 </Trans>
190 )
191 ) : null}
192 </Text>
193 )}
194 </View>
195 </View>
196 </>
197 )
198}