mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React from 'react'
2import {StyleSheet, TouchableOpacity, View} from 'react-native'
3import Animated from 'react-native-reanimated'
4import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
5import {msg} from '@lingui/macro'
6import {useLingui} from '@lingui/react'
7import {useNavigation} from '@react-navigation/native'
8
9import {useSetDrawerOpen} from '#/state/shell'
10import {useAnalytics} from 'lib/analytics/analytics'
11import {useMinimalShellHeaderTransform} from 'lib/hooks/useMinimalShellTransform'
12import {usePalette} from 'lib/hooks/usePalette'
13import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
14import {NavigationProp} from 'lib/routes/types'
15import {useTheme} from '#/alf'
16import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
17import {Text} from './text/Text'
18import {CenteredView} from './Views'
19
20const BACK_HITSLOP = {left: 20, top: 20, right: 50, bottom: 20}
21
22export function ViewHeader({
23 title,
24 subtitle,
25 canGoBack,
26 showBackButton = true,
27 hideOnScroll,
28 showOnDesktop,
29 showBorder,
30 renderButton,
31}: {
32 title: string
33 subtitle?: string
34 canGoBack?: boolean
35 showBackButton?: boolean
36 hideOnScroll?: boolean
37 showOnDesktop?: boolean
38 showBorder?: boolean
39 renderButton?: () => JSX.Element
40}) {
41 const pal = usePalette('default')
42 const {_} = useLingui()
43 const setDrawerOpen = useSetDrawerOpen()
44 const navigation = useNavigation<NavigationProp>()
45 const {track} = useAnalytics()
46 const {isDesktop, isTablet} = useWebMediaQueries()
47 const t = useTheme()
48
49 const onPressBack = React.useCallback(() => {
50 if (navigation.canGoBack()) {
51 navigation.goBack()
52 } else {
53 navigation.navigate('Home')
54 }
55 }, [navigation])
56
57 const onPressMenu = React.useCallback(() => {
58 track('ViewHeader:MenuButtonClicked')
59 setDrawerOpen(true)
60 }, [track, setDrawerOpen])
61
62 if (isDesktop) {
63 if (showOnDesktop) {
64 return (
65 <DesktopWebHeader
66 title={title}
67 subtitle={subtitle}
68 renderButton={renderButton}
69 showBorder={showBorder}
70 />
71 )
72 }
73 return null
74 } else {
75 if (typeof canGoBack === 'undefined') {
76 canGoBack = navigation.canGoBack()
77 }
78
79 return (
80 <Container hideOnScroll={hideOnScroll || false} showBorder={showBorder}>
81 <View style={{flex: 1}}>
82 <View style={{flexDirection: 'row', alignItems: 'center'}}>
83 {showBackButton ? (
84 <TouchableOpacity
85 testID="viewHeaderDrawerBtn"
86 onPress={canGoBack ? onPressBack : onPressMenu}
87 hitSlop={BACK_HITSLOP}
88 style={canGoBack ? styles.backBtn : styles.backBtnWide}
89 accessibilityRole="button"
90 accessibilityLabel={canGoBack ? _(msg`Back`) : _(msg`Menu`)}
91 accessibilityHint={
92 canGoBack ? '' : _(msg`Access navigation links and settings`)
93 }>
94 {canGoBack ? (
95 <FontAwesomeIcon
96 size={18}
97 icon="angle-left"
98 style={[styles.backIcon, pal.text]}
99 />
100 ) : !isTablet ? (
101 <Menu size="lg" style={[{marginTop: 3}, pal.textLight]} />
102 ) : null}
103 </TouchableOpacity>
104 ) : null}
105 <View style={styles.titleContainer} pointerEvents="none">
106 <Text type="title" style={[pal.text, styles.title]}>
107 {title}
108 </Text>
109 </View>
110 {renderButton ? (
111 renderButton()
112 ) : showBackButton ? (
113 <View style={canGoBack ? styles.backBtn : styles.backBtnWide} />
114 ) : null}
115 </View>
116 {subtitle ? (
117 <View
118 style={[styles.titleContainer, {marginTop: -3}]}
119 pointerEvents="none">
120 <Text
121 style={[
122 pal.text,
123 styles.subtitle,
124 t.atoms.text_contrast_medium,
125 ]}>
126 {subtitle}
127 </Text>
128 </View>
129 ) : undefined}
130 </View>
131 </Container>
132 )
133 }
134}
135
136function DesktopWebHeader({
137 title,
138 subtitle,
139 renderButton,
140 showBorder = true,
141}: {
142 title: string
143 subtitle?: string
144 renderButton?: () => JSX.Element
145 showBorder?: boolean
146}) {
147 const pal = usePalette('default')
148 const t = useTheme()
149 return (
150 <CenteredView
151 style={[
152 styles.header,
153 styles.desktopHeader,
154 pal.border,
155 {
156 borderBottomWidth: showBorder ? StyleSheet.hairlineWidth : 0,
157 },
158 {display: 'flex', flexDirection: 'column'},
159 ]}>
160 <View>
161 <View style={styles.titleContainer} pointerEvents="none">
162 <Text type="title-lg" style={[pal.text, styles.title]}>
163 {title}
164 </Text>
165 </View>
166 {renderButton?.()}
167 </View>
168 {subtitle ? (
169 <View>
170 <View style={[styles.titleContainer]} pointerEvents="none">
171 <Text
172 style={[
173 pal.text,
174 styles.subtitleDesktop,
175 t.atoms.text_contrast_medium,
176 ]}>
177 {subtitle}
178 </Text>
179 </View>
180 </View>
181 ) : null}
182 </CenteredView>
183 )
184}
185
186function Container({
187 children,
188 hideOnScroll,
189 showBorder,
190}: {
191 children: React.ReactNode
192 hideOnScroll: boolean
193 showBorder?: boolean
194}) {
195 const pal = usePalette('default')
196 const headerMinimalShellTransform = useMinimalShellHeaderTransform()
197
198 if (!hideOnScroll) {
199 return (
200 <View
201 style={[
202 styles.header,
203 pal.view,
204 pal.border,
205 showBorder && styles.border,
206 ]}>
207 {children}
208 </View>
209 )
210 }
211 return (
212 <Animated.View
213 style={[
214 styles.header,
215 styles.headerFloating,
216 pal.view,
217 pal.border,
218 headerMinimalShellTransform,
219 showBorder && styles.border,
220 ]}>
221 {children}
222 </Animated.View>
223 )
224}
225
226const styles = StyleSheet.create({
227 header: {
228 flexDirection: 'row',
229 paddingHorizontal: 12,
230 paddingVertical: 6,
231 width: '100%',
232 },
233 headerFloating: {
234 position: 'absolute',
235 top: 0,
236 width: '100%',
237 },
238 desktopHeader: {
239 paddingVertical: 12,
240 maxWidth: 600,
241 marginLeft: 'auto',
242 marginRight: 'auto',
243 },
244 border: {
245 borderBottomWidth: StyleSheet.hairlineWidth,
246 },
247 titleContainer: {
248 marginLeft: 'auto',
249 marginRight: 'auto',
250 alignItems: 'center',
251 },
252 title: {
253 fontWeight: 'bold',
254 },
255 subtitle: {
256 fontSize: 13,
257 },
258 subtitleDesktop: {
259 fontSize: 15,
260 },
261 backBtn: {
262 width: 30,
263 height: 30,
264 },
265 backBtnWide: {
266 width: 30,
267 height: 30,
268 paddingLeft: 4,
269 marginRight: 4,
270 },
271 backIcon: {
272 marginTop: 6,
273 },
274})