diff --git a/native/navigation/app-navigator.react.js b/native/navigation/app-navigator.react.js index 118df8b4a..0a03b0611 100644 --- a/native/navigation/app-navigator.react.js +++ b/native/navigation/app-navigator.react.js @@ -1,251 +1,268 @@ // @flow import type { BottomTabNavigationProp } from '@react-navigation/bottom-tabs'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import * as SplashScreen from 'expo-splash-screen'; import * as React from 'react'; +import { Alert } from 'react-native'; import { PersistGate } from 'redux-persist/integration/react'; import { unreadCount } from 'lib/selectors/thread-selectors'; +import { getMessageForException } from 'lib/utils/errors'; +import sleep from 'lib/utils/sleep'; import AppsDirectory from '../apps/apps-directory.react'; import Calendar from '../calendar/calendar.react'; import Chat from '../chat/chat.react'; import { MultimediaMessageTooltipModal } from '../chat/multimedia-message-tooltip-modal.react'; import { RobotextMessageTooltipModal } from '../chat/robotext-message-tooltip-modal.react'; import ThreadSettingsMemberTooltipModal from '../chat/settings/thread-settings-member-tooltip-modal.react'; import { TextMessageTooltipModal } from '../chat/text-message-tooltip-modal.react'; import SWMansionIcon from '../components/swmansion-icon.react'; import { type SQLiteContextType, SQLiteContext } from '../data/sqlite-context'; import KeyboardStateContainer from '../keyboard/keyboard-state-container.react'; import CameraModal from '../media/camera-modal.react'; import ImageModal from '../media/image-modal.react'; import VideoPlaybackModal from '../media/video-playback-modal.react'; import Profile from '../profile/profile.react'; import RelationshipListItemTooltipModal from '../profile/relationship-list-item-tooltip-modal.react'; import PushHandler from '../push/push-handler.react'; import { getPersistor } from '../redux/persist'; import { useSelector } from '../redux/redux-utils'; import { RootContext } from '../root-context'; import { waitForInteractions } from '../utils/timers'; import ActionResultModal from './action-result-modal.react'; import { createOverlayNavigator } from './overlay-navigator.react'; import type { OverlayRouterNavigationProp } from './overlay-router'; import type { RootNavigationProp } from './root-navigator.react'; import { CalendarRouteName, ChatRouteName, ProfileRouteName, TabNavigatorRouteName, ImageModalRouteName, MultimediaMessageTooltipModalRouteName, ActionResultModalRouteName, TextMessageTooltipModalRouteName, ThreadSettingsMemberTooltipModalRouteName, RelationshipListItemTooltipModalRouteName, RobotextMessageTooltipModalRouteName, CameraModalRouteName, VideoPlaybackModalRouteName, AppsRouteName, type ScreenParamList, type TabParamList, type OverlayParamList, } from './route-names'; import { tabBar } from './tab-bar.react'; let splashScreenHasHidden = false; const calendarTabOptions = { tabBarLabel: 'Calendar', // eslint-disable-next-line react/display-name tabBarIcon: ({ color }) => ( ), }; const getChatTabOptions = (badge: number) => ({ tabBarLabel: 'Inbox', // eslint-disable-next-line react/display-name tabBarIcon: ({ color }) => ( ), tabBarBadge: badge ? badge : undefined, }); const profileTabOptions = { tabBarLabel: 'Profile', // eslint-disable-next-line react/display-name tabBarIcon: ({ color }) => ( ), }; const appsTabOptions = { tabBarLabel: 'Apps', // eslint-disable-next-line react/display-name tabBarIcon: ({ color }) => ( ), }; export type TabNavigationProp< RouteName: $Keys = $Keys, > = BottomTabNavigationProp; const Tab = createBottomTabNavigator< ScreenParamList, TabParamList, TabNavigationProp<>, >(); const tabBarOptions = { keyboardHidesTabBar: false, activeTintColor: '#AE94DB', style: { backgroundColor: '#0A0A0A', borderTopWidth: 1, }, }; function TabNavigator() { const chatBadge = useSelector(unreadCount); const isCalendarEnabled = useSelector(state => state.enabledApps.calendar); let calendarTab; if (isCalendarEnabled) { calendarTab = ( ); } return ( {calendarTab} ); } export type AppNavigationProp< RouteName: $Keys = $Keys, > = OverlayRouterNavigationProp; const App = createOverlayNavigator< ScreenParamList, OverlayParamList, AppNavigationProp<>, >(); type AppNavigatorProps = { navigation: RootNavigationProp<'App'>, ... }; function AppNavigator(props: AppNavigatorProps): React.Node { const { navigation } = props; const rootContext = React.useContext(RootContext); const localDatabaseContext: ?SQLiteContextType = React.useContext( SQLiteContext, ); const storeLoadedFromLocalDatabase = localDatabaseContext?.threadStoreLoaded; const setNavStateInitialized = rootContext && rootContext.setNavStateInitialized; React.useEffect(() => { setNavStateInitialized && setNavStateInitialized(); }, [setNavStateInitialized]); const [ localSplashScreenHasHidden, setLocalSplashScreenHasHidden, ] = React.useState(splashScreenHasHidden); React.useEffect(() => { if (localSplashScreenHasHidden) { return; } splashScreenHasHidden = true; (async () => { await waitForInteractions(); try { - await SplashScreen.hideAsync(); + await Promise.race([ + SplashScreen.hideAsync(), + (async () => { + await sleep(5000); + throw new Error( + 'SplashScreen.hideAsync did not resolve/reject within 5 seconds', + ); + })(), + ]); + } catch (e) { + Alert.alert( + 'SplashScreen.hideAsync() failed :(', + getMessageForException(e), + ); + } finally { setLocalSplashScreenHasHidden(true); - } catch {} + } })(); }, [localSplashScreenHasHidden]); let pushHandler; if (localSplashScreenHasHidden) { pushHandler = ( ); } if (!storeLoadedFromLocalDatabase) { return null; } return ( {pushHandler} ); } const styles = { icon: { fontSize: 28, }, }; export default AppNavigator;