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