diff --git a/lib/shared/state-sync/current-user-state-sync-spec.js b/lib/shared/state-sync/current-user-state-sync-spec.js index dcd1d3bf4..8e79dfc06 100644 --- a/lib/shared/state-sync/current-user-state-sync-spec.js +++ b/lib/shared/state-sync/current-user-state-sync-spec.js @@ -1,28 +1,33 @@ // @flow import { createSelector } from 'reselect'; -import type { StateSyncSpec } from './state-sync-spec.js'; +import type { StateSyncSpec, BoundStateSyncSpec } from './state-sync-spec.js'; import type { AppState } from '../../types/redux-types'; import { type CurrentUserInfo } from '../../types/user-types.js'; import { hash } from '../../utils/objects.js'; +const selector: ( + state: AppState, +) => BoundStateSyncSpec = + createSelector( + (state: AppState) => state.currentUserInfo, + (currentUserInfo: ?CurrentUserInfo) => ({ + ...currentUserStateSyncSpec, + getInfoHash: () => hash(currentUserInfo), + getAllInfosHash: () => hash(currentUserInfo), + getIDs: () => ([]: string[]), + }), + ); + export const currentUserStateSyncSpec: StateSyncSpec< CurrentUserInfo, CurrentUserInfo, void, > = Object.freeze({ hashKey: 'currentUserInfo', findStoreInconsistencies() { return undefined; }, - selector: createSelector( - (state: AppState) => state.currentUserInfo, - currentUserInfo => ({ - ...currentUserStateSyncSpec, - getInfoHash: () => hash(currentUserInfo), - getAllInfosHash: () => hash(currentUserInfo), - getIDs: () => [], - }), - ), + selector, }); diff --git a/lib/shared/state-sync/entries-state-sync-spec.js b/lib/shared/state-sync/entries-state-sync-spec.js index 04f993fa4..2bc5de56a 100644 --- a/lib/shared/state-sync/entries-state-sync-spec.js +++ b/lib/shared/state-sync/entries-state-sync-spec.js @@ -1,110 +1,117 @@ // @flow import _isEqual from 'lodash/fp/isEqual.js'; import { createSelector } from 'reselect'; -import type { StateSyncSpec } from './state-sync-spec.js'; +import type { StateSyncSpec, BoundStateSyncSpec } from './state-sync-spec.js'; import { type CalendarQuery, type RawEntryInfos, type RawEntryInfo, } from '../../types/entry-types.js'; import type { AppState } from '../../types/redux-types.js'; import { reportTypes, type ClientEntryInconsistencyReportCreationRequest, } from '../../types/report-types.js'; import type { ProcessServerRequestAction } from '../../types/request-types.js'; import { actionLogger } from '../../utils/action-logger.js'; import { getConfig } from '../../utils/config.js'; import { values, combineUnorderedHashes, hash } from '../../utils/objects.js'; import { generateReportID } from '../../utils/report-utils.js'; import { sanitizeActionSecrets } from '../../utils/sanitization.js'; import { ashoatKeyserverID } from '../../utils/validation-utils.js'; import { filterRawEntryInfosByCalendarQuery, serverEntryInfosObject, } from '../entry-utils.js'; +const selector: ( + state: AppState, +) => BoundStateSyncSpec< + RawEntryInfos, + RawEntryInfo, + $ReadOnlyArray, +> = createSelector( + (state: AppState) => state.entryStore.entryInfos, + (entryInfos: RawEntryInfos) => ({ + ...entriesStateSyncSpec, + getInfoHash: (id: string) => hash(entryInfos[`${ashoatKeyserverID}|${id}`]), + getAllInfosHash: (calendarQuery: CalendarQuery) => + getEntryInfosHash(entryInfos, calendarQuery), + getIDs: (calendarQuery: CalendarQuery) => + getEntryIDs(entryInfos, calendarQuery), + }), +); + export const entriesStateSyncSpec: StateSyncSpec< RawEntryInfos, RawEntryInfo, $ReadOnlyArray, > = Object.freeze({ hashKey: 'entryInfos', innerHashSpec: { hashKey: 'entryInfo', deleteKey: 'deleteEntryIDs', rawInfosKey: 'rawEntryInfos', }, findStoreInconsistencies( action: ProcessServerRequestAction, beforeStateCheck: RawEntryInfos, afterStateCheck: RawEntryInfos, ) { const calendarQuery = action.payload.calendarQuery; // We don't want to bother reporting an inconsistency if it's just because // of extraneous EntryInfos (not within the current calendarQuery) on either // side const filteredBeforeResult = filterRawEntryInfosByCalendarQuery( serverEntryInfosObject(values(beforeStateCheck)), calendarQuery, ); const filteredAfterResult = filterRawEntryInfosByCalendarQuery( serverEntryInfosObject(values(afterStateCheck)), calendarQuery, ); if (_isEqual(filteredBeforeResult)(filteredAfterResult)) { return emptyArray; } return [ { type: reportTypes.ENTRY_INCONSISTENCY, platformDetails: getConfig().platformDetails, beforeAction: beforeStateCheck, action: sanitizeActionSecrets(action), calendarQuery, pushResult: afterStateCheck, lastActions: actionLogger.interestingActionSummaries, time: Date.now(), id: generateReportID(), }, ]; }, - selector: createSelector( - (state: AppState) => state.entryStore.entryInfos, - entryInfos => ({ - ...entriesStateSyncSpec, - getInfoHash: (id: string) => - hash(entryInfos[`${ashoatKeyserverID}|${id}`]), - getAllInfosHash: (calendarQuery: CalendarQuery) => - getEntryInfosHash(entryInfos, calendarQuery), - getIDs: (calendarQuery: CalendarQuery) => - getEntryIDs(entryInfos, calendarQuery), - }), - ), + selector, }); const emptyArray: $ReadOnlyArray = []; function getEntryInfosHash( entryInfos: RawEntryInfos, calendarQuery: CalendarQuery, ) { const filteredEntryInfos = filterRawEntryInfosByCalendarQuery( serverEntryInfosObject(values(entryInfos)), calendarQuery, ); return combineUnorderedHashes(Object.values(filteredEntryInfos).map(hash)); } function getEntryIDs(entryInfos: RawEntryInfos, calendarQuery: CalendarQuery) { const filteredEntryInfos = filterRawEntryInfosByCalendarQuery( serverEntryInfosObject(values(entryInfos)), calendarQuery, ); return Object.keys(filteredEntryInfos).map(id => id.split('|')[1]); } diff --git a/lib/shared/state-sync/threads-state-sync-spec.js b/lib/shared/state-sync/threads-state-sync-spec.js index af4926348..0e95d5b04 100644 --- a/lib/shared/state-sync/threads-state-sync-spec.js +++ b/lib/shared/state-sync/threads-state-sync-spec.js @@ -1,74 +1,81 @@ // @flow import _isEqual from 'lodash/fp/isEqual.js'; import { createSelector } from 'reselect'; -import type { StateSyncSpec } from './state-sync-spec.js'; +import type { StateSyncSpec, BoundStateSyncSpec } from './state-sync-spec.js'; import type { AppState } from '../../types/redux-types.js'; import { reportTypes, type ClientThreadInconsistencyReportCreationRequest, } from '../../types/report-types.js'; import type { ProcessServerRequestAction } from '../../types/request-types.js'; import { type RawThreadInfos, type RawThreadInfo, } from '../../types/thread-types.js'; import { actionLogger } from '../../utils/action-logger.js'; import { getConfig } from '../../utils/config.js'; import { combineUnorderedHashes, values } from '../../utils/objects.js'; import { generateReportID } from '../../utils/report-utils.js'; import { sanitizeActionSecrets } from '../../utils/sanitization.js'; import { ashoatKeyserverID } from '../../utils/validation-utils.js'; +const selector: ( + state: AppState, +) => BoundStateSyncSpec< + RawThreadInfos, + RawThreadInfo, + $ReadOnlyArray, +> = createSelector( + (state: AppState) => state.integrityStore.threadHashes, + (state: AppState) => state.integrityStore.threadHashingStatus === 'completed', + (threadHashes: { +[string]: number }, threadHashingComplete: boolean) => ({ + ...threadsStateSyncSpec, + getInfoHash: (id: string) => threadHashes[`${ashoatKeyserverID}|${id}`], + getAllInfosHash: threadHashingComplete + ? () => combineUnorderedHashes(values(threadHashes)) + : () => null, + getIDs: threadHashingComplete + ? () => Object.keys(threadHashes).map(id => id.split('|')[1]) + : () => null, + }), +); + export const threadsStateSyncSpec: StateSyncSpec< RawThreadInfos, RawThreadInfo, $ReadOnlyArray, > = Object.freeze({ hashKey: 'threadInfos', innerHashSpec: { hashKey: 'threadInfo', deleteKey: 'deleteThreadIDs', rawInfosKey: 'rawThreadInfos', }, findStoreInconsistencies( action: ProcessServerRequestAction, beforeStateCheck: RawThreadInfos, afterStateCheck: RawThreadInfos, ) { if (_isEqual(beforeStateCheck)(afterStateCheck)) { return emptyArray; } return [ { type: reportTypes.THREAD_INCONSISTENCY, platformDetails: getConfig().platformDetails, beforeAction: beforeStateCheck, action: sanitizeActionSecrets(action), pushResult: afterStateCheck, lastActions: actionLogger.interestingActionSummaries, time: Date.now(), id: generateReportID(), }, ]; }, - selector: createSelector( - (state: AppState) => state.integrityStore.threadHashes, - (state: AppState) => - state.integrityStore.threadHashingStatus === 'completed', - (threadHashes, threadHashingComplete) => ({ - ...threadsStateSyncSpec, - getInfoHash: (id: string) => threadHashes[`${ashoatKeyserverID}|${id}`], - getAllInfosHash: threadHashingComplete - ? () => combineUnorderedHashes(values(threadHashes)) - : () => null, - getIDs: threadHashingComplete - ? () => Object.keys(threadHashes).map(id => id.split('|')[1]) - : () => null, - }), - ), + selector, }); const emptyArray: $ReadOnlyArray = []; diff --git a/lib/shared/state-sync/users-state-sync-spec.js b/lib/shared/state-sync/users-state-sync-spec.js index 3740dbc8d..0c0c11a06 100644 --- a/lib/shared/state-sync/users-state-sync-spec.js +++ b/lib/shared/state-sync/users-state-sync-spec.js @@ -1,68 +1,76 @@ // @flow import _isEqual from 'lodash/fp/isEqual.js'; import { createSelector } from 'reselect'; -import type { StateSyncSpec } from './state-sync-spec.js'; +import type { StateSyncSpec, BoundStateSyncSpec } from './state-sync-spec.js'; import type { AppState } from '../../types/redux-types'; import { type ClientUserInconsistencyReportCreationRequest, reportTypes, } from '../../types/report-types.js'; import type { ProcessServerRequestAction } from '../../types/request-types.js'; import { type UserInfo, type UserInfos } from '../../types/user-types.js'; import { actionLogger } from '../../utils/action-logger.js'; import { getConfig } from '../../utils/config.js'; import { combineUnorderedHashes, hash } from '../../utils/objects.js'; import { generateReportID } from '../../utils/report-utils.js'; import { sanitizeActionSecrets } from '../../utils/sanitization.js'; +const selector: ( + state: AppState, +) => BoundStateSyncSpec< + UserInfos, + UserInfo, + $ReadOnlyArray, +> = createSelector( + (state: AppState) => state.userStore.userInfos, + (userInfos: UserInfos) => ({ + ...usersStateSyncSpec, + getInfoHash: (id: string) => hash(userInfos[id]), + getAllInfosHash: () => + combineUnorderedHashes(Object.values(userInfos).map(hash)), + getIDs: () => Object.keys(userInfos), + }), +); + export const usersStateSyncSpec: StateSyncSpec< UserInfos, UserInfo, $ReadOnlyArray, > = Object.freeze({ hashKey: 'userInfos', innerHashSpec: { hashKey: 'userInfo', deleteKey: 'deleteUserInfoIDs', rawInfosKey: 'userInfos', additionalDeleteCondition(user: UserInfo) { return !user.username; }, }, findStoreInconsistencies( action: ProcessServerRequestAction, beforeStateCheck: UserInfos, afterStateCheck: UserInfos, ) { if (_isEqual(beforeStateCheck)(afterStateCheck)) { return emptyArray; } return [ { type: reportTypes.USER_INCONSISTENCY, platformDetails: getConfig().platformDetails, action: sanitizeActionSecrets(action), beforeStateCheck, afterStateCheck, lastActions: actionLogger.interestingActionSummaries, time: Date.now(), id: generateReportID(), }, ]; }, - selector: createSelector( - (state: AppState) => state.userStore.userInfos, - userInfos => ({ - ...usersStateSyncSpec, - getInfoHash: (id: string) => hash(userInfos[id]), - getAllInfosHash: () => - combineUnorderedHashes(Object.values(userInfos).map(hash)), - getIDs: () => Object.keys(userInfos), - }), - ), + selector, }); const emptyArray: $ReadOnlyArray = [];