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

Configure Feed

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

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