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