diff --git a/web/redux/default-state.js b/web/redux/default-state.js --- a/web/redux/default-state.js +++ b/web/redux/default-state.js @@ -104,6 +104,7 @@ holderStore: { storedHolders: {}, }, + clientDBStateLoaded: false, }); export { defaultWebState }; diff --git a/web/redux/initial-state-gate.js b/web/redux/initial-state-gate.js --- a/web/redux/initial-state-gate.js +++ b/web/redux/initial-state-gate.js @@ -12,6 +12,7 @@ import { getMessageSearchStoreOps } from 'lib/reducers/db-ops-reducer.js'; import { allUpdatesCurrentAsOfSelector } from 'lib/selectors/keyserver-selectors.js'; import type { RawThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; +import type { ClientStore } from 'lib/types/store-ops-types.js'; import { threadIDIsThick } from 'lib/types/thread-types.js'; import { getConfig } from 'lib/utils/config.js'; import { convertIDToNewSchema } from 'lib/utils/migration-utils.js'; @@ -28,6 +29,20 @@ import Loading from '../loading.react.js'; import type { InitialReduxStateActionPayload } from '../types/redux-types.js'; +type CurrentStep = + | { +step: 'before_rehydrate' } + | { + +step: 'client_store_dispatched', + +clientDBStore: ClientStore, + } + | { + +step: 'client_store_ready', + +clientDBStore: ClientStore, + } + | { + +step: 'initial_redux_state_dispatched', + }; + type Props = { +persistor: Persistor, +children: React.Node, @@ -46,14 +61,51 @@ const isRehydrated = useSelector(state => !!state._persist?.rehydrated); const allUpdatesCurrentAsOf = useSelector(allUpdatesCurrentAsOfSelector); + const clientDBStateLoaded = useSelector(state => state.clientDBStateLoaded); + const [step, setStep] = React.useState({ + step: 'before_rehydrate', + }); const prevIsRehydrated = React.useRef(false); React.useEffect(() => { - if (prevIsRehydrated.current || !isRehydrated) { + if ( + step.step !== 'before_rehydrate' || + prevIsRehydrated.current || + !isRehydrated + ) { return; } prevIsRehydrated.current = isRehydrated; + + void (async () => { + try { + const { sqliteAPI } = getConfig(); + const clientDBStore = await sqliteAPI.getClientDBStore(null); + dispatch({ + type: setClientDBStoreActionType, + payload: clientDBStore, + }); + setStep({ step: 'client_store_dispatched', clientDBStore }); + } catch (err) { + setInitError(err); + } + })(); + }, [dispatch, isRehydrated, step.step]); + + React.useEffect(() => { + if (step.step !== 'client_store_dispatched' || !clientDBStateLoaded) { + return; + } + + setStep({ step: 'client_store_ready', clientDBStore: step.clientDBStore }); + }, [clientDBStateLoaded, dispatch, isRehydrated, step]); + + React.useEffect(() => { + if (step.step !== 'client_store_ready') { + return; + } void (async () => { + const clientDBStore = step.clientDBStore; try { let urlInfo = infoFromURL(decodeURI(window.location.href)); const isThickThreadOpen = @@ -69,13 +121,6 @@ }; } - const { sqliteAPI } = getConfig(); - const clientDBStore = await sqliteAPI.getClientDBStore(null); - dispatch({ - type: setClientDBStoreActionType, - payload: clientDBStore, - }); - const payload = await callGetInitialReduxState({ urlInfo, excludedData: { @@ -172,6 +217,7 @@ const messageSearchStoreOperations = getMessageSearchStoreOps( messageStoreOperations, ); + const { sqliteAPI } = getConfig(); await sqliteAPI.processDBStoreOperations({ threadStoreOperations, draftStoreOperations: [], @@ -195,9 +241,17 @@ }); } catch (err) { setInitError(err); + } finally { + setStep({ step: 'initial_redux_state_dispatched' }); } })(); - }, [callGetInitialReduxState, dispatch, isRehydrated, allUpdatesCurrentAsOf]); + }, [ + callGetInitialReduxState, + dispatch, + isRehydrated, + allUpdatesCurrentAsOf, + step, + ]); const initialStateLoaded = useSelector(state => state.initialStateLoaded); diff --git a/web/redux/redux-setup.js b/web/redux/redux-setup.js --- a/web/redux/redux-setup.js +++ b/web/redux/redux-setup.js @@ -3,6 +3,7 @@ import invariant from 'invariant'; import type { PersistState } from 'redux-persist/es/types.js'; +import { setClientDBStoreActionType } from 'lib/actions/client-db-store-actions.js'; import { logOutActionTypes, deleteKeyserverAccountActionTypes, @@ -96,6 +97,7 @@ 'initialStateLoaded', '_persist', 'customServer', + 'clientDBStateLoaded', ]; // Before making changes here, make sure to consider how the added property @@ -140,6 +142,7 @@ +tunnelbrokerDeviceToken: TunnelbrokerDeviceToken, +queuedDMOperations: QueuedDMOperations, +holderStore: HolderStore, + +clientDBStateLoaded: boolean, }; export type Action = $ReadOnly< @@ -337,6 +340,11 @@ keyserverIDs: newKeyserverIDs, }, }; + } else if (action.type === setClientDBStoreActionType) { + state = { + ...state, + clientDBStateLoaded: true, + }; } if (action.type !== updateNavInfoActionType) {