Bluesky app fork with some witchin' additions 馃挮
at 5ee667f307bc459ba53cdaabdad00a0ea1ee6846 274 lines 11 kB view raw
1import '#/logger/sentry/setup' 2import '#/view/icons' 3 4import React, {useEffect, useState} from 'react' 5import {GestureHandlerRootView} from 'react-native-gesture-handler' 6import {KeyboardProvider as KeyboardControllerProvider} from 'react-native-keyboard-controller' 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/core/macro' 15import {useLingui} from '@lingui/react' 16import * as Sentry from '@sentry/react-native' 17 18import {Provider as HideBottomBarBorderProvider} from '#/lib/hooks/useHideBottomBarBorder' 19import {QueryProvider} from '#/lib/react-query' 20import {s} from '#/lib/styles' 21import {ThemeProvider} from '#/lib/ThemeContext' 22import I18nProvider from '#/locale/i18nProvider' 23import {logger} from '#/logger' 24import {Provider as A11yProvider} from '#/state/a11y' 25import { 26 prefetchAppConfig, 27 Provider as AppConfigProvider, 28} from '#/state/appConfig' 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 {GlobalGestureEventsProvider} from '#/state/global-gesture-events' 34import {Provider as HomeBadgeProvider} from '#/state/home-badge' 35import {Provider as LightboxStateProvider} from '#/state/lightbox' 36import {MessagesProvider} from '#/state/messages' 37import {Provider as ModalStateProvider} from '#/state/modals' 38import {init as initPersistedState} from '#/state/persisted' 39import {Provider as PrefsStateProvider} from '#/state/preferences' 40import {Provider as LabelDefsProvider} from '#/state/preferences/label-defs' 41import {Provider as ModerationOptsProvider} from '#/state/preferences/moderation-opts' 42import {Provider as UnreadNotifsProvider} from '#/state/queries/notifications/unread' 43import {Provider as ServiceAccountManager} from '#/state/service-config' 44import { 45 Provider as SessionProvider, 46 type 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 OnboardingProvider} from '#/state/shell/onboarding' 55import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide' 56import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed' 57import {Provider as StarterPackProvider} from '#/state/shell/starter-pack' 58import {Provider as HiddenRepliesProvider} from '#/state/threadgate-hidden-replies' 59import {TestCtrls} from '#/view/com/testing/TestCtrls' 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 {Provider as ContextMenuProvider} from '#/components/ContextMenu' 65import {useStarterPackEntry} from '#/components/hooks/useStarterPackEntry' 66import {Provider as IntentDialogProvider} from '#/components/intents/IntentDialogs' 67import {Provider as PolicyUpdateOverlayProvider} from '#/components/PolicyUpdateOverlay' 68import {Provider as PortalProvider} from '#/components/Portal' 69import {Provider as VideoVolumeProvider} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext' 70import {ToastOutlet} from '#/components/Toast' 71import { 72 prefetchAgeAssuranceConfig, 73 Provider as AgeAssuranceV2Provider, 74} from '#/ageAssurance' 75import { 76 AnalyticsContext, 77 AnalyticsFeaturesContext, 78 features, 79 setupDeviceId, 80} from '#/analytics' 81import {IS_ANDROID, IS_IOS} from '#/env' 82import { 83 prefetchLiveEvents, 84 Provider as LiveEventsProvider, 85} from '#/features/liveEvents/context' 86import * as Geo from '#/geolocation' 87import {Splash} from '#/Splash' 88import {BottomSheetProvider} from '../modules/bottom-sheet' 89import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider' 90 91SplashScreen.preventAutoHideAsync() 92if (IS_IOS) { 93 SystemUI.setBackgroundColorAsync('black') 94} 95if (IS_ANDROID) { 96 // iOS is handled by the config plugin -sfn 97 ScreenOrientation.lockAsync( 98 ScreenOrientation.OrientationLock.PORTRAIT_UP, 99 ).catch(error => 100 logger.debug('Could not lock orientation', {safeMessage: error}), 101 ) 102} 103 104/** 105 * Begin geolocation ASAP 106 */ 107Geo.resolve() 108prefetchAgeAssuranceConfig() 109prefetchLiveEvents() 110prefetchAppConfig() 111 112function InnerApp() { 113 const [isReady, setIsReady] = React.useState(false) 114 const {currentAccount} = useSession() 115 const {resumeSession} = useSessionApi() 116 const theme = useColorModeTheme() 117 const {_} = useLingui() 118 const hasCheckedReferrer = useStarterPackEntry() 119 120 // init 121 useEffect(() => { 122 async function onLaunch(account?: SessionAccount) { 123 try { 124 if (account) { 125 await resumeSession(account) 126 } else { 127 await features.init 128 } 129 } catch (e) { 130 logger.error(`session: resume failed`, {message: e}) 131 } finally { 132 setIsReady(true) 133 } 134 } 135 const account = readLastActiveAccount() 136 onLaunch(account) 137 }, [resumeSession]) 138 139 useEffect(() => { 140 return listenSessionDropped(() => { 141 Toast.show( 142 _(msg`Sorry! Your session expired. Please sign in again.`), 143 'info', 144 ) 145 }) 146 }, [_]) 147 148 return ( 149 <Alf theme={theme}> 150 <ThemeProvider theme={theme}> 151 <ContextMenuProvider> 152 <Splash isReady={isReady && hasCheckedReferrer}> 153 <VideoVolumeProvider> 154 <React.Fragment 155 // Resets the entire tree below when it changes: 156 key={currentAccount?.did}> 157 <AnalyticsFeaturesContext> 158 <QueryProvider currentDid={currentAccount?.did}> 159 <PolicyUpdateOverlayProvider> 160 <LiveEventsProvider> 161 <AgeAssuranceV2Provider> 162 <ComposerProvider> 163 <MessagesProvider> 164 {/* LabelDefsProvider MUST come before ModerationOptsProvider */} 165 <LabelDefsProvider> 166 <ModerationOptsProvider> 167 <LoggedOutViewProvider> 168 <SelectedFeedProvider> 169 <HiddenRepliesProvider> 170 <HomeBadgeProvider> 171 <UnreadNotifsProvider> 172 <BackgroundNotificationPreferencesProvider> 173 <MutedThreadsProvider> 174 <ProgressGuideProvider> 175 <ServiceAccountManager> 176 <EmailVerificationProvider> 177 <HideBottomBarBorderProvider> 178 <GestureHandlerRootView 179 style={s.h100pct}> 180 <GlobalGestureEventsProvider> 181 <IntentDialogProvider> 182 <TestCtrls /> 183 <Shell /> 184 <ToastOutlet /> 185 </IntentDialogProvider> 186 </GlobalGestureEventsProvider> 187 </GestureHandlerRootView> 188 </HideBottomBarBorderProvider> 189 </EmailVerificationProvider> 190 </ServiceAccountManager> 191 </ProgressGuideProvider> 192 </MutedThreadsProvider> 193 </BackgroundNotificationPreferencesProvider> 194 </UnreadNotifsProvider> 195 </HomeBadgeProvider> 196 </HiddenRepliesProvider> 197 </SelectedFeedProvider> 198 </LoggedOutViewProvider> 199 </ModerationOptsProvider> 200 </LabelDefsProvider> 201 </MessagesProvider> 202 </ComposerProvider> 203 </AgeAssuranceV2Provider> 204 </LiveEventsProvider> 205 </PolicyUpdateOverlayProvider> 206 </QueryProvider> 207 </AnalyticsFeaturesContext> 208 </React.Fragment> 209 </VideoVolumeProvider> 210 </Splash> 211 </ContextMenuProvider> 212 </ThemeProvider> 213 </Alf> 214 ) 215} 216 217function App() { 218 const [isReady, setReady] = useState(false) 219 220 React.useEffect(() => { 221 Promise.all([initPersistedState(), Geo.resolve(), setupDeviceId]).then(() => 222 setReady(true), 223 ) 224 }, []) 225 226 if (!isReady) { 227 return null 228 } 229 230 /* 231 * NOTE: only nothing here can depend on other data or session state, since 232 * that is set up in the InnerApp component above. 233 */ 234 return ( 235 <Geo.Provider> 236 <AppConfigProvider> 237 <A11yProvider> 238 <KeyboardControllerProvider> 239 <OnboardingProvider> 240 <AnalyticsContext> 241 <SessionProvider> 242 <PrefsStateProvider> 243 <I18nProvider> 244 <ShellStateProvider> 245 <ModalStateProvider> 246 <DialogStateProvider> 247 <LightboxStateProvider> 248 <PortalProvider> 249 <BottomSheetProvider> 250 <StarterPackProvider> 251 <SafeAreaProvider 252 initialMetrics={initialWindowMetrics}> 253 <InnerApp /> 254 </SafeAreaProvider> 255 </StarterPackProvider> 256 </BottomSheetProvider> 257 </PortalProvider> 258 </LightboxStateProvider> 259 </DialogStateProvider> 260 </ModalStateProvider> 261 </ShellStateProvider> 262 </I18nProvider> 263 </PrefsStateProvider> 264 </SessionProvider> 265 </AnalyticsContext> 266 </OnboardingProvider> 267 </KeyboardControllerProvider> 268 </A11yProvider> 269 </AppConfigProvider> 270 </Geo.Provider> 271 ) 272} 273 274export default Sentry.wrap(App)