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