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