diff --git a/lib/reducers/thread-reducer.js b/lib/reducers/thread-reducer.js index 429de923a..ae817408b 100644 --- a/lib/reducers/thread-reducer.js +++ b/lib/reducers/thread-reducer.js @@ -1,415 +1,524 @@ // @flow import { setThreadUnreadStatusActionTypes, updateActivityActionTypes, } from '../actions/activity-actions.js'; import { setClientDBStoreActionType } from '../actions/client-db-store-actions.js'; import { saveMessagesActionType } from '../actions/message-actions.js'; import { siweAuthActionTypes } from '../actions/siwe-actions.js'; import { changeThreadSettingsActionTypes, deleteThreadActionTypes, newThreadActionTypes, removeUsersFromThreadActionTypes, changeThreadMemberRolesActionTypes, joinThreadActionTypes, leaveThreadActionTypes, modifyCommunityRoleActionTypes, deleteCommunityRoleActionTypes, } from '../actions/thread-actions.js'; import { logOutActionTypes, deleteKeyserverAccountActionTypes, logInActionTypes, registerActionTypes, updateSubscriptionActionTypes, } from '../actions/user-actions.js'; import { type ThreadStoreOperation, threadStoreOpsHandlers, } from '../ops/thread-store-ops.js'; import { stateSyncSpecs } from '../shared/state-sync/state-sync-specs.js'; import { updateSpecs } from '../shared/updates/update-specs.js'; import type { BaseAction } from '../types/redux-types.js'; import { type ClientThreadInconsistencyReportCreationRequest } from '../types/report-types.js'; import { serverRequestTypes, processServerRequestsActionType, } from '../types/request-types.js'; import { fullStateSyncActionType, incrementalStateSyncActionType, } from '../types/socket-types.js'; import type { - LegacyRawThreadInfo, - LegacyRawThreadInfos, + RawThreadInfo, + RawThreadInfos, ThreadStore, } from '../types/thread-types.js'; import { type ClientUpdateInfo, processUpdatesActionType, } from '../types/update-types.js'; import { setNewSessionActionType } from '../utils/action-utils.js'; const { processStoreOperations: processThreadStoreOperations } = threadStoreOpsHandlers; function generateOpsForThreadUpdates( - threadInfos: LegacyRawThreadInfos, + threadInfos: RawThreadInfos, payload: { +updatesResult: { +newUpdates: $ReadOnlyArray, ... }, ... }, ): $ReadOnlyArray { return payload.updatesResult.newUpdates .map(update => updateSpecs[update.type].generateOpsForThreadUpdates?.( threadInfos, update, ), ) .filter(Boolean) .flat(); } function reduceThreadInfos( state: ThreadStore, action: BaseAction, ): { threadStore: ThreadStore, newThreadInconsistencies: $ReadOnlyArray, threadStoreOperations: $ReadOnlyArray, } { if ( action.type === logInActionTypes.success || action.type === siweAuthActionTypes.success || action.type === registerActionTypes.success || action.type === fullStateSyncActionType ) { const newThreadInfos = action.payload.threadInfos; const threadStoreOperations = [ { type: 'remove_all', }, ...Object.keys(newThreadInfos).map((id: string) => ({ type: 'replace', payload: { id, threadInfo: newThreadInfos[id] }, })), ]; const updatedThreadStore = processThreadStoreOperations( state, threadStoreOperations, ); return { threadStore: updatedThreadStore, newThreadInconsistencies: [], threadStoreOperations, }; } else if ( action.type === logOutActionTypes.success || action.type === deleteKeyserverAccountActionTypes.success || (action.type === setNewSessionActionType && action.payload.sessionChange.cookieInvalidated) ) { if (Object.keys(state.threadInfos).length === 0) { return { threadStore: state, newThreadInconsistencies: [], threadStoreOperations: [], }; } const threadStoreOperations = [ { type: 'remove_all', }, ]; const updatedThreadStore = processThreadStoreOperations( state, threadStoreOperations, ); return { threadStore: updatedThreadStore, newThreadInconsistencies: [], threadStoreOperations, }; } else if ( action.type === joinThreadActionTypes.success || action.type === leaveThreadActionTypes.success || action.type === deleteThreadActionTypes.success || action.type === changeThreadSettingsActionTypes.success || action.type === removeUsersFromThreadActionTypes.success || action.type === changeThreadMemberRolesActionTypes.success || action.type === incrementalStateSyncActionType || action.type === processUpdatesActionType || action.type === newThreadActionTypes.success || action.type === modifyCommunityRoleActionTypes.success || action.type === deleteCommunityRoleActionTypes.success ) { const { newUpdates } = action.payload.updatesResult; if (newUpdates.length === 0) { return { threadStore: state, newThreadInconsistencies: [], threadStoreOperations: [], }; } const threadStoreOperations = generateOpsForThreadUpdates( state.threadInfos, action.payload, ); const updatedThreadStore = processThreadStoreOperations( state, threadStoreOperations, ); return { threadStore: updatedThreadStore, newThreadInconsistencies: [], threadStoreOperations, }; } else if (action.type === updateSubscriptionActionTypes.success) { const { threadID, subscription } = action.payload; - const newThreadInfo = { - ...state.threadInfos[threadID], - currentUser: { - ...state.threadInfos[threadID].currentUser, - subscription, - }, - }; - const threadStoreOperations = [ - { - type: 'replace', - payload: { - id: threadID, - threadInfo: newThreadInfo, + const threadInfo = state.threadInfos[threadID]; + // TODO (atul): Try to get rid of this ridiculous branching. + if (threadInfo.minimallyEncoded) { + const newThreadInfo = { + ...threadInfo, + currentUser: { + ...threadInfo.currentUser, + subscription, }, - }, - ]; - const updatedThreadStore = processThreadStoreOperations( - state, - threadStoreOperations, - ); - return { - threadStore: updatedThreadStore, - newThreadInconsistencies: [], - threadStoreOperations, - }; + }; + const threadStoreOperations = [ + { + type: 'replace', + payload: { + id: threadID, + threadInfo: newThreadInfo, + }, + }, + ]; + const updatedThreadStore = processThreadStoreOperations( + state, + threadStoreOperations, + ); + return { + threadStore: updatedThreadStore, + newThreadInconsistencies: [], + threadStoreOperations, + }; + } else { + const newThreadInfo = { + ...threadInfo, + currentUser: { + ...threadInfo.currentUser, + subscription, + }, + }; + const threadStoreOperations = [ + { + type: 'replace', + payload: { + id: threadID, + threadInfo: newThreadInfo, + }, + }, + ]; + const updatedThreadStore = processThreadStoreOperations( + state, + threadStoreOperations, + ); + return { + threadStore: updatedThreadStore, + newThreadInconsistencies: [], + threadStoreOperations, + }; + } } else if (action.type === saveMessagesActionType) { const threadIDToMostRecentTime = new Map(); for (const messageInfo of action.payload.rawMessageInfos) { const current = threadIDToMostRecentTime.get(messageInfo.threadID); if (!current || current < messageInfo.time) { threadIDToMostRecentTime.set(messageInfo.threadID, messageInfo.time); } } - const changedThreadInfos: { [string]: LegacyRawThreadInfo } = {}; + const changedThreadInfos: { [string]: RawThreadInfo } = {}; for (const [threadID, mostRecentTime] of threadIDToMostRecentTime) { const threadInfo = state.threadInfos[threadID]; if ( !threadInfo || threadInfo.currentUser.unread || action.payload.updatesCurrentAsOf > mostRecentTime ) { continue; } - changedThreadInfos[threadID] = { - ...state.threadInfos[threadID], - currentUser: { - ...state.threadInfos[threadID].currentUser, - unread: true, - }, - }; + const changedThreadInfo = state.threadInfos[threadID]; + // TODO (atul): Try to get rid of this ridiculous branching. + if (changedThreadInfo.minimallyEncoded) { + changedThreadInfos[threadID] = { + ...changedThreadInfo, + currentUser: { + ...changedThreadInfo.currentUser, + unread: true, + }, + }; + } else { + changedThreadInfos[threadID] = { + ...changedThreadInfo, + currentUser: { + ...changedThreadInfo.currentUser, + unread: true, + }, + }; + } } if (Object.keys(changedThreadInfos).length !== 0) { const threadStoreOperations = Object.keys(changedThreadInfos).map(id => ({ type: 'replace', payload: { id, threadInfo: changedThreadInfos[id], }, })); const updatedThreadStore = processThreadStoreOperations( state, threadStoreOperations, ); return { threadStore: updatedThreadStore, newThreadInconsistencies: [], threadStoreOperations, }; } } else if (action.type === processServerRequestsActionType) { const checkStateRequest = action.payload.serverRequests.find( candidate => candidate.type === serverRequestTypes.CHECK_STATE, ); if (!checkStateRequest || !checkStateRequest.stateChanges) { return { threadStore: state, newThreadInconsistencies: [], threadStoreOperations: [], }; } const { rawThreadInfos, deleteThreadIDs } = checkStateRequest.stateChanges; if (!rawThreadInfos && !deleteThreadIDs) { return { threadStore: state, newThreadInconsistencies: [], threadStoreOperations: [], }; } const threadStoreOperations: ThreadStoreOperation[] = []; if (rawThreadInfos) { for (const rawThreadInfo of rawThreadInfos) { threadStoreOperations.push({ type: 'replace', payload: { id: rawThreadInfo.id, threadInfo: rawThreadInfo, }, }); } } if (deleteThreadIDs) { threadStoreOperations.push({ type: 'remove', payload: { ids: deleteThreadIDs, }, }); } const updatedThreadStore = processThreadStoreOperations( state, threadStoreOperations, ); const newThreadInconsistencies = stateSyncSpecs.threads.findStoreInconsistencies( action, state.threadInfos, updatedThreadStore.threadInfos, ); return { threadStore: updatedThreadStore, newThreadInconsistencies, threadStoreOperations, }; } else if (action.type === updateActivityActionTypes.success) { - const updatedThreadInfos: { [string]: LegacyRawThreadInfo } = {}; + const updatedThreadInfos: { [string]: RawThreadInfo } = {}; for (const setToUnread of action.payload.result.unfocusedToUnread) { const threadInfo = state.threadInfos[setToUnread]; - if (threadInfo && !threadInfo.currentUser.unread) { - updatedThreadInfos[setToUnread] = { - ...threadInfo, - currentUser: { - ...threadInfo.currentUser, - unread: true, - }, - }; + // TODO (atul): Try to get rid of this ridiculous branching. + if (threadInfo.minimallyEncoded) { + if (threadInfo && !threadInfo.currentUser.unread) { + updatedThreadInfos[setToUnread] = { + ...threadInfo, + currentUser: { + ...threadInfo.currentUser, + unread: true, + }, + }; + } + } else { + if (threadInfo && !threadInfo.currentUser.unread) { + updatedThreadInfos[setToUnread] = { + ...threadInfo, + currentUser: { + ...threadInfo.currentUser, + unread: true, + }, + }; + } } } + if (Object.keys(updatedThreadInfos).length === 0) { return { threadStore: state, newThreadInconsistencies: [], threadStoreOperations: [], }; } const threadStoreOperations = Object.keys(updatedThreadInfos).map(id => ({ type: 'replace', payload: { id, threadInfo: updatedThreadInfos[id], }, })); const updatedThreadStore = processThreadStoreOperations( state, threadStoreOperations, ); return { threadStore: updatedThreadStore, newThreadInconsistencies: [], threadStoreOperations, }; } else if (action.type === setThreadUnreadStatusActionTypes.started) { const { threadID, unread } = action.payload; - const updatedThreadInfo = { - ...state.threadInfos[threadID], - currentUser: { - ...state.threadInfos[threadID].currentUser, - unread, - }, - }; - const threadStoreOperations = [ - { - type: 'replace', - payload: { - id: threadID, - threadInfo: updatedThreadInfo, + const threadInfo = state.threadInfos[threadID]; + // TODO (atul): Try to get rid of this ridiculous branching. + if (threadInfo.minimallyEncoded) { + const updatedThreadInfo = { + ...threadInfo, + currentUser: { + ...threadInfo.currentUser, + unread, }, - }, - ]; - const updatedThreadStore = processThreadStoreOperations( - state, - threadStoreOperations, - ); - return { - threadStore: updatedThreadStore, - newThreadInconsistencies: [], - threadStoreOperations, - }; + }; + const threadStoreOperations = [ + { + type: 'replace', + payload: { + id: threadID, + threadInfo: updatedThreadInfo, + }, + }, + ]; + const updatedThreadStore = processThreadStoreOperations( + state, + threadStoreOperations, + ); + return { + threadStore: updatedThreadStore, + newThreadInconsistencies: [], + threadStoreOperations, + }; + } else { + const updatedThreadInfo = { + ...threadInfo, + currentUser: { + ...threadInfo.currentUser, + unread, + }, + }; + const threadStoreOperations = [ + { + type: 'replace', + payload: { + id: threadID, + threadInfo: updatedThreadInfo, + }, + }, + ]; + const updatedThreadStore = processThreadStoreOperations( + state, + threadStoreOperations, + ); + return { + threadStore: updatedThreadStore, + newThreadInconsistencies: [], + threadStoreOperations, + }; + } } else if (action.type === setThreadUnreadStatusActionTypes.success) { const { threadID, resetToUnread } = action.payload; const currentUser = state.threadInfos[threadID].currentUser; if (!resetToUnread || currentUser.unread) { return { threadStore: state, newThreadInconsistencies: [], threadStoreOperations: [], }; } - const updatedUser = { - ...currentUser, - unread: true, - }; - const updatedThread = { - ...state.threadInfos[threadID], - currentUser: updatedUser, - }; - const threadStoreOperations = [ - { - type: 'replace', - payload: { - id: threadID, - threadInfo: updatedThread, + const threadInfo = state.threadInfos[threadID]; + // TODO (atul): Try to get rid of this ridiculous branching. + if (threadInfo.minimallyEncoded) { + const updatedThread = { + ...threadInfo, + currentUser: { ...threadInfo.currentUser, unread: true }, + }; + const threadStoreOperations = [ + { + type: 'replace', + payload: { + id: threadID, + threadInfo: updatedThread, + }, }, - }, - ]; - const updatedThreadStore = processThreadStoreOperations( - state, - threadStoreOperations, - ); - return { - threadStore: updatedThreadStore, - newThreadInconsistencies: [], - threadStoreOperations, - }; + ]; + const updatedThreadStore = processThreadStoreOperations( + state, + threadStoreOperations, + ); + return { + threadStore: updatedThreadStore, + newThreadInconsistencies: [], + threadStoreOperations, + }; + } else { + const updatedThread = { + ...threadInfo, + currentUser: { ...threadInfo.currentUser, unread: true }, + }; + const threadStoreOperations = [ + { + type: 'replace', + payload: { + id: threadID, + threadInfo: updatedThread, + }, + }, + ]; + const updatedThreadStore = processThreadStoreOperations( + state, + threadStoreOperations, + ); + return { + threadStore: updatedThreadStore, + newThreadInconsistencies: [], + threadStoreOperations, + }; + } } else if (action.type === setClientDBStoreActionType) { return { threadStore: action.payload.threadStore ?? state, newThreadInconsistencies: [], threadStoreOperations: [], }; } return { threadStore: state, newThreadInconsistencies: [], threadStoreOperations: [], }; } export { reduceThreadInfos };