diff --git a/keyserver/src/responders/redux-state-responders.js b/keyserver/src/responders/redux-state-responders.js --- a/keyserver/src/responders/redux-state-responders.js +++ b/keyserver/src/responders/redux-state-responders.js @@ -70,6 +70,7 @@ tShape({ urlInfo: urlInfoValidator, excludedData: excludedDataValidator, + clientUpdatesCurrentAsOf: t.Number, }); const initialKeyserverInfoValidator = tShape({ @@ -95,7 +96,7 @@ viewer: Viewer, request: InitialReduxStateRequest, ): Promise { - const { urlInfo, excludedData } = request; + const { urlInfo, excludedData, clientUpdatesCurrentAsOf } = request; const useDatabase = viewer.loggedIn && canUseDatabaseOnWeb(viewer.userID); const hasNotAcknowledgedPoliciesPromise = hasAnyNotAcknowledgedPolicies( @@ -140,7 +141,10 @@ }; })(); const messageSelectionCriteria = { joinedThreads: true }; - const initialTime = Date.now(); + const serverUpdatesCurrentAsOf = + useDatabase && clientUpdatesCurrentAsOf + ? clientUpdatesCurrentAsOf + : Date.now(); const threadInfoPromise = fetchThreadInfos(viewer); const messageInfoPromise = fetchMessageInfos( @@ -158,7 +162,7 @@ const sessionIDPromise = (async () => { const calendarQuery = await calendarQueryPromise; if (viewer.loggedIn) { - await setNewSession(viewer, calendarQuery, initialTime); + await setNewSession(viewer, calendarQuery, serverUpdatesCurrentAsOf); } return viewer.sessionID; })(); @@ -197,7 +201,7 @@ { [ashoatKeyserverID]: mostRecentMessageTimestamp( rawMessageInfos, - initialTime, + serverUpdatesCurrentAsOf, ), }, threadInfos, @@ -219,7 +223,7 @@ return { entryInfos: _keyBy('id')(rawEntryInfos), daysToEntries: daysToEntriesFromEntryInfos(rawEntryInfos), - lastUserInteractionCalendar: initialTime, + lastUserInteractionCalendar: serverUpdatesCurrentAsOf, }; })(); const userInfosPromise = (async () => { @@ -303,7 +307,7 @@ })(); const currentAsOfPromise = (async () => { const hasNotAcknowledgedPolicies = await hasNotAcknowledgedPoliciesPromise; - return hasNotAcknowledgedPolicies ? 0 : initialTime; + return hasNotAcknowledgedPolicies ? 0 : serverUpdatesCurrentAsOf; })(); const pushApiPublicKeyPromise: Promise = (async () => { diff --git a/lib/selectors/keyserver-selectors.js b/lib/selectors/keyserver-selectors.js --- a/lib/selectors/keyserver-selectors.js +++ b/lib/selectors/keyserver-selectors.js @@ -51,6 +51,20 @@ keyserverID: string, ) => (state: AppState) => number = _memoize(baseUpdatesCurrentAsOfSelector); +const allUpdatesCurrentAsOfSelector: (state: AppState) => { + +[keyserverID: string]: number, +} = createSelector( + (state: AppState) => state.keyserverStore.keyserverInfos, + (infos: { +[key: string]: KeyserverInfo }) => { + const allUpdatesCurrentAsOf: { [string]: number } = {}; + for (const keyserverID in infos) { + allUpdatesCurrentAsOf[keyserverID] = + infos[keyserverID].updatesCurrentAsOf; + } + return allUpdatesCurrentAsOf; + }, +); + const baseCurrentAsOfSelector: ( keyserverID: string, ) => (state: AppState) => number = keyserverID => (state: AppState) => @@ -153,4 +167,5 @@ deviceTokensSelector, deviceTokenSelector, selectedKeyserversSelector, + allUpdatesCurrentAsOfSelector, }; diff --git a/web/redux/action-types.js b/web/redux/action-types.js --- a/web/redux/action-types.js +++ b/web/redux/action-types.js @@ -4,9 +4,11 @@ import { extractKeyserverIDFromID } from 'lib/utils/action-utils.js'; import { useKeyserverCall } from 'lib/utils/keyserver-call.js'; import type { CallKeyserverEndpoint } from 'lib/utils/keyserver-call.js'; +import type { URLInfo } from 'lib/utils/url-utils.js'; import { ashoatKeyserverID } from 'lib/utils/validation-utils.js'; import type { + ExcludedData, InitialReduxState, InitialReduxStateResponse, InitialKeyserverInfo, @@ -19,22 +21,42 @@ export const setInitialReduxState = 'SET_INITIAL_REDUX_STATE'; const getInitialReduxStateCallServerEndpointOptions = { timeout: 300000 }; + +type GetInitialReduxStateInput = { + +urlInfo: URLInfo, + +excludedData: ExcludedData, + +allUpdatesCurrentAsOf: { + +[keyserverID: string]: number, + }, +}; const getInitialReduxState = ( callKeyserverEndpoint: CallKeyserverEndpoint, allKeyserverIDs: $ReadOnlyArray, - ): ((input: InitialReduxStateRequest) => Promise) => + ): ((input: GetInitialReduxStateInput) => Promise) => async input => { const requests: { [string]: InitialReduxStateRequest } = {}; - const { urlInfo, excludedData } = input; + const { urlInfo, excludedData, allUpdatesCurrentAsOf } = input; const { thread, inviteSecret, ...rest } = urlInfo; const threadKeyserverID = thread ? extractKeyserverIDFromID(thread) : null; for (const keyserverID of allKeyserverIDs) { + const clientUpdatesCurrentAsOf = allUpdatesCurrentAsOf[keyserverID]; + const keyserverExcludedData: ExcludedData = { + threadStore: !!excludedData.threadStore && !!clientUpdatesCurrentAsOf, + }; if (keyserverID === threadKeyserverID) { - requests[keyserverID] = { urlInfo, excludedData }; + requests[keyserverID] = { + urlInfo, + excludedData: keyserverExcludedData, + clientUpdatesCurrentAsOf, + }; } else { - requests[keyserverID] = { urlInfo: rest, excludedData }; + requests[keyserverID] = { + urlInfo: rest, + excludedData: keyserverExcludedData, + clientUpdatesCurrentAsOf, + }; } } @@ -142,7 +164,7 @@ }; function useGetInitialReduxState(): ( - input: InitialReduxStateRequest, + input: GetInitialReduxStateInput, ) => Promise { return useKeyserverCall(getInitialReduxState); } 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 @@ -6,6 +6,7 @@ import { setClientDBStoreActionType } from 'lib/actions/client-db-store-actions.js'; import type { ThreadStoreOperation } from 'lib/ops/thread-store-ops.js'; +import { allUpdatesCurrentAsOfSelector } from 'lib/selectors/keyserver-selectors.js'; import { canUseDatabaseOnWeb } from 'lib/shared/web-database.js'; import type { RawThreadInfo } from 'lib/types/thread-types.js'; import { convertIDToNewSchema } from 'lib/utils/migration-utils.js'; @@ -42,6 +43,8 @@ }, [initError]); const isRehydrated = useSelector(state => !!state._persist?.rehydrated); + const allUpdatesCurrentAsOf = useSelector(allUpdatesCurrentAsOfSelector); + const prevIsRehydrated = React.useRef(false); React.useEffect(() => { if (!prevIsRehydrated.current && isRehydrated) { @@ -60,7 +63,10 @@ const payload = await callGetInitialReduxState({ urlInfo, - excludedData: { threadStore: !!clientDBStore.threadStore }, + excludedData: { + threadStore: !!clientDBStore.threadStore, + }, + allUpdatesCurrentAsOf, }); const currentLoggedInUserID = payload.currentUserInfo?.anonymous @@ -113,7 +119,7 @@ } })(); } - }, [callGetInitialReduxState, dispatch, isRehydrated]); + }, [callGetInitialReduxState, dispatch, isRehydrated, allUpdatesCurrentAsOf]); 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 @@ -15,7 +15,6 @@ import { mostRecentlyReadThreadSelector } from 'lib/selectors/thread-selectors.js'; import { isLoggedIn } from 'lib/selectors/user-selectors.js'; import { invalidSessionDowngrade } from 'lib/shared/session-utils.js'; -import { canUseDatabaseOnWeb } from 'lib/shared/web-database.js'; import type { Shape } from 'lib/types/core.js'; import type { CryptoStore } from 'lib/types/crypto-types.js'; import type { DraftStore } from 'lib/types/draft-types.js'; @@ -126,13 +125,9 @@ const { userInfos, keyserverInfos, ...rest } = action.payload; const newKeyserverInfos = { ...state.keyserverStore.keyserverInfos }; for (const keyserverID in keyserverInfos) { - const newUpdatesCurrentAsOf = canUseDatabaseOnWeb(rest.currentUserInfo.id) - ? newKeyserverInfos[keyserverID].updatesCurrentAsOf - : keyserverInfos[keyserverID].updatesCurrentAsOf; newKeyserverInfos[keyserverID] = { ...newKeyserverInfos[keyserverID], ...keyserverInfos[keyserverID], - updatesCurrentAsOf: newUpdatesCurrentAsOf, }; } return validateStateAndProcessDBOperations( diff --git a/web/types/redux-types.js b/web/types/redux-types.js --- a/web/types/redux-types.js +++ b/web/types/redux-types.js @@ -49,4 +49,5 @@ export type InitialReduxStateRequest = { +urlInfo: URLInfo, +excludedData: ExcludedData, + +clientUpdatesCurrentAsOf: number, };