Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

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

at 06a8a7efc2946247d44adb982e2b2cb367fd7b64 205 lines 6.4 kB view raw
1import {useCallback, useEffect, useLayoutEffect, useState} from 'react' 2import {StyleSheet, TouchableWithoutFeedback, View} from 'react-native' 3import {msg} from '@lingui/macro' 4import {useLingui} from '@lingui/react' 5import {useNavigation} from '@react-navigation/native' 6import {RemoveScrollBar} from 'react-remove-scroll-bar' 7 8import {useIntentHandler} from '#/lib/hooks/useIntentHandler' 9import {type NavigationProp} from '#/lib/routes/types' 10import {useSession} from '#/state/session' 11import {useIsDrawerOpen, useSetDrawerOpen} from '#/state/shell' 12import {useComposerKeyboardShortcut} from '#/state/shell/composer/useComposerKeyboardShortcut' 13import {useCloseAllActiveElements} from '#/state/util' 14import {Lightbox} from '#/view/com/lightbox/Lightbox' 15import {ModalsContainer} from '#/view/com/modals/Modal' 16import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' 17import {Deactivated} from '#/screens/Deactivated' 18import {Takendown} from '#/screens/Takendown' 19import {atoms as a, select, useBreakpoints, useTheme} from '#/alf' 20import {AgeAssuranceRedirectDialog} from '#/components/ageAssurance/AgeAssuranceRedirectDialog' 21import {EmailDialog} from '#/components/dialogs/EmailDialog' 22import {LinkWarningDialog} from '#/components/dialogs/LinkWarning' 23import {MutedWordsDialog} from '#/components/dialogs/MutedWords' 24import {NuxDialogs} from '#/components/dialogs/nuxs' 25import {SigninDialog} from '#/components/dialogs/Signin' 26import {useWelcomeModal} from '#/components/hooks/useWelcomeModal' 27import {GlobalReportDialog} from '#/components/moderation/ReportDialog' 28import { 29 Outlet as PolicyUpdateOverlayPortalOutlet, 30 usePolicyUpdateContext, 31} from '#/components/PolicyUpdateOverlay' 32import {Outlet as PortalOutlet} from '#/components/Portal' 33import {WelcomeModal} from '#/components/WelcomeModal' 34import {useAgeAssurance} from '#/ageAssurance' 35import {NoAccessScreen} from '#/ageAssurance/components/NoAccessScreen' 36import {RedirectOverlay} from '#/ageAssurance/components/RedirectOverlay' 37import {FlatNavigator, RoutesContainer} from '#/Navigation' 38import {Composer} from './Composer.web' 39import {DrawerContent} from './Drawer' 40 41function ShellInner() { 42 const navigator = useNavigation<NavigationProp>() 43 const closeAllActiveElements = useCloseAllActiveElements() 44 const {state: policyUpdateState} = usePolicyUpdateContext() 45 const welcomeModalControl = useWelcomeModal() 46 47 useComposerKeyboardShortcut() 48 useIntentHandler() 49 50 useEffect(() => { 51 const unsubscribe = navigator.addListener('state', () => { 52 closeAllActiveElements() 53 }) 54 return unsubscribe 55 }, [navigator, closeAllActiveElements]) 56 57 const drawerLayout = useCallback( 58 ({children}: {children: React.ReactNode}) => ( 59 <DrawerLayout>{children}</DrawerLayout> 60 ), 61 [], 62 ) 63 return ( 64 <> 65 <ErrorBoundary> 66 <FlatNavigator layout={drawerLayout} /> 67 </ErrorBoundary> 68 <Composer winHeight={0} /> 69 <ModalsContainer /> 70 <MutedWordsDialog /> 71 <SigninDialog /> 72 <EmailDialog /> 73 <AgeAssuranceRedirectDialog /> 74 <LinkWarningDialog /> 75 <Lightbox /> 76 <NuxDialogs /> 77 <GlobalReportDialog /> 78 79 {welcomeModalControl.isOpen && ( 80 <WelcomeModal control={welcomeModalControl} /> 81 )} 82 83 {/* Until policy update has been completed by the user, don't render anything that is portaled */} 84 {policyUpdateState.completed && ( 85 <> 86 <PortalOutlet /> 87 </> 88 )} 89 90 <PolicyUpdateOverlayPortalOutlet /> 91 </> 92 ) 93} 94 95function DrawerLayout({children}: {children: React.ReactNode}) { 96 const t = useTheme() 97 const isDrawerOpen = useIsDrawerOpen() 98 const setDrawerOpen = useSetDrawerOpen() 99 const {gtTablet} = useBreakpoints() 100 const {_} = useLingui() 101 const showDrawer = !gtTablet && isDrawerOpen 102 const [showDrawerDelayedExit, setShowDrawerDelayedExit] = useState(showDrawer) 103 104 useLayoutEffect(() => { 105 if (showDrawer !== showDrawerDelayedExit) { 106 if (showDrawer) { 107 setShowDrawerDelayedExit(true) 108 } else { 109 const timeout = setTimeout(() => { 110 setShowDrawerDelayedExit(false) 111 }, 160) 112 return () => clearTimeout(timeout) 113 } 114 } 115 }, [showDrawer, showDrawerDelayedExit]) 116 117 return ( 118 <> 119 {children} 120 {showDrawerDelayedExit && ( 121 <> 122 <RemoveScrollBar /> 123 <TouchableWithoutFeedback 124 onPress={ev => { 125 // Only close if press happens outside of the drawer 126 if (ev.target === ev.currentTarget) { 127 setDrawerOpen(false) 128 } 129 }} 130 accessibilityLabel={_(msg`Close drawer menu`)} 131 accessibilityHint=""> 132 <View 133 style={[ 134 styles.drawerMask, 135 { 136 backgroundColor: showDrawer 137 ? select(t.name, { 138 light: 'rgba(0, 57, 117, 0.1)', 139 dark: 'rgba(1, 82, 168, 0.1)', 140 dim: 'rgba(10, 13, 16, 0.8)', 141 }) 142 : 'transparent', 143 }, 144 a.transition_color, 145 ]}> 146 <View 147 style={[ 148 styles.drawerContainer, 149 showDrawer ? a.slide_in_left : a.slide_out_left, 150 ]}> 151 <DrawerContent /> 152 </View> 153 </View> 154 </TouchableWithoutFeedback> 155 </> 156 )} 157 </> 158 ) 159} 160 161export function Shell() { 162 const t = useTheme() 163 const aa = useAgeAssurance() 164 const {currentAccount} = useSession() 165 return ( 166 <View style={[a.util_screen_outer, t.atoms.bg]}> 167 {currentAccount?.status === 'takendown' ? ( 168 <Takendown /> 169 ) : currentAccount?.status === 'deactivated' ? ( 170 <Deactivated /> 171 ) : ( 172 <> 173 {aa.state.access === aa.Access.None ? ( 174 <NoAccessScreen /> 175 ) : ( 176 <RoutesContainer> 177 <ShellInner /> 178 </RoutesContainer> 179 )} 180 181 <RedirectOverlay /> 182 </> 183 )} 184 </View> 185 ) 186} 187 188const styles = StyleSheet.create({ 189 drawerMask: { 190 ...a.fixed, 191 width: '100%', 192 height: '100%', 193 top: 0, 194 left: 0, 195 }, 196 drawerContainer: { 197 display: 'flex', 198 ...a.fixed, 199 top: 0, 200 left: 0, 201 height: '100%', 202 width: 330, 203 maxWidth: '80%', 204 }, 205})