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