my fork of the bluesky client
at main 227 lines 8.8 kB view raw
1import 'react-native-url-polyfill/auto' 2import '#/lib/sentry' // must be near top 3import '#/view/icons' 4 5import React, {useEffect, useState} from 'react' 6import {GestureHandlerRootView} from 'react-native-gesture-handler' 7import {RootSiblingParent} from 'react-native-root-siblings' 8import { 9 initialWindowMetrics, 10 SafeAreaProvider, 11} from 'react-native-safe-area-context' 12import * as SplashScreen from 'expo-splash-screen' 13import {msg} from '@lingui/macro' 14import {useLingui} from '@lingui/react' 15 16import {QueryProvider} from '#/lib/react-query' 17import { 18 initialize, 19 Provider as StatsigProvider, 20 tryFetchGates, 21} from '#/lib/statsig/statsig' 22import {s} from '#/lib/styles' 23import {ThemeProvider} from '#/lib/ThemeContext' 24import I18nProvider from '#/locale/i18nProvider' 25import {logger} from '#/logger' 26import {Provider as A11yProvider} from '#/state/a11y' 27import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes' 28import {Provider as DialogStateProvider} from '#/state/dialogs' 29import {listenSessionDropped} from '#/state/events' 30import { 31 beginResolveGeolocation, 32 ensureGeolocationResolved, 33 Provider as GeolocationProvider, 34} from '#/state/geolocation' 35import {Provider as InvitesStateProvider} from '#/state/invites' 36import {Provider as LightboxStateProvider} from '#/state/lightbox' 37import {MessagesProvider} from '#/state/messages' 38import {Provider as ModalStateProvider} from '#/state/modals' 39import {init as initPersistedState} from '#/state/persisted' 40import {Provider as PrefsStateProvider} from '#/state/preferences' 41import {Provider as LabelDefsProvider} from '#/state/preferences/label-defs' 42import {Provider as ModerationOptsProvider} from '#/state/preferences/moderation-opts' 43import {Provider as UnreadNotifsProvider} from '#/state/queries/notifications/unread' 44import { 45 Provider as SessionProvider, 46 SessionAccount, 47 useSession, 48 useSessionApi, 49} from '#/state/session' 50import {readLastActiveAccount} from '#/state/session/util' 51import {Provider as ShellStateProvider} from '#/state/shell' 52import {Provider as ComposerProvider} from '#/state/shell/composer' 53import {Provider as LoggedOutViewProvider} from '#/state/shell/logged-out' 54import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide' 55import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed' 56import {Provider as StarterPackProvider} from '#/state/shell/starter-pack' 57import {Provider as HiddenRepliesProvider} from '#/state/threadgate-hidden-replies' 58import {TestCtrls} from '#/view/com/testing/TestCtrls' 59import {Provider as VideoVolumeProvider} from '#/view/com/util/post-embeds/VideoVolumeContext' 60import * as Toast from '#/view/com/util/Toast' 61import {Shell} from '#/view/shell' 62import {ThemeProvider as Alf} from '#/alf' 63import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 64import {NuxDialogs} from '#/components/dialogs/nuxs' 65import {useStarterPackEntry} from '#/components/hooks/useStarterPackEntry' 66import {Provider as IntentDialogProvider} from '#/components/intents/IntentDialogs' 67import {Provider as PortalProvider} from '#/components/Portal' 68import {Splash} from '#/Splash' 69import {BottomSheetProvider} from '../modules/bottom-sheet' 70import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider' 71import {AppProfiler} from './AppProfiler' 72import {KeyboardControllerProvider} from './lib/hooks/useEnableKeyboardController' 73 74SplashScreen.preventAutoHideAsync() 75 76/** 77 * Begin geolocation ASAP 78 */ 79beginResolveGeolocation() 80 81function InnerApp() { 82 const [isReady, setIsReady] = React.useState(false) 83 const {currentAccount} = useSession() 84 const {resumeSession} = useSessionApi() 85 const theme = useColorModeTheme() 86 const {_} = useLingui() 87 88 const hasCheckedReferrer = useStarterPackEntry() 89 90 // init 91 useEffect(() => { 92 async function onLaunch(account?: SessionAccount) { 93 try { 94 if (account) { 95 await resumeSession(account) 96 } else { 97 await initialize() 98 await tryFetchGates(undefined, 'prefer-fresh-gates') 99 } 100 } catch (e) { 101 logger.error(`session: resume failed`, {message: e}) 102 } finally { 103 setIsReady(true) 104 } 105 } 106 const account = readLastActiveAccount() 107 onLaunch(account) 108 }, [resumeSession]) 109 110 useEffect(() => { 111 return listenSessionDropped(() => { 112 Toast.show( 113 _(msg`Sorry! Your session expired. Please log in again.`), 114 'info', 115 ) 116 }) 117 }, [_]) 118 119 return ( 120 <Alf theme={theme}> 121 <ThemeProvider theme={theme}> 122 <Splash isReady={isReady && hasCheckedReferrer}> 123 <RootSiblingParent> 124 <VideoVolumeProvider> 125 <React.Fragment 126 // Resets the entire tree below when it changes: 127 key={currentAccount?.did}> 128 <QueryProvider currentDid={currentAccount?.did}> 129 <ComposerProvider> 130 <StatsigProvider> 131 <MessagesProvider> 132 {/* LabelDefsProvider MUST come before ModerationOptsProvider */} 133 <LabelDefsProvider> 134 <ModerationOptsProvider> 135 <LoggedOutViewProvider> 136 <SelectedFeedProvider> 137 <HiddenRepliesProvider> 138 <UnreadNotifsProvider> 139 <BackgroundNotificationPreferencesProvider> 140 <MutedThreadsProvider> 141 <ProgressGuideProvider> 142 <GestureHandlerRootView 143 style={s.h100pct}> 144 <TestCtrls /> 145 <Shell /> 146 <NuxDialogs /> 147 </GestureHandlerRootView> 148 </ProgressGuideProvider> 149 </MutedThreadsProvider> 150 </BackgroundNotificationPreferencesProvider> 151 </UnreadNotifsProvider> 152 </HiddenRepliesProvider> 153 </SelectedFeedProvider> 154 </LoggedOutViewProvider> 155 </ModerationOptsProvider> 156 </LabelDefsProvider> 157 </MessagesProvider> 158 </StatsigProvider> 159 </ComposerProvider> 160 </QueryProvider> 161 </React.Fragment> 162 </VideoVolumeProvider> 163 </RootSiblingParent> 164 </Splash> 165 </ThemeProvider> 166 </Alf> 167 ) 168} 169 170function App() { 171 const [isReady, setReady] = useState(false) 172 173 React.useEffect(() => { 174 Promise.all([initPersistedState(), ensureGeolocationResolved()]).then(() => 175 setReady(true), 176 ) 177 }, []) 178 179 if (!isReady) { 180 return null 181 } 182 183 /* 184 * NOTE: only nothing here can depend on other data or session state, since 185 * that is set up in the InnerApp component above. 186 */ 187 return ( 188 <AppProfiler> 189 <GeolocationProvider> 190 <A11yProvider> 191 <KeyboardControllerProvider> 192 <SessionProvider> 193 <PrefsStateProvider> 194 <I18nProvider> 195 <ShellStateProvider> 196 <InvitesStateProvider> 197 <ModalStateProvider> 198 <DialogStateProvider> 199 <LightboxStateProvider> 200 <PortalProvider> 201 <BottomSheetProvider> 202 <StarterPackProvider> 203 <SafeAreaProvider 204 initialMetrics={initialWindowMetrics}> 205 <IntentDialogProvider> 206 <InnerApp /> 207 </IntentDialogProvider> 208 </SafeAreaProvider> 209 </StarterPackProvider> 210 </BottomSheetProvider> 211 </PortalProvider> 212 </LightboxStateProvider> 213 </DialogStateProvider> 214 </ModalStateProvider> 215 </InvitesStateProvider> 216 </ShellStateProvider> 217 </I18nProvider> 218 </PrefsStateProvider> 219 </SessionProvider> 220 </KeyboardControllerProvider> 221 </A11yProvider> 222 </GeolocationProvider> 223 </AppProfiler> 224 ) 225} 226 227export default App