mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at samuel/fancy-queue 271 lines 6.8 kB view raw
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})