diff --git a/native/data/sqlite-context-provider.js b/native/data/sqlite-context-provider.js index d4dbcb813..d5056471b 100644 --- a/native/data/sqlite-context-provider.js +++ b/native/data/sqlite-context-provider.js @@ -1,71 +1,86 @@ // @flow import * as React from 'react'; import { useDispatch } from 'react-redux'; import { setThreadStoreActionType } from 'lib/actions/thread-actions'; import { sqliteLoadFailure } from 'lib/actions/user-actions'; import { fetchNewCookieFromNativeCredentials } from 'lib/utils/action-utils'; import { convertClientDBThreadInfosToRawThreadInfos } from 'lib/utils/thread-ops-utils'; +import { persistConfig } from '../redux/persist'; import { useSelector } from '../redux/redux-utils'; import { SQLiteContext } from './sqlite-context'; type Props = { +children: React.Node, }; function SQLiteContextProvider(props: Props): React.Node { const [threadStoreLoaded, setThreadStoreLoaded] = React.useState( false, ); + const [messageStoreLoaded, setMessageStoreLoaded] = React.useState( + false, + ); const dispatch = useDispatch(); const rehydrateConcluded = useSelector( state => !!(state._persist && state._persist.rehydrated), ); const cookie = useSelector(state => state.cookie); const urlPrefix = useSelector(state => state.urlPrefix); React.useEffect(() => { - if (threadStoreLoaded || !rehydrateConcluded) { + if ((threadStoreLoaded && messageStoreLoaded) || !rehydrateConcluded) { return; } + if (persistConfig.version < 31) { + setMessageStoreLoaded(true); + } (async () => { try { const threads = await global.CommCoreModule.getAllThreads(); const threadInfosFromDB = convertClientDBThreadInfosToRawThreadInfos( threads, ); dispatch({ type: setThreadStoreActionType, payload: { threadInfos: threadInfosFromDB }, }); } catch { await fetchNewCookieFromNativeCredentials( dispatch, cookie, urlPrefix, sqliteLoadFailure, ); } finally { setThreadStoreLoaded(true); } })(); - }, [threadStoreLoaded, urlPrefix, rehydrateConcluded, cookie, dispatch]); + }, [ + threadStoreLoaded, + urlPrefix, + rehydrateConcluded, + cookie, + dispatch, + messageStoreLoaded, + ]); const contextValue = React.useMemo( () => ({ threadStoreLoaded, + messageStoreLoaded, }), - [threadStoreLoaded], + [threadStoreLoaded, messageStoreLoaded], ); return ( {props.children} ); } export { SQLiteContextProvider }; diff --git a/native/data/sqlite-context.js b/native/data/sqlite-context.js index 89ca8a1ec..1ac36707a 100644 --- a/native/data/sqlite-context.js +++ b/native/data/sqlite-context.js @@ -1,13 +1,14 @@ // @flow import * as React from 'react'; export type SQLiteContextType = { +threadStoreLoaded: boolean, + +messageStoreLoaded: boolean, }; const SQLiteContext: React.Context = React.createContext( null, ); export { SQLiteContext }; diff --git a/native/navigation/app-navigator.react.js b/native/navigation/app-navigator.react.js index c3e03145d..117a72a2a 100644 --- a/native/navigation/app-navigator.react.js +++ b/native/navigation/app-navigator.react.js @@ -1,252 +1,254 @@ // @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 { PersistGate } from 'redux-persist/integration/react'; import { unreadCount } from 'lib/selectors/thread-selectors'; 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 storeLoadedFromLocalDatabase = + localDatabaseContext?.threadStoreLoaded && + localDatabaseContext?.messageStoreLoaded; 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(); } finally { setLocalSplashScreenHasHidden(true); } })(); }, [localSplashScreenHasHidden]); let pushHandler; if (localSplashScreenHasHidden) { pushHandler = ( ); } if (!storeLoadedFromLocalDatabase) { return null; } return ( {pushHandler} ); } const styles = { icon: { fontSize: 28, }, }; export default AppNavigator; diff --git a/native/selectors/app-state-selectors.js b/native/selectors/app-state-selectors.js index 8298276e3..5580f9f70 100644 --- a/native/selectors/app-state-selectors.js +++ b/native/selectors/app-state-selectors.js @@ -1,15 +1,19 @@ // @flow import * as React from 'react'; import { useSelector } from 'react-redux'; import { SQLiteContext } from '../data/sqlite-context'; function usePersistedStateLoaded(): boolean { const rehydrateConcluded = useSelector(state => !!state._persist?.rehydrated); const localDatabaseContext = React.useContext(SQLiteContext); - return rehydrateConcluded && !!localDatabaseContext?.threadStoreLoaded; + return ( + rehydrateConcluded && + !!localDatabaseContext?.threadStoreLoaded && + !!localDatabaseContext?.messageStoreLoaded + ); } export { usePersistedStateLoaded };