From fefca512c8fbf1d15ed7832e8f9a209b11bcffc6 Mon Sep 17 00:00:00 2001 From: Safouen Turki Date: Thu, 4 May 2023 16:09:27 +0100 Subject: [PATCH] MM-28338 Fix Combined system messages (#22608) * Web App -> Monorepo * Update message data * update snapshots * update tests * update tests, add null filter for userIds. * Revert "update snapshots" This reverts commit 4b3f5b192d355866bca978cfc708352bb5218694. * Remove not related tests * Update post_list * Update test (removing usernames for UserIds) * Update webapp/channels/src/packages/mattermost-redux/src/utils/post_list.ts better readability. Co-authored-by: Harrison Healey * remove unnecessary tests. --------- Co-authored-by: Harrison Healey Co-authored-by: Mattermost Build --- .../combined_system_message.test.tsx.snap | 153 ++-- .../combined_system_message.test.tsx | 72 +- .../combined_system_message.tsx | 26 +- .../src/utils/post_list.test.ts | 792 ++++++------------ .../mattermost-redux/src/utils/post_list.ts | 153 +--- webapp/platform/types/src/posts.ts | 6 + 6 files changed, 455 insertions(+), 747 deletions(-) diff --git a/webapp/channels/src/components/post_view/combined_system_message/__snapshots__/combined_system_message.test.tsx.snap b/webapp/channels/src/components/post_view/combined_system_message/__snapshots__/combined_system_message.test.tsx.snap index 0e114bba2e..2dfeac790f 100644 --- a/webapp/channels/src/components/post_view/combined_system_message/__snapshots__/combined_system_message.test.tsx.snap +++ b/webapp/channels/src/components/post_view/combined_system_message/__snapshots__/combined_system_message.test.tsx.snap @@ -147,10 +147,10 @@ exports[`components/post_view/CombinedSystemMessage should match snapshot, "remo `; -exports[`components/post_view/CombinedSystemMessage should match snapshot, combining users removed from channel by all actors 1`] = ` +exports[`components/post_view/CombinedSystemMessage should render messages in chronological order 1`] = ` -
-
-`; - -exports[`components/post_view/CombinedSystemMessage should match snapshot, when current user is removed from then rejoined the channel 1`] = ` - - -
- +
+ +
+ +
+ +
+
diff --git a/webapp/channels/src/components/post_view/combined_system_message/combined_system_message.test.tsx b/webapp/channels/src/components/post_view/combined_system_message/combined_system_message.test.tsx index 3589533140..de0eb3df73 100644 --- a/webapp/channels/src/components/post_view/combined_system_message/combined_system_message.test.tsx +++ b/webapp/channels/src/components/post_view/combined_system_message/combined_system_message.test.tsx @@ -61,25 +61,6 @@ describe('components/post_view/CombinedSystemMessage', () => { expect(wrapper).toMatchSnapshot(); }); - test('should match snapshot, combining users removed from channel by all actors', () => { - const allUserIds = ['current_user_id', 'other_user_id_1', 'removed_user_id_1', 'removed_user_id_2']; - const messageData = [{ - actorId: 'current_user_id', - postType: Posts.POST_TYPES.REMOVE_FROM_CHANNEL, - userIds: ['removed_user_id_1'], - }, { - actorId: 'other_user_id_1', - postType: Posts.POST_TYPES.REMOVE_FROM_CHANNEL, - userIds: ['removed_user_id_2'], - }]; - const props = {...baseProps, messageData, allUserIds}; - const wrapper = shallowWithIntl( - , - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when join leave messages are turned off', () => { const wrapper = shallowWithIntl( { expect(wrapper).toMatchSnapshot(); }); - test('should match snapshot, when current user is removed from then rejoined the channel', () => { - const allUserIds = ['current_user_id', 'other_user_id_1', 'removed_user_id_1', 'removed_user_id_2']; - const messageData = [{ - actorId: '', - postType: Posts.POST_TYPES.JOIN_CHANNEL, - userIds: ['current_user_id'], - }, { - actorId: 'current_user_id', - postType: Posts.POST_TYPES.REMOVE_FROM_CHANNEL, - userIds: ['removed_user_id_1', 'removed_user_id_2'], - }, { - actorId: 'other_user_id_1', - postType: Posts.POST_TYPES.REMOVE_FROM_CHANNEL, - userIds: ['removed_user_id_2', 'current_user_id'], - }]; - const props = {...baseProps, messageData, allUserIds}; - const wrapper = shallowWithIntl( - , - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should call getMissingProfilesByIds and/or getMissingProfilesByUsernames on loadUserProfiles', () => { const props = { ...baseProps, @@ -164,4 +122,34 @@ describe('components/post_view/CombinedSystemMessage', () => { expect(props.actions.getMissingProfilesByUsernames).toHaveBeenCalledTimes(1); expect(props.actions.getMissingProfilesByUsernames).toHaveBeenCalledWith(['user1']); }); + test('should render messages in chronological order', () => { + const allUserIds = ['current_user_id', 'other_user_id_1', 'user_id_1', 'user_id_2', 'join_last']; + const messageData = [{ + actorId: 'current_user_id', + postType: Posts.POST_TYPES.REMOVE_FROM_CHANNEL, + userIds: ['removed_user_id_1'], + }, { + actorId: 'other_user_id_1', + postType: Posts.POST_TYPES.ADD_TO_CHANNEL, + userIds: ['removed_user_id_2'], + }, { + actorId: 'other_user_id_1', + postType: Posts.POST_TYPES.REMOVE_FROM_CHANNEL, + userIds: ['removed_user_id_2'], + }, { + actorId: 'user_id_1', + postType: Posts.POST_TYPES.ADD_TO_CHANNEL, + userIds: ['user_id_2'], + }, { + actorId: 'join_last', + postType: Posts.POST_TYPES.JOIN_CHANNEL, + userIds: [''], + }]; + const props = {...baseProps, messageData, allUserIds}; + const wrapper = shallowWithIntl( + , + ); + + expect(wrapper).toMatchSnapshot(); + }); }); diff --git a/webapp/channels/src/components/post_view/combined_system_message/combined_system_message.tsx b/webapp/channels/src/components/post_view/combined_system_message/combined_system_message.tsx index 3a6dc6ec12..5d90c13c50 100644 --- a/webapp/channels/src/components/post_view/combined_system_message/combined_system_message.tsx +++ b/webapp/channels/src/components/post_view/combined_system_message/combined_system_message.tsx @@ -238,30 +238,32 @@ export class CombinedSystemMessage extends React.PureComponent { return usernames; }; - getUsernamesByIds = (userIds: string[] = []): string[] => { + getUsernamesByIds = (userIds: string | string[] = []): string[] => { + const userIdsArray = Array.isArray(userIds) ? userIds : [userIds]; const {currentUserId, currentUsername} = this.props; const allUsernames = this.getAllUsernames(); const {formatMessage} = this.props.intl; const someone = formatMessage({id: t('channel_loader.someone'), defaultMessage: 'Someone'}); - const usernames = userIds. + const usernames = userIdsArray. filter((userId) => { return userId !== currentUserId && userId !== currentUsername; }). map((userId) => { return allUsernames[userId] ? `@${allUsernames[userId]}` : someone; - }).filter((username) => { + }). + filter((username) => { return username && username !== ''; }); - if (userIds.includes(currentUserId)) { + if (userIdsArray.includes(currentUserId)) { usernames.unshift(allUsernames[currentUserId]); - } else if (userIds.includes(currentUsername)) { + } else if (userIdsArray.includes(currentUsername)) { usernames.unshift(allUsernames[currentUsername]); } - return usernames; + return Array.from(new Set(usernames)); }; renderFormattedMessage(postType: string, userIds: string[], actorId?: string): JSX.Element { @@ -328,7 +330,6 @@ export class CombinedSystemMessage extends React.PureComponent { ); } - render(): JSX.Element { const { currentUserId, @@ -336,7 +337,6 @@ export class CombinedSystemMessage extends React.PureComponent { } = this.props; const content = []; - const removedUserIds = []; for (const message of messageData) { const { postType, @@ -356,19 +356,9 @@ export class CombinedSystemMessage extends React.PureComponent { } } - if (postType === REMOVE_FROM_CHANNEL) { - removedUserIds.push(...userIds); - continue; - } - content.push(this.renderMessage(postType, userIds, actorId)); } - if (removedUserIds.length > 0) { - const uniqueRemovedUserIds = removedUserIds.filter((id, index, arr) => arr.indexOf(id) === index); - content.push(this.renderMessage(REMOVE_FROM_CHANNEL, uniqueRemovedUserIds, currentUserId)); - } - return ( {content} diff --git a/webapp/channels/src/packages/mattermost-redux/src/utils/post_list.test.ts b/webapp/channels/src/packages/mattermost-redux/src/utils/post_list.test.ts index b04224f62b..2c8d12c461 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/utils/post_list.test.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/utils/post_list.test.ts @@ -2,7 +2,7 @@ // See LICENSE.txt for license information. import {GlobalState} from '@mattermost/types/store'; -import {Post} from '@mattermost/types/posts'; +import {ActivityEntry, Post} from '@mattermost/types/posts'; import deepFreeze from 'mattermost-redux/utils/deep_freeze'; import {getPreferenceKey} from 'mattermost-redux/utils/preference_utils'; import {Posts, Preferences} from '../constants'; @@ -11,7 +11,6 @@ import TestHelper from '../../test/test_helper'; import { COMBINED_USER_ACTIVITY, combineUserActivitySystemPost, - comparePostTypes, DATE_LINE, getDateForDateLine, getFirstPostId, @@ -23,7 +22,7 @@ import { makeCombineUserActivityPosts, makeFilterPostsAndAddSeparators, makeGenerateCombinedPost, - postTypePriority, + extractUserActivityData, START_OF_NEW_MESSAGES, } from './post_list'; @@ -1014,8 +1013,11 @@ describe('makeGenerateCombinedPost', () => { 'bill joined the channel.', ], user_activity: { - allUserIds: ['user2', 'user3', 'user1'], - allUsernames: [], + allUserIds: ['user1', 'user3', 'user2'], + allUsernames: [ + 'alice', + 'joe', + ], messageData: [ { postType: Posts.POST_TYPES.JOIN_CHANNEL, @@ -1023,18 +1025,18 @@ describe('makeGenerateCombinedPost', () => { }, { postType: Posts.POST_TYPES.ADD_TO_CHANNEL, - userIds: ['user2', 'user3'], + userIds: ['user3', 'user2'], actorId: 'user1', }, ], }, }, - system_post_ids: ['post1', 'post2', 'post3'], + system_post_ids: ['post3', 'post2', 'post1'], type: Posts.POST_TYPES.COMBINED_USER_ACTIVITY, user_activity_posts: [ - state.entities.posts.posts.post1, - state.entities.posts.posts.post2, state.entities.posts.posts.post3, + state.entities.posts.posts.post2, + state.entities.posts.posts.post1, ], user_id: '', metadata: {}, @@ -1109,7 +1111,7 @@ describe('makeGenerateCombinedPost', () => { }; generateCombinedPost(state, initialCombinedId); - expect((generateCombinedPost as any).recomputations()).toBe(1); + expect((generateCombinedPost as any).recomputations()).toBe(2); }); test('should recalculate when one of the included posts change', () => { @@ -1141,574 +1143,332 @@ describe('makeGenerateCombinedPost', () => { }); }); }); +const PostTypes = Posts.POST_TYPES; +describe('extractUserActivityData', () => { + const postAddToChannel: ActivityEntry = { + postType: PostTypes.ADD_TO_CHANNEL, + actorId: ['user_id_1'], + userIds: ['added_user_id_1'], + usernames: ['added_username_1'], + }; + const postAddToTeam: ActivityEntry = { + postType: PostTypes.ADD_TO_TEAM, + actorId: ['user_id_1'], + userIds: ['added_user_id_1', 'added_user_id_2'], + usernames: ['added_username_1', 'added_username_2'], + }; + const postLeaveChannel: ActivityEntry = { + postType: PostTypes.LEAVE_CHANNEL, + actorId: ['user_id_1'], + userIds: [], + usernames: [], + }; -describe('combineUserActivitySystemPost', () => { - const PostTypes = Posts.POST_TYPES; + const postJoinChannel: ActivityEntry = { + postType: PostTypes.JOIN_CHANNEL, + actorId: ['user_id_1'], + userIds: [], + usernames: [], + }; - it('should return null', () => { - expect(Boolean(combineUserActivitySystemPost())).toBe(false); - expect(Boolean(combineUserActivitySystemPost([]))).toBe(false); + const postRemoveFromChannel: ActivityEntry = { + postType: PostTypes.REMOVE_FROM_CHANNEL, + actorId: ['user_id_1'], + userIds: ['removed_user_id_1'], + usernames: ['removed_username_1'], + }; + const postLeaveTeam: ActivityEntry = { + postType: PostTypes.LEAVE_TEAM, + actorId: ['user_id_1'], + userIds: [], + usernames: [], + }; + + const postJoinTeam: ActivityEntry = { + postType: PostTypes.JOIN_TEAM, + actorId: ['user_id_1'], + userIds: [], + usernames: [], + }; + const postRemoveFromTeam: ActivityEntry = { + postType: PostTypes.REMOVE_FROM_TEAM, + actorId: ['user_id_1'], + userIds: [], + usernames: [], + }; + it('should return empty activity when empty ', () => { + expect(extractUserActivityData([])).toEqual({allUserIds: [], allUsernames: [], messageData: []}); }); - - const postAddToChannel1 = TestHelper.getPostMock({type: PostTypes.ADD_TO_CHANNEL, user_id: 'user_id_1', props: {addedUserId: 'added_user_id_1', addedUsername: 'added_username_1'}}); - const postAddToChannel2 = TestHelper.getPostMock({type: PostTypes.ADD_TO_CHANNEL, user_id: 'user_id_1', props: {addedUserId: 'added_user_id_2', addedUsername: 'added_username_2'}}); - const postAddToChannel3 = TestHelper.getPostMock({type: PostTypes.ADD_TO_CHANNEL, user_id: 'user_id_1', props: {addedUserId: 'added_user_id_3', addedUsername: 'added_username_3'}}); - const postAddToChannel4 = TestHelper.getPostMock({type: PostTypes.ADD_TO_CHANNEL, user_id: 'user_id_2', props: {addedUserId: 'added_user_id_4', addedUsername: 'added_username_4'}}); - const postAddToChannel5 = TestHelper.getPostMock({type: PostTypes.ADD_TO_CHANNEL, user_id: 'user_id_1', props: {addedUsername: 'added_username_1'}}); - it('should match return for ADD_TO_CHANNEL', () => { - const out1 = { - allUserIds: ['added_user_id_1', 'user_id_1'], - allUsernames: [], - messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_id_1']}], - }; - expect(combineUserActivitySystemPost([postAddToChannel1])).toEqual(out1); - - const out2 = { - allUserIds: ['added_user_id_1', 'added_user_id_2', 'user_id_1'], - allUsernames: [], - messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_id_1', 'added_user_id_2']}], - }; - expect(combineUserActivitySystemPost([postAddToChannel1, postAddToChannel2])).toEqual(out2); - - const out3 = { - allUserIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3', 'user_id_1'], - allUsernames: [], - messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3']}], - }; - expect(combineUserActivitySystemPost([postAddToChannel1, postAddToChannel2, postAddToChannel3])).toEqual(out3); - - const out4 = { - allUserIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3', 'user_id_1', 'added_user_id_4', 'user_id_2'], - allUsernames: [], - messageData: [ - {actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3']}, - {actorId: 'user_id_2', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_id_4']}, - ], - }; - expect(combineUserActivitySystemPost([postAddToChannel1, postAddToChannel2, postAddToChannel3, postAddToChannel4])).toEqual(out4); - - const out5 = { - allUserIds: ['user_id_1'], - allUsernames: ['added_username_1'], - messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_username_1']}], - }; - expect(combineUserActivitySystemPost([postAddToChannel5])).toEqual(out5); - - const out6 = { - allUserIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3', 'user_id_1', 'added_user_id_4', 'user_id_2'], - allUsernames: ['added_username_1'], - messageData: [ - {actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_username_1', 'added_user_id_1', 'added_user_id_2', 'added_user_id_3']}, - {actorId: 'user_id_2', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_id_4']}, - ], - }; - expect(combineUserActivitySystemPost([postAddToChannel1, postAddToChannel2, postAddToChannel3, postAddToChannel4, postAddToChannel5])).toEqual(out6); - }); - - it('should match return for ADD_TO_CHANNEL, backward compatibility with addedUsername', () => { - const out1 = { - allUserIds: ['user_id_1'], - allUsernames: ['added_user_name_1'], - messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_name_1']}], - }; - expect(combineUserActivitySystemPost([{...postAddToChannel1, props: {addedUsername: 'added_user_name_1'}}])).toEqual(out1); - - const out2 = { - allUserIds: ['added_user_id_2', 'user_id_1'], - allUsernames: ['added_user_name_1'], - messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_name_1', 'added_user_id_2']}], - }; - expect(combineUserActivitySystemPost([{...postAddToChannel1, props: {addedUsername: 'added_user_name_1'}}, postAddToChannel2])).toEqual(out2); - - const out3 = { - allUserIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3', 'user_id_1', 'user_id_2'], - allUsernames: ['added_user_name_4'], - messageData: [ - {actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3']}, - {actorId: 'user_id_2', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_name_4']}, - ], - }; - expect(combineUserActivitySystemPost([postAddToChannel1, postAddToChannel2, postAddToChannel3, {...postAddToChannel4, props: {addedUsername: 'added_user_name_4'}}])).toEqual(out3); - }); - - const postAddToTeam1 = TestHelper.getPostMock({type: PostTypes.ADD_TO_TEAM, user_id: 'user_id_1', props: {addedUserId: 'added_user_id_1'}}); - const postAddToTeam2 = TestHelper.getPostMock({type: PostTypes.ADD_TO_TEAM, user_id: 'user_id_1', props: {addedUserId: 'added_user_id_2'}}); - const postAddToTeam3 = TestHelper.getPostMock({type: PostTypes.ADD_TO_TEAM, user_id: 'user_id_1', props: {addedUserId: 'added_user_id_3'}}); - const postAddToTeam4 = TestHelper.getPostMock({type: PostTypes.ADD_TO_TEAM, user_id: 'user_id_2', props: {addedUserId: 'added_user_id_4'}}); - it('should match return for ADD_TO_TEAM', () => { - const out1 = { - allUserIds: ['added_user_id_1', 'user_id_1'], - allUsernames: [], - messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_id_1']}], - }; - expect(combineUserActivitySystemPost([postAddToTeam1])).toEqual(out1); - - const out2 = { - allUserIds: ['added_user_id_1', 'added_user_id_2', 'user_id_1'], - allUsernames: [], - messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_id_1', 'added_user_id_2']}], - }; - expect(combineUserActivitySystemPost([postAddToTeam1, postAddToTeam2])).toEqual(out2); - - const out3 = { - allUserIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3', 'user_id_1'], - allUsernames: [], - messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3']}], - }; - expect(combineUserActivitySystemPost([postAddToTeam1, postAddToTeam2, postAddToTeam3])).toEqual(out3); - - const out4 = { - allUserIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3', 'user_id_1', 'added_user_id_4', 'user_id_2'], - allUsernames: [], - messageData: [ - {actorId: 'user_id_1', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3']}, - {actorId: 'user_id_2', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_id_4']}, - ], - }; - expect(combineUserActivitySystemPost([postAddToTeam1, postAddToTeam2, postAddToTeam3, postAddToTeam4])).toEqual(out4); - }); - - it('should match return for ADD_TO_TEAM, backward compatibility with addedUsername', () => { - const out1 = { - allUserIds: ['user_id_1'], - allUsernames: ['added_user_name_1'], - messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_name_1']}], - }; - expect(combineUserActivitySystemPost([{...postAddToTeam1, props: {addedUsername: 'added_user_name_1'}}])).toEqual(out1); - - const out2 = { - allUserIds: ['added_user_id_2', 'user_id_1'], - allUsernames: ['added_user_name_1'], - messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_name_1', 'added_user_id_2']}], - }; - expect(combineUserActivitySystemPost([{...postAddToTeam1, props: {addedUsername: 'added_user_name_1'}}, postAddToTeam2])).toEqual(out2); - - const out3 = { - allUserIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3', 'user_id_1', 'user_id_2'], - allUsernames: ['added_user_name_4'], - messageData: [ - {actorId: 'user_id_1', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3']}, - {actorId: 'user_id_2', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_name_4']}, - ], - }; - expect(combineUserActivitySystemPost([postAddToTeam1, postAddToTeam2, postAddToTeam3, {...postAddToTeam4, props: {addedUsername: 'added_user_name_4'}}])).toEqual(out3); - }); - - const postJoinChannel1 = TestHelper.getPostMock({type: PostTypes.JOIN_CHANNEL, user_id: 'user_id_1'}); - const postJoinChannel2 = TestHelper.getPostMock({type: PostTypes.JOIN_CHANNEL, user_id: 'user_id_2'}); - const postJoinChannel3 = TestHelper.getPostMock({type: PostTypes.JOIN_CHANNEL, user_id: 'user_id_3'}); - const postJoinChannel4 = TestHelper.getPostMock({type: PostTypes.JOIN_CHANNEL, user_id: 'user_id_4'}); it('should match return for JOIN_CHANNEL', () => { - const out1 = { + const userActivities = [postJoinChannel]; + const expectedOutput = { allUserIds: ['user_id_1'], allUsernames: [], messageData: [{postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1']}], }; - expect(combineUserActivitySystemPost([postJoinChannel1])).toEqual(out1); - - const out2 = { - allUserIds: ['user_id_1', 'user_id_2'], - allUsernames: [], - messageData: [{postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1', 'user_id_2']}], + expect(extractUserActivityData(userActivities)).toEqual(expectedOutput); + const postJoinChannel2: ActivityEntry = { + postType: PostTypes.JOIN_CHANNEL, + actorId: ['user_id_2', 'user_id_3', 'user_id_4', 'user_id_5'], + userIds: [], + usernames: [], }; - expect(combineUserActivitySystemPost([postJoinChannel1, postJoinChannel2])).toEqual(out2); - - const out3 = { - allUserIds: ['user_id_1', 'user_id_2', 'user_id_3'], + const expectedOutput2 = { + allUserIds: ['user_id_2', 'user_id_3', 'user_id_4', 'user_id_5'], allUsernames: [], - messageData: [{postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1', 'user_id_2', 'user_id_3']}], + messageData: [{postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_2', 'user_id_3', 'user_id_4', 'user_id_5']}], }; - expect(combineUserActivitySystemPost([postJoinChannel1, postJoinChannel2, postJoinChannel3])).toEqual(out3); - - const out4 = { - allUserIds: ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4'], - allUsernames: [], - messageData: [{postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4']}], - }; - expect(combineUserActivitySystemPost([postJoinChannel1, postJoinChannel2, postJoinChannel3, postJoinChannel4])).toEqual(out4); + expect(extractUserActivityData([postJoinChannel2])).toEqual(expectedOutput2); }); - const postJoinTeam1 = TestHelper.getPostMock({type: PostTypes.JOIN_TEAM, user_id: 'user_id_1'}); - const postJoinTeam2 = TestHelper.getPostMock({type: PostTypes.JOIN_TEAM, user_id: 'user_id_2'}); - const postJoinTeam3 = TestHelper.getPostMock({type: PostTypes.JOIN_TEAM, user_id: 'user_id_3'}); - const postJoinTeam4 = TestHelper.getPostMock({type: PostTypes.JOIN_TEAM, user_id: 'user_id_4'}); - it('should match return for JOIN_TEAM', () => { - const out1 = { + it('should return expected data for ADD_TO_CHANNEL', () => { + const userActivities = [postAddToChannel]; + const expectedOutput = { + allUserIds: ['added_user_id_1', 'user_id_1'], + allUsernames: ['added_username_1'], + messageData: [{postType: PostTypes.ADD_TO_CHANNEL, actorId: 'user_id_1', userIds: ['added_user_id_1']}], + }; + expect(extractUserActivityData(userActivities)).toEqual(expectedOutput); + const postAddToChannel2: ActivityEntry = { + postType: PostTypes.ADD_TO_CHANNEL, + actorId: ['user_id_2'], + userIds: ['added_user_id_2', 'added_user_id_3', 'added_user_id_4'], + usernames: ['added_username_2', 'added_username_3', 'added_username_4'], + }; + const userActivities2 = [postAddToChannel, postAddToChannel2]; + const expectedOutput2 = { + allUserIds: ['added_user_id_1', 'user_id_1', 'added_user_id_2', 'added_user_id_3', 'added_user_id_4', 'user_id_2'], + allUsernames: ['added_username_1', 'added_username_2', 'added_username_3', 'added_username_4'], + messageData: [ + {postType: PostTypes.ADD_TO_CHANNEL, actorId: 'user_id_1', userIds: ['added_user_id_1']}, + {postType: PostTypes.ADD_TO_CHANNEL, actorId: 'user_id_2', userIds: ['added_user_id_2', 'added_user_id_3', 'added_user_id_4']}, + ], + }; + expect(extractUserActivityData(userActivities2)).toEqual(expectedOutput2); + }); + + it('should return expected data for ADD_TO_TEAM', () => { + const userActivities = [postAddToTeam]; + const expectedOutput = { + allUserIds: ['added_user_id_1', 'added_user_id_2', 'user_id_1'], + allUsernames: ['added_username_1', 'added_username_2'], + messageData: [{postType: PostTypes.ADD_TO_TEAM, actorId: 'user_id_1', userIds: ['added_user_id_1', 'added_user_id_2']}], + }; + expect(extractUserActivityData(userActivities)).toEqual(expectedOutput); + const postAddToTeam2: ActivityEntry = { + postType: PostTypes.ADD_TO_TEAM, + actorId: ['user_id_2'], + userIds: ['added_user_id_3', 'added_user_id_4'], + usernames: ['added_username_3', 'added_username_4'], + }; + const userActivities2 = [postAddToTeam, postAddToTeam2]; + const expectedOutput2 = { + allUserIds: ['added_user_id_1', 'added_user_id_2', 'user_id_1', 'added_user_id_3', 'added_user_id_4', 'user_id_2'], + allUsernames: ['added_username_1', 'added_username_2', 'added_username_3', 'added_username_4'], + messageData: [ + {postType: PostTypes.ADD_TO_TEAM, actorId: 'user_id_1', userIds: ['added_user_id_1', 'added_user_id_2']}, + {postType: PostTypes.ADD_TO_TEAM, actorId: 'user_id_2', userIds: ['added_user_id_3', 'added_user_id_4']}, + ], + }; + expect(extractUserActivityData(userActivities2)).toEqual(expectedOutput2); + }); + + it('should return expected data for JOIN_TEAM', () => { + const userActivities = [postJoinTeam]; + const expectedOutput = { allUserIds: ['user_id_1'], allUsernames: [], messageData: [{postType: PostTypes.JOIN_TEAM, userIds: ['user_id_1']}], }; - expect(combineUserActivitySystemPost([postJoinTeam1])).toEqual(out1); - - const out2 = { - allUserIds: ['user_id_1', 'user_id_2'], - allUsernames: [], - messageData: [{postType: PostTypes.JOIN_TEAM, userIds: ['user_id_1', 'user_id_2']}], + expect(extractUserActivityData(userActivities)).toEqual(expectedOutput); + const postJoinTeam2: ActivityEntry = { + postType: PostTypes.JOIN_TEAM, + actorId: ['user_id_2', 'user_id_3', 'user_id_4', 'user_id_5'], + userIds: [], + usernames: [], }; - expect(combineUserActivitySystemPost([postJoinTeam1, postJoinTeam2])).toEqual(out2); - - const out3 = { - allUserIds: ['user_id_1', 'user_id_2', 'user_id_3'], + const userActivities2 = [postJoinTeam, postJoinTeam2]; + const expectedOutput2 = { + allUserIds: ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4', 'user_id_5'], allUsernames: [], - messageData: [{postType: PostTypes.JOIN_TEAM, userIds: ['user_id_1', 'user_id_2', 'user_id_3']}], + messageData: [ + {postType: PostTypes.JOIN_TEAM, userIds: ['user_id_1']}, + {postType: PostTypes.JOIN_TEAM, userIds: ['user_id_2', 'user_id_3', 'user_id_4', 'user_id_5']}, + ], }; - expect(combineUserActivitySystemPost([postJoinTeam1, postJoinTeam2, postJoinTeam3])).toEqual(out3); - - const out4 = { - allUserIds: ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4'], - allUsernames: [], - messageData: [{postType: PostTypes.JOIN_TEAM, userIds: ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4']}], - }; - expect(combineUserActivitySystemPost([postJoinTeam1, postJoinTeam2, postJoinTeam3, postJoinTeam4])).toEqual(out4); + expect(extractUserActivityData(userActivities2)).toEqual(expectedOutput2); }); - const postLeaveChannel1 = TestHelper.getPostMock({type: PostTypes.LEAVE_CHANNEL, user_id: 'user_id_1'}); - const postLeaveChannel2 = TestHelper.getPostMock({type: PostTypes.LEAVE_CHANNEL, user_id: 'user_id_2'}); - const postLeaveChannel3 = TestHelper.getPostMock({type: PostTypes.LEAVE_CHANNEL, user_id: 'user_id_3'}); - const postLeaveChannel4 = TestHelper.getPostMock({type: PostTypes.LEAVE_CHANNEL, user_id: 'user_id_4'}); - it('should match return for LEAVE_CHANNEL', () => { - const out1 = { + it('should return expected data for LEAVE_CHANNEL', () => { + const userActivities = [postLeaveChannel]; + const expectedOutput = { allUserIds: ['user_id_1'], allUsernames: [], messageData: [{postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_1']}], }; - expect(combineUserActivitySystemPost([postLeaveChannel1])).toEqual(out1); + expect(extractUserActivityData(userActivities)).toEqual(expectedOutput); - const out2 = { - allUserIds: ['user_id_1', 'user_id_2'], - allUsernames: [], - messageData: [{postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_1', 'user_id_2']}], + const postLeaveChannel2: ActivityEntry = { + postType: PostTypes.LEAVE_CHANNEL, + actorId: ['user_id_2', 'user_id_3', 'user_id_4', 'user_id_5'], + userIds: [], + usernames: [], }; - expect(combineUserActivitySystemPost([postLeaveChannel1, postLeaveChannel2])).toEqual(out2); + const userActivities2 = [postLeaveChannel, postLeaveChannel2]; + const expectedOutput2 = { + allUserIds: ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4', 'user_id_5'], + allUsernames: [], + messageData: [ - const out3 = { - allUserIds: ['user_id_1', 'user_id_2', 'user_id_3'], - allUsernames: [], - messageData: [{postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_1', 'user_id_2', 'user_id_3']}], + {postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_1']}, + {postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_2', 'user_id_3', 'user_id_4', 'user_id_5']}, + ], }; - expect(combineUserActivitySystemPost([postLeaveChannel1, postLeaveChannel2, postLeaveChannel3])).toEqual(out3); - - const out4 = { - allUserIds: ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4'], - allUsernames: [], - messageData: [{postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4']}], - }; - expect(combineUserActivitySystemPost([postLeaveChannel1, postLeaveChannel2, postLeaveChannel3, postLeaveChannel4])).toEqual(out4); + expect(extractUserActivityData(userActivities2)).toEqual(expectedOutput2); }); - const postLeaveTeam1 = TestHelper.getPostMock({type: PostTypes.LEAVE_TEAM, user_id: 'user_id_1'}); - const postLeaveTeam2 = TestHelper.getPostMock({type: PostTypes.LEAVE_TEAM, user_id: 'user_id_2'}); - const postLeaveTeam3 = TestHelper.getPostMock({type: PostTypes.LEAVE_TEAM, user_id: 'user_id_3'}); - const postLeaveTeam4 = TestHelper.getPostMock({type: PostTypes.LEAVE_TEAM, user_id: 'user_id_4'}); - it('should match return for LEAVE_TEAM', () => { - const out1 = { + it('should return expected data for LEAVE_TEAM', () => { + const userActivities = [postLeaveTeam]; + const expectedOutput = { allUserIds: ['user_id_1'], allUsernames: [], messageData: [{postType: PostTypes.LEAVE_TEAM, userIds: ['user_id_1']}], }; - expect(combineUserActivitySystemPost([postLeaveTeam1])).toEqual(out1); - - const out2 = { - allUserIds: ['user_id_1', 'user_id_2'], - allUsernames: [], - messageData: [{postType: PostTypes.LEAVE_TEAM, userIds: ['user_id_1', 'user_id_2']}], + expect(extractUserActivityData(userActivities)).toEqual(expectedOutput); + const postLeaveTeam2: ActivityEntry = { + postType: PostTypes.LEAVE_TEAM, + actorId: ['user_id_2', 'user_id_3', 'user_id_4', 'user_id_5'], + userIds: [], + usernames: [], }; - expect(combineUserActivitySystemPost([postLeaveTeam1, postLeaveTeam2])).toEqual(out2); - - const out3 = { - allUserIds: ['user_id_1', 'user_id_2', 'user_id_3'], + const userActivities2 = [postLeaveTeam, postLeaveTeam2]; + const expectedOutput2 = { + allUserIds: ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4', 'user_id_5'], allUsernames: [], - messageData: [{postType: PostTypes.LEAVE_TEAM, userIds: ['user_id_1', 'user_id_2', 'user_id_3']}], + messageData: [ + {postType: PostTypes.LEAVE_TEAM, userIds: ['user_id_1']}, + {postType: PostTypes.LEAVE_TEAM, userIds: ['user_id_2', 'user_id_3', 'user_id_4', 'user_id_5']}, + ], }; - expect(combineUserActivitySystemPost([postLeaveTeam1, postLeaveTeam2, postLeaveTeam3])).toEqual(out3); - - const out4 = { - allUserIds: ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4'], - allUsernames: [], - messageData: [{postType: PostTypes.LEAVE_TEAM, userIds: ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4']}], - }; - expect(combineUserActivitySystemPost([postLeaveTeam1, postLeaveTeam2, postLeaveTeam3, postLeaveTeam4])).toEqual(out4); + expect(extractUserActivityData(userActivities2)).toEqual(expectedOutput2); }); - - const postRemoveFromChannel1 = TestHelper.getPostMock({type: PostTypes.REMOVE_FROM_CHANNEL, user_id: 'user_id_1', props: {removedUserId: 'removed_user_id_1'}}); - const postRemoveFromChannel2 = TestHelper.getPostMock({type: PostTypes.REMOVE_FROM_CHANNEL, user_id: 'user_id_1', props: {removedUserId: 'removed_user_id_2'}}); - const postRemoveFromChannel3 = TestHelper.getPostMock({type: PostTypes.REMOVE_FROM_CHANNEL, user_id: 'user_id_1', props: {removedUserId: 'removed_user_id_3'}}); - const postRemoveFromChannel4 = TestHelper.getPostMock({type: PostTypes.REMOVE_FROM_CHANNEL, user_id: 'user_id_1', props: {removedUserId: 'removed_user_id_4'}}); - it('should match return for REMOVE_FROM_CHANNEL', () => { - const out1 = { + it('should return expected data for REMOVE_FROM_CHANNEL', () => { + const userActivities = [postRemoveFromChannel]; + const expectedOutput = { allUserIds: ['removed_user_id_1', 'user_id_1'], - allUsernames: [], - messageData: [{actorId: 'user_id_1', postType: PostTypes.REMOVE_FROM_CHANNEL, userIds: ['removed_user_id_1']}], + allUsernames: ['removed_username_1'], + messageData: [{postType: PostTypes.REMOVE_FROM_CHANNEL, actorId: 'user_id_1', userIds: ['removed_user_id_1']}], }; - expect(combineUserActivitySystemPost([postRemoveFromChannel1])).toEqual(out1); + expect(extractUserActivityData(userActivities)).toEqual(expectedOutput); - const out2 = { - allUserIds: ['removed_user_id_1', 'removed_user_id_2', 'user_id_1'], - allUsernames: [], - messageData: [{actorId: 'user_id_1', postType: PostTypes.REMOVE_FROM_CHANNEL, userIds: ['removed_user_id_1', 'removed_user_id_2']}], + const postRemoveFromChannel2: ActivityEntry = { + postType: PostTypes.REMOVE_FROM_CHANNEL, + actorId: ['user_id_2'], + userIds: ['removed_user_id_2', 'removed_user_id_3', 'removed_user_id_4', 'removed_user_id_5'], + usernames: ['removed_username_2', 'removed_username_3', 'removed_username_4', 'removed_username_5'], }; - expect(combineUserActivitySystemPost([postRemoveFromChannel1, postRemoveFromChannel2])).toEqual(out2); - - const out3 = { - allUserIds: ['removed_user_id_1', 'removed_user_id_2', 'removed_user_id_3', 'user_id_1'], - allUsernames: [], - messageData: [{actorId: 'user_id_1', postType: PostTypes.REMOVE_FROM_CHANNEL, userIds: ['removed_user_id_1', 'removed_user_id_2', 'removed_user_id_3']}], + const userActivities2 = [postRemoveFromChannel, postRemoveFromChannel2]; + const expectedOutput2 = { + allUserIds: ['removed_user_id_1', 'user_id_1', 'removed_user_id_2', 'removed_user_id_3', 'removed_user_id_4', 'removed_user_id_5', 'user_id_2'], + allUsernames: ['removed_username_1', 'removed_username_2', 'removed_username_3', 'removed_username_4', 'removed_username_5'], + messageData: [ + {postType: PostTypes.REMOVE_FROM_CHANNEL, actorId: 'user_id_1', userIds: ['removed_user_id_1']}, + {postType: PostTypes.REMOVE_FROM_CHANNEL, actorId: 'user_id_2', userIds: ['removed_user_id_2', 'removed_user_id_3', 'removed_user_id_4', 'removed_user_id_5']}, + ], }; - expect(combineUserActivitySystemPost([postRemoveFromChannel1, postRemoveFromChannel2, postRemoveFromChannel3])).toEqual(out3); - - const out4 = { - allUserIds: ['removed_user_id_1', 'removed_user_id_2', 'removed_user_id_3', 'removed_user_id_4', 'user_id_1'], - allUsernames: [], - messageData: [{actorId: 'user_id_1', postType: PostTypes.REMOVE_FROM_CHANNEL, userIds: ['removed_user_id_1', 'removed_user_id_2', 'removed_user_id_3', 'removed_user_id_4']}], - }; - expect(combineUserActivitySystemPost([postRemoveFromChannel1, postRemoveFromChannel2, postRemoveFromChannel3, postRemoveFromChannel4])).toEqual(out4); + expect(extractUserActivityData(userActivities2)).toEqual(expectedOutput2); }); - - const postRemoveFromTeam1 = TestHelper.getPostMock({type: PostTypes.REMOVE_FROM_TEAM, user_id: 'user_id_1'}); - const postRemoveFromTeam2 = TestHelper.getPostMock({type: PostTypes.REMOVE_FROM_TEAM, user_id: 'user_id_2'}); - const postRemoveFromTeam3 = TestHelper.getPostMock({type: PostTypes.REMOVE_FROM_TEAM, user_id: 'user_id_3'}); - const postRemoveFromTeam4 = TestHelper.getPostMock({type: PostTypes.REMOVE_FROM_TEAM, user_id: 'user_id_4'}); - it('should match return for REMOVE_FROM_TEAM', () => { - const out1 = { + it('should return expected data for REMOVE_FROM_TEAM', () => { + const userActivities = [postRemoveFromTeam]; + const expectedOutput = { allUserIds: ['user_id_1'], allUsernames: [], messageData: [{postType: PostTypes.REMOVE_FROM_TEAM, userIds: ['user_id_1']}], }; - expect(combineUserActivitySystemPost([postRemoveFromTeam1])).toEqual(out1); + expect(extractUserActivityData(userActivities)).toEqual(expectedOutput); - const out2 = { - allUserIds: ['user_id_1', 'user_id_2'], - allUsernames: [], - messageData: [{postType: PostTypes.REMOVE_FROM_TEAM, userIds: ['user_id_1', 'user_id_2']}], + const postRemoveFromTeam2: ActivityEntry = { + postType: PostTypes.REMOVE_FROM_TEAM, + actorId: ['user_id_2', 'user_id_3', 'user_id_4', 'user_id_5'], + userIds: [], + usernames: [], }; - expect(combineUserActivitySystemPost([postRemoveFromTeam1, postRemoveFromTeam2])).toEqual(out2); - - const out3 = { - allUserIds: ['user_id_1', 'user_id_2', 'user_id_3'], + const userActivities2 = [postRemoveFromTeam, postRemoveFromTeam2]; + const expectedOutput2 = { + allUserIds: ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4', 'user_id_5'], allUsernames: [], - messageData: [{postType: PostTypes.REMOVE_FROM_TEAM, userIds: ['user_id_1', 'user_id_2', 'user_id_3']}], + messageData: [ + {postType: PostTypes.REMOVE_FROM_TEAM, userIds: ['user_id_1']}, + {postType: PostTypes.REMOVE_FROM_TEAM, userIds: ['user_id_2', 'user_id_3', 'user_id_4', 'user_id_5']}, + ], }; - expect(combineUserActivitySystemPost([postRemoveFromTeam1, postRemoveFromTeam2, postRemoveFromTeam3])).toEqual(out3); - - const out4 = { - allUserIds: ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4'], - allUsernames: [], - messageData: [{postType: PostTypes.REMOVE_FROM_TEAM, userIds: ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4']}], - }; - expect(combineUserActivitySystemPost([postRemoveFromTeam1, postRemoveFromTeam2, postRemoveFromTeam3, postRemoveFromTeam4])).toEqual(out4); + expect(extractUserActivityData(userActivities2)).toEqual(expectedOutput2); }); - - it('should match return on combination', () => { - const out1 = { - allUserIds: ['added_user_id_1', 'added_user_id_2', 'user_id_1'], - allUsernames: [], + it('should return expected data for multiple post types', () => { + const userActivities = [postAddToChannel, postAddToTeam, postJoinChannel, postJoinTeam, postLeaveChannel, postLeaveTeam, postRemoveFromChannel, postRemoveFromTeam]; + const expectedOutput = { + allUserIds: ['added_user_id_1', 'user_id_1', 'added_user_id_2', 'removed_user_id_1'], + allUsernames: ['added_username_1', 'added_username_2', 'removed_username_1'], messageData: [ - {actorId: 'user_id_1', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_id_1', 'added_user_id_2']}, - {actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_id_1', 'added_user_id_2']}, - ], - }; - expect(combineUserActivitySystemPost([postAddToChannel1, postAddToChannel2, postAddToTeam1, postAddToTeam2])).toEqual(out1); - - const out2 = { - allUserIds: ['user_id_1', 'user_id_2'], - allUsernames: [], - messageData: [ - {postType: PostTypes.JOIN_TEAM, userIds: ['user_id_1', 'user_id_2']}, - {postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1', 'user_id_2']}, - ], - }; - expect(combineUserActivitySystemPost([postJoinChannel1, postJoinChannel2, postJoinTeam1, postJoinTeam2])).toEqual(out2); - - const out3 = { - allUserIds: ['user_id_1', 'user_id_2'], - allUsernames: [], - messageData: [ - {postType: PostTypes.LEAVE_TEAM, userIds: ['user_id_1', 'user_id_2']}, - {postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_1', 'user_id_2']}, - ], - }; - expect(combineUserActivitySystemPost([postLeaveChannel1, postLeaveChannel2, postLeaveTeam1, postLeaveTeam2])).toEqual(out3); - - const out4 = { - allUserIds: ['removed_user_id_1', 'removed_user_id_2', 'user_id_1', 'user_id_2'], - allUsernames: [], - messageData: [ - {postType: PostTypes.REMOVE_FROM_TEAM, userIds: ['user_id_1', 'user_id_2']}, - {actorId: 'user_id_1', postType: PostTypes.REMOVE_FROM_CHANNEL, userIds: ['removed_user_id_1', 'removed_user_id_2']}, - ], - }; - expect(combineUserActivitySystemPost([postRemoveFromChannel1, postRemoveFromChannel2, postRemoveFromTeam1, postRemoveFromTeam2])).toEqual(out4); - - const out5 = { - allUserIds: ['added_user_id_1', 'added_user_id_2', 'user_id_1', 'user_id_2', 'removed_user_id_1', 'removed_user_id_2'], - allUsernames: [], - messageData: [ - {postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1', 'user_id_2']}, - {actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_id_1', 'added_user_id_2']}, - {postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_1', 'user_id_2']}, - {actorId: 'user_id_1', postType: PostTypes.REMOVE_FROM_CHANNEL, userIds: ['removed_user_id_1', 'removed_user_id_2']}, - ], - }; - expect(combineUserActivitySystemPost([ - postAddToChannel1, - postJoinChannel1, - postLeaveChannel1, - postRemoveFromChannel1, - postAddToChannel2, - postJoinChannel2, - postLeaveChannel2, - postRemoveFromChannel2, - ])).toEqual(out5); - - const out6 = { - allUserIds: ['added_user_id_3', 'user_id_1', 'added_user_id_4', 'user_id_2', 'user_id_3', 'user_id_4'], - allUsernames: [], - messageData: [ - {postType: PostTypes.JOIN_TEAM, userIds: ['user_id_3', 'user_id_4']}, - {actorId: 'user_id_1', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_id_3']}, - {actorId: 'user_id_2', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_id_4']}, - {postType: PostTypes.LEAVE_TEAM, userIds: ['user_id_3', 'user_id_4']}, - {postType: PostTypes.REMOVE_FROM_TEAM, userIds: ['user_id_3', 'user_id_4']}, - ], - }; - expect(combineUserActivitySystemPost([ - postAddToTeam3, - postJoinTeam3, - postLeaveTeam3, - postRemoveFromTeam3, - postAddToTeam4, - postJoinTeam4, - postLeaveTeam4, - postRemoveFromTeam4, - ])).toEqual(out6); - - const out7 = { - allUserIds: ['added_user_id_3', 'added_user_id_1', 'added_user_id_2', 'user_id_1', 'added_user_id_4', 'user_id_2', 'user_id_3', 'user_id_4', 'removed_user_id_1', 'removed_user_id_2', 'removed_user_id_3', 'removed_user_id_4'], - allUsernames: [], - messageData: [ - {postType: PostTypes.JOIN_TEAM, userIds: ['user_id_3', 'user_id_4', 'user_id_1', 'user_id_2']}, - {actorId: 'user_id_1', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_id_3', 'added_user_id_1', 'added_user_id_2']}, - {actorId: 'user_id_2', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_id_4']}, - {postType: PostTypes.LEAVE_TEAM, userIds: ['user_id_3', 'user_id_4', 'user_id_1', 'user_id_2']}, - {postType: PostTypes.REMOVE_FROM_TEAM, userIds: ['user_id_3', 'user_id_4', 'user_id_1', 'user_id_2']}, - {postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4']}, - {actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3']}, - {actorId: 'user_id_2', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_id_4']}, - {postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4']}, - {actorId: 'user_id_1', postType: PostTypes.REMOVE_FROM_CHANNEL, userIds: ['removed_user_id_1', 'removed_user_id_2', 'removed_user_id_3', 'removed_user_id_4']}, - ], - }; - expect(combineUserActivitySystemPost([ - postAddToTeam3, - postJoinTeam3, - postLeaveTeam3, - postRemoveFromTeam3, - postAddToTeam4, - postJoinTeam4, - postLeaveTeam4, - postRemoveFromTeam4, - - postAddToChannel1, - postJoinChannel1, - postLeaveChannel1, - postRemoveFromChannel1, - postAddToChannel2, - postJoinChannel2, - postLeaveChannel2, - postRemoveFromChannel2, - - postAddToChannel3, - postJoinChannel3, - postLeaveChannel3, - postRemoveFromChannel3, - postAddToChannel4, - postJoinChannel4, - postLeaveChannel4, - postRemoveFromChannel4, - - postAddToTeam1, - postJoinTeam1, - postLeaveTeam1, - postRemoveFromTeam1, - postAddToTeam2, - postJoinTeam2, - postLeaveTeam2, - postRemoveFromTeam2, - ])).toEqual(out7); - - const out8 = { - allUserIds: ['added_user_id_3', 'user_id_1', 'user_id_3', 'added_user_id_1', 'removed_user_id_1'], - allUsernames: [], - messageData: [ - {postType: PostTypes.JOIN_TEAM, userIds: ['user_id_3']}, - {actorId: 'user_id_1', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_id_3']}, - {postType: PostTypes.LEAVE_TEAM, userIds: ['user_id_3']}, - {postType: PostTypes.REMOVE_FROM_TEAM, userIds: ['user_id_3']}, + {postType: PostTypes.ADD_TO_CHANNEL, actorId: 'user_id_1', userIds: ['added_user_id_1']}, + {postType: PostTypes.ADD_TO_TEAM, actorId: 'user_id_1', userIds: ['added_user_id_1', 'added_user_id_2']}, {postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1']}, - {actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_id_1']}, + {postType: PostTypes.JOIN_TEAM, userIds: ['user_id_1']}, {postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_1']}, - {actorId: 'user_id_1', postType: PostTypes.REMOVE_FROM_CHANNEL, userIds: ['removed_user_id_1']}, + {postType: PostTypes.LEAVE_TEAM, userIds: ['user_id_1']}, + {postType: PostTypes.REMOVE_FROM_CHANNEL, actorId: 'user_id_1', userIds: ['removed_user_id_1']}, + {postType: PostTypes.REMOVE_FROM_TEAM, userIds: ['user_id_1']}, ], }; - expect(combineUserActivitySystemPost([ - postAddToTeam3, - postAddToTeam3, - postJoinTeam3, - postJoinTeam3, - postLeaveTeam3, - postLeaveTeam3, - postRemoveFromTeam3, - postRemoveFromTeam3, - - postAddToChannel1, - postAddToChannel1, - postJoinChannel1, - postJoinChannel1, - postLeaveChannel1, - postLeaveChannel1, - postRemoveFromChannel1, - postRemoveFromChannel1, - ])).toEqual(out8); + expect(extractUserActivityData(userActivities)).toEqual(expectedOutput); }); }); -describe('comparePostTypes', () => { - const { - JOIN_TEAM, - ADD_TO_TEAM, - LEAVE_TEAM, - REMOVE_FROM_TEAM, - JOIN_CHANNEL, - ADD_TO_CHANNEL, - LEAVE_CHANNEL, - REMOVE_FROM_CHANNEL, - } = Posts.POST_TYPES; +describe('combineUserActivityData', () => { + it('combineUserActivitySystemPost returns null when systemPosts is an empty array', () => { + expect(combineUserActivitySystemPost([])).toBeNull(); + }); + it('correctly combine different post types and actorIds by order', () => { + const postAddToChannel1 = TestHelper.getPostMock({type: PostTypes.ADD_TO_CHANNEL, user_id: 'user_id_1', props: {addedUserId: 'added_user_id_1', addedUsername: 'added_username_1'}}); + const postAddToTeam1 = TestHelper.getPostMock({type: PostTypes.ADD_TO_TEAM, user_id: 'user_id_1', props: {addedUserId: 'added_user_id_1'}}); + const postJoinChannel1 = TestHelper.getPostMock({type: PostTypes.JOIN_CHANNEL, user_id: 'user_id_1'}); + const postJoinTeam1 = TestHelper.getPostMock({type: PostTypes.JOIN_TEAM, user_id: 'user_id_1'}); + const postLeaveChannel1 = TestHelper.getPostMock({type: PostTypes.LEAVE_CHANNEL, user_id: 'user_id_1'}); + const postLeaveTeam1 = TestHelper.getPostMock({type: PostTypes.LEAVE_TEAM, user_id: 'user_id_1'}); + const postRemoveFromChannel1 = TestHelper.getPostMock({type: PostTypes.REMOVE_FROM_CHANNEL, user_id: 'user_id_1', props: {removedUserId: 'removed_user_id_1', removedUsername: 'removed_username_1'}}); + const postRemoveFromTeam1 = TestHelper.getPostMock({type: PostTypes.REMOVE_FROM_TEAM, user_id: 'user_id_1'}); + const posts = [postAddToChannel1, postAddToTeam1, postJoinChannel1, postJoinTeam1, postLeaveChannel1, postLeaveTeam1, postRemoveFromChannel1, postRemoveFromTeam1].reverse(); + const expectedOutput = { + allUserIds: ['added_user_id_1', 'user_id_1', 'removed_user_id_1'], + allUsernames: ['added_username_1', 'removed_username_1'], + messageData: [ + {postType: PostTypes.ADD_TO_CHANNEL, actorId: 'user_id_1', userIds: ['added_user_id_1']}, + {postType: PostTypes.ADD_TO_TEAM, actorId: 'user_id_1', userIds: ['added_user_id_1']}, + {postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1']}, + {postType: PostTypes.JOIN_TEAM, userIds: ['user_id_1']}, + {postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_1']}, + {postType: PostTypes.LEAVE_TEAM, userIds: ['user_id_1']}, + {postType: PostTypes.REMOVE_FROM_CHANNEL, actorId: 'user_id_1', userIds: ['removed_user_id_1']}, + {postType: PostTypes.REMOVE_FROM_TEAM, userIds: ['user_id_1']}, + ], + }; + expect(combineUserActivitySystemPost(posts)).toEqual(expectedOutput); + }); + it('correctly combine same post types', () => { + const postAddToChannel1 = TestHelper.getPostMock({type: PostTypes.ADD_TO_CHANNEL, user_id: 'user_id_1', props: {addedUserId: 'added_user_id_1', addedUsername: 'added_username_1'}}); + const postAddToChannel2 = TestHelper.getPostMock({type: PostTypes.ADD_TO_CHANNEL, user_id: 'user_id_2', props: {addedUserId: 'added_user_id_2', addedUsername: 'added_username_2'}}); + const postAddToChannel3 = TestHelper.getPostMock({type: PostTypes.ADD_TO_CHANNEL, user_id: 'user_id_3', props: {addedUserId: 'added_user_id_3', addedUsername: 'added_username_3'}}); - const testCases = [ - [], - [{postType: JOIN_TEAM}], - [{postType: JOIN_TEAM}, {postType: ADD_TO_TEAM}], - [{postType: ADD_TO_TEAM}, {postType: JOIN_TEAM}], - [{postType: ADD_TO_TEAM}, {postType: ADD_TO_TEAM}, {postType: JOIN_TEAM}], - [{postType: JOIN_TEAM}, {postType: ADD_TO_TEAM}, {postType: LEAVE_TEAM}, {postType: REMOVE_FROM_TEAM}], - [{postType: REMOVE_FROM_TEAM}, {postType: LEAVE_TEAM}, {postType: ADD_TO_TEAM}, {postType: JOIN_TEAM}], - [{postType: JOIN_CHANNEL}, {postType: ADD_TO_CHANNEL}, {postType: LEAVE_CHANNEL}, {postType: REMOVE_FROM_CHANNEL}], - [{postType: REMOVE_FROM_CHANNEL}, {postType: LEAVE_CHANNEL}, {postType: ADD_TO_CHANNEL}, {postType: JOIN_CHANNEL}], - [{postType: LEAVE_CHANNEL}, {postType: REMOVE_FROM_CHANNEL}, {postType: LEAVE_TEAM}, {postType: REMOVE_FROM_TEAM}], - [{postType: LEAVE_TEAM}, {postType: REMOVE_FROM_TEAM}, {postType: LEAVE_CHANNEL}, {postType: REMOVE_FROM_CHANNEL}], - [{postType: JOIN_CHANNEL}, {postType: LEAVE_CHANNEL}, {postType: JOIN_CHANNEL}, {postType: REMOVE_FROM_CHANNEL}, {postType: ADD_TO_CHANNEL}], - ]; - - for (const testCase of testCases) { - let previousType = ''; - testCase.sort(comparePostTypes as any).forEach((sortedTestCase, index) => { - it(`should sort post type correctly: ${previousType} should come first before ${sortedTestCase.postType}`, () => { - if (index > 0) { - expect(postTypePriority[previousType] <= postTypePriority[sortedTestCase.postType]).toBeTruthy(); - } - - previousType = sortedTestCase.postType; - }); - }); - } + const posts = [postAddToChannel1, postAddToChannel2, postAddToChannel3].reverse(); + const expectedOutput = { + allUserIds: ['added_user_id_1', 'user_id_1', 'added_user_id_2', 'user_id_2', 'added_user_id_3', 'user_id_3'], + allUsernames: ['added_username_1', 'added_username_2', 'added_username_3'], + messageData: [ + {postType: PostTypes.ADD_TO_CHANNEL, actorId: 'user_id_1', userIds: ['added_user_id_1']}, + {postType: PostTypes.ADD_TO_CHANNEL, actorId: 'user_id_2', userIds: ['added_user_id_2']}, + {postType: PostTypes.ADD_TO_CHANNEL, actorId: 'user_id_3', userIds: ['added_user_id_3']}, + ], + }; + expect(combineUserActivitySystemPost(posts)).toEqual(expectedOutput); + }); }); diff --git a/webapp/channels/src/packages/mattermost-redux/src/utils/post_list.ts b/webapp/channels/src/packages/mattermost-redux/src/utils/post_list.ts index 4e03cc0162..5bd511616f 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/utils/post_list.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/utils/post_list.ts @@ -16,7 +16,7 @@ import {createIdsSelector, memoizeResult} from 'mattermost-redux/utils/helpers'; import {isUserActivityPost, shouldFilterJoinLeavePost, isFromWebhook} from 'mattermost-redux/utils/post_utils'; import {getUserCurrentTimezone} from 'mattermost-redux/utils/timezone_utils'; -import {Post} from '@mattermost/types/posts'; +import {ActivityEntry, Post} from '@mattermost/types/posts'; import {GlobalState} from '@mattermost/types/store'; export const COMBINED_USER_ACTIVITY = 'user-activity-'; @@ -285,7 +285,6 @@ export function makeGenerateCombinedPost(): (state: GlobalState, combinedId: str // Assume that the last post is the oldest one const createAt = posts[posts.length - 1].create_at; - const messages = posts.map((post) => post.message); return { @@ -323,66 +322,30 @@ export function makeGenerateCombinedPost(): (state: GlobalState, combinedId: str ); } -export const postTypePriority = { - [Posts.POST_TYPES.JOIN_TEAM]: 0, - [Posts.POST_TYPES.ADD_TO_TEAM]: 1, - [Posts.POST_TYPES.LEAVE_TEAM]: 2, - [Posts.POST_TYPES.REMOVE_FROM_TEAM]: 3, - [Posts.POST_TYPES.JOIN_CHANNEL]: 4, - [Posts.POST_TYPES.ADD_TO_CHANNEL]: 5, - [Posts.POST_TYPES.LEAVE_CHANNEL]: 6, - [Posts.POST_TYPES.REMOVE_FROM_CHANNEL]: 7, - [Posts.POST_TYPES.PURPOSE_CHANGE]: 8, - [Posts.POST_TYPES.HEADER_CHANGE]: 9, - [Posts.POST_TYPES.JOIN_LEAVE]: 10, - [Posts.POST_TYPES.DISPLAYNAME_CHANGE]: 11, - [Posts.POST_TYPES.CONVERT_CHANNEL]: 12, - [Posts.POST_TYPES.CHANNEL_DELETED]: 13, - [Posts.POST_TYPES.CHANNEL_UNARCHIVED]: 14, - [Posts.POST_TYPES.ADD_REMOVE]: 15, - [Posts.POST_TYPES.EPHEMERAL]: 16, -}; - -export function comparePostTypes(a: typeof postTypePriority, b: typeof postTypePriority) { - return postTypePriority[a.postType] - postTypePriority[b.postType]; -} - -function extractUserActivityData(userActivities: any) { +export function extractUserActivityData(userActivities: ActivityEntry[]) { const messageData: any[] = []; const allUserIds: string[] = []; const allUsernames: string[] = []; - Object.entries(userActivities).forEach(([postType, values]: [string, any]) => { - if ( - postType === Posts.POST_TYPES.ADD_TO_TEAM || - postType === Posts.POST_TYPES.ADD_TO_CHANNEL || - postType === Posts.POST_TYPES.REMOVE_FROM_CHANNEL - ) { - Object.keys(values).map((key) => [key, values[key]]).forEach(([actorId, users]) => { - if (Array.isArray(users)) { - throw new Error('Invalid Post activity data'); + userActivities.forEach((activity) => { + if (isUsersRelatedPost(activity.postType)) { + const {postType, actorId, userIds, usernames} = activity; + if (usernames && userIds) { + messageData.push({postType, userIds: [...userIds], actorId: actorId[0]}); + if (userIds.length > 0) { + allUserIds.push(...userIds.filter((userId) => userId)); } - const {ids, usernames} = users; - messageData.push({postType, userIds: [...usernames, ...ids], actorId}); - if (ids.length > 0) { - allUserIds.push(...ids); - } - if (usernames.length > 0) { - allUsernames.push(...usernames); + allUsernames.push(...usernames.filter((username) => username)); } - allUserIds.push(actorId); - }); - } else { - if (!Array.isArray(values)) { - throw new Error('Invalid Post activity data'); + allUserIds.push(actorId[0]); } - messageData.push({postType, userIds: values}); - allUserIds.push(...values); + } else { + const {postType, actorId} = activity; + const userIds = actorId; + messageData.push({postType, userIds}); + allUserIds.push(...userIds); } }); - - messageData.sort(comparePostTypes); - function reduceUsers(acc: string[], curr: string) { if (!acc.includes(curr)) { acc.push(curr); @@ -396,69 +359,45 @@ function extractUserActivityData(userActivities: any) { messageData, }; } - +function isUsersRelatedPost(postType: string) { + return ( + postType === Posts.POST_TYPES.ADD_TO_TEAM || + postType === Posts.POST_TYPES.ADD_TO_CHANNEL || + postType === Posts.POST_TYPES.REMOVE_FROM_CHANNEL + ); +} export function combineUserActivitySystemPost(systemPosts: Post[] = []) { if (systemPosts.length === 0) { return null; } - - const userActivities = systemPosts.reduce((acc: any, post: Post) => { + const userActivities: ActivityEntry[] = []; + systemPosts.reverse().forEach((post: Post) => { const postType = post.type; - let userActivityProps = acc; - const combinedPostType = userActivityProps[postType as string]; + const actorId = post.user_id; - if ( - postType === Posts.POST_TYPES.ADD_TO_TEAM || - postType === Posts.POST_TYPES.ADD_TO_CHANNEL || - postType === Posts.POST_TYPES.REMOVE_FROM_CHANNEL - ) { - const userId = post.props.addedUserId || post.props.removedUserId; - const username = post.props.addedUsername || post.props.removedUsername; - if (combinedPostType) { - if (Array.isArray(combinedPostType[post.user_id])) { - throw new Error('Invalid Post activity data'); - } - const users = combinedPostType[post.user_id] || {ids: [], usernames: []}; - if (userId) { - if (!users.ids.includes(userId)) { - users.ids.push(userId); - } - } else if (username && !users.usernames.includes(username)) { - users.usernames.push(username); - } - combinedPostType[post.user_id] = users; - } else { - const users = { - ids: [] as string[], - usernames: [] as string[], - }; + // When combining removed posts, the actorId does not need to be the same for each post. + // All removed posts will be combined regardless of their respective actorIds. + const isRemovedPost = post.type === Posts.POST_TYPES.REMOVE_FROM_CHANNEL; + const userId = isUsersRelatedPost(postType) ? post.props.addedUserId || post.props.removedUserId : ''; + const username = isUsersRelatedPost(postType) ? post.props.addedUsername || post.props.removedUsername : ''; + const prevPost = userActivities[userActivities.length - 1]; + const isSamePostType = prevPost && prevPost.postType === post.type; + const isSameActor = prevPost && prevPost.actorId[0] === post.user_id; - if (userId) { - users.ids.push(userId); - } else if (username) { - users.usernames.push(username); - } - userActivityProps[postType] = { - [post.user_id]: users, - }; - } + if (prevPost && isSamePostType && (isSameActor || isRemovedPost)) { + prevPost.userIds.push(userId); + prevPost.usernames.push(username); + } else if (isSamePostType && !isSameActor && !isUsersRelatedPost(postType)) { + prevPost.actorId.push(actorId); } else { - const propsUserId = post.user_id; - - if (combinedPostType) { - if (!Array.isArray(combinedPostType)) { - throw new Error('Invalid Post activity data'); - } - if (!combinedPostType.includes(propsUserId)) { - userActivityProps[postType] = [...combinedPostType, propsUserId]; - } - } else { - userActivityProps = {...userActivityProps, [postType]: [propsUserId]}; - } + userActivities.push({ + actorId: [actorId], + userIds: [userId], + usernames: [username], + postType, + }); } - - return userActivityProps; - }, {}); + }); return extractUserActivityData(userActivities); } diff --git a/webapp/platform/types/src/posts.ts b/webapp/platform/types/src/posts.ts index f590f00b09..afd6383034 100644 --- a/webapp/platform/types/src/posts.ts +++ b/webapp/platform/types/src/posts.ts @@ -204,3 +204,9 @@ export type PostAnalytics = { requested_ack?: boolean; persistent_notifications?: boolean; } +export type ActivityEntry = { + postType: Post['type']; + actorId: string[]; + userIds: string[]; + usernames: string[]; +} \ No newline at end of file