diff --git a/lib/reducers/calendar-filters-reducer.js b/lib/reducers/calendar-filters-reducer.js index 3cb82cd7f..a4f366ec5 100644 --- a/lib/reducers/calendar-filters-reducer.js +++ b/lib/reducers/calendar-filters-reducer.js @@ -1,205 +1,211 @@ // @flow import { updateCalendarCommunityFilter, clearCalendarCommunityFilter, } from '../actions/community-actions.js'; import { siweAuthActionTypes } from '../actions/siwe-actions.js'; import { newThreadActionTypes, joinThreadActionTypes, leaveThreadActionTypes, deleteThreadActionTypes, } from '../actions/thread-actions.js'; import { + keyserverAuthActionTypes, logOutActionTypes, deleteKeyserverAccountActionTypes, logInActionTypes, registerActionTypes, + tempIdentityLoginActionTypes, } from '../actions/user-actions.js'; import { filteredThreadIDs, nonThreadCalendarFilters, nonExcludeDeletedCalendarFilters, } from '../selectors/calendar-filter-selectors.js'; import { threadInFilterList } from '../shared/thread-utils.js'; import { updateSpecs } from '../shared/updates/update-specs.js'; import { type CalendarFilter, defaultCalendarFilters, updateCalendarThreadFilter, clearCalendarThreadFilter, setCalendarDeletedFilter, calendarThreadFilterTypes, } from '../types/filter-types.js'; import type { BaseAction } from '../types/redux-types.js'; import { fullStateSyncActionType, incrementalStateSyncActionType, } from '../types/socket-types.js'; import type { RawThreadInfos, ThreadStore } from '../types/thread-types.js'; import { type ClientUpdateInfo, processUpdatesActionType, } from '../types/update-types.js'; import { extractKeyserverIDFromID, setNewSessionActionType, } from '../utils/action-utils.js'; import { filterThreadIDsBelongingToCommunity } from '../utils/drawer-utils.react.js'; export default function reduceCalendarFilters( state: $ReadOnlyArray, action: BaseAction, threadStore: ThreadStore, ): $ReadOnlyArray { if ( + action.type === tempIdentityLoginActionTypes.success || action.type === logOutActionTypes.success || action.type === deleteKeyserverAccountActionTypes.success || action.type === logInActionTypes.success || action.type === siweAuthActionTypes.success || action.type === registerActionTypes.success || (action.type === setNewSessionActionType && action.payload.sessionChange.cookieInvalidated) ) { return defaultCalendarFilters; + } else if (action.type === keyserverAuthActionTypes.success) { + const keyserverIDs = Object.keys(action.payload.updatesCurrentAsOf); + return removeKeyserverThreadIDsFromFilterList(state, keyserverIDs); } else if (action.type === updateCalendarThreadFilter) { const nonThreadFilters = nonThreadCalendarFilters(state); return [ ...nonThreadFilters, { type: calendarThreadFilterTypes.THREAD_LIST, threadIDs: action.payload.threadIDs, }, ]; } else if (action.type === clearCalendarThreadFilter) { return nonThreadCalendarFilters(state); } else if (action.type === setCalendarDeletedFilter) { const otherFilters = nonExcludeDeletedCalendarFilters(state); if (action.payload.includeDeleted && otherFilters.length === state.length) { // Attempting to remove NOT_DELETED filter, but it doesn't exist return state; } else if (action.payload.includeDeleted) { // Removing NOT_DELETED filter return otherFilters; } else if (otherFilters.length < state.length) { // Attempting to add NOT_DELETED filter, but it already exists return state; } else { // Adding NOT_DELETED filter return [...state, { type: calendarThreadFilterTypes.NOT_DELETED }]; } } else if ( action.type === newThreadActionTypes.success || action.type === joinThreadActionTypes.success || action.type === leaveThreadActionTypes.success || action.type === deleteThreadActionTypes.success || action.type === processUpdatesActionType ) { return updateFilterListFromUpdateInfos( state, action.payload.updatesResult.newUpdates, ); } else if (action.type === incrementalStateSyncActionType) { return updateFilterListFromUpdateInfos( state, action.payload.updatesResult.newUpdates, ); } else if (action.type === fullStateSyncActionType) { return removeDeletedThreadIDsFromFilterList( state, action.payload.threadInfos, ); } else if (action.type === updateCalendarCommunityFilter) { const nonThreadFilters = nonThreadCalendarFilters(state); const threadIDs = Array.from( filterThreadIDsBelongingToCommunity( action.payload, threadStore.threadInfos, ), ); return [ ...nonThreadFilters, { type: calendarThreadFilterTypes.THREAD_LIST, threadIDs, }, ]; } else if (action.type === clearCalendarCommunityFilter) { const nonThreadFilters = nonThreadCalendarFilters(state); return nonThreadFilters; } return state; } function updateFilterListFromUpdateInfos( state: $ReadOnlyArray, updateInfos: $ReadOnlyArray, ): $ReadOnlyArray { const currentlyFilteredIDs: ?$ReadOnlySet = filteredThreadIDs(state); if (!currentlyFilteredIDs) { return state; } const newFilteredThreadIDs = updateInfos.reduce( (reducedFilteredThreadIDs, update) => { const { reduceCalendarThreadFilters } = updateSpecs[update.type]; return reduceCalendarThreadFilters ? reduceCalendarThreadFilters(reducedFilteredThreadIDs, update) : reducedFilteredThreadIDs; }, currentlyFilteredIDs, ); if (currentlyFilteredIDs !== newFilteredThreadIDs) { return [ ...nonThreadCalendarFilters(state), { type: 'threads', threadIDs: [...newFilteredThreadIDs] }, ]; } return state; } function filterThreadIDsInFilterList( state: $ReadOnlyArray, filterCondition: (threadID: string) => boolean, ): $ReadOnlyArray { const currentlyFilteredIDs = filteredThreadIDs(state); if (!currentlyFilteredIDs) { return state; } const filtered = [...currentlyFilteredIDs].filter(filterCondition); if (filtered.length < currentlyFilteredIDs.size) { return [ ...nonThreadCalendarFilters(state), { type: 'threads', threadIDs: filtered }, ]; } return state; } function removeDeletedThreadIDsFromFilterList( state: $ReadOnlyArray, threadInfos: RawThreadInfos, ): $ReadOnlyArray { const filterCondition = (threadID: string) => threadInFilterList(threadInfos[threadID]); return filterThreadIDsInFilterList(state, filterCondition); } function removeKeyserverThreadIDsFromFilterList( state: $ReadOnlyArray, keyserverIDs: $ReadOnlyArray, ): $ReadOnlyArray { const keyserverIDsSet = new Set(keyserverIDs); const filterCondition = (threadID: string) => !keyserverIDsSet.has(extractKeyserverIDFromID(threadID)); return filterThreadIDsInFilterList(state, filterCondition); } export { removeDeletedThreadIDsFromFilterList, removeKeyserverThreadIDsFromFilterList, }; diff --git a/lib/reducers/calendar-filters-reducer.test.js b/lib/reducers/calendar-filters-reducer.test.js index f3db9178f..5acebcfa9 100644 --- a/lib/reducers/calendar-filters-reducer.test.js +++ b/lib/reducers/calendar-filters-reducer.test.js @@ -1,173 +1,221 @@ // @flow -import { +import reduceCalendarFilters, { removeDeletedThreadIDsFromFilterList, removeKeyserverThreadIDsFromFilterList, } from './calendar-filters-reducer.js'; +import { keyserverAuthActionTypes } from '../actions/user-actions.js'; +import type { RawMessageInfo } from '../types/message-types.js'; import type { ThreadStore } from '../types/thread-types'; const calendarFilters = [ { type: 'threads', threadIDs: ['256|1', '256|83815'] }, ]; const threadStore: ThreadStore = { threadInfos: { '256|1': { id: '256|1', type: 12, name: 'GENESIS', description: '', color: '648caa', creationTime: 1689091732528, parentThreadID: null, repliesCount: 0, containingThreadID: null, community: null, pinnedCount: 0, minimallyEncoded: true, members: [ { id: '256', role: '256|83796', permissions: '3f73ff', isSender: true, minimallyEncoded: true, }, { id: '83810', role: '256|83795', permissions: '3', isSender: false, minimallyEncoded: true, }, ], roles: { '256|83795': { id: '256|83795', name: 'Members', permissions: ['000', '010', '005', '015', '0a7'], isDefault: true, minimallyEncoded: true, }, '256|83796': { id: '256|83796', name: 'Admins', permissions: ['000', '010', '005', '015', '0a7'], isDefault: false, minimallyEncoded: true, }, }, currentUser: { role: '256|83795', permissions: '3', subscription: { home: true, pushNotifs: true, }, unread: false, minimallyEncoded: true, }, }, '256|83815': { id: '256|83815', type: 7, name: '', description: 'This is your private chat, where you can set reminders and jot notes in private!', color: '57697f', creationTime: 1689248242797, parentThreadID: '256|1', repliesCount: 0, containingThreadID: '256|1', community: '256|1', pinnedCount: 0, minimallyEncoded: true, members: [ { id: '256', role: null, permissions: '2c7fff', isSender: false, minimallyEncoded: true, }, { id: '83810', role: '256|83816', permissions: '3026f', isSender: true, minimallyEncoded: true, }, ], roles: { '256|83816': { id: '256|83816', name: 'Members', permissions: ['000', '010', '005', '015', '0a7'], isDefault: true, minimallyEncoded: true, }, }, currentUser: { role: null, permissions: '3026f', subscription: { home: true, pushNotifs: true, }, unread: false, minimallyEncoded: true, }, }, }, }; describe('removeDeletedThreadIDsFromFilterList', () => { it('Removes threads the user is not a member of anymore', () => { expect( removeDeletedThreadIDsFromFilterList( calendarFilters, threadStore.threadInfos, ), ).toEqual([{ type: 'threads', threadIDs: ['256|1'] }]); }); }); const threadIDsToStay = [ '256|1', '256|2', '200|4', '300|5', '300|6', '256|100', ]; const keyserverToRemove1 = '100'; const keyserverToRemove2 = '400'; const threadIDsToRemove = [ keyserverToRemove1 + '|3', keyserverToRemove1 + '|7', keyserverToRemove2 + '|8', ]; const calendarFiltersState = [ { type: 'not_deleted' }, { type: 'threads', threadIDs: [...threadIDsToStay, ...threadIDsToRemove], }, ]; describe('removeKeyserverThreadIDsFromFilterList', () => { it('Removes threads from the given keyserver', () => { expect( removeKeyserverThreadIDsFromFilterList(calendarFiltersState, [ keyserverToRemove1, keyserverToRemove2, ]), ).toEqual([ { type: 'not_deleted' }, { type: 'threads', threadIDs: threadIDsToStay, }, ]); }); }); + +describe('reduceCalendarFilters', () => { + it('Removes from filters thread ids of the keyservers that were logged into', () => { + const messageInfos: RawMessageInfo[] = []; + const payload = { + currentUserInfo: { id: '5', username: 'me' }, + threadInfos: {}, + messagesResult: { + currentAsOf: {}, + messageInfos, + truncationStatus: {}, + watchedIDsAtRequestTime: [], + }, + userInfos: [], + calendarResult: { + rawEntryInfos: [], + calendarQuery: { startDate: '0', endDate: '0', filters: [] }, + }, + // only updatesCurrentAsOf is relevant to this test + updatesCurrentAsOf: { [keyserverToRemove1]: 5, [keyserverToRemove2]: 5 }, + logInActionSource: 'SOCKET_AUTH_ERROR_RESOLUTION_ATTEMPT', + }; + + expect( + reduceCalendarFilters( + calendarFiltersState, + { + type: keyserverAuthActionTypes.success, + payload, + loadingInfo: { + customKeyName: null, + trackMultipleRequests: false, + fetchIndex: 1, + }, + }, + { threadInfos: {} }, + ), + ).toEqual([ + { type: 'not_deleted' }, + { + type: 'threads', + threadIDs: threadIDsToStay, + }, + ]); + }); +});