diff --git a/lib/reducers/thread-activity-reducer.js b/lib/reducers/thread-activity-reducer.js index 90d96c949..12fcdfade 100644 --- a/lib/reducers/thread-activity-reducer.js +++ b/lib/reducers/thread-activity-reducer.js @@ -1,103 +1,118 @@ // @flow import invariant from 'invariant'; import { messageStorePruneActionType } from '../actions/message-actions.js'; import { changeThreadMemberRolesActionTypes, changeThreadSettingsActionTypes, deleteCommunityRoleActionTypes, deleteThreadActionTypes, joinThreadActionTypes, leaveThreadActionTypes, modifyCommunityRoleActionTypes, newThreadActionTypes, removeUsersFromThreadActionTypes, } from '../actions/thread-actions.js'; import { logOutActionTypes, deleteKeyserverAccountActionTypes, + deleteAccountActionTypes, } from '../actions/user-actions.js'; +import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js'; import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js'; import type { BaseAction } from '../types/redux-types.js'; import { incrementalStateSyncActionType } from '../types/socket-types.js'; import type { ThreadActivityStore } from '../types/thread-activity-types.js'; import { updateThreadLastNavigatedActionType } from '../types/thread-activity-types.js'; import { updateTypes } from '../types/update-types-enum.js'; import type { ClientUpdateInfo } from '../types/update-types.js'; import { processUpdatesActionType } from '../types/update-types.js'; function reduceThreadActivity( state: ThreadActivityStore, action: BaseAction, ): ThreadActivityStore { if (action.type === updateThreadLastNavigatedActionType) { const { threadID, time } = action.payload; const updatedThreadActivityStore = { ...state, [threadID]: { ...state[threadID], lastNavigatedTo: time, }, }; return updatedThreadActivityStore; } else if (action.type === messageStorePruneActionType) { const now = Date.now(); let updatedThreadActivityStore = { ...state }; for (const threadID: string of action.payload.threadIDs) { updatedThreadActivityStore = { ...updatedThreadActivityStore, [threadID]: { ...updatedThreadActivityStore[threadID], lastPruned: now, }, }; } return updatedThreadActivityStore; } 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 state; } const deleteThreadUpdates = newUpdates.filter( (update: ClientUpdateInfo) => update.type === updateTypes.DELETE_THREAD, ); if (deleteThreadUpdates.length === 0) { return state; } let updatedState = { ...state }; for (const update: ClientUpdateInfo of deleteThreadUpdates) { invariant( update.type === updateTypes.DELETE_THREAD, 'update must be of type DELETE_THREAD', ); const { [update.threadID]: _, ...stateSansRemovedThread } = updatedState; updatedState = stateSansRemovedThread; } + return updatedState; + } else if (action.type === deleteKeyserverAccountActionTypes.success) { + let updatedState = { ...state }; + const keyserverIDsSet = new Set(action.payload.keyserverIDs); + + for (const threadID in state) { + if (!keyserverIDsSet.has(extractKeyserverIDFromID(threadID))) { + continue; + } + const { [threadID]: _, ...stateSansRemovedThread } = updatedState; + updatedState = stateSansRemovedThread; + } + return updatedState; } else if ( action.type === logOutActionTypes.success || - action.type === deleteKeyserverAccountActionTypes.success || + action.type === deleteAccountActionTypes.success || (action.type === setNewSessionActionType && action.payload.sessionChange.cookieInvalidated) ) { return {}; } return state; } export { reduceThreadActivity }; diff --git a/lib/reducers/thread-activity-reducer.test.js b/lib/reducers/thread-activity-reducer.test.js index bf03c7b40..0488872d7 100644 --- a/lib/reducers/thread-activity-reducer.test.js +++ b/lib/reducers/thread-activity-reducer.test.js @@ -1,69 +1,111 @@ // @flow import { reduceThreadActivity } from './thread-activity-reducer.js'; +import { deleteKeyserverAccountActionTypes } from '../actions/user-actions.js'; import { updateThreadLastNavigatedActionType } from '../types/thread-activity-types.js'; // NOTE: These unit tests were generated by GitHub Copilot. describe('reduceThreadActivity', () => { test('updates the lastNavigatedTo time for a thread', () => { const initialState = { thread1: { lastNavigatedTo: 1639522314170, lastPruned: 1639522314170, }, thread2: { lastNavigatedTo: 1639522314170, lastPruned: 1639522314170, }, }; const action = { type: updateThreadLastNavigatedActionType, payload: { threadID: 'thread1', time: 1639522317443, }, }; const expectedState = { thread1: { lastNavigatedTo: 1639522317443, lastPruned: 1639522314170, }, thread2: { lastNavigatedTo: 1639522314170, lastPruned: 1639522314170, }, }; const result = reduceThreadActivity(initialState, action); expect(result).toEqual(expectedState); }); test('returns the initial state if the action type is not recognized', () => { const initialState = { thread1: { lastNavigatedTo: 1639522314170, lastPruned: 1639522314170, }, thread2: { lastNavigatedTo: 1639522314170, lastPruned: 1639522314170, }, }; const action = { type: 'UPDATE_REPORTS_ENABLED', payload: {}, }; const expectedState = { thread1: { lastNavigatedTo: 1639522314170, lastPruned: 1639522314170, }, thread2: { lastNavigatedTo: 1639522314170, lastPruned: 1639522314170, }, }; const result = reduceThreadActivity(initialState, action); expect(result).toEqual(expectedState); }); + + test('removes threads of keyserver the user has disconnected from', () => { + const keyserver1 = '100'; + const keyserver2 = '200'; + const keyserver3 = '300'; + const threads1 = { + [keyserver1 + '|1']: { lastNavigatedTo: 1, lastPruned: 1 }, + [keyserver1 + '|2']: { lastNavigatedTo: 1, lastPruned: 1 }, + }; + const threads2 = { + [keyserver2 + '|1']: { lastNavigatedTo: 1, lastPruned: 1 }, + [keyserver2 + '|2']: { lastNavigatedTo: 1, lastPruned: 1 }, + }; + const threads3 = { + [keyserver3 + '|1']: { lastNavigatedTo: 1, lastPruned: 1 }, + [keyserver3 + '|2']: { lastNavigatedTo: 1, lastPruned: 1 }, + }; + + const threads = { ...threads1, ...threads2, ...threads3 }; + const action = { + type: deleteKeyserverAccountActionTypes.success, + payload: { + currentUserInfo: { anonymous: true }, + preRequestUserState: { + cookiesAndSessions: {}, + currentUserInfo: { + id: '83810', + username: 'user1', + }, + }, + keyserverIDs: [keyserver1, keyserver2], + }, + loadingInfo: { + fetchIndex: 1, + trackMultipleRequests: false, + customKeyName: undefined, + }, + }; + + expect(reduceThreadActivity(threads, action)).toEqual(threads3); + }); });