diff --git a/lib/reducers/keyserver-reducer.js b/lib/reducers/keyserver-reducer.js index d4b7cdaf0..e2d4d2305 100644 --- a/lib/reducers/keyserver-reducer.js +++ b/lib/reducers/keyserver-reducer.js @@ -1,542 +1,621 @@ // @flow import { unsupervisedBackgroundActionType } from './lifecycle-state-reducer.js'; import { updateActivityActionTypes } from '../actions/activity-actions.js'; import { updateLastCommunicatedPlatformDetailsActionType, setDeviceTokenActionTypes, } from '../actions/device-actions.js'; import { addKeyserverActionType, removeKeyserverActionType, } from '../actions/keyserver-actions.js'; import { siweAuthActionTypes } from '../actions/siwe-actions.js'; import { keyserverAuthActionTypes, logOutActionTypes, deleteKeyserverAccountActionTypes, deleteAccountActionTypes, keyserverRegisterActionTypes, logInActionTypes, resetUserStateActionType, } from '../actions/user-actions.js'; import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js'; import { keyserverStoreOpsHandlers, type ReplaceKeyserverOperation, type RemoveKeyserversOperation, type KeyserverStoreOperation, } from '../ops/keyserver-store-ops.js'; import { queueActivityUpdatesActionType } from '../types/activity-types.js'; import type { KeyserverStore } from '../types/keyserver-types.js'; import type { BaseAction } from '../types/redux-types.js'; import { fullStateSyncActionType, incrementalStateSyncActionType, updateConnectionStatusActionType, setLateResponseActionType, updateDisconnectedBarActionType, setConnectionIssueActionType, } from '../types/socket-types.js'; import { updateTypes } from '../types/update-types-enum.js'; import { processUpdatesActionType } from '../types/update-types.js'; import { getConfig } from '../utils/config.js'; import { setURLPrefix } from '../utils/url-utils.js'; import { ashoatKeyserverID } from '../utils/validation-utils.js'; const { processStoreOperations: processStoreOps } = keyserverStoreOpsHandlers; export default function reduceKeyserverStore( state: KeyserverStore, action: BaseAction, -): KeyserverStore { +): { + keyserverStore: KeyserverStore, + keyserverStoreOperations: $ReadOnlyArray, +} { if (action.type === addKeyserverActionType) { const replaceOperation: ReplaceKeyserverOperation = { type: 'replace_keyserver', payload: { id: action.payload.keyserverAdminUserID, keyserverInfo: { ...action.payload.newKeyserverInfo, }, }, }; return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, [replaceOperation]), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, [ + replaceOperation, + ]), + }, + keyserverStoreOperations: [replaceOperation], }; } else if (action.type === removeKeyserverActionType) { const removeOperation: RemoveKeyserversOperation = { type: 'remove_keyservers', payload: { ids: [action.payload.keyserverAdminUserID], }, }; return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, [removeOperation]), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, [ + removeOperation, + ]), + }, + keyserverStoreOperations: [removeOperation], }; } else if (action.type === resetUserStateActionType) { // this action is only dispatched on native const replaceOperations: ReplaceKeyserverOperation[] = []; for (const keyserverID in state.keyserverInfos) { const stateCookie = state.keyserverInfos[keyserverID]?.cookie; if (stateCookie && stateCookie.startsWith('anonymous=')) { continue; } replaceOperations.push({ type: 'replace_keyserver', payload: { id: keyserverID, keyserverInfo: { ...state.keyserverInfos[keyserverID], cookie: null, }, }, }); } return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, replaceOperations), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps( + state.keyserverInfos, + replaceOperations, + ), + }, + keyserverStoreOperations: replaceOperations, }; } else if (action.type === setNewSessionActionType) { const { keyserverID, sessionChange } = action.payload; if (!state.keyserverInfos[keyserverID]) { if (sessionChange.cookie?.startsWith('user=')) { console.log( 'received sessionChange with user cookie, ' + `but keyserver ${keyserverID} is not in KeyserverStore!`, ); } - return state; + return { + keyserverStore: state, + keyserverStoreOperations: [], + }; } let newKeyserverInfo = { ...state.keyserverInfos[keyserverID], }; let keyserverUpdated = false; if (sessionChange.cookie !== undefined) { newKeyserverInfo = { ...newKeyserverInfo, cookie: sessionChange.cookie, }; keyserverUpdated = true; } if (sessionChange.cookieInvalidated) { newKeyserverInfo = { ...newKeyserverInfo, connection: { ...newKeyserverInfo.connection, queuedActivityUpdates: [], }, }; keyserverUpdated = true; } const operations: ReplaceKeyserverOperation[] = []; if (keyserverUpdated) { operations.push({ type: 'replace_keyserver', payload: { id: keyserverID, keyserverInfo: newKeyserverInfo, }, }); } return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, operations), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, operations), + }, + keyserverStoreOperations: operations, }; } else if ( action.type === logInActionTypes.success || action.type === siweAuthActionTypes.success || action.type === keyserverAuthActionTypes.success ) { const { updatesCurrentAsOf } = action.payload; const operations: ReplaceKeyserverOperation[] = []; for (const keyserverID in updatesCurrentAsOf) { operations.push({ type: 'replace_keyserver', payload: { id: keyserverID, keyserverInfo: { ...state.keyserverInfos[keyserverID], updatesCurrentAsOf: updatesCurrentAsOf[keyserverID], lastCommunicatedPlatformDetails: getConfig().platformDetails, }, }, }); } return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, operations), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, operations), + }, + keyserverStoreOperations: operations, }; } else if (action.type === fullStateSyncActionType) { const { keyserverID } = action.payload; const operation: ReplaceKeyserverOperation = { type: 'replace_keyserver', payload: { id: keyserverID, keyserverInfo: { ...state.keyserverInfos[keyserverID], updatesCurrentAsOf: action.payload.updatesCurrentAsOf, }, }, }; return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + }, + keyserverStoreOperations: [operation], }; } else if (action.type === incrementalStateSyncActionType) { const { keyserverID } = action.payload; let { deviceToken } = state.keyserverInfos[keyserverID]; for (const update of action.payload.updatesResult.newUpdates) { if ( update.type === updateTypes.BAD_DEVICE_TOKEN && update.deviceToken === state.keyserverInfos[keyserverID].deviceToken ) { deviceToken = null; break; } } const operation: ReplaceKeyserverOperation = { type: 'replace_keyserver', payload: { id: keyserverID, keyserverInfo: { ...state.keyserverInfos[keyserverID], updatesCurrentAsOf: action.payload.updatesResult.currentAsOf, deviceToken, }, }, }; return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + }, + keyserverStoreOperations: [operation], }; } else if (action.type === processUpdatesActionType) { const { keyserverID } = action.payload; const updatesCurrentAsOf = Math.max( action.payload.updatesResult.currentAsOf, state.keyserverInfos[keyserverID].updatesCurrentAsOf, ); const operation: ReplaceKeyserverOperation = { type: 'replace_keyserver', payload: { id: keyserverID, keyserverInfo: { ...state.keyserverInfos[keyserverID], updatesCurrentAsOf, }, }, }; return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + }, + keyserverStoreOperations: [operation], }; } else if (action.type === setURLPrefix) { const operation: ReplaceKeyserverOperation = { type: 'replace_keyserver', payload: { id: ashoatKeyserverID, keyserverInfo: { ...state.keyserverInfos[ashoatKeyserverID], urlPrefix: action.payload, }, }, }; return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + }, + keyserverStoreOperations: [operation], }; } else if (action.type === updateLastCommunicatedPlatformDetailsActionType) { const { keyserverID, platformDetails } = action.payload; const operation: ReplaceKeyserverOperation = { type: 'replace_keyserver', payload: { id: keyserverID, keyserverInfo: { ...state.keyserverInfos[keyserverID], lastCommunicatedPlatformDetails: platformDetails, }, }, }; return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + }, + keyserverStoreOperations: [operation], }; } else if (action.type === keyserverRegisterActionTypes.success) { const operation: ReplaceKeyserverOperation = { type: 'replace_keyserver', payload: { id: ashoatKeyserverID, keyserverInfo: { ...state.keyserverInfos[ashoatKeyserverID], lastCommunicatedPlatformDetails: getConfig().platformDetails, }, }, }; return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + }, + keyserverStoreOperations: [operation], }; } else if (action.type === updateConnectionStatusActionType) { const { keyserverID, status } = action.payload; const operation: ReplaceKeyserverOperation = { type: 'replace_keyserver', payload: { id: keyserverID, keyserverInfo: { ...state.keyserverInfos[keyserverID], connection: { ...state.keyserverInfos[keyserverID].connection, status, lateResponses: [], }, }, }, }; return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + }, + keyserverStoreOperations: [operation], }; } else if (action.type === unsupervisedBackgroundActionType) { const { keyserverID } = action.payload; const operation: ReplaceKeyserverOperation = { type: 'replace_keyserver', payload: { id: keyserverID, keyserverInfo: { ...state.keyserverInfos[keyserverID], connection: { ...state.keyserverInfos[keyserverID].connection, status: 'disconnected', lateResponses: [], }, }, }, }; return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + }, + keyserverStoreOperations: [operation], }; } else if (action.type === queueActivityUpdatesActionType) { const { activityUpdates, keyserverID } = action.payload; const oldConnection = state.keyserverInfos[keyserverID].connection; const connection = { ...oldConnection, queuedActivityUpdates: [ ...oldConnection.queuedActivityUpdates.filter(existingUpdate => { for (const activityUpdate of activityUpdates) { if ( ((existingUpdate.focus && activityUpdate.focus) || (existingUpdate.focus === false && activityUpdate.focus !== undefined)) && existingUpdate.threadID === activityUpdate.threadID ) { return false; } } return true; }), ...activityUpdates, ], }; const operation: ReplaceKeyserverOperation = { type: 'replace_keyserver', payload: { id: keyserverID, keyserverInfo: { ...state.keyserverInfos[keyserverID], connection, }, }, }; return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + }, + keyserverStoreOperations: [operation], }; } else if (action.type === updateActivityActionTypes.success) { const { activityUpdates } = action.payload; const operations: ReplaceKeyserverOperation[] = []; for (const keyserverID in activityUpdates) { const oldConnection = state.keyserverInfos[keyserverID].connection; const queuedActivityUpdates = oldConnection.queuedActivityUpdates.filter( activityUpdate => !activityUpdates[keyserverID].includes(activityUpdate), ); operations.push({ type: 'replace_keyserver', payload: { id: keyserverID, keyserverInfo: { ...state.keyserverInfos[keyserverID], connection: { ...oldConnection, queuedActivityUpdates }, }, }, }); } return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, operations), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, operations), + }, + keyserverStoreOperations: operations, }; } else if ( action.type === logOutActionTypes.success || action.type === deleteAccountActionTypes.success ) { // We want to remove all keyservers but Ashoat's keyserver const oldConnection = state.keyserverInfos[ashoatKeyserverID].connection; const operations: KeyserverStoreOperation[] = [ { type: 'remove_all_keyservers' }, ]; operations.push({ type: 'replace_keyserver', payload: { id: ashoatKeyserverID, keyserverInfo: { ...state.keyserverInfos[ashoatKeyserverID], connection: { ...oldConnection, connectionIssue: null, queuedActivityUpdates: [], }, cookie: null, }, }, }); return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, operations), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, operations), + }, + keyserverStoreOperations: operations, }; } else if (action.type === deleteKeyserverAccountActionTypes.success) { const operations: KeyserverStoreOperation[] = [ { type: 'remove_keyservers', payload: { ids: action.payload.keyserverIDs }, }, ]; if (action.payload.keyserverIDs.includes(ashoatKeyserverID)) { const oldConnection = state.keyserverInfos[ashoatKeyserverID].connection; operations.push({ type: 'replace_keyserver', payload: { id: ashoatKeyserverID, keyserverInfo: { ...state.keyserverInfos[ashoatKeyserverID], connection: { ...oldConnection, connectionIssue: null, queuedActivityUpdates: [], lateResponses: [], }, cookie: null, }, }, }); } return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, operations), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, operations), + }, + keyserverStoreOperations: operations, }; } else if (action.type === setLateResponseActionType) { const { messageID, isLate, keyserverID } = action.payload; const lateResponsesSet = new Set( state.keyserverInfos[keyserverID].connection.lateResponses, ); if (isLate) { lateResponsesSet.add(messageID); } else { lateResponsesSet.delete(messageID); } const operation: ReplaceKeyserverOperation = { type: 'replace_keyserver', payload: { id: keyserverID, keyserverInfo: { ...state.keyserverInfos[keyserverID], connection: { ...state.keyserverInfos[keyserverID].connection, lateResponses: [...lateResponsesSet], }, }, }, }; return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + }, + keyserverStoreOperations: [operation], }; } else if (action.type === updateDisconnectedBarActionType) { const { keyserverID } = action.payload; const operation: ReplaceKeyserverOperation = { type: 'replace_keyserver', payload: { id: keyserverID, keyserverInfo: { ...state.keyserverInfos[keyserverID], connection: { ...state.keyserverInfos[keyserverID].connection, showDisconnectedBar: action.payload.visible, }, }, }, }; return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + }, + keyserverStoreOperations: [operation], }; } else if (action.type === setDeviceTokenActionTypes.success) { const { deviceTokens } = action.payload; const operations: ReplaceKeyserverOperation[] = []; for (const keyserverID in deviceTokens) { operations.push({ type: 'replace_keyserver', payload: { id: keyserverID, keyserverInfo: { ...state.keyserverInfos[keyserverID], deviceToken: deviceTokens[keyserverID], }, }, }); } return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, operations), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, operations), + }, + keyserverStoreOperations: operations, }; } else if (action.type === setConnectionIssueActionType) { const { connectionIssue, keyserverID } = action.payload; const operation: ReplaceKeyserverOperation = { type: 'replace_keyserver', payload: { id: keyserverID, keyserverInfo: { ...state.keyserverInfos[keyserverID], connection: { ...state.keyserverInfos[keyserverID].connection, connectionIssue, }, }, }, }; return { - ...state, - keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + keyserverStore: { + ...state, + keyserverInfos: processStoreOps(state.keyserverInfos, [operation]), + }, + keyserverStoreOperations: [operation], }; } - return state; + return { + keyserverStore: state, + keyserverStoreOperations: [], + }; } diff --git a/lib/reducers/keyserver-reducer.test.js b/lib/reducers/keyserver-reducer.test.js index fd9aa65b9..7de9b3310 100644 --- a/lib/reducers/keyserver-reducer.test.js +++ b/lib/reducers/keyserver-reducer.test.js @@ -1,121 +1,124 @@ // @flow import reduceKeyserverStore from './keyserver-reducer.js'; import { deleteKeyserverAccountActionTypes } from '../actions/user-actions.js'; import { defaultKeyserverInfo } from '../types/keyserver-types.js'; import { ashoatKeyserverID } from '../utils/validation-utils.js'; describe('reduceKeyserverStore', () => { it('removes from the store keyservers the user has disconnected from', () => { const oldKeyserverStore = { keyserverInfos: { ['0']: defaultKeyserverInfo('url1'), ['100']: defaultKeyserverInfo('url2'), ['200']: defaultKeyserverInfo('url3'), }, }; const deleteAccountAction = { type: deleteKeyserverAccountActionTypes.success, payload: { currentUserInfo: { anonymous: true }, preRequestUserState: { cookiesAndSessions: {}, currentUserInfo: { id: '1000', username: 'test', }, }, keyserverIDs: ['100', '200'], }, loadingInfo: { fetchIndex: 1, trackMultipleRequests: false, customKeyName: undefined, }, }; expect( - reduceKeyserverStore(oldKeyserverStore, deleteAccountAction), + reduceKeyserverStore(oldKeyserverStore, deleteAccountAction) + .keyserverStore, ).toEqual({ keyserverInfos: { ['0']: defaultKeyserverInfo('url1') } }); }); it('update keyserverInfo with ashoatKeyserverID', () => { const defaultAshoatKeyserverInfo = defaultKeyserverInfo('url1'); const oldKeyserverStore = { keyserverInfos: { [ashoatKeyserverID]: { ...defaultAshoatKeyserverInfo, connection: { ...defaultAshoatKeyserverInfo.connection, connectionIssue: 'not_logged_in_error', }, }, }, }; const deleteAccountAction = { type: deleteKeyserverAccountActionTypes.success, payload: { currentUserInfo: { anonymous: true }, preRequestUserState: { cookiesAndSessions: {}, currentUserInfo: { id: '1000', username: 'test', }, }, keyserverIDs: [ashoatKeyserverID], }, loadingInfo: { fetchIndex: 1, trackMultipleRequests: false, customKeyName: undefined, }, }; expect( reduceKeyserverStore(oldKeyserverStore, deleteAccountAction) - .keyserverInfos[ashoatKeyserverID].connection.connectionIssue, + .keyserverStore.keyserverInfos[ashoatKeyserverID].connection + .connectionIssue, ).toEqual(null); }); it('return the same keyserverInfo with ashoatKeyserverID', () => { const defaultAshoatKeyserverInfo = defaultKeyserverInfo('url1'); const oldKeyserverStore = { keyserverInfos: { [ashoatKeyserverID]: { ...defaultAshoatKeyserverInfo, connection: { ...defaultAshoatKeyserverInfo.connection, connectionIssue: 'not_logged_in_error', }, }, }, }; const deleteAccountAction = { type: deleteKeyserverAccountActionTypes.success, payload: { currentUserInfo: { anonymous: true }, preRequestUserState: { cookiesAndSessions: {}, currentUserInfo: { id: '1000', username: 'test', }, }, keyserverIDs: ['100', '200'], }, loadingInfo: { fetchIndex: 1, trackMultipleRequests: false, customKeyName: undefined, }, }; expect( reduceKeyserverStore(oldKeyserverStore, deleteAccountAction) - .keyserverInfos[ashoatKeyserverID].connection.connectionIssue, + .keyserverStore.keyserverInfos[ashoatKeyserverID].connection + .connectionIssue, ).toEqual('not_logged_in_error'); }); }); diff --git a/lib/reducers/master-reducer.js b/lib/reducers/master-reducer.js index 9dd8f21e6..afb7e8822 100644 --- a/lib/reducers/master-reducer.js +++ b/lib/reducers/master-reducer.js @@ -1,193 +1,193 @@ // @flow import reduceCalendarFilters from './calendar-filters-reducer.js'; import { reduceCalendarQuery } from './calendar-query-reducer.js'; import reduceCustomerServer from './custom-server-reducer.js'; import reduceDataLoaded from './data-loaded-reducer.js'; import { reduceDraftStore } from './draft-reducer.js'; import reduceEnabledApps from './enabled-apps-reducer.js'; import { reduceEntryInfos } from './entry-reducer.js'; import { reduceIntegrityStore } from './integrity-reducer.js'; import reduceInviteLinks from './invite-links-reducer.js'; import reduceKeyserverStore from './keyserver-reducer.js'; import reduceLifecycleState from './lifecycle-state-reducer.js'; import { reduceLoadingStatuses } from './loading-reducer.js'; import reduceNextLocalID from './local-id-reducer.js'; import { reduceMessageStore } from './message-reducer.js'; import reduceBaseNavInfo from './nav-reducer.js'; import { reduceNotifPermissionAlertInfo } from './notif-permission-alert-info-reducer.js'; import policiesReducer from './policies-reducer.js'; import reduceReportStore from './report-store-reducer.js'; import reduceServicesAccessToken from './services-access-token-reducer.js'; import reduceGlobalThemeInfo from './theme-reducer.js'; import { reduceThreadActivity } from './thread-activity-reducer.js'; import { reduceThreadInfos } from './thread-reducer.js'; import { reduceCurrentUserInfo, reduceUserInfos } from './user-reducer.js'; import { addKeyserverActionType } from '../actions/keyserver-actions.js'; import { siweAuthActionTypes } from '../actions/siwe-actions.js'; import { keyserverRegisterActionTypes, logInActionTypes, } from '../actions/user-actions.js'; import { isStaff } from '../shared/staff-utils.js'; import type { BaseNavInfo } from '../types/nav-types.js'; import type { BaseAppState, BaseAction } from '../types/redux-types.js'; import { fullStateSyncActionType, incrementalStateSyncActionType, } from '../types/socket-types.js'; import type { StoreOperations } from '../types/store-ops-types.js'; import { isDev } from '../utils/dev-utils.js'; export default function baseReducer>( state: T, action: BaseAction, onStateDifference: (message: string) => mixed, ): { state: T, storeOperations: StoreOperations } { const { threadStore, newThreadInconsistencies, threadStoreOperations } = reduceThreadInfos(state.threadStore, action); const { threadInfos } = threadStore; const [entryStore, newEntryInconsistencies] = reduceEntryInfos( state.entryStore, action, threadInfos, ); const onStateDifferenceForStaff = (message: string) => { const isCurrentUserStaff = state.currentUserInfo?.id ? isStaff(state.currentUserInfo.id) : false; if (isCurrentUserStaff || isDev) { onStateDifference(message); } }; const [userStore, newUserInconsistencies, userStoreOperations] = reduceUserInfos(state.userStore, action, onStateDifferenceForStaff); const newInconsistencies = [ ...newEntryInconsistencies, ...newThreadInconsistencies, ...newUserInconsistencies, ]; // Only allow checkpoints to increase if we are connected // or if the action is a STATE_SYNC const { messageStoreOperations, messageStore: reducedMessageStore } = reduceMessageStore(state.messageStore, action, threadInfos); let messageStore = reducedMessageStore; - let keyserverStore = reduceKeyserverStore(state.keyserverStore, action); + let { keyserverStore } = reduceKeyserverStore(state.keyserverStore, action); if ( action.type !== incrementalStateSyncActionType && action.type !== fullStateSyncActionType && action.type !== keyserverRegisterActionTypes.success && action.type !== logInActionTypes.success && action.type !== siweAuthActionTypes.success && action.type !== addKeyserverActionType ) { for (const keyserverID in keyserverStore.keyserverInfos) { if ( keyserverStore.keyserverInfos[keyserverID].connection.status === 'connected' ) { continue; } if ( messageStore.currentAsOf[keyserverID] !== state.messageStore.currentAsOf[keyserverID] ) { messageStore = { ...messageStore, currentAsOf: { ...messageStore.currentAsOf, [keyserverID]: state.messageStore.currentAsOf[keyserverID], }, }; } if ( state.keyserverStore.keyserverInfos[keyserverID] && keyserverStore.keyserverInfos[keyserverID].updatesCurrentAsOf !== state.keyserverStore.keyserverInfos[keyserverID].updatesCurrentAsOf ) { const keyserverInfos = { ...keyserverStore.keyserverInfos }; keyserverInfos[keyserverID] = { ...keyserverInfos[keyserverID], updatesCurrentAsOf: state.keyserverStore.keyserverInfos[keyserverID].updatesCurrentAsOf, }; keyserverStore = { ...keyserverStore, keyserverInfos }; } } } const { draftStore, draftStoreOperations } = reduceDraftStore( state.draftStore, action, ); const { reportStore, reportStoreOperations } = reduceReportStore( state.reportStore, action, newInconsistencies, ); return { state: { ...state, navInfo: reduceBaseNavInfo(state.navInfo, action), draftStore, entryStore, loadingStatuses: reduceLoadingStatuses(state.loadingStatuses, action), currentUserInfo: reduceCurrentUserInfo(state.currentUserInfo, action), threadStore, userStore, messageStore, calendarFilters: reduceCalendarFilters( state.calendarFilters, action, threadStore, ), notifPermissionAlertInfo: reduceNotifPermissionAlertInfo( state.notifPermissionAlertInfo, action, ), actualizedCalendarQuery: reduceCalendarQuery( state.actualizedCalendarQuery, action, ), lifecycleState: reduceLifecycleState(state.lifecycleState, action), enabledApps: reduceEnabledApps(state.enabledApps, action), reportStore, nextLocalID: reduceNextLocalID(state.nextLocalID, action), dataLoaded: reduceDataLoaded(state.dataLoaded, action), userPolicies: policiesReducer(state.userPolicies, action), commServicesAccessToken: reduceServicesAccessToken( state.commServicesAccessToken, action, ), inviteLinksStore: reduceInviteLinks(state.inviteLinksStore, action), keyserverStore, threadActivityStore: reduceThreadActivity( state.threadActivityStore, action, ), integrityStore: reduceIntegrityStore( state.integrityStore, action, threadStore.threadInfos, threadStoreOperations, ), globalThemeInfo: reduceGlobalThemeInfo(state.globalThemeInfo, action), customServer: reduceCustomerServer(state.customServer, action), }, storeOperations: { draftStoreOperations, threadStoreOperations, messageStoreOperations, reportStoreOperations, userStoreOperations, }, }; }