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