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

Configure Feed

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

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