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