diff --git a/lib/reducers/thread-reducer.js b/lib/reducers/thread-reducer.js --- a/lib/reducers/thread-reducer.js +++ b/lib/reducers/thread-reducer.js @@ -31,6 +31,7 @@ type ThreadStoreOperation, threadStoreOpsHandlers, } from '../ops/thread-store-ops.js'; +import { updateSpecs } from '../shared/updates/update-specs.js'; import type { BaseAction } from '../types/redux-types.js'; import { type ClientThreadInconsistencyReportCreationRequest, @@ -45,7 +46,6 @@ incrementalStateSyncActionType, } from '../types/socket-types.js'; import type { RawThreadInfo, ThreadStore } from '../types/thread-types.js'; -import { updateTypes } from '../types/update-types-enum.js'; import { type ClientUpdateInfo, processUpdatesActionType, @@ -66,72 +66,15 @@ ... }, ): $ReadOnlyArray { - const threadOperations: ThreadStoreOperation[] = []; - for (const update of payload.updatesResult.newUpdates) { - if ( - (update.type === updateTypes.UPDATE_THREAD || - update.type === updateTypes.JOIN_THREAD) && - !_isEqual(threadInfos[update.threadInfo.id])(update.threadInfo) - ) { - threadOperations.push({ - type: 'replace', - payload: { - id: update.threadInfo.id, - threadInfo: update.threadInfo, - }, - }); - } else if ( - update.type === updateTypes.UPDATE_THREAD_READ_STATUS && - threadInfos[update.threadID] && - threadInfos[update.threadID].currentUser.unread !== update.unread - ) { - const updatedThread = { - ...threadInfos[update.threadID], - currentUser: { - ...threadInfos[update.threadID].currentUser, - unread: update.unread, - }, - }; - threadOperations.push({ - type: 'replace', - payload: { - id: update.threadID, - threadInfo: updatedThread, - }, - }); - } else if ( - update.type === updateTypes.DELETE_THREAD && - threadInfos[update.threadID] - ) { - threadOperations.push({ - type: 'remove', - payload: { - ids: [update.threadID], - }, - }); - } else if (update.type === updateTypes.DELETE_ACCOUNT) { - for (const threadID in threadInfos) { - const threadInfo = threadInfos[threadID]; - const newMembers = threadInfo.members.filter( - member => member.id !== update.deletedUserID, - ); - if (newMembers.length < threadInfo.members.length) { - const updatedThread = { - ...threadInfo, - members: newMembers, - }; - threadOperations.push({ - type: 'replace', - payload: { - id: threadID, - threadInfo: updatedThread, - }, - }); - } - } - } - } - return threadOperations; + return payload.updatesResult.newUpdates + .map(update => + updateSpecs[update.type].generateOpsForThreadUpdates?.( + threadInfos, + update, + ), + ) + .filter(Boolean) + .flat(); } function findInconsistencies( diff --git a/lib/shared/updates/bad-device-token-spec.js b/lib/shared/updates/bad-device-token-spec.js --- a/lib/shared/updates/bad-device-token-spec.js +++ b/lib/shared/updates/bad-device-token-spec.js @@ -1,5 +1,7 @@ // @flow import type { UpdateSpec } from './update-spec.js'; +import type { BadDeviceTokenUpdateInfo } from '../../types/update-types.js'; -export const badDeviceTokenSpec: UpdateSpec = Object.freeze({}); +export const badDeviceTokenSpec: UpdateSpec = + Object.freeze({}); diff --git a/lib/shared/updates/delete-account-spec.js b/lib/shared/updates/delete-account-spec.js --- a/lib/shared/updates/delete-account-spec.js +++ b/lib/shared/updates/delete-account-spec.js @@ -1,5 +1,35 @@ // @flow import type { UpdateSpec } from './update-spec.js'; +import type { RawThreadInfos } from '../../types/thread-types.js'; +import type { AccountDeletionUpdateInfo } from '../../types/update-types.js'; -export const deleteAccountSpec: UpdateSpec = Object.freeze({}); +export const deleteAccountSpec: UpdateSpec = + Object.freeze({ + generateOpsForThreadUpdates( + storeThreadInfos: RawThreadInfos, + update: AccountDeletionUpdateInfo, + ) { + const operations = []; + for (const threadID in storeThreadInfos) { + const threadInfo = storeThreadInfos[threadID]; + const newMembers = threadInfo.members.filter( + member => member.id !== update.deletedUserID, + ); + if (newMembers.length < threadInfo.members.length) { + const updatedThread = { + ...threadInfo, + members: newMembers, + }; + operations.push({ + type: 'replace', + payload: { + id: threadID, + threadInfo: updatedThread, + }, + }); + } + } + return operations; + }, + }); diff --git a/lib/shared/updates/delete-thread-spec.js b/lib/shared/updates/delete-thread-spec.js --- a/lib/shared/updates/delete-thread-spec.js +++ b/lib/shared/updates/delete-thread-spec.js @@ -1,5 +1,25 @@ // @flow import type { UpdateSpec } from './update-spec.js'; +import type { RawThreadInfos } from '../../types/thread-types.js'; +import type { ThreadDeletionUpdateInfo } from '../../types/update-types.js'; -export const deleteThreadSpec: UpdateSpec = Object.freeze({}); +export const deleteThreadSpec: UpdateSpec = + Object.freeze({ + generateOpsForThreadUpdates( + storeThreadInfos: RawThreadInfos, + update: ThreadDeletionUpdateInfo, + ) { + if (storeThreadInfos[update.threadID]) { + return [ + { + type: 'remove', + payload: { + ids: [update.threadID], + }, + }, + ]; + } + return null; + }, + }); diff --git a/lib/shared/updates/join-thread-spec.js b/lib/shared/updates/join-thread-spec.js --- a/lib/shared/updates/join-thread-spec.js +++ b/lib/shared/updates/join-thread-spec.js @@ -1,5 +1,27 @@ // @flow +import _isEqual from 'lodash/fp/isEqual.js'; + import type { UpdateSpec } from './update-spec.js'; +import type { RawThreadInfos } from '../../types/thread-types.js'; +import type { ThreadJoinUpdateInfo } from '../../types/update-types.js'; -export const joinThreadSpec: UpdateSpec = Object.freeze({}); +export const joinThreadSpec: UpdateSpec = Object.freeze({ + generateOpsForThreadUpdates( + storeThreadInfos: RawThreadInfos, + update: ThreadJoinUpdateInfo, + ) { + if (_isEqual(storeThreadInfos[update.threadInfo.id])(update.threadInfo)) { + return null; + } + return [ + { + type: 'replace', + payload: { + id: update.threadInfo.id, + threadInfo: update.threadInfo, + }, + }, + ]; + }, +}); diff --git a/lib/shared/updates/update-current-user-spec.js b/lib/shared/updates/update-current-user-spec.js --- a/lib/shared/updates/update-current-user-spec.js +++ b/lib/shared/updates/update-current-user-spec.js @@ -1,5 +1,7 @@ // @flow import type { UpdateSpec } from './update-spec.js'; +import type { CurrentUserUpdateInfo } from '../../types/update-types.js'; -export const updateCurrentUserSpec: UpdateSpec = Object.freeze({}); +export const updateCurrentUserSpec: UpdateSpec = + Object.freeze({}); diff --git a/lib/shared/updates/update-entry-spec.js b/lib/shared/updates/update-entry-spec.js --- a/lib/shared/updates/update-entry-spec.js +++ b/lib/shared/updates/update-entry-spec.js @@ -1,5 +1,6 @@ // @flow import type { UpdateSpec } from './update-spec.js'; +import type { EntryUpdateInfo } from '../../types/update-types.js'; -export const updateEntrySpec: UpdateSpec = Object.freeze({}); +export const updateEntrySpec: UpdateSpec = Object.freeze({}); diff --git a/lib/shared/updates/update-spec.js b/lib/shared/updates/update-spec.js --- a/lib/shared/updates/update-spec.js +++ b/lib/shared/updates/update-spec.js @@ -1,3 +1,12 @@ // @flow -export type UpdateSpec = {}; +import type { ThreadStoreOperation } from '../../ops/thread-store-ops.js'; +import type { RawThreadInfos } from '../../types/thread-types.js'; +import type { ClientUpdateInfo } from '../../types/update-types.js'; + +export type UpdateSpec = { + +generateOpsForThreadUpdates?: ( + storeThreadInfos: RawThreadInfos, + update: UpdateInfo, + ) => ?$ReadOnlyArray, +}; diff --git a/lib/shared/updates/update-specs.js b/lib/shared/updates/update-specs.js --- a/lib/shared/updates/update-specs.js +++ b/lib/shared/updates/update-specs.js @@ -13,7 +13,7 @@ import { updateTypes, type UpdateType } from '../../types/update-types-enum.js'; export const updateSpecs: { - +[UpdateType]: UpdateSpec, + +[UpdateType]: UpdateSpec<*>, } = Object.freeze({ [updateTypes.DELETE_ACCOUNT]: deleteAccountSpec, [updateTypes.UPDATE_THREAD]: updateThreadSpec, diff --git a/lib/shared/updates/update-thread-read-status-spec.js b/lib/shared/updates/update-thread-read-status-spec.js --- a/lib/shared/updates/update-thread-read-status-spec.js +++ b/lib/shared/updates/update-thread-read-status-spec.js @@ -1,5 +1,40 @@ // @flow import type { UpdateSpec } from './update-spec.js'; +import type { + RawThreadInfo, + RawThreadInfos, +} from '../../types/thread-types.js'; +import type { ThreadReadStatusUpdateInfo } from '../../types/update-types.js'; -export const updateThreadReadStatusSpec: UpdateSpec = Object.freeze({}); +export const updateThreadReadStatusSpec: UpdateSpec = + Object.freeze({ + generateOpsForThreadUpdates( + storeThreadInfos: RawThreadInfos, + update: ThreadReadStatusUpdateInfo, + ) { + const storeThreadInfo: ?RawThreadInfo = storeThreadInfos[update.threadID]; + if ( + !storeThreadInfo || + storeThreadInfo.currentUser.unread === update.unread + ) { + return null; + } + const updatedThread = { + ...storeThreadInfo, + currentUser: { + ...storeThreadInfo.currentUser, + unread: update.unread, + }, + }; + return [ + { + type: 'replace', + payload: { + id: update.threadID, + threadInfo: updatedThread, + }, + }, + ]; + }, + }); diff --git a/lib/shared/updates/update-thread-spec.js b/lib/shared/updates/update-thread-spec.js --- a/lib/shared/updates/update-thread-spec.js +++ b/lib/shared/updates/update-thread-spec.js @@ -1,5 +1,27 @@ // @flow +import _isEqual from 'lodash/fp/isEqual.js'; + import type { UpdateSpec } from './update-spec.js'; +import type { RawThreadInfos } from '../../types/thread-types.js'; +import type { ThreadUpdateInfo } from '../../types/update-types.js'; -export const updateThreadSpec: UpdateSpec = Object.freeze({}); +export const updateThreadSpec: UpdateSpec = Object.freeze({ + generateOpsForThreadUpdates( + storeThreadInfos: RawThreadInfos, + update: ThreadUpdateInfo, + ) { + if (_isEqual(storeThreadInfos[update.threadInfo.id])(update.threadInfo)) { + return null; + } + return [ + { + type: 'replace', + payload: { + id: update.threadInfo.id, + threadInfo: update.threadInfo, + }, + }, + ]; + }, +}); diff --git a/lib/shared/updates/update-user-spec.js b/lib/shared/updates/update-user-spec.js --- a/lib/shared/updates/update-user-spec.js +++ b/lib/shared/updates/update-user-spec.js @@ -1,5 +1,6 @@ // @flow import type { UpdateSpec } from './update-spec.js'; +import type { UserUpdateInfo } from '../../types/update-types.js'; -export const updateUserSpec: UpdateSpec = Object.freeze({}); +export const updateUserSpec: UpdateSpec = Object.freeze({}); diff --git a/lib/types/update-types.js b/lib/types/update-types.js --- a/lib/types/update-types.js +++ b/lib/types/update-types.js @@ -174,7 +174,7 @@ | CurrentUserRawUpdateInfo | UserRawUpdateInfo; -type AccountDeletionUpdateInfo = { +export type AccountDeletionUpdateInfo = { +type: 0, +id: string, +time: number, @@ -188,7 +188,7 @@ deletedUserID: t.String, }); -type ThreadUpdateInfo = { +export type ThreadUpdateInfo = { +type: 1, +id: string, +time: number, @@ -201,7 +201,7 @@ time: t.Number, threadInfo: rawThreadInfoValidator, }); -type ThreadReadStatusUpdateInfo = { +export type ThreadReadStatusUpdateInfo = { +type: 2, +id: string, +time: number, @@ -216,7 +216,7 @@ threadID: tID, unread: t.Boolean, }); -type ThreadDeletionUpdateInfo = { +export type ThreadDeletionUpdateInfo = { +type: 3, +id: string, +time: number, @@ -230,7 +230,7 @@ threadID: tID, }); -type ThreadJoinUpdateInfo = { +export type ThreadJoinUpdateInfo = { +type: 4, +id: string, +time: number, @@ -249,7 +249,7 @@ truncationStatus: messageTruncationStatusValidator, rawEntryInfos: t.list(rawEntryInfoValidator), }); -type BadDeviceTokenUpdateInfo = { +export type BadDeviceTokenUpdateInfo = { +type: 5, +id: string, +time: number, @@ -262,7 +262,7 @@ time: t.Number, deviceToken: t.String, }); -type EntryUpdateInfo = { +export type EntryUpdateInfo = { +type: 6, +id: string, +time: number, @@ -275,13 +275,13 @@ time: t.Number, entryInfo: rawEntryInfoValidator, }); -type CurrentUserUpdateInfo = { +export type CurrentUserUpdateInfo = { +type: 7, +id: string, +time: number, +currentUserInfo: LoggedInUserInfo, }; -type UserUpdateInfo = { +export type UserUpdateInfo = { +type: 8, +id: string, +time: number,