mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at rm-open 183 lines 6.6 kB view raw
1import {useCallback, useEffect} from 'react' 2import {BackHandler, useWindowDimensions, View} from 'react-native' 3import {Drawer} from 'react-native-drawer-layout' 4import {useSafeAreaInsets} from 'react-native-safe-area-context' 5import {StatusBar} from 'expo-status-bar' 6import {useNavigation, useNavigationState} from '@react-navigation/native' 7 8import {useDedupe} from '#/lib/hooks/useDedupe' 9import {useIntentHandler} from '#/lib/hooks/useIntentHandler' 10import {useNotificationsHandler} from '#/lib/hooks/useNotificationHandler' 11import {useNotificationsRegistration} from '#/lib/notifications/notifications' 12import {isStateAtTabRoot} from '#/lib/routes/helpers' 13import {isAndroid, isIOS} from '#/platform/detection' 14import {useDialogStateControlContext} from '#/state/dialogs' 15import {useSession} from '#/state/session' 16import { 17 useIsDrawerOpen, 18 useIsDrawerSwipeDisabled, 19 useSetDrawerOpen, 20} from '#/state/shell' 21import {useLightStatusBar} from '#/state/shell/light-status-bar' 22import {useCloseAnyActiveElement} from '#/state/util' 23import {Lightbox} from '#/view/com/lightbox/Lightbox' 24import {ModalsContainer} from '#/view/com/modals/Modal' 25import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' 26import {atoms as a, select, useTheme} from '#/alf' 27import {setNavigationBar} from '#/alf/util/navigationBar' 28import {MutedWordsDialog} from '#/components/dialogs/MutedWords' 29import {SigninDialog} from '#/components/dialogs/Signin' 30import {Outlet as PortalOutlet} from '#/components/Portal' 31import {RoutesContainer, TabsNavigator} from '#/Navigation' 32import {BottomSheetOutlet} from '../../../modules/bottom-sheet' 33import {updateActiveViewAsync} from '../../../modules/expo-bluesky-swiss-army/src/VisibilityView' 34import {Composer} from './Composer' 35import {DrawerContent} from './Drawer' 36 37function ShellInner() { 38 const t = useTheme() 39 const isDrawerOpen = useIsDrawerOpen() 40 const isDrawerSwipeDisabled = useIsDrawerSwipeDisabled() 41 const setIsDrawerOpen = useSetDrawerOpen() 42 const winDim = useWindowDimensions() 43 const insets = useSafeAreaInsets() 44 45 const renderDrawerContent = useCallback(() => <DrawerContent />, []) 46 const onOpenDrawer = useCallback( 47 () => setIsDrawerOpen(true), 48 [setIsDrawerOpen], 49 ) 50 const onCloseDrawer = useCallback( 51 () => setIsDrawerOpen(false), 52 [setIsDrawerOpen], 53 ) 54 const canGoBack = useNavigationState(state => !isStateAtTabRoot(state)) 55 const {hasSession} = useSession() 56 const closeAnyActiveElement = useCloseAnyActiveElement() 57 58 useNotificationsRegistration() 59 useNotificationsHandler() 60 61 useEffect(() => { 62 if (isAndroid) { 63 const listener = BackHandler.addEventListener('hardwareBackPress', () => { 64 return closeAnyActiveElement() 65 }) 66 67 return () => { 68 listener.remove() 69 } 70 } 71 }, [closeAnyActiveElement]) 72 73 // HACK 74 // expo-video doesn't like it when you try and move a `player` to another `VideoView`. Instead, we need to actually 75 // unregister that player to let the new screen register it. This is only a problem on Android, so we only need to 76 // apply it there. 77 // The `state` event should only fire whenever we push or pop to a screen, and should not fire consecutively quickly. 78 // To be certain though, we will also dedupe these calls. 79 const navigation = useNavigation() 80 const dedupe = useDedupe(1000) 81 useEffect(() => { 82 if (!isAndroid) return 83 const onFocusOrBlur = () => { 84 setTimeout(() => { 85 dedupe(updateActiveViewAsync) 86 }, 500) 87 } 88 navigation.addListener('state', onFocusOrBlur) 89 return () => { 90 navigation.removeListener('state', onFocusOrBlur) 91 } 92 }, [dedupe, navigation]) 93 94 const swipeEnabled = !canGoBack && hasSession && !isDrawerSwipeDisabled 95 return ( 96 <> 97 <View style={[a.h_full]}> 98 <ErrorBoundary 99 style={{paddingTop: insets.top, paddingBottom: insets.bottom}}> 100 <Drawer 101 renderDrawerContent={renderDrawerContent} 102 drawerStyle={{width: Math.min(400, winDim.width * 0.8)}} 103 configureGestureHandler={handler => { 104 if (swipeEnabled) { 105 if (isDrawerOpen) { 106 return handler.activeOffsetX([-1, 1]) 107 } else { 108 return ( 109 handler 110 // Any movement to the left is a pager swipe 111 // so fail the drawer gesture immediately. 112 .failOffsetX(-1) 113 // Don't rush declaring that a movement to the right 114 // is a drawer swipe. It could be a vertical scroll. 115 .activeOffsetX(5) 116 ) 117 } 118 } else { 119 // Fail the gesture immediately. 120 // This seems more reliable than the `swipeEnabled` prop. 121 // With `swipeEnabled` alone, the gesture may freeze after toggling off/on. 122 return handler.failOffsetX([0, 0]).failOffsetY([0, 0]) 123 } 124 }} 125 open={isDrawerOpen} 126 onOpen={onOpenDrawer} 127 onClose={onCloseDrawer} 128 swipeEdgeWidth={winDim.width} 129 swipeMinVelocity={100} 130 swipeMinDistance={10} 131 drawerType={isIOS ? 'slide' : 'front'} 132 overlayStyle={{ 133 backgroundColor: select(t.name, { 134 light: 'rgba(0, 57, 117, 0.1)', 135 dark: isAndroid 136 ? 'rgba(16, 133, 254, 0.1)' 137 : 'rgba(1, 82, 168, 0.1)', 138 dim: 'rgba(10, 13, 16, 0.8)', 139 }), 140 }}> 141 <TabsNavigator /> 142 </Drawer> 143 </ErrorBoundary> 144 </View> 145 <Composer winHeight={winDim.height} /> 146 <ModalsContainer /> 147 <MutedWordsDialog /> 148 <SigninDialog /> 149 <Lightbox /> 150 <PortalOutlet /> 151 <BottomSheetOutlet /> 152 </> 153 ) 154} 155 156export const Shell: React.FC = function ShellImpl() { 157 const {fullyExpandedCount} = useDialogStateControlContext() 158 const lightStatusBar = useLightStatusBar() 159 const t = useTheme() 160 useIntentHandler() 161 162 useEffect(() => { 163 setNavigationBar('theme', t) 164 }, [t]) 165 166 return ( 167 <View testID="mobileShellView" style={[a.h_full, t.atoms.bg]}> 168 <StatusBar 169 style={ 170 t.name !== 'light' || 171 (isIOS && fullyExpandedCount > 0) || 172 lightStatusBar 173 ? 'light' 174 : 'dark' 175 } 176 animated 177 /> 178 <RoutesContainer> 179 <ShellInner /> 180 </RoutesContainer> 181 </View> 182 ) 183}