@@ -18,10 +18,11 @@ import {SelectProvider} from '@mobile-reality/react-native-select-pro';
18
18
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs' ;
19
19
import { NavigationContainer , useNavigationContainerRef } from '@react-navigation/native' ;
20
20
import * as SplashScreen from 'expo-splash-screen' ;
21
- import { AppState , AppStateStatus , Platform , StatusBar , StyleSheet , UIManager , useColorScheme , View } from 'react-native' ;
21
+ import { ActivityIndicator , AppState , AppStateStatus , Image , Platform , StatusBar , StyleSheet , UIManager , useColorScheme , View } from 'react-native' ;
22
22
import { SafeAreaProvider } from 'react-native-safe-area-context' ;
23
23
24
24
import * as BackgroundFetch from 'expo-background-fetch' ;
25
+ import Constants from 'expo-constants' ;
25
26
import * as TaskManager from 'expo-task-manager' ;
26
27
import * as Sentry from 'sentry-expo' ;
27
28
@@ -62,9 +63,10 @@ import {NotFoundError} from 'types/requests';
62
63
import { formatRequestedTime , RequestedTime } from 'utils/date' ;
63
64
64
65
import * as messages from 'compiled-lang/en.json' ;
66
+ import { Center } from 'components/core' ;
65
67
import KillSwitchMonitor from 'components/KillSwitchMonitor' ;
66
68
import { filterLoggedData } from 'logging/filterLoggedData' ;
67
- import { updateCheck , UpdateStatus } from 'Updates' ;
69
+ import { startupUpdateCheck , UpdateStatus } from 'Updates' ;
68
70
69
71
logger . info ( 'App starting.' ) ;
70
72
@@ -116,7 +118,10 @@ axios.interceptors.response.use(response => {
116
118
} ) ;
117
119
118
120
// The SplashScreen stays up until we've loaded all of our fonts and other assets
119
- void SplashScreen . preventAutoHideAsync ( ) ;
121
+ void SplashScreen . preventAutoHideAsync ( ) . catch ( ( error : Error ) => {
122
+ // We really don't care about these errors, they're common and not actionable
123
+ logger . debug ( 'SplashScreen.preventAutoHideAsync threw error, ignoring' , { error} ) ;
124
+ } ) ;
120
125
121
126
if ( Sentry ?. init ) {
122
127
const dsn = process . env . EXPO_PUBLIC_SENTRY_DSN ;
@@ -310,11 +315,31 @@ const BaseApp: React.FunctionComponent<{
310
315
311
316
const navigationRef = useNavigationContainerRef ( ) ;
312
317
313
- // Hide the splash screen after fonts load and updates are applied.
314
- // TODO: for maximum seamlessness, hide it after the map view is ready
318
+ const [ splashScreenVisible , setSplashScreenVisible ] = useState ( true ) ;
319
+ useEffect ( ( ) => {
320
+ // Hide the splash screen, but bake in a delay so that we are ready to render a view
321
+ // that looks just like it
322
+ if ( splashScreenVisible ) {
323
+ setSplashScreenVisible ( false ) ;
324
+ setTimeout (
325
+ ( ) =>
326
+ void ( async ( ) => {
327
+ try {
328
+ await SplashScreen . hideAsync ( ) ;
329
+ } catch ( error ) {
330
+ // We really don't care about these errors, they're common and not actionable
331
+ logger . debug ( { error} , 'Error from SplashScreen.hideAsync, ignoring' ) ;
332
+ }
333
+ } ) ( ) ,
334
+ 500 ,
335
+ ) ;
336
+ }
337
+ } , [ splashScreenVisible , setSplashScreenVisible , logger ] ) ;
338
+
339
+ // Check for updates
315
340
const [ updateStatus , setUpdateStatus ] = useState < UpdateStatus > ( 'checking' ) ;
316
341
useEffect ( ( ) => {
317
- updateCheck ( )
342
+ startupUpdateCheck ( )
318
343
. then ( setUpdateStatus )
319
344
. catch ( ( error : Error ) => {
320
345
logger . error ( { error} , 'Unexpected error checking for updates' ) ;
@@ -323,24 +348,31 @@ const BaseApp: React.FunctionComponent<{
323
348
} ) ;
324
349
} , [ setUpdateStatus , logger ] ) ;
325
350
326
- const [ splashScreenState , setSplashScreenState ] = React . useState < 'visible' | 'hiding' | 'hidden' > ( 'visible' ) ;
327
- useEffect ( ( ) => {
328
- void ( async ( ) => {
329
- if ( fontsLoaded && updateStatus === 'ready' && splashScreenState === 'visible' ) {
330
- setSplashScreenState ( 'hiding' ) ;
331
- try {
332
- await SplashScreen . hideAsync ( ) ;
333
- } catch ( error ) {
334
- logger . error ( { error} , 'Error from SplashScreen.hideAsync' ) ;
335
- }
336
- setSplashScreenState ( 'hidden' ) ;
337
- }
338
- } ) ( ) ;
339
- } , [ fontsLoaded , logger , splashScreenState , setSplashScreenState , updateStatus ] ) ;
340
-
341
- if ( ! fontsLoaded || splashScreenState !== 'hidden' || updateStatus !== 'ready' ) {
342
- // The splash screen keeps rendering while fonts are loading or updates are in progress
343
- return null ;
351
+ if ( ! fontsLoaded || updateStatus !== 'ready' ) {
352
+ // Here, we render a view that looks exactly like the splash screen but now has an activity indicator
353
+ return (
354
+ < View
355
+ pointerEvents = "none"
356
+ style = { [
357
+ StyleSheet . absoluteFill ,
358
+ {
359
+ backgroundColor : Constants . expoConfig ?. splash ?. backgroundColor ,
360
+ } ,
361
+ ] } >
362
+ < Image
363
+ style = { {
364
+ width : '100%' ,
365
+ height : '100%' ,
366
+ resizeMode : Constants . expoConfig ?. splash ?. resizeMode || 'contain' ,
367
+ } }
368
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
369
+ source = { require ( './assets/splash.png' ) }
370
+ />
371
+ < Center style = { { position : 'absolute' , top : 0 , bottom : 0 , left : 0 , right : 0 } } >
372
+ < ActivityIndicator size = "large" style = { { marginTop : 200 } } />
373
+ </ Center >
374
+ </ View >
375
+ ) ;
344
376
}
345
377
346
378
return (
0 commit comments