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