diff --git a/webapp/channels/src/actions/user_actions.ts b/webapp/channels/src/actions/user_actions.ts index 93973084ba..afcb9ba377 100644 --- a/webapp/channels/src/actions/user_actions.ts +++ b/webapp/channels/src/actions/user_actions.ts @@ -432,7 +432,8 @@ export function autoResetStatus(): ActionFuncAsync { } const {currentUserId} = state.entities.users; - const {data: userStatus} = await doDispatch(UserActions.getStatus(currentUserId)); + const {data} = await doDispatch(UserActions.getStatusesByIds([currentUserId])); + const userStatus = data?.[0]; if (userStatus?.status === UserStatuses.OUT_OF_OFFICE || !userStatus?.manual) { return {data: userStatus}; diff --git a/webapp/channels/src/actions/websocket_actions.jsx b/webapp/channels/src/actions/websocket_actions.jsx index 05ff6bb751..877c8bbad0 100644 --- a/webapp/channels/src/actions/websocket_actions.jsx +++ b/webapp/channels/src/actions/websocket_actions.jsx @@ -717,7 +717,7 @@ export function handleNewPostEvent(msg) { ) { myDispatch({ type: UserTypes.RECEIVED_STATUSES, - data: [{user_id: post.user_id, status: UserStatuses.ONLINE}], + data: [{[post.user_id]: UserStatuses.ONLINE}], }); } }; @@ -1265,7 +1265,7 @@ function addedNewGmUser(preference) { function handleStatusChangedEvent(msg) { dispatch({ type: UserTypes.RECEIVED_STATUSES, - data: [{user_id: msg.data.user_id, status: msg.data.status}], + data: [{[msg.data.user_id]: msg.data.status}], }); } diff --git a/webapp/channels/src/actions/websocket_actions.test.jsx b/webapp/channels/src/actions/websocket_actions.test.jsx index 8f48186615..651ddff497 100644 --- a/webapp/channels/src/actions/websocket_actions.test.jsx +++ b/webapp/channels/src/actions/websocket_actions.test.jsx @@ -507,7 +507,7 @@ describe('handleNewPostEvent', () => { expect(testStore.getActions()).toContainEqual({ type: UserTypes.RECEIVED_STATUSES, - data: [{user_id: post.user_id, status: UserStatuses.ONLINE}], + data: [{[post.user_id]: UserStatuses.ONLINE}], }); }); @@ -526,7 +526,7 @@ describe('handleNewPostEvent', () => { expect(testStore.getActions()).not.toContainEqual({ type: UserTypes.RECEIVED_STATUSES, - data: [{user_id: post.user_id, status: UserStatuses.ONLINE}], + data: [{[post.user_id]: UserStatuses.ONLINE}], }); }); @@ -556,7 +556,7 @@ describe('handleNewPostEvent', () => { expect(testStore.getActions()).not.toContainEqual({ type: UserTypes.RECEIVED_STATUSES, - data: [{user_id: post.user_id, status: UserStatuses.ONLINE}], + data: [{[post.user_id]: UserStatuses.ONLINE}], }); }); @@ -575,7 +575,7 @@ describe('handleNewPostEvent', () => { expect(testStore.getActions()).not.toContainEqual({ type: UserTypes.RECEIVED_STATUSES, - data: [{user_id: post.user_id, status: UserStatuses.ONLINE}], + data: [{[post.user_id]: UserStatuses.ONLINE}], }); }); }); diff --git a/webapp/channels/src/packages/mattermost-redux/src/action_types/users.ts b/webapp/channels/src/packages/mattermost-redux/src/action_types/users.ts index a7eb46d1d9..7726a8b4ee 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/action_types/users.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/action_types/users.ts @@ -54,8 +54,12 @@ export default keyMirror({ RECEIVED_SESSIONS: null, RECEIVED_REVOKED_SESSION: null, RECEIVED_AUDITS: null, - RECEIVED_STATUS: null, + RECEIVED_STATUSES: null, + RECEIVED_DND_END_TIMES: null, + RECEIVED_STATUSES_IS_MANUAL: null, + RECEIVED_LAST_ACTIVITIES: null, + RECEIVED_AUTOCOMPLETE_IN_CHANNEL: null, RESET_LOGOUT_STATE: null, RECEIVED_MY_USER_ACCESS_TOKEN: null, diff --git a/webapp/channels/src/packages/mattermost-redux/src/actions/users.test.ts b/webapp/channels/src/packages/mattermost-redux/src/actions/users.test.ts index f8740ced6f..4ba67e3c78 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/actions/users.test.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/actions/users.test.ts @@ -525,16 +525,27 @@ describe('Actions.Users', () => { it('getStatusesByIds', async () => { nock(Client4.getBaseRoute()). post('/users/status/ids'). - reply(200, [{user_id: TestHelper.basicUser!.id, status: 'online', manual: false, last_activity_at: 1507662212199}]); + reply(200, [{ + user_id: TestHelper.basicUser!.id, + status: 'online', + manual: false, + last_activity_at: 1507662212199, + dnd_end_time: 0, + }]); await store.dispatch(Actions.getStatusesByIds( [TestHelper.basicUser!.id], )); const statuses = store.getState().entities.users.statuses; + const dndEndTimes = store.getState().entities.users.dndEndTimes; + const lastActivity = store.getState().entities.users.lastActivity; + const isManualStatus = store.getState().entities.users.isManualStatus; - expect(statuses[TestHelper.basicUser!.id]).toBeTruthy(); - expect(Object.keys(statuses).length).toEqual(1); + expect(statuses[TestHelper.basicUser!.id]).toBe('online'); + expect(dndEndTimes[TestHelper.basicUser!.id]).toBe(0); + expect(lastActivity[TestHelper.basicUser!.id]).toBe(1507662212199); + expect(isManualStatus[TestHelper.basicUser!.id]).toBe(false); }); it('getTotalUsersStats', async () => { @@ -548,25 +559,10 @@ describe('Actions.Users', () => { expect(stats.total_users_count).toEqual(2605); }); - it('getStatus', async () => { - const user = TestHelper.basicUser; - - nock(Client4.getBaseRoute()). - get(`/users/${user!.id}/status`). - reply(200, {user_id: user!.id, status: 'online', manual: false, last_activity_at: 1507662212199}); - - await store.dispatch(Actions.getStatus( - user!.id, - )); - - const statuses = store.getState().entities.users.statuses; - expect(statuses[user!.id]).toBeTruthy(); - }); - it('setStatus', async () => { nock(Client4.getBaseRoute()). put(`/users/${TestHelper.basicUser!.id}/status`). - reply(200, OK_RESPONSE); + reply(200, {user_id: TestHelper.basicUser!.id, status: 'away'}); await store.dispatch(Actions.setStatus( {user_id: TestHelper.basicUser!.id, status: 'away'}, diff --git a/webapp/channels/src/packages/mattermost-redux/src/actions/users.ts b/webapp/channels/src/packages/mattermost-redux/src/actions/users.ts index 1501e9b894..aa67e6ea33 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/actions/users.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/actions/users.ts @@ -11,7 +11,7 @@ import type {UserProfile, UserStatus, GetFilteredUsersStatsOpts, UsersStats, Use import {UserTypes, AdminTypes} from 'mattermost-redux/action_types'; import {logError} from 'mattermost-redux/actions/errors'; import {setServerVersion, getClientConfig, getLicenseConfig} from 'mattermost-redux/actions/general'; -import {bindClientFunc, forceLogoutIfNecessary, debounce} from 'mattermost-redux/actions/helpers'; +import {bindClientFunc, forceLogoutIfNecessary} from 'mattermost-redux/actions/helpers'; import {getServerLimits} from 'mattermost-redux/actions/limits'; import {getMyPreferences} from 'mattermost-redux/actions/preferences'; import {loadRolesIfNeeded} from 'mattermost-redux/actions/roles'; @@ -22,7 +22,7 @@ import {getIsUserStatusesConfigEnabled} from 'mattermost-redux/selectors/entitie import {getServerVersion} from 'mattermost-redux/selectors/entities/general'; import {isCollapsedThreadsEnabled} from 'mattermost-redux/selectors/entities/preferences'; import {getCurrentUserId, getUsers} from 'mattermost-redux/selectors/entities/users'; -import type {DispatchFunc, ActionFuncAsync} from 'mattermost-redux/types/actions'; +import type {ActionFuncAsync} from 'mattermost-redux/types/actions'; import {isMinimumServerVersion} from 'mattermost-redux/utils/helpers'; export function generateMfaSecret(userId: string) { @@ -589,59 +589,94 @@ export function getUserByEmail(email: string) { }); } -// We create an array to hold the id's that we want to get a status for. We build our -// debounced function that will get called after a set period of idle time in which -// the array of id's will be passed to the getStatusesByIds with a cb that clears out -// the array. Helps with performance because instead of making 75 different calls for -// statuses, we are only making one call for 75 ids. -// We could maybe clean it up somewhat by storing the array of ids in redux state possbily? -let ids: string[] = []; -const debouncedGetStatusesByIds = debounce(async (dispatch: DispatchFunc) => { - dispatch(getStatusesByIds([...new Set(ids)])); -}, 20, false, () => { - ids = []; -}); -export function getStatusesByIdsBatchedDebounced(id: string) { - ids = [...ids, id]; - return debouncedGetStatusesByIds; -} - -export function getStatusesByIds(userIds: string[]) { - return bindClientFunc({ - clientFunc: Client4.getStatusesByIds, - onSuccess: UserTypes.RECEIVED_STATUSES, - params: [ - userIds, - ], - }); -} - -export function getStatus(userId: string) { - return bindClientFunc({ - clientFunc: Client4.getStatus, - onSuccess: UserTypes.RECEIVED_STATUS, - params: [ - userId, - ], - }); -} - -export function setStatus(status: UserStatus): ActionFuncAsync { +export function getStatusesByIds(userIds: Array): ActionFuncAsync { return async (dispatch, getState) => { + if (!userIds || userIds.length === 0) { + return {data: []}; + } + + let recievedStatuses: UserStatus[]; try { - await Client4.updateStatus(status); + recievedStatuses = await Client4.getStatusesByIds(userIds); } catch (error) { forceLogoutIfNecessary(error, dispatch, getState); dispatch(logError(error)); return {error}; } - dispatch({ - type: UserTypes.RECEIVED_STATUS, - data: status, - }); + const statuses: Record = {}; + const dndEndTimes: Record = {}; + const isManualStatuses: Record = {}; + const lastActivity: Record = {}; - return {data: status}; + for (const recievedStatus of recievedStatuses) { + statuses[recievedStatus.user_id] = recievedStatus?.status ?? ''; + dndEndTimes[recievedStatus.user_id] = recievedStatus?.dnd_end_time ?? 0; + isManualStatuses[recievedStatus.user_id] = recievedStatus?.manual ?? false; + lastActivity[recievedStatus.user_id] = recievedStatus?.last_activity_at ?? 0; + } + + dispatch(batchActions([ + { + type: UserTypes.RECEIVED_STATUSES, + data: statuses, + }, + { + type: UserTypes.RECEIVED_DND_END_TIMES, + data: dndEndTimes, + }, + { + type: UserTypes.RECEIVED_STATUSES_IS_MANUAL, + data: isManualStatuses, + }, + { + type: UserTypes.RECEIVED_LAST_ACTIVITIES, + data: lastActivity, + }, + ], + 'BATCHING_STATUSES', + )); + + return {data: recievedStatuses}; + }; +} + +export function setStatus(status: UserStatus): ActionFuncAsync { + return async (dispatch, getState) => { + let recievedStatus: UserStatus; + try { + recievedStatus = await Client4.updateStatus(status); + } catch (error) { + forceLogoutIfNecessary(error, dispatch, getState); + dispatch(logError(error)); + return {error}; + } + + const updatedStatus = {[recievedStatus.user_id]: recievedStatus.status}; + const dndEndTimes = {[recievedStatus.user_id]: recievedStatus?.dnd_end_time ?? 0}; + const isManualStatus = {[recievedStatus.user_id]: recievedStatus?.manual ?? false}; + const lastActivity = {[recievedStatus.user_id]: recievedStatus?.last_activity_at ?? 0}; + + dispatch(batchActions([ + { + type: UserTypes.RECEIVED_STATUSES, + data: updatedStatus, + }, + { + type: UserTypes.RECEIVED_DND_END_TIMES, + data: dndEndTimes, + }, + { + type: UserTypes.RECEIVED_STATUSES_IS_MANUAL, + data: isManualStatus, + }, + { + type: UserTypes.RECEIVED_LAST_ACTIVITIES, + data: lastActivity, + }, + ], 'BATCHING_STATUS')); + + return {data: recievedStatus}; }; } @@ -1305,7 +1340,6 @@ export default { getUser, getMe, getUserByUsername, - getStatus, getStatusesByIds, getSessions, getTotalUsersStats, diff --git a/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/users.test.ts b/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/users.test.ts index 7e88a27b8a..0279d16bf4 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/users.test.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/users.test.ts @@ -1115,3 +1115,288 @@ describe('Reducers.users', () => { }); }); }); + +describe('dndEndTimes', () => { + const initialState = {} as UsersState; + + test('should return the initial state', () => { + expect(reducer(initialState, {} as any).dndEndTimes).toEqual({}); + expect(reducer(undefined, {} as any).dndEndTimes).toEqual({}); + + const usersState1 = deepFreezeAndThrowOnMutation({ + dndEndTimes: {}, + }) as UsersState; + expect(reducer(usersState1, {} as any).dndEndTimes).toBe(usersState1.dndEndTimes); + + const usersState2 = deepFreezeAndThrowOnMutation({ + dndEndTimes: { + test_user_id: 123456789, + }, + }) as UsersState; + expect(reducer(usersState2, {} as any).dndEndTimes).toBe(usersState2.dndEndTimes); + }); + + test('should store the dnd end time', () => { + const action1 = { + type: UserTypes.RECEIVED_DND_END_TIMES, + data: { + test_user_id: 123456789, + }, + }; + expect(reducer(initialState, action1).dndEndTimes).toEqual({ + test_user_id: 123456789, + }); + + const state = deepFreezeAndThrowOnMutation({ + dndEndTimes: { + test_user_id: 123456789, + }, + }) as UsersState; + const action2 = { + type: UserTypes.RECEIVED_DND_END_TIMES, + data: { + test_user_id: 987654321, + }, + }; + expect(reducer(state, action2).dndEndTimes).toEqual({ + test_user_id: 987654321, + }); + }); + + test('should store the dnd end time for multiple users', () => { + const action1 = { + type: UserTypes.RECEIVED_DND_END_TIMES, + data: { + test_user_id: 123456789, + test_user_id_2: 987654321, + }, + }; + expect(reducer(initialState, action1).dndEndTimes).toEqual({ + test_user_id: 123456789, + test_user_id_2: 987654321, + }); + + const state = deepFreezeAndThrowOnMutation({ + dndEndTimes: { + test_user_id: 1, + test_user_id_2: 2, + test_user_id_4: 4, + }, + }) as UsersState; + const action2 = { + type: UserTypes.RECEIVED_DND_END_TIMES, + data: { + test_user_id: 10, + test_user_id_2: 20, + test_user_id_3: 30, + }, + }; + expect(reducer(state, action2).dndEndTimes).toEqual({ + test_user_id: 10, + test_user_id_2: 20, + test_user_id_3: 30, + test_user_id_4: 4, + }); + }); +}); + +describe('statuses', () => { + const initialState = {} as UsersState; + + test('should return the initial state', () => { + expect(reducer(initialState, {} as any).statuses).toEqual({}); + expect(reducer(undefined, {} as any).statuses).toEqual({}); + + const usersState1 = deepFreezeAndThrowOnMutation({ + statuses: { + test_user_id: 'online', + }, + }) as UsersState; + expect(reducer(usersState1, {} as any).statuses).toBe(usersState1.statuses); + }); + + test('should store the status', () => { + const action1 = { + type: UserTypes.RECEIVED_STATUSES, + data: { + test_user_id: 'away', + }, + }; + expect(reducer(initialState, action1).statuses).toEqual({ + test_user_id: 'away', + }); + + const state = deepFreezeAndThrowOnMutation({ + statuses: { + test_user_id: 'away', + }, + }) as UsersState; + const action2 = { + type: UserTypes.RECEIVED_STATUSES, + data: { + test_user_id: 'dnd', + }, + }; + expect(reducer(state, action2).statuses).toEqual({ + test_user_id: 'dnd', + }); + }); + + test('should store the status for multiple users', () => { + const action1 = { + type: UserTypes.RECEIVED_STATUSES, + data: { + test_user_id: 'away', + test_user_id_2: 'dnd', + }, + }; + expect(reducer(initialState, action1).statuses).toEqual({ + test_user_id: 'away', + test_user_id_2: 'dnd', + }); + + const state = deepFreezeAndThrowOnMutation({ + statuses: { + test_user_id: 'away', + test_user_id_2: 'dnd', + test_user_id_4: 'offline', + }, + }) as UsersState; + const action2 = { + type: UserTypes.RECEIVED_STATUSES, + data: { + test_user_id: 'online', + test_user_id_2: 'offline', + test_user_id_3: 'away', + }, + }; + expect(reducer(state, action2).statuses).toEqual({ + test_user_id: 'online', + test_user_id_2: 'offline', + test_user_id_3: 'away', + test_user_id_4: 'offline', + }); + }); +}); + +describe('isManualStatus', () => { + const initialState = {} as UsersState; + + test('should return the initial state', () => { + expect(reducer(initialState, {} as any).isManualStatus).toEqual({}); + expect(reducer(undefined, {} as any).isManualStatus).toEqual({}); + + const usersState1 = deepFreezeAndThrowOnMutation({ + isManualStatus: { + test_user_id: true, + }, + }) as UsersState; + expect(reducer(usersState1, {} as any).isManualStatus).toBe(usersState1.isManualStatus); + }); + + test('should store the isManualStatus', () => { + const action1 = { + type: UserTypes.RECEIVED_STATUSES_IS_MANUAL, + data: { + test_user_id: true, + }, + }; + expect(reducer(initialState, action1).isManualStatus).toEqual({ + test_user_id: true, + }); + + const state = deepFreezeAndThrowOnMutation({ + isManualStatus: { + test_user_id: false, + }, + }) as UsersState; + const action2 = { + type: UserTypes.RECEIVED_STATUSES_IS_MANUAL, + data: { + test_user_id: true, + }, + }; + expect(reducer(state, action2).isManualStatus).toEqual({ + test_user_id: true, + }); + }); +}); + +describe('lastActivity', () => { + const initialState = {} as UsersState; + + test('should return the initial state', () => { + expect(reducer(initialState, {} as any).lastActivity).toEqual({}); + expect(reducer(undefined, {} as any).lastActivity).toEqual({}); + + const state = deepFreezeAndThrowOnMutation({ + lastActivity: { + test_user_id: 123456789, + }, + }) as UsersState; + expect(reducer(state, {} as any).lastActivity).toBe(state.lastActivity); + }); + + test('should store the last activity', () => { + const action1 = { + type: UserTypes.RECEIVED_LAST_ACTIVITIES, + data: { + test_user_id: 123456789, + }, + }; + expect(reducer(initialState, action1).lastActivity).toEqual({ + test_user_id: 123456789, + }); + + const state = deepFreezeAndThrowOnMutation({ + lastActivity: { + test_user_id: 123456789, + }, + }) as UsersState; + const action2 = { + type: UserTypes.RECEIVED_LAST_ACTIVITIES, + data: { + test_user_id: 987654321, + }, + }; + expect(reducer(state, action2).lastActivity).toEqual({ + test_user_id: 987654321, + }); + }); + + test('should store the last activity for multiple users', () => { + const action1 = { + type: UserTypes.RECEIVED_LAST_ACTIVITIES, + data: { + test_user_id: 123456789, + test_user_id_2: 987654321, + }, + }; + expect(reducer(initialState, action1).lastActivity).toEqual({ + test_user_id: 123456789, + test_user_id_2: 987654321, + }); + + const state = deepFreezeAndThrowOnMutation({ + lastActivity: { + test_user_id: 1, + test_user_id_2: 2, + test_user_id_4: 4, + }, + }) as UsersState; + const action2 = { + type: UserTypes.RECEIVED_LAST_ACTIVITIES, + data: { + test_user_id: 10, + test_user_id_2: 20, + test_user_id_3: 30, + }, + }; + expect(reducer(state, action2).lastActivity).toEqual({ + test_user_id: 10, + test_user_id_2: 20, + test_user_id_3: 30, + test_user_id_4: 4, + }); + }); +}); diff --git a/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/users.ts b/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/users.ts index b3a4aac89a..9c96f3eefd 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/users.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/reducers/entities/users.ts @@ -6,7 +6,7 @@ import type {AnyAction} from 'redux'; import {combineReducers} from 'redux'; import type {Team} from '@mattermost/types/teams'; -import type {UserAccessToken, UserProfile, UserStatus, UsersState} from '@mattermost/types/users'; +import type {UserProfile, UsersState} from '@mattermost/types/users'; import type {IDMappedObjects, RelationOneToManyUnique, RelationOneToOne} from '@mattermost/types/utilities'; import {UserTypes, ChannelTypes} from 'mattermost-redux/action_types'; @@ -82,7 +82,7 @@ function removeProfileFromSet(state: RelationOneToManyUnique, }; } -function currentUserId(state = '', action: AnyAction) { +function currentUserId(state: UsersState['currentUserId'] = '', action: AnyAction) { switch (action.type) { case UserTypes.RECEIVED_ME: { const data = action.data; @@ -102,7 +102,7 @@ function currentUserId(state = '', action: AnyAction) { return state; } -function mySessions(state: Array<{id: string}> = [], action: AnyAction) { +function mySessions(state: UsersState['mySessions'] = [], action: AnyAction) { switch (action.type) { case UserTypes.RECEIVED_SESSIONS: return [...action.data]; @@ -212,7 +212,7 @@ function receiveUserProfile(state: IDMappedObjects, received: UserP }; } -function profiles(state: IDMappedObjects = {}, action: AnyAction) { +function profiles(state: UsersState['profiles'] = {}, action: AnyAction) { switch (action.type) { case UserTypes.RECEIVED_ME: case UserTypes.RECEIVED_PROFILE: { @@ -490,29 +490,10 @@ function profilesNotInGroup(state: UsersState['profilesNotInGroup'] = {}, action } } -function addToState(state: Record, key: string, value: T): Record { - if (state[key] === value) { - return state; - } - - return { - ...state, - [key]: value, - }; -} - -function dndEndTimes(state: RelationOneToOne = {}, action: AnyAction) { +function dndEndTimes(state: UsersState['dndEndTimes'] = {}, action: AnyAction) { switch (action.type) { - case UserTypes.RECEIVED_STATUS: { - const userId = action.data.user_id; - const endTime = action.data.dnd_end_time; - - return addToState(state, userId, endTime); - } - case UserTypes.RECEIVED_STATUSES: { - const userStatuses: UserStatus[] = action.data; - - return userStatuses.reduce((nextState, userStatus) => addToState(nextState, userStatus.user_id, userStatus.dnd_end_time || 0), state); + case UserTypes.RECEIVED_DND_END_TIMES: { + return {...state, ...action.data}; } case UserTypes.PROFILE_NO_LONGER_VISIBLE: { if (state[action.data.user_id]) { @@ -532,16 +513,8 @@ function dndEndTimes(state: RelationOneToOne = {}, action: function statuses(state: RelationOneToOne = {}, action: AnyAction) { switch (action.type) { - case UserTypes.RECEIVED_STATUS: { - const userId = action.data.user_id; - const status = action.data.status; - - return addToState(state, userId, status); - } case UserTypes.RECEIVED_STATUSES: { - const userStatuses: UserStatus[] = action.data; - - return userStatuses.reduce((nextState, userStatus) => addToState(nextState, userStatus.user_id, userStatus.status), state); + return {...state, ...action.data}; } case UserTypes.PROFILE_NO_LONGER_VISIBLE: { @@ -562,16 +535,8 @@ function statuses(state: RelationOneToOne = {}, action: Any function isManualStatus(state: RelationOneToOne = {}, action: AnyAction) { switch (action.type) { - case UserTypes.RECEIVED_STATUS: { - const userId = action.data.user_id; - const manual = action.data.manual; - - return addToState(state, userId, manual); - } - case UserTypes.RECEIVED_STATUSES: { - const userStatuses: UserStatus[] = action.data; - - return userStatuses.reduce((nextState, userStatus) => addToState(nextState, userStatus.user_id, userStatus.manual || false), state); + case UserTypes.RECEIVED_STATUSES_IS_MANUAL: { + return {...state, ...action.data}; } case UserTypes.PROFILE_NO_LONGER_VISIBLE: { @@ -590,7 +555,7 @@ function isManualStatus(state: RelationOneToOne = {}, acti } } -function myUserAccessTokens(state: Record = {}, action: AnyAction) { +function myUserAccessTokens(state: UsersState['myUserAccessTokens'] = {}, action: AnyAction) { switch (action.type) { case UserTypes.RECEIVED_MY_USER_ACCESS_TOKEN: { const nextState = {...state}; @@ -671,20 +636,8 @@ function filteredStats(state: UsersState['filteredStats'] = {}, action: AnyActio function lastActivity(state: UsersState['lastActivity'] = {}, action: AnyAction) { switch (action.type) { - case UserTypes.RECEIVED_STATUS: { - const nextState = Object.assign({}, state); - nextState[action.data.user_id] = action.data.last_activity_at; - - return nextState; - } - case UserTypes.RECEIVED_STATUSES: { - const nextState = Object.assign({}, state); - - for (const s of action.data) { - nextState[s.user_id] = s.last_activity_at; - } - - return nextState; + case UserTypes.RECEIVED_LAST_ACTIVITIES: { + return {...state, ...action.data}; } case UserTypes.LOGOUT_SUCCESS: return {};