forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
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)