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 {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
12import {usePalette} from 'lib/hooks/usePalette'
13import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
14import {NavigationProp} from 'lib/routes/types'
15import {useTheme} from '#/alf'
16import {Text} from './text/Text'
17import {CenteredView} from './Views'
18import hairlineWidth = StyleSheet.hairlineWidth
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 <FontAwesomeIcon
102 size={18}
103 icon="bars"
104 style={[styles.backIcon, pal.textLight]}
105 />
106 ) : null}
107 </TouchableOpacity>
108 ) : null}
109 <View style={styles.titleContainer} pointerEvents="none">
110 <Text type="title" style={[pal.text, styles.title]}>
111 {title}
112 </Text>
113 </View>
114 {renderButton ? (
115 renderButton()
116 ) : showBackButton ? (
117 <View style={canGoBack ? styles.backBtn : styles.backBtnWide} />
118 ) : null}
119 </View>
120 {subtitle ? (
121 <View
122 style={[styles.titleContainer, {marginTop: -3}]}
123 pointerEvents="none">
124 <Text
125 style={[
126 pal.text,
127 styles.subtitle,
128 t.atoms.text_contrast_medium,
129 ]}>
130 {subtitle}
131 </Text>
132 </View>
133 ) : undefined}
134 </View>
135 </Container>
136 )
137 }
138}
139
140function DesktopWebHeader({
141 title,
142 subtitle,
143 renderButton,
144 showBorder = true,
145}: {
146 title: string
147 subtitle?: string
148 renderButton?: () => JSX.Element
149 showBorder?: boolean
150}) {
151 const pal = usePalette('default')
152 const t = useTheme()
153 return (
154 <CenteredView
155 style={[
156 styles.header,
157 styles.desktopHeader,
158 pal.border,
159 {
160 borderBottomWidth: showBorder ? hairlineWidth : 0,
161 },
162 {display: 'flex', flexDirection: 'column'},
163 ]}>
164 <View>
165 <View style={styles.titleContainer} pointerEvents="none">
166 <Text type="title-lg" style={[pal.text, styles.title]}>
167 {title}
168 </Text>
169 </View>
170 {renderButton?.()}
171 </View>
172 {subtitle ? (
173 <View>
174 <View style={[styles.titleContainer]} pointerEvents="none">
175 <Text
176 style={[
177 pal.text,
178 styles.subtitleDesktop,
179 t.atoms.text_contrast_medium,
180 ]}>
181 {subtitle}
182 </Text>
183 </View>
184 </View>
185 ) : null}
186 </CenteredView>
187 )
188}
189
190function Container({
191 children,
192 hideOnScroll,
193 showBorder,
194}: {
195 children: React.ReactNode
196 hideOnScroll: boolean
197 showBorder?: boolean
198}) {
199 const pal = usePalette('default')
200 const {headerMinimalShellTransform} = useMinimalShellMode()
201
202 if (!hideOnScroll) {
203 return (
204 <View
205 style={[
206 styles.header,
207 pal.view,
208 pal.border,
209 showBorder && styles.border,
210 ]}>
211 {children}
212 </View>
213 )
214 }
215 return (
216 <Animated.View
217 style={[
218 styles.header,
219 styles.headerFloating,
220 pal.view,
221 pal.border,
222 headerMinimalShellTransform,
223 showBorder && styles.border,
224 ]}>
225 {children}
226 </Animated.View>
227 )
228}
229
230const styles = StyleSheet.create({
231 header: {
232 flexDirection: 'row',
233 paddingHorizontal: 12,
234 paddingVertical: 6,
235 width: '100%',
236 },
237 headerFloating: {
238 position: 'absolute',
239 top: 0,
240 width: '100%',
241 },
242 desktopHeader: {
243 paddingVertical: 12,
244 maxWidth: 600,
245 marginLeft: 'auto',
246 marginRight: 'auto',
247 },
248 border: {
249 borderBottomWidth: hairlineWidth,
250 },
251 titleContainer: {
252 marginLeft: 'auto',
253 marginRight: 'auto',
254 alignItems: 'center',
255 },
256 title: {
257 fontWeight: 'bold',
258 },
259 subtitle: {
260 fontSize: 13,
261 },
262 subtitleDesktop: {
263 fontSize: 15,
264 },
265 backBtn: {
266 width: 30,
267 height: 30,
268 },
269 backBtnWide: {
270 width: 30,
271 height: 30,
272 paddingHorizontal: 6,
273 },
274 backIcon: {
275 marginTop: 6,
276 },
277})