forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import '#/logger/sentry/setup' // must be near top
2import '#/view/icons'
3import './style.css'
4
5import React, {useEffect, useState} from 'react'
6import {SafeAreaProvider} from 'react-native-safe-area-context'
7import {msg} from '@lingui/macro'
8import {useLingui} from '@lingui/react'
9import * as Sentry from '@sentry/react-native'
10
11import {QueryProvider} from '#/lib/react-query'
12import {ThemeProvider} from '#/lib/ThemeContext'
13import I18nProvider from '#/locale/i18nProvider'
14import {logger} from '#/logger'
15import {Provider as A11yProvider} from '#/state/a11y'
16import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
17import {Provider as DialogStateProvider} from '#/state/dialogs'
18import {Provider as EmailVerificationProvider} from '#/state/email-verification'
19import {listenSessionDropped} from '#/state/events'
20import {Provider as HomeBadgeProvider} from '#/state/home-badge'
21import {Provider as LightboxStateProvider} from '#/state/lightbox'
22import {MessagesProvider} from '#/state/messages'
23import {Provider as ModalStateProvider} from '#/state/modals'
24import {init as initPersistedState} from '#/state/persisted'
25import {Provider as PrefsStateProvider} from '#/state/preferences'
26import {Provider as LabelDefsProvider} from '#/state/preferences/label-defs'
27import {Provider as ModerationOptsProvider} from '#/state/preferences/moderation-opts'
28import {Provider as UnreadNotifsProvider} from '#/state/queries/notifications/unread'
29import {Provider as ServiceConfigProvider} from '#/state/service-config'
30import {
31 Provider as SessionProvider,
32 type SessionAccount,
33 useSession,
34 useSessionApi,
35} from '#/state/session'
36import {readLastActiveAccount} from '#/state/session/util'
37import {Provider as ShellStateProvider} from '#/state/shell'
38import {Provider as ComposerProvider} from '#/state/shell/composer'
39import {Provider as LoggedOutViewProvider} from '#/state/shell/logged-out'
40import {Provider as OnboardingProvider} from '#/state/shell/onboarding'
41import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide'
42import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed'
43import {Provider as StarterPackProvider} from '#/state/shell/starter-pack'
44import {Provider as HiddenRepliesProvider} from '#/state/threadgate-hidden-replies'
45import * as Toast from '#/view/com/util/Toast'
46import {Shell} from '#/view/shell/index'
47import {ThemeProvider as Alf} from '#/alf'
48import {useColorModeTheme} from '#/alf/util/useColorModeTheme'
49import {Provider as ContextMenuProvider} from '#/components/ContextMenu'
50import {useStarterPackEntry} from '#/components/hooks/useStarterPackEntry'
51import {Provider as IntentDialogProvider} from '#/components/intents/IntentDialogs'
52import {Provider as PolicyUpdateOverlayProvider} from '#/components/PolicyUpdateOverlay'
53import {Provider as PortalProvider} from '#/components/Portal'
54import {Provider as ActiveVideoProvider} from '#/components/Post/Embed/VideoEmbed/ActiveVideoWebContext'
55import {Provider as VideoVolumeProvider} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext'
56import {ToastOutlet} from '#/components/Toast'
57import {
58 prefetchAgeAssuranceConfig,
59 Provider as AgeAssuranceV2Provider,
60} from '#/ageAssurance'
61import {
62 AnalyticsContext,
63 AnalyticsFeaturesContext,
64 features,
65 setupDeviceId,
66} from '#/analytics'
67import {
68 prefetchLiveEvents,
69 Provider as LiveEventsProvider,
70} from '#/features/liveEvents/context'
71import * as Geo from '#/geolocation'
72import {Splash} from '#/Splash'
73import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider'
74import {Provider as HideBottomBarBorderProvider} from './lib/hooks/useHideBottomBarBorder'
75
76/**
77 * Begin geolocation ASAP
78 */
79Geo.resolve()
80prefetchAgeAssuranceConfig()
81prefetchLiveEvents()
82
83function InnerApp() {
84 const [isReady, setIsReady] = React.useState(false)
85 const {currentAccount} = useSession()
86 const {resumeSession} = useSessionApi()
87 const theme = useColorModeTheme()
88 const {_} = useLingui()
89 const hasCheckedReferrer = useStarterPackEntry()
90
91 // init
92 useEffect(() => {
93 async function onLaunch(account?: SessionAccount) {
94 try {
95 if (account) {
96 await resumeSession(account)
97 } else {
98 await features.init
99 }
100 } catch (e) {
101 logger.error(`session: resumeSession failed`, {message: e})
102 } finally {
103 setIsReady(true)
104 }
105 }
106 const account = readLastActiveAccount()
107 onLaunch(account)
108 }, [resumeSession])
109
110 useEffect(() => {
111 return listenSessionDropped(() => {
112 Toast.show(
113 _(msg`Sorry! Your session expired. Please sign in again.`),
114 'info',
115 )
116 })
117 }, [_])
118
119 // wait for session to resume
120 if (!isReady || !hasCheckedReferrer) return <Splash isReady />
121
122 return (
123 <Alf theme={theme}>
124 <ThemeProvider theme={theme}>
125 <ContextMenuProvider>
126 <VideoVolumeProvider>
127 <ActiveVideoProvider>
128 <React.Fragment
129 // Resets the entire tree below when it changes:
130 key={currentAccount?.did}>
131 <AnalyticsFeaturesContext>
132 <QueryProvider currentDid={currentAccount?.did}>
133 <PolicyUpdateOverlayProvider>
134 <LiveEventsProvider>
135 <AgeAssuranceV2Provider>
136 <ComposerProvider>
137 <MessagesProvider>
138 {/* LabelDefsProvider MUST come before ModerationOptsProvider */}
139 <LabelDefsProvider>
140 <ModerationOptsProvider>
141 <LoggedOutViewProvider>
142 <SelectedFeedProvider>
143 <HiddenRepliesProvider>
144 <HomeBadgeProvider>
145 <UnreadNotifsProvider>
146 <BackgroundNotificationPreferencesProvider>
147 <MutedThreadsProvider>
148 <SafeAreaProvider>
149 <ProgressGuideProvider>
150 <ServiceConfigProvider>
151 <EmailVerificationProvider>
152 <HideBottomBarBorderProvider>
153 <IntentDialogProvider>
154 <Shell />
155 <ToastOutlet />
156 </IntentDialogProvider>
157 </HideBottomBarBorderProvider>
158 </EmailVerificationProvider>
159 </ServiceConfigProvider>
160 </ProgressGuideProvider>
161 </SafeAreaProvider>
162 </MutedThreadsProvider>
163 </BackgroundNotificationPreferencesProvider>
164 </UnreadNotifsProvider>
165 </HomeBadgeProvider>
166 </HiddenRepliesProvider>
167 </SelectedFeedProvider>
168 </LoggedOutViewProvider>
169 </ModerationOptsProvider>
170 </LabelDefsProvider>
171 </MessagesProvider>
172 </ComposerProvider>
173 </AgeAssuranceV2Provider>
174 </LiveEventsProvider>
175 </PolicyUpdateOverlayProvider>
176 </QueryProvider>
177 </AnalyticsFeaturesContext>
178 </React.Fragment>
179 </ActiveVideoProvider>
180 </VideoVolumeProvider>
181 </ContextMenuProvider>
182 </ThemeProvider>
183 </Alf>
184 )
185}
186
187function App() {
188 const [isReady, setReady] = useState(false)
189
190 React.useEffect(() => {
191 Promise.all([initPersistedState(), Geo.resolve(), setupDeviceId]).then(() =>
192 setReady(true),
193 )
194 }, [])
195
196 if (!isReady) {
197 return <Splash isReady />
198 }
199
200 /*
201 * NOTE: only nothing here can depend on other data or session state, since
202 * that is set up in the InnerApp component above.
203 */
204 return (
205 <Geo.Provider>
206 <A11yProvider>
207 <OnboardingProvider>
208 <AnalyticsContext>
209 <SessionProvider>
210 <PrefsStateProvider>
211 <I18nProvider>
212 <ShellStateProvider>
213 <ModalStateProvider>
214 <DialogStateProvider>
215 <LightboxStateProvider>
216 <PortalProvider>
217 <StarterPackProvider>
218 <InnerApp />
219 </StarterPackProvider>
220 </PortalProvider>
221 </LightboxStateProvider>
222 </DialogStateProvider>
223 </ModalStateProvider>
224 </ShellStateProvider>
225 </I18nProvider>
226 </PrefsStateProvider>
227 </SessionProvider>
228 </AnalyticsContext>
229 </OnboardingProvider>
230 </A11yProvider>
231 </Geo.Provider>
232 )
233}
234
235export default Sentry.wrap(App)