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