mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
MM-24276 Added "User joined and left" system messages (#24332)
* update log message * Delete package-lock.json * update Message ID * Add Logic for Joined-Left Event * fix join-leave single user * Revert Log File * Fix i18 extract file * Add tests * update tests * Add a few more test cases --------- Co-authored-by: Asis Rout <asisrout@Asiss-MacBook-Air.local> Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Harrison Healey <harrisonmhealey@gmail.com>
This commit is contained in:
parent
b7f1a7f262
commit
631d59249e
@ -17,7 +17,7 @@ import {t} from 'utils/i18n';
|
||||
import LastUsers from './last_users';
|
||||
|
||||
const {
|
||||
JOIN_CHANNEL, ADD_TO_CHANNEL, REMOVE_FROM_CHANNEL, LEAVE_CHANNEL,
|
||||
JOIN_CHANNEL, ADD_TO_CHANNEL, REMOVE_FROM_CHANNEL, LEAVE_CHANNEL, JOIN_LEAVE_CHANNEL,
|
||||
JOIN_TEAM, ADD_TO_TEAM, REMOVE_FROM_TEAM, LEAVE_TEAM,
|
||||
} = Posts.POST_TYPES;
|
||||
|
||||
@ -94,6 +94,24 @@ const postTypeMessage = {
|
||||
defaultMessage: '{users} and {lastUser} **left the channel**.',
|
||||
},
|
||||
},
|
||||
[JOIN_LEAVE_CHANNEL]: {
|
||||
one: {
|
||||
id: t('combined_system_message.join_left_channel.one'),
|
||||
defaultMessage: '{firstUser} **joined and left the channel**.',
|
||||
},
|
||||
one_you: {
|
||||
id: t('combined_system_message.join_left_channel.one_you'),
|
||||
defaultMessage: 'You **joined and left the channel**.',
|
||||
},
|
||||
two: {
|
||||
id: t('combined_system_message.join_left_channel.two'),
|
||||
defaultMessage: '{firstUser} and {secondUser} **joined and left the channel**.',
|
||||
},
|
||||
many_expanded: {
|
||||
id: t('combined_system_message.join_left_channel.many_expanded'),
|
||||
defaultMessage: '{users} and {lastUser} **joined and left the channel**.',
|
||||
},
|
||||
},
|
||||
[JOIN_TEAM]: {
|
||||
one: {
|
||||
id: t('combined_system_message.joined_team.one'),
|
||||
|
@ -25,6 +25,10 @@ const typeMessage = {
|
||||
id: t('last_users_message.left_channel.type'),
|
||||
defaultMessage: '**left the channel**.',
|
||||
},
|
||||
[Posts.POST_TYPES.JOIN_LEAVE_CHANNEL]: {
|
||||
id: t('last_users_message.joined_left_channel.type'),
|
||||
defaultMessage: '**joined and left the channel**.',
|
||||
},
|
||||
[Posts.POST_TYPES.REMOVE_FROM_CHANNEL]: {
|
||||
id: t('last_users_message.removed_from_channel.type'),
|
||||
defaultMessage: 'were **removed from the channel**.',
|
||||
|
@ -3132,6 +3132,10 @@
|
||||
"combined_system_message.added_to_team.one": "{firstUser} **added to the team** by {actor}.",
|
||||
"combined_system_message.added_to_team.one_you": "You were **added to the team** by {actor}.",
|
||||
"combined_system_message.added_to_team.two": "{firstUser} and {secondUser} **added to the team** by {actor}.",
|
||||
"combined_system_message.join_left_channel.many_expanded": "{users} and {lastUser} **joined and left the channel**.",
|
||||
"combined_system_message.join_left_channel.one": "{firstUser} **joined and left the channel**.",
|
||||
"combined_system_message.join_left_channel.one_you": "You **joined and left the channel**.",
|
||||
"combined_system_message.join_left_channel.two": "{firstUser} and {secondUser} **joined and left the channel**.",
|
||||
"combined_system_message.joined_channel.many_expanded": "{users} and {lastUser} **joined the channel**.",
|
||||
"combined_system_message.joined_channel.one": "{firstUser} **joined the channel**.",
|
||||
"combined_system_message.joined_channel.one_you": "You **joined the channel**.",
|
||||
@ -3843,6 +3847,7 @@
|
||||
"last_users_message.added_to_team.type": "were **added to the team** by {actor}.",
|
||||
"last_users_message.first": "{firstUser} and ",
|
||||
"last_users_message.joined_channel.type": "**joined the channel**.",
|
||||
"last_users_message.joined_left_channel.type": "**joined and left the channel**.",
|
||||
"last_users_message.joined_team.type": "**joined the team**.",
|
||||
"last_users_message.left_channel.type": "**left the channel**.",
|
||||
"last_users_message.left_team.type": "**left the team**.",
|
||||
|
@ -17,6 +17,7 @@ export const PostTypes = {
|
||||
JOIN_CHANNEL: 'system_join_channel' as PostType,
|
||||
GUEST_JOIN_CHANNEL: 'system_guest_join_channel' as PostType,
|
||||
LEAVE_CHANNEL: 'system_leave_channel' as PostType,
|
||||
JOIN_LEAVE_CHANNEL: 'system_join_leave_channel' as PostType,
|
||||
ADD_REMOVE: 'system_add_remove' as PostType,
|
||||
ADD_TO_CHANNEL: 'system_add_to_channel' as PostType,
|
||||
ADD_GUEST_TO_CHANNEL: 'system_add_guest_to_chan' as PostType,
|
||||
|
@ -1476,6 +1476,211 @@ describe('combineUserActivityData', () => {
|
||||
};
|
||||
expect(combineUserActivitySystemPost(posts)).toEqual(expectedOutput);
|
||||
});
|
||||
it('correctly combine Join and Leave Posts', () => {
|
||||
const postJoinChannel1 = TestHelper.getPostMock({type: PostTypes.JOIN_CHANNEL, user_id: 'user_id_1'});
|
||||
const postLeaveChannel1 = TestHelper.getPostMock({type: PostTypes.LEAVE_CHANNEL, user_id: 'user_id_1'});
|
||||
const postJoinChannel2 = TestHelper.getPostMock({type: PostTypes.JOIN_CHANNEL, user_id: 'user_id_2'});
|
||||
const postLeaveChannel2 = TestHelper.getPostMock({type: PostTypes.LEAVE_CHANNEL, user_id: 'user_id_2'});
|
||||
const postJoinChannel3 = TestHelper.getPostMock({type: PostTypes.JOIN_CHANNEL, user_id: 'user_id_3'});
|
||||
const postLeaveChannel3 = TestHelper.getPostMock({type: PostTypes.LEAVE_CHANNEL, user_id: 'user_id_3'});
|
||||
|
||||
const post = [postJoinChannel1, postLeaveChannel1].reverse();
|
||||
const expectedOutput = {
|
||||
allUserIds: ['user_id_1'],
|
||||
allUsernames: [],
|
||||
messageData: [
|
||||
{postType: PostTypes.JOIN_LEAVE_CHANNEL, userIds: ['user_id_1']},
|
||||
],
|
||||
};
|
||||
expect(combineUserActivitySystemPost(post)).toEqual(expectedOutput);
|
||||
|
||||
const post1 = [postJoinChannel1, postLeaveChannel1, postJoinChannel2, postLeaveChannel2, postJoinChannel3, postLeaveChannel3].reverse();
|
||||
const expectedOutput1 = {
|
||||
allUserIds: ['user_id_1', 'user_id_2', 'user_id_3'],
|
||||
allUsernames: [],
|
||||
messageData: [
|
||||
{postType: PostTypes.JOIN_LEAVE_CHANNEL, userIds: ['user_id_1', 'user_id_2', 'user_id_3']},
|
||||
],
|
||||
};
|
||||
expect(combineUserActivitySystemPost(post1)).toEqual(expectedOutput1);
|
||||
|
||||
const post2 = [postJoinChannel1, postJoinChannel2, postJoinChannel3, postLeaveChannel1, postLeaveChannel2, postLeaveChannel3].reverse();
|
||||
const expectedOutput2 = {
|
||||
allUserIds: ['user_id_1', 'user_id_2', 'user_id_3'],
|
||||
allUsernames: [],
|
||||
messageData: [
|
||||
{postType: PostTypes.JOIN_LEAVE_CHANNEL, userIds: ['user_id_1', 'user_id_2', 'user_id_3']},
|
||||
],
|
||||
};
|
||||
expect(combineUserActivitySystemPost(post2)).toEqual(expectedOutput2);
|
||||
|
||||
const post3 = [postJoinChannel1, postJoinChannel2, postLeaveChannel2, postLeaveChannel1, postJoinChannel3, postLeaveChannel3].reverse();
|
||||
const expectedOutput3 = {
|
||||
allUserIds: ['user_id_1', 'user_id_2', 'user_id_3'],
|
||||
allUsernames: [],
|
||||
messageData: [
|
||||
{postType: PostTypes.JOIN_LEAVE_CHANNEL, userIds: ['user_id_1', 'user_id_2', 'user_id_3']},
|
||||
],
|
||||
};
|
||||
expect(combineUserActivitySystemPost(post3)).toEqual(expectedOutput3);
|
||||
});
|
||||
it('should only partially combine mismatched join and leave posts', () => {
|
||||
const postJoinChannel1 = TestHelper.getPostMock({type: PostTypes.JOIN_CHANNEL, user_id: 'user_id_1'});
|
||||
const postLeaveChannel1 = TestHelper.getPostMock({type: PostTypes.LEAVE_CHANNEL, user_id: 'user_id_1'});
|
||||
const postJoinChannel2 = TestHelper.getPostMock({type: PostTypes.JOIN_CHANNEL, user_id: 'user_id_2'});
|
||||
const postLeaveChannel2 = TestHelper.getPostMock({type: PostTypes.LEAVE_CHANNEL, user_id: 'user_id_2'});
|
||||
|
||||
let posts = [postJoinChannel1, postLeaveChannel1, postJoinChannel2].reverse();
|
||||
let expectedOutput = {
|
||||
allUserIds: ['user_id_1', 'user_id_2'],
|
||||
allUsernames: [],
|
||||
messageData: [
|
||||
{postType: PostTypes.JOIN_LEAVE_CHANNEL, userIds: ['user_id_1']},
|
||||
{postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_2']},
|
||||
],
|
||||
};
|
||||
expect(combineUserActivitySystemPost(posts)).toEqual(expectedOutput);
|
||||
|
||||
posts = [postJoinChannel1, postLeaveChannel1, postLeaveChannel2].reverse();
|
||||
expectedOutput = {
|
||||
allUserIds: ['user_id_1', 'user_id_2'],
|
||||
allUsernames: [],
|
||||
messageData: [
|
||||
{postType: PostTypes.JOIN_LEAVE_CHANNEL, userIds: ['user_id_1']},
|
||||
{postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_2']},
|
||||
],
|
||||
};
|
||||
expect(combineUserActivitySystemPost(posts)).toEqual(expectedOutput);
|
||||
|
||||
posts = [postJoinChannel1, postJoinChannel2, postLeaveChannel1].reverse();
|
||||
expectedOutput = {
|
||||
allUserIds: ['user_id_1', 'user_id_2'],
|
||||
allUsernames: [],
|
||||
messageData: [
|
||||
{postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1', 'user_id_2']},
|
||||
{postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_1']},
|
||||
],
|
||||
};
|
||||
expect(combineUserActivitySystemPost(posts)).toEqual(expectedOutput);
|
||||
|
||||
posts = [postJoinChannel1, postLeaveChannel2, postLeaveChannel1].reverse();
|
||||
expectedOutput = {
|
||||
allUserIds: ['user_id_1', 'user_id_2'],
|
||||
allUsernames: [],
|
||||
messageData: [
|
||||
{postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1']},
|
||||
{postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_2', 'user_id_1']},
|
||||
],
|
||||
};
|
||||
expect(combineUserActivitySystemPost(posts)).toEqual(expectedOutput);
|
||||
|
||||
posts = [postJoinChannel2, postJoinChannel1, postLeaveChannel1].reverse();
|
||||
expectedOutput = {
|
||||
allUserIds: ['user_id_2', 'user_id_1'],
|
||||
allUsernames: [],
|
||||
messageData: [
|
||||
|
||||
// This case is arguably incorrect, but it's an edge case
|
||||
{postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_2', 'user_id_1']},
|
||||
{postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_1']},
|
||||
],
|
||||
};
|
||||
expect(combineUserActivitySystemPost(posts)).toEqual(expectedOutput);
|
||||
|
||||
posts = [postLeaveChannel2, postJoinChannel1, postLeaveChannel1].reverse();
|
||||
expectedOutput = {
|
||||
allUserIds: ['user_id_2', 'user_id_1'],
|
||||
allUsernames: [],
|
||||
messageData: [
|
||||
{postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_2']},
|
||||
{postType: PostTypes.JOIN_LEAVE_CHANNEL, userIds: ['user_id_1']},
|
||||
],
|
||||
};
|
||||
expect(combineUserActivitySystemPost(posts)).toEqual(expectedOutput);
|
||||
});
|
||||
it('should not combine join and leave posts with other actions in between', () => {
|
||||
const postJoinChannel1 = TestHelper.getPostMock({type: PostTypes.JOIN_CHANNEL, user_id: 'user_id_1'});
|
||||
const postLeaveChannel1 = TestHelper.getPostMock({type: PostTypes.LEAVE_CHANNEL, user_id: 'user_id_1'});
|
||||
|
||||
const postAddToChannel2 = TestHelper.getPostMock({type: PostTypes.ADD_TO_CHANNEL, user_id: 'user_id_2', props: {addedUserId: 'added_user_id_1', addedUsername: 'added_username_1'}});
|
||||
const postAddToTeam2 = TestHelper.getPostMock({type: PostTypes.ADD_TO_TEAM, user_id: 'user_id_2', props: {addedUserId: 'added_user_id_1'}});
|
||||
const postJoinTeam2 = TestHelper.getPostMock({type: PostTypes.JOIN_TEAM, user_id: 'user_id_2'});
|
||||
const postLeaveTeam2 = TestHelper.getPostMock({type: PostTypes.LEAVE_TEAM, user_id: 'user_id_2'});
|
||||
const postRemoveFromChannel2 = TestHelper.getPostMock({type: PostTypes.REMOVE_FROM_CHANNEL, user_id: 'user_id_2', props: {removedUserId: 'removed_user_id_1', removedUsername: 'removed_username_1'}});
|
||||
const postRemoveFromTeam2 = TestHelper.getPostMock({type: PostTypes.REMOVE_FROM_TEAM, user_id: 'removed_user_id_1'});
|
||||
|
||||
let posts = [postJoinChannel1, postAddToChannel2, postLeaveChannel1].reverse();
|
||||
let expectedOutput = {
|
||||
allUserIds: ['user_id_1', 'added_user_id_1', 'user_id_2'],
|
||||
allUsernames: ['added_username_1'],
|
||||
messageData: [
|
||||
{postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1']},
|
||||
{postType: PostTypes.ADD_TO_CHANNEL, actorId: 'user_id_2', userIds: ['added_user_id_1']},
|
||||
{postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_1']},
|
||||
],
|
||||
};
|
||||
expect(combineUserActivitySystemPost(posts)).toEqual(expectedOutput);
|
||||
|
||||
posts = [postJoinChannel1, postAddToTeam2, postLeaveChannel1].reverse();
|
||||
expectedOutput = {
|
||||
allUserIds: ['user_id_1', 'added_user_id_1', 'user_id_2'],
|
||||
allUsernames: [],
|
||||
messageData: [
|
||||
{postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1']},
|
||||
{postType: PostTypes.ADD_TO_TEAM, actorId: 'user_id_2', userIds: ['added_user_id_1']},
|
||||
{postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_1']},
|
||||
],
|
||||
};
|
||||
expect(combineUserActivitySystemPost(posts)).toEqual(expectedOutput);
|
||||
|
||||
posts = [postJoinChannel1, postJoinTeam2, postLeaveChannel1].reverse();
|
||||
expectedOutput = {
|
||||
allUserIds: ['user_id_1', 'user_id_2'],
|
||||
allUsernames: [],
|
||||
messageData: [
|
||||
{postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1']},
|
||||
{postType: PostTypes.JOIN_TEAM, userIds: ['user_id_2']},
|
||||
{postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_1']},
|
||||
],
|
||||
};
|
||||
expect(combineUserActivitySystemPost(posts)).toEqual(expectedOutput);
|
||||
|
||||
posts = [postJoinChannel1, postLeaveTeam2, postLeaveChannel1].reverse();
|
||||
expectedOutput = {
|
||||
allUserIds: ['user_id_1', 'user_id_2'],
|
||||
allUsernames: [],
|
||||
messageData: [
|
||||
{postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1']},
|
||||
{postType: PostTypes.LEAVE_TEAM, userIds: ['user_id_2']},
|
||||
{postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_1']},
|
||||
],
|
||||
};
|
||||
expect(combineUserActivitySystemPost(posts)).toEqual(expectedOutput);
|
||||
|
||||
posts = [postJoinChannel1, postRemoveFromChannel2, postLeaveChannel1].reverse();
|
||||
expectedOutput = {
|
||||
allUserIds: ['user_id_1', 'removed_user_id_1', 'user_id_2'],
|
||||
allUsernames: ['removed_username_1'],
|
||||
messageData: [
|
||||
{postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1']},
|
||||
{postType: PostTypes.REMOVE_FROM_CHANNEL, actorId: 'user_id_2', userIds: ['removed_user_id_1']},
|
||||
{postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_1']},
|
||||
],
|
||||
};
|
||||
expect(combineUserActivitySystemPost(posts)).toEqual(expectedOutput);
|
||||
|
||||
posts = [postJoinChannel1, postRemoveFromTeam2, postLeaveChannel1].reverse();
|
||||
expectedOutput = {
|
||||
allUserIds: ['user_id_1', 'removed_user_id_1'],
|
||||
allUsernames: [],
|
||||
messageData: [
|
||||
{postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1']},
|
||||
{postType: PostTypes.REMOVE_FROM_TEAM, userIds: ['removed_user_id_1']},
|
||||
{postType: PostTypes.LEAVE_CHANNEL, userIds: ['user_id_1']},
|
||||
],
|
||||
};
|
||||
expect(combineUserActivitySystemPost(posts)).toEqual(expectedOutput);
|
||||
});
|
||||
});
|
||||
|
||||
describe('shouldShowJoinLeaveMessages', () => {
|
||||
|
@ -368,6 +368,33 @@ function isUsersRelatedPost(postType: string) {
|
||||
postType === Posts.POST_TYPES.REMOVE_FROM_CHANNEL
|
||||
);
|
||||
}
|
||||
function mergeLastSimilarPosts(userActivities: ActivityEntry[]) {
|
||||
const prevPost = userActivities[userActivities.length - 1];
|
||||
const prePrevPost = userActivities[userActivities.length - 2];
|
||||
const prevPostType = prevPost && prevPost.postType;
|
||||
const prePrevPostType = prePrevPost && prePrevPost.postType;
|
||||
|
||||
if (prevPostType === prePrevPostType) {
|
||||
userActivities.pop();
|
||||
prePrevPost.actorId.push(...prevPost.actorId);
|
||||
}
|
||||
}
|
||||
function isSameActorsInUserActivities(prevActivity: ActivityEntry, curActivity: ActivityEntry) {
|
||||
const prevPostActorsSet = new Set(prevActivity.actorId);
|
||||
const currentPostActorsSet = new Set(curActivity.actorId);
|
||||
|
||||
if (prevPostActorsSet.size !== currentPostActorsSet.size) {
|
||||
return false;
|
||||
}
|
||||
let hasAllActors = true;
|
||||
|
||||
currentPostActorsSet.forEach((actor) => {
|
||||
if (!prevPostActorsSet.has(actor)) {
|
||||
hasAllActors = false;
|
||||
}
|
||||
});
|
||||
return hasAllActors;
|
||||
}
|
||||
export function combineUserActivitySystemPost(systemPosts: Post[] = []) {
|
||||
if (systemPosts.length === 0) {
|
||||
return null;
|
||||
@ -385,12 +412,26 @@ export function combineUserActivitySystemPost(systemPosts: Post[] = []) {
|
||||
const prevPost = userActivities[userActivities.length - 1];
|
||||
const isSamePostType = prevPost && prevPost.postType === post.type;
|
||||
const isSameActor = prevPost && prevPost.actorId[0] === post.user_id;
|
||||
const isJoinedPrevPost = prevPost && prevPost.postType === Posts.POST_TYPES.JOIN_CHANNEL;
|
||||
const isLeftCurrentPost = post.type === Posts.POST_TYPES.LEAVE_CHANNEL;
|
||||
const prePrevPost = userActivities[userActivities.length - 2];
|
||||
const isJoinedPrePrevPost = prePrevPost && prePrevPost.postType === Posts.POST_TYPES.JOIN_CHANNEL;
|
||||
const isLeftPrevPost = prevPost && prevPost.postType === Posts.POST_TYPES.LEAVE_CHANNEL;
|
||||
|
||||
if (prevPost && isSamePostType && (isSameActor || isRemovedPost)) {
|
||||
prevPost.userIds.push(userId);
|
||||
prevPost.usernames.push(username);
|
||||
} else if (isSamePostType && !isSameActor && !isUsersRelatedPost(postType)) {
|
||||
prevPost.actorId.push(actorId);
|
||||
const isSameActors = (prePrevPost && isSameActorsInUserActivities(prePrevPost, prevPost));
|
||||
if (isJoinedPrePrevPost && isLeftPrevPost && isSameActors) {
|
||||
userActivities.pop();
|
||||
prePrevPost.postType = Posts.POST_TYPES.JOIN_LEAVE_CHANNEL;
|
||||
mergeLastSimilarPosts(userActivities);
|
||||
}
|
||||
} else if (isJoinedPrevPost && isLeftCurrentPost && prevPost.actorId.length === 1 && isSameActor) {
|
||||
prevPost.postType = Posts.POST_TYPES.JOIN_LEAVE_CHANNEL;
|
||||
mergeLastSimilarPosts(userActivities);
|
||||
} else {
|
||||
userActivities.push({
|
||||
actorId: [actorId],
|
||||
|
Loading…
Reference in New Issue
Block a user