From 2e004bb7bc12e1236da668949a07efc2dd7576c4 Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Tue, 6 Aug 2024 16:03:47 -0400 Subject: [PATCH] Remove unused post selectors (#27712) * Remove makeGetPostIdsAroundPost and makeGetPostsAroundPost * Remove formatPostInChannel and the one place it is unnecessarily used * Remove makeGetPostsInChannel * Update comment for getPostsInCurrentChannel --- .../src/selectors/entities/posts.test.ts | 966 +----------------- .../src/selectors/entities/posts.ts | 229 +---- 2 files changed, 37 insertions(+), 1158 deletions(-) diff --git a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/posts.test.ts b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/posts.test.ts index ce0a810810..3f20c19b68 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/posts.test.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/posts.test.ts @@ -8,13 +8,12 @@ import type {UserProfile} from '@mattermost/types/users'; import {Posts, Preferences} from 'mattermost-redux/constants'; import * as Selectors from 'mattermost-redux/selectors/entities/posts'; -import type {PostWithFormatData} from 'mattermost-redux/selectors/entities/posts'; import {makeGetProfilesForReactions} from 'mattermost-redux/selectors/entities/users'; import deepFreezeAndThrowOnMutation from 'mattermost-redux/utils/deep_freeze'; import TestHelper from '../../../test/test_helper'; -const p = (override: Partial) => Object.assign(TestHelper.getPostMock(override), override); +const p = (override: Partial) => TestHelper.getPostMock(override); describe('Selectors.Posts', () => { const user1 = TestHelper.fakeUserWithId(); @@ -22,12 +21,12 @@ describe('Selectors.Posts', () => { profiles[user1.id] = user1; const posts = { - a: p({id: 'a', channel_id: '1', create_at: 1, highlight: false, user_id: user1.id}), - b: p({id: 'b', channel_id: '1', create_at: 2, highlight: false, user_id: user1.id}), - c: p({id: 'c', root_id: 'a', channel_id: '1', create_at: 3, highlight: false, user_id: 'b'}), - d: p({id: 'd', root_id: 'b', channel_id: '1', create_at: 4, highlight: false, user_id: 'b'}), - e: p({id: 'e', root_id: 'a', channel_id: '1', create_at: 5, highlight: false, user_id: 'b'}), - f: p({id: 'f', channel_id: '2', create_at: 6, highlight: false, user_id: 'b'}), + a: p({id: 'a', channel_id: '1', create_at: 1, user_id: user1.id}), + b: p({id: 'b', channel_id: '1', create_at: 2, user_id: user1.id}), + c: p({id: 'c', root_id: 'a', channel_id: '1', create_at: 3, user_id: 'b'}), + d: p({id: 'd', root_id: 'b', channel_id: '1', create_at: 4, user_id: 'b'}), + e: p({id: 'e', root_id: 'a', channel_id: '1', create_at: 5, user_id: 'b'}), + f: p({id: 'f', channel_id: '2', create_at: 6, user_id: 'b'}), }; const reaction1 = {user_id: user1.id, emoji_name: '+1'} as Reaction; @@ -110,660 +109,6 @@ describe('Selectors.Posts', () => { expect(getProfilesForReactions(testState, [reaction1])).toEqual([user1]); }); - it('get posts in channel', () => { - const post1 = { - ...posts.a, - isFirstReply: false, - isLastReply: false, - previousPostIsComment: false, - commentedOnPost: undefined, - consecutivePostByUser: false, - replyCount: 2, - isCommentMention: false, - }; - - const post2 = { - ...posts.b, - isFirstReply: false, - isLastReply: false, - previousPostIsComment: false, - commentedOnPost: undefined, - consecutivePostByUser: true, - replyCount: 1, - isCommentMention: false, - }; - - const post3 = { - ...posts.c, - isFirstReply: true, - isLastReply: true, - previousPostIsComment: false, - commentedOnPost: posts.a, - consecutivePostByUser: false, - replyCount: 2, - isCommentMention: false, - }; - - const post4 = { - ...posts.d, - isFirstReply: true, - isLastReply: true, - previousPostIsComment: true, - commentedOnPost: posts.b, - consecutivePostByUser: true, - replyCount: 1, - isCommentMention: false, - }; - - const post5 = { - ...posts.e, - isFirstReply: true, - isLastReply: true, - previousPostIsComment: true, - commentedOnPost: posts.a, - consecutivePostByUser: true, - replyCount: 2, - isCommentMention: false, - }; - - const getPostsInChannel = Selectors.makeGetPostsInChannel(); - expect(getPostsInChannel(testState, '1', 30)).toEqual([post5, post4, post3, post2, post1]); - }); - - it('get posts around post in channel', () => { - const post1: PostWithFormatData = { - ...posts.a, - isFirstReply: false, - isLastReply: false, - previousPostIsComment: false, - commentedOnPost: undefined, - consecutivePostByUser: false, - replyCount: 2, - isCommentMention: false, - highlight: false, - }; - - const post2: PostWithFormatData = { - ...posts.b, - isFirstReply: false, - isLastReply: false, - previousPostIsComment: false, - commentedOnPost: undefined, - consecutivePostByUser: true, - replyCount: 1, - isCommentMention: false, - highlight: false, - }; - - const post3: PostWithFormatData = { - ...posts.c, - isFirstReply: true, - isLastReply: true, - previousPostIsComment: false, - commentedOnPost: posts.a, - consecutivePostByUser: false, - replyCount: 2, - isCommentMention: false, - highlight: true, - }; - - const post4: PostWithFormatData = { - ...posts.d, - isFirstReply: true, - isLastReply: true, - previousPostIsComment: true, - commentedOnPost: posts.b, - consecutivePostByUser: true, - replyCount: 1, - isCommentMention: false, - highlight: false, - }; - - const post5: PostWithFormatData = { - ...posts.e, - isFirstReply: true, - isLastReply: true, - previousPostIsComment: true, - commentedOnPost: posts.a, - consecutivePostByUser: true, - replyCount: 2, - isCommentMention: false, - highlight: false, - }; - - const getPostsAroundPost = Selectors.makeGetPostsAroundPost(); - expect(getPostsAroundPost(testState, post3.id, '1')).toEqual([post5, post4, post3, post2, post1]); - }); - - it('get posts in channel with notify comments as any', () => { - const userAny = TestHelper.fakeUserWithId(); - userAny.notify_props = {comments: 'any'} as UserProfile['notify_props']; - const profilesAny: Record = {}; - profilesAny[userAny.id] = userAny; - - const postsAny = { - a: p({id: 'a', channel_id: '1', create_at: 1, highlight: false, user_id: userAny.id}), - b: p({id: 'b', channel_id: '1', create_at: 2, highlight: false, user_id: 'b'}), - c: p({id: 'c', root_id: 'a', channel_id: '1', create_at: 3, highlight: false, user_id: 'b'}), - d: p({id: 'd', root_id: 'b', channel_id: '1', create_at: 4, highlight: false, user_id: userAny.id}), - e: p({id: 'e', root_id: 'a', channel_id: '1', create_at: 5, highlight: false, user_id: 'b'}), - f: p({id: 'f', root_id: 'b', channel_id: '1', create_at: 6, highlight: false, user_id: 'b'}), - g: p({id: 'g', channel_id: '2', create_at: 7, highlight: false, user_id: 'b'}), - }; - - const testStateAny = deepFreezeAndThrowOnMutation({ - entities: { - general: { - config: { - EnableJoinLeaveMessageByDefault: 'true', - }, - }, - users: { - currentUserId: userAny.id, - profiles: profilesAny, - }, - posts: { - posts: postsAny, - postsInChannel: { - 1: [ - {order: ['f', 'e', 'd', 'c', 'b', 'a'], recent: true}, - ], - 2: [ - {order: ['g'], recent: true}, - ], - }, - postsInThread: { - a: ['c', 'e'], - b: ['d', 'f'], - }, - }, - preferences: { - myPreferences: {}, - }, - }, - }); - - const post1: PostWithFormatData = { - ...postsAny.a, - isFirstReply: false, - isLastReply: false, - previousPostIsComment: false, - commentedOnPost: undefined, - consecutivePostByUser: false, - replyCount: 2, - isCommentMention: false, - highlight: false, - }; - - const post2: PostWithFormatData = { - ...postsAny.b, - isFirstReply: false, - isLastReply: false, - previousPostIsComment: false, - commentedOnPost: undefined, - consecutivePostByUser: false, - replyCount: 2, - isCommentMention: true, - highlight: false, - }; - - const post3: PostWithFormatData = { - ...postsAny.c, - isFirstReply: true, - isLastReply: true, - previousPostIsComment: false, - commentedOnPost: postsAny.a, - consecutivePostByUser: true, - replyCount: 2, - isCommentMention: true, - highlight: false, - }; - - const post4: PostWithFormatData = { - ...postsAny.d, - isFirstReply: true, - isLastReply: true, - previousPostIsComment: true, - commentedOnPost: postsAny.b, - consecutivePostByUser: false, - replyCount: 2, - isCommentMention: false, - highlight: false, - }; - - const post5: PostWithFormatData = { - ...postsAny.e, - isFirstReply: true, - isLastReply: true, - previousPostIsComment: true, - commentedOnPost: postsAny.a, - consecutivePostByUser: false, - replyCount: 2, - isCommentMention: true, - highlight: false, - }; - - const post6: PostWithFormatData = { - ...postsAny.f, - isFirstReply: true, - isLastReply: true, - previousPostIsComment: true, - commentedOnPost: postsAny.b, - consecutivePostByUser: true, - replyCount: 2, - isCommentMention: true, - highlight: false, - }; - - const getPostsInChannel = Selectors.makeGetPostsInChannel(); - expect(getPostsInChannel(testStateAny, '1', 30)).toEqual([post6, post5, post4, post3, post2, post1]); - }); - - it('get posts in channel with notify comments as root', () => { - const userRoot = TestHelper.fakeUserWithId(); - userRoot.notify_props = {comments: 'root'} as UserProfile['notify_props']; - const profilesRoot: Record = {}; - profilesRoot[userRoot.id] = userRoot; - - const postsRoot = { - a: p({id: 'a', channel_id: '1', create_at: 1, highlight: false, user_id: userRoot.id}), - b: p({id: 'b', channel_id: '1', create_at: 2, highlight: false, user_id: 'b'}), - c: p({id: 'c', root_id: 'a', channel_id: '1', create_at: 3, highlight: false, user_id: 'b'}), - d: p({id: 'd', root_id: 'b', channel_id: '1', create_at: 4, highlight: false, user_id: userRoot.id}), - e: p({id: 'e', root_id: 'a', channel_id: '1', create_at: 5, highlight: false, user_id: 'b'}), - f: p({id: 'f', root_id: 'b', channel_id: '1', create_at: 6, highlight: false, user_id: 'b'}), - g: p({id: 'g', channel_id: '2', create_at: 7, highlight: false, user_id: 'b'}), - }; - - const testStateRoot = deepFreezeAndThrowOnMutation({ - entities: { - general: { - config: { - EnableJoinLeaveMessageByDefault: 'true', - }, - }, - users: { - currentUserId: userRoot.id, - profiles: profilesRoot, - }, - posts: { - posts: postsRoot, - postsInChannel: { - 1: [ - {order: ['f', 'e', 'd', 'c', 'b', 'a'], recent: true}, - ], - 2: [ - {order: ['g'], recent: true}, - ], - }, - postsInThread: { - a: ['c', 'e'], - b: ['d', 'f'], - }, - }, - preferences: { - myPreferences: {}, - }, - }, - }); - - const post1 = { - ...postsRoot.a, - isFirstReply: false, - isLastReply: false, - previousPostIsComment: false, - commentedOnPost: undefined, - consecutivePostByUser: false, - replyCount: 2, - isCommentMention: false, - }; - - const post2 = { - ...postsRoot.b, - isFirstReply: false, - isLastReply: false, - previousPostIsComment: false, - commentedOnPost: undefined, - consecutivePostByUser: false, - replyCount: 2, - isCommentMention: false, - }; - - const post3 = { - ...postsRoot.c, - isFirstReply: true, - isLastReply: true, - previousPostIsComment: false, - commentedOnPost: postsRoot.a, - consecutivePostByUser: true, - replyCount: 2, - isCommentMention: true, - }; - - const post4 = { - ...postsRoot.d, - isFirstReply: true, - isLastReply: true, - previousPostIsComment: true, - commentedOnPost: postsRoot.b, - consecutivePostByUser: false, - replyCount: 2, - isCommentMention: false, - }; - - const post5 = { - ...postsRoot.e, - isFirstReply: true, - isLastReply: true, - previousPostIsComment: true, - commentedOnPost: postsRoot.a, - consecutivePostByUser: false, - replyCount: 2, - isCommentMention: true, - }; - - const post6 = { - ...postsRoot.f, - isFirstReply: true, - isLastReply: true, - previousPostIsComment: true, - commentedOnPost: postsRoot.b, - consecutivePostByUser: true, - replyCount: 2, - isCommentMention: false, - }; - - const getPostsInChannel = Selectors.makeGetPostsInChannel(); - expect(getPostsInChannel(testStateRoot, '1', 30)).toEqual([post6, post5, post4, post3, post2, post1]); - }); - - it('get posts in channel with notify comments as never', () => { - const userNever = TestHelper.fakeUserWithId(); - userNever.notify_props = {comments: 'never'} as UserProfile['notify_props']; - const profilesNever: Record = {}; - profilesNever[userNever.id] = userNever; - - const postsNever = { - a: p({id: 'a', channel_id: '1', create_at: 1, highlight: false, user_id: userNever.id}), - b: p({id: 'b', channel_id: '1', create_at: 2, highlight: false, user_id: 'b'}), - c: p({id: 'c', root_id: 'a', channel_id: '1', create_at: 3, highlight: false, user_id: 'b'}), - d: p({id: 'd', root_id: 'b', channel_id: '1', create_at: 4, highlight: false, user_id: userNever.id}), - e: p({id: 'e', root_id: 'a', channel_id: '1', create_at: 5, highlight: false, user_id: 'b'}), - f: p({id: 'f', root_id: 'b', channel_id: '1', create_at: 6, highlight: false, user_id: 'b'}), - g: p({id: 'g', channel_id: '2', create_at: 7, highlight: false, user_id: 'b'}), - }; - - const testStateNever = deepFreezeAndThrowOnMutation({ - entities: { - general: { - config: { - EnableJoinLeaveMessageByDefault: 'true', - }, - }, - users: { - currentUserId: userNever.id, - profiles: profilesNever, - }, - posts: { - posts: postsNever, - postsInChannel: { - 1: [ - {order: ['f', 'e', 'd', 'c', 'b', 'a'], recent: true}, - ], - 2: [ - {order: ['g'], recent: true}, - ], - }, - postsInThread: { - a: ['c', 'e'], - b: ['d', 'f'], - }, - }, - preferences: { - myPreferences: {}, - }, - }, - }); - - const post1: PostWithFormatData = { - ...postsNever.a, - isFirstReply: false, - isLastReply: false, - previousPostIsComment: false, - commentedOnPost: undefined, - consecutivePostByUser: false, - replyCount: 2, - isCommentMention: false, - highlight: false, - }; - - const post2: PostWithFormatData = { - ...postsNever.b, - isFirstReply: false, - isLastReply: false, - previousPostIsComment: false, - commentedOnPost: undefined, - consecutivePostByUser: false, - replyCount: 2, - isCommentMention: false, - highlight: false, - }; - - const post3: PostWithFormatData = { - ...postsNever.c, - isFirstReply: true, - isLastReply: true, - previousPostIsComment: false, - commentedOnPost: postsNever.a, - consecutivePostByUser: true, - replyCount: 2, - isCommentMention: false, - highlight: false, - }; - - const post4: PostWithFormatData = { - ...postsNever.d, - isFirstReply: true, - isLastReply: true, - previousPostIsComment: true, - commentedOnPost: postsNever.b, - consecutivePostByUser: false, - replyCount: 2, - isCommentMention: false, - highlight: false, - }; - - const post5: PostWithFormatData = { - ...postsNever.e, - isFirstReply: true, - isLastReply: true, - previousPostIsComment: true, - commentedOnPost: postsNever.a, - consecutivePostByUser: false, - replyCount: 2, - isCommentMention: false, - highlight: false, - }; - - const post6: PostWithFormatData = { - ...postsNever.f, - isFirstReply: true, - isLastReply: true, - previousPostIsComment: true, - commentedOnPost: postsNever.b, - consecutivePostByUser: true, - replyCount: 2, - isCommentMention: false, - highlight: false, - }; - - const getPostsInChannel = Selectors.makeGetPostsInChannel(); - expect(getPostsInChannel(testStateNever, '1', 30)).toEqual([post6, post5, post4, post3, post2, post1]); - }); - - it('gets posts around post in channel not adding ephemeral post to replyCount', () => { - const userAny = TestHelper.fakeUserWithId(); - userAny.notify_props = {comments: 'any'} as UserProfile['notify_props']; - const profilesAny: Record = {}; - profilesAny[userAny.id] = userAny; - - const postsAny = { - a: {id: 'a', channel_id: '1', create_at: 1, highlight: false, user_id: userAny.id}, - b: {id: 'b', root_id: 'a', channel_id: '1', create_at: 2, highlight: false, user_id: 'b'}, - c: {id: 'c', root_id: 'a', channel_id: '1', create_at: 3, highlight: false, user_id: 'b', type: Posts.POST_TYPES.EPHEMERAL}, - d: {id: 'd', channel_id: '2', create_at: 4, highlight: false, user_id: 'b'}, - }; - - const testStateAny = deepFreezeAndThrowOnMutation({ - entities: { - general: { - config: { - EnableJoinLeaveMessageByDefault: 'true', - }, - }, - users: { - currentUserId: userAny.id, - profiles: profilesAny, - }, - posts: { - posts: postsAny, - postsInChannel: { - 1: [ - {order: ['c', 'b', 'a'], recent: true}, - ], - 2: [ - {order: ['d'], recent: true}, - ], - }, - postsInThread: { - a: ['b', 'c'], - }, - }, - preferences: { - myPreferences: {}, - }, - }, - }); - - const post1 = { - ...postsAny.a, - isFirstReply: false, - isLastReply: false, - previousPostIsComment: false, - commentedOnPost: undefined, - consecutivePostByUser: false, - replyCount: 1, - isCommentMention: false, - highlight: true, - }; - - const post2 = { - ...postsAny.b, - isFirstReply: true, - isLastReply: false, - previousPostIsComment: false, - commentedOnPost: undefined, - consecutivePostByUser: false, - replyCount: 1, - isCommentMention: true, - }; - - const post3 = { - ...postsAny.c, - isFirstReply: false, - isLastReply: true, - previousPostIsComment: true, - commentedOnPost: undefined, - consecutivePostByUser: false, - replyCount: 1, - isCommentMention: true, - }; - - const getPostsAroundPost = Selectors.makeGetPostsAroundPost(); - expect(getPostsAroundPost(testStateAny, post1.id, '1')).toEqual([post3, post2, post1]); - }); - - it('gets posts in channel not adding ephemeral post to replyCount', () => { - const userAny = TestHelper.fakeUserWithId(); - userAny.notify_props = {comments: 'any'} as UserProfile['notify_props']; - const profilesAny: Record = {}; - profilesAny[userAny.id] = userAny; - - const postsAny = { - a: {id: 'a', channel_id: '1', create_at: 1, highlight: false, user_id: userAny.id}, - b: {id: 'b', root_id: 'a', channel_id: '1', create_at: 2, highlight: false, user_id: 'b', type: Posts.POST_TYPES.EPHEMERAL}, - c: {id: 'c', root_id: 'a', channel_id: '1', create_at: 3, highlight: false, user_id: 'b', state: Posts.POST_DELETED}, - d: {id: 'd', channel_id: '2', create_at: 4, highlight: false, user_id: 'b'}, - }; - - const testStateAny = deepFreezeAndThrowOnMutation({ - entities: { - general: { - config: { - EnableJoinLeaveMessageByDefault: 'true', - }, - }, - users: { - currentUserId: userAny.id, - profiles: profilesAny, - }, - posts: { - posts: postsAny, - postsInChannel: { - 1: [ - {order: ['c', 'b', 'a'], recent: true}, - ], - 2: [ - {order: ['d'], recent: true}, - ], - }, - postsInThread: { - a: ['b', 'c'], - }, - }, - preferences: { - myPreferences: {}, - }, - }, - }); - - const post1 = { - ...postsAny.a, - isFirstReply: false, - isLastReply: false, - previousPostIsComment: false, - commentedOnPost: undefined, - consecutivePostByUser: false, - replyCount: 0, - isCommentMention: false, - }; - - const post2 = { - ...postsAny.b, - isFirstReply: true, - isLastReply: false, - previousPostIsComment: false, - commentedOnPost: undefined, - consecutivePostByUser: false, - replyCount: 0, - isCommentMention: true, - }; - - const post3 = { - ...postsAny.c, - isFirstReply: false, - isLastReply: true, - previousPostIsComment: true, - commentedOnPost: undefined, - consecutivePostByUser: false, - replyCount: 0, - isCommentMention: true, - }; - - const getPostsInChannel = Selectors.makeGetPostsInChannel(); - expect(getPostsInChannel(testStateAny, '1', 30)).toEqual([post3, post2, post1]); - }); - it('get current history item', () => { const testState1 = deepFreezeAndThrowOnMutation({ entities: { @@ -1062,293 +407,6 @@ describe('Selectors.Posts', () => { }); }); - describe('getPostIdsAroundPost', () => { - it('no posts around', () => { - const getPostIdsAroundPost = Selectors.makeGetPostIdsAroundPost(); - - const state = { - entities: { - posts: { - postsInChannel: { - 1234: [ - {order: ['a'], recent: true}, - ], - }, - }, - }, - } as unknown as GlobalState; - - expect(getPostIdsAroundPost(state, 'a', '1234')).toEqual(['a']); - }); - - it('posts around', () => { - const getPostIdsAroundPost = Selectors.makeGetPostIdsAroundPost(); - - const state = { - entities: { - posts: { - postsInChannel: { - 1234: [ - {order: ['a', 'b', 'c', 'd', 'e'], recent: true}, - ], - }, - }, - }, - } as unknown as GlobalState; - - expect(getPostIdsAroundPost(state, 'c', '1234')).toEqual(['a', 'b', 'c', 'd', 'e']); - }); - - it('posts before limit', () => { - const getPostIdsAroundPost = Selectors.makeGetPostIdsAroundPost(); - - const state = { - entities: { - posts: { - postsInChannel: { - 1234: [ - {order: ['a', 'b', 'c', 'd', 'e'], recent: true}, - ], - }, - }, - }, - } as unknown as GlobalState; - - expect(getPostIdsAroundPost(state, 'a', '1234', {postsBeforeCount: 2})).toEqual(['a', 'b', 'c']); - }); - - it('posts after limit', () => { - const getPostIdsAroundPost = Selectors.makeGetPostIdsAroundPost(); - - const state = { - entities: { - posts: { - postsInChannel: { - 1234: [ - {order: ['a', 'b', 'c', 'd', 'e'], recent: true}, - ], - }, - }, - }, - } as unknown as GlobalState; - - expect(getPostIdsAroundPost(state, 'e', '1234', {postsAfterCount: 3})).toEqual(['b', 'c', 'd', 'e']); - }); - - it('posts before/after limit', () => { - const getPostIdsAroundPost = Selectors.makeGetPostIdsAroundPost(); - - const state = { - entities: { - posts: { - postsInChannel: { - 1234: [ - {order: ['a', 'b', 'c', 'd', 'e', 'f'], recent: true}, - ], - }, - }, - }, - } as unknown as GlobalState; - - expect(getPostIdsAroundPost(state, 'c', '1234', {postsBeforeCount: 2, postsAfterCount: 1})).toEqual(['b', 'c', 'd', 'e']); - }); - - it('memoization', () => { - const getPostIdsAroundPost = Selectors.makeGetPostIdsAroundPost(); - - let state = { - entities: { - posts: { - postsInChannel: { - 1234: [ - {order: ['a', 'b', 'c', 'd', 'e'], recent: true}, - ], - }, - }, - }, - } as unknown as GlobalState; - - // No limit, no changes - let previous = getPostIdsAroundPost(state, 'c', '1234'); - let now = getPostIdsAroundPost(state, 'c', '1234'); - expect(now).toEqual(['a', 'b', 'c', 'd', 'e']); - expect(now).toBe(previous); - - // Changes to posts in another channel - state = { - ...state, - entities: { - ...state.entities, - posts: { - ...state.entities.posts, - postsInChannel: { - ...state.entities.posts.postsInChannel, - abcd: [ - {order: ['g', 'h', 'i', 'j', 'k', 'l'], recent: true}, - ], - }, - }, - }, - }; - - previous = now; - now = getPostIdsAroundPost(state, 'c', '1234'); - expect(now).toEqual(['a', 'b', 'c', 'd', 'e']); - expect(now).toBe(previous); - - // Changes to posts in this channel - state = { - ...state, - entities: { - ...state.entities, - posts: { - ...state.entities.posts, - postsInChannel: { - ...state.entities.posts.postsInChannel, - 1234: [ - {order: ['a', 'b', 'c', 'd', 'e', 'f'], recent: true}, - ], - }, - }, - }, - }; - - previous = now; - now = getPostIdsAroundPost(state, 'c', '1234'); - expect(now).toEqual(['a', 'b', 'c', 'd', 'e', 'f']); - expect(now).not.toBe(previous); - - previous = now; - now = getPostIdsAroundPost(state, 'c', '1234'); - expect(now).toEqual(['a', 'b', 'c', 'd', 'e', 'f']); - expect(now).toBe(previous); - - // Change of channel - previous = now; - now = getPostIdsAroundPost(state, 'i', 'abcd'); - expect(now).toEqual(['g', 'h', 'i', 'j', 'k', 'l']); - expect(now).not.toBe(previous); - - previous = now; - now = getPostIdsAroundPost(state, 'i', 'abcd'); - expect(now).toEqual(['g', 'h', 'i', 'j', 'k', 'l']); - expect(now).toBe(previous); - - // With limits - previous = now; - now = getPostIdsAroundPost(state, 'i', 'abcd', {postsBeforeCount: 2, postsAfterCount: 1}); - expect(now).toEqual(['h', 'i', 'j', 'k']); - expect(now).not.toBe(previous); - - previous = now; - now = getPostIdsAroundPost(state, 'i', 'abcd', {postsBeforeCount: 2, postsAfterCount: 1}); // Note that the options object is a new object each time - expect(now).toEqual(['h', 'i', 'j', 'k']); - expect(now).toBe(previous); - - // Change of limits - previous = now; - now = getPostIdsAroundPost(state, 'i', 'abcd', {postsBeforeCount: 1, postsAfterCount: 2}); - expect(now).toEqual(['g', 'h', 'i', 'j']); - expect(now).not.toBe(previous); - - previous = now; - now = getPostIdsAroundPost(state, 'i', 'abcd', {postsBeforeCount: 1, postsAfterCount: 2}); - expect(now).toEqual(['g', 'h', 'i', 'j']); - expect(now).toBe(previous); - - // Change of post - previous = now; - now = getPostIdsAroundPost(state, 'j', 'abcd', {postsBeforeCount: 1, postsAfterCount: 2}); - expect(now).toEqual(['h', 'i', 'j', 'k']); - expect(now).not.toBe(previous); - - previous = now; - now = getPostIdsAroundPost(state, 'j', 'abcd', {postsBeforeCount: 1, postsAfterCount: 2}); - expect(now).toEqual(['h', 'i', 'j', 'k']); - expect(now).toBe(previous); - - // Change of posts past limit - state = { - ...state, - entities: { - ...state.entities, - posts: { - ...state.entities.posts, - postsInChannel: { - ...state.entities.posts.postsInChannel, - abcd: [ - {order: ['y', 'g', 'h', 'i', 'j', 'k', 'l', 'f', 'z'], recent: true}, - ], - }, - }, - }, - }; - previous = now; - now = getPostIdsAroundPost(state, 'j', 'abcd', {postsBeforeCount: 1, postsAfterCount: 2}); - expect(now).toEqual(['h', 'i', 'j', 'k']); - expect(now).toBe(previous); - - // Change of post order - state = { - ...state, - entities: { - ...state.entities, - posts: { - ...state.entities.posts, - postsInChannel: { - ...state.entities.posts.postsInChannel, - abcd: [ - {order: ['y', 'g', 'i', 'h', 'j', 'l', 'k', 'z'], recent: true}, - ], - }, - }, - }, - }; - - previous = now; - now = getPostIdsAroundPost(state, 'j', 'abcd', {postsBeforeCount: 1, postsAfterCount: 2}); - expect(now).toEqual(['i', 'h', 'j', 'l']); - expect(now).not.toBe(previous); - - previous = now; - now = getPostIdsAroundPost(state, 'j', 'abcd', {postsBeforeCount: 1, postsAfterCount: 2}); - expect(now).toEqual(['i', 'h', 'j', 'l']); - expect(now).toBe(previous); - }); - - it('memoization with multiple selectors', () => { - const getPostIdsAroundPost1 = Selectors.makeGetPostIdsAroundPost(); - const getPostIdsAroundPost2 = Selectors.makeGetPostIdsAroundPost(); - - const state = { - entities: { - posts: { - postsInChannel: { - 1234: [ - {order: ['a', 'b', 'c', 'd', 'e', 'f'], recent: true}, - ], - abcd: [ - {order: ['g', 'h', 'i'], recent: true}, - ], - }, - }, - }, - } as unknown as GlobalState; - - const previous1 = getPostIdsAroundPost1(state, 'c', '1234'); - const previous2 = getPostIdsAroundPost2(state, 'h', 'abcd', {postsBeforeCount: 1, postsAfterCount: 0}); - - expect(previous1).not.toBe(previous2); - - const now1 = getPostIdsAroundPost1(state, 'c', '1234'); - const now2 = getPostIdsAroundPost2(state, 'i', 'abcd', {postsBeforeCount: 1, postsAfterCount: 0}); - - expect(now1).toBe(previous1); - expect(now2).not.toBe(previous2); - expect(now1).not.toBe(now2); - }); - }); - describe('makeGetPostsChunkAroundPost', () => { it('no posts around', () => { const getPostsChunkAroundPost = Selectors.makeGetPostsChunkAroundPost(); @@ -1939,11 +997,11 @@ describe('Selectors.Posts', () => { it('return first post which dosent have POST_DELETED state', () => { const postsAny = { - a: {id: 'a', channel_id: 'a', create_at: 1, highlight: false, user_id: 'a'}, - b: {id: 'b', root_id: 'a', channel_id: 'abcd', create_at: 3, highlight: false, user_id: 'b', state: Posts.POST_DELETED}, - c: {id: 'c', root_id: 'a', channel_id: 'abcd', create_at: 3, highlight: false, user_id: 'b', type: 'system_join_channel'}, - d: {id: 'd', root_id: 'a', channel_id: 'abcd', create_at: 3, highlight: false, user_id: 'b', type: Posts.POST_TYPES.EPHEMERAL}, - e: {id: 'e', channel_id: 'abcd', create_at: 4, highlight: false, user_id: 'b'}, + a: {id: 'a', channel_id: 'a', create_at: 1, user_id: 'a'}, + b: {id: 'b', root_id: 'a', channel_id: 'abcd', create_at: 3, user_id: 'b', state: Posts.POST_DELETED}, + c: {id: 'c', root_id: 'a', channel_id: 'abcd', create_at: 3, user_id: 'b', type: 'system_join_channel'}, + d: {id: 'd', root_id: 'a', channel_id: 'abcd', create_at: 3, user_id: 'b', type: Posts.POST_TYPES.EPHEMERAL}, + e: {id: 'e', channel_id: 'abcd', create_at: 4, user_id: 'b'}, }; const state = { entities: { diff --git a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/posts.ts b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/posts.ts index cbdbb0aabf..50e21923dc 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/posts.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/posts.ts @@ -14,7 +14,6 @@ import type {GlobalState} from '@mattermost/types/store'; import type {Team} from '@mattermost/types/teams'; import type {UserProfile} from '@mattermost/types/users'; import type { - IDMappedObjects, RelationOneToOne, RelationOneToMany, } from '@mattermost/types/utilities'; @@ -113,24 +112,6 @@ export function getPostIdsInCurrentChannel(state: GlobalState): Array PostWithFormatData[] | undefined | null = (() => { - const getPostsInChannel = makeGetPostsInChannel(); - return (state: GlobalState) => getPostsInChannel(state, state.entities.channels.currentChannelId, -1); -})(); - export function makeGetPostIdsForThread(): (state: GlobalState, postId: Post['id']) => Array { const getPostsForThread = makeGetPostsForThread(); @@ -170,119 +151,6 @@ export function makeGetPostsChunkAroundPost(): (state: GlobalState, postId: Post ); } -export function makeGetPostIdsAroundPost(): (state: GlobalState, postId: Post['id'], channelId: Channel['id'], a?: { - postsBeforeCount?: number; - postsAfterCount?: number; -}) => Array | undefined | null { - const getPostsChunkAroundPost = makeGetPostsChunkAroundPost(); - return createIdsSelector( - 'makeGetPostIdsAroundPost', - (state: GlobalState, postId: string, channelId: string) => getPostsChunkAroundPost(state, postId, channelId), - (state: GlobalState, postId) => postId, - (state: GlobalState, postId, channelId, options) => options && options.postsBeforeCount, - (state: GlobalState, postId, channelId, options) => options && options.postsAfterCount, - (postsChunk, postId, postsBeforeCount = Posts.POST_CHUNK_SIZE / 2, postsAfterCount = Posts.POST_CHUNK_SIZE / 2) => { - if (!postsChunk || !postsChunk.order) { - return null; - } - - const postIds = postsChunk.order; - const index = postIds.indexOf(postId); - - // Remember that posts that come after the post have a smaller index - const minPostIndex = postsAfterCount === -1 ? 0 : Math.max(index - postsAfterCount, 0); - const maxPostIndex = postsBeforeCount === -1 ? postIds.length : Math.min(index + postsBeforeCount + 1, postIds.length); // Needs the extra 1 to include the focused post - - return postIds.slice(minPostIndex, maxPostIndex); - }, - ); -} - -function formatPostInChannel(post: Post, previousPost: Post | undefined | null, index: number, allPosts: IDMappedObjects, postsInThread: RelationOneToMany, postIds: Array, currentUser: UserProfile, focusedPostId: Post['id']): PostWithFormatData { - let isFirstReply = false; - let isLastReply = false; - let highlight = false; - let commentedOnPost: Post| undefined; - - if (post.id === focusedPostId) { - highlight = true; - } - - if (post.root_id) { - if (previousPost && previousPost.root_id !== post.root_id) { - // Post is the first reply in a list of consecutive replies - isFirstReply = true; - - if (previousPost && previousPost.id !== post.root_id) { - commentedOnPost = allPosts[post.root_id]; - } - } - - if (index - 1 < 0 || allPosts[postIds[index - 1]].root_id !== post.root_id) { - // Post is the last reply in a list of consecutive replies - isLastReply = true; - } - } - - let previousPostIsComment = false; - - if (previousPost && previousPost.root_id) { - previousPostIsComment = true; - } - - const postFromWebhook = Boolean(post.props && post.props.from_webhook); - const prevPostFromWebhook = Boolean(previousPost && previousPost.props && previousPost.props.from_webhook); - let consecutivePostByUser = false; - if (previousPost && - previousPost.user_id === post.user_id && - post.create_at - previousPost.create_at <= Posts.POST_COLLAPSE_TIMEOUT && - !postFromWebhook && !prevPostFromWebhook && - !isSystemMessage(post) && !isSystemMessage(previousPost)) { - // The last post and this post were made by the same user within some time - consecutivePostByUser = true; - } - - let threadRepliedToByCurrentUser = false; - let replyCount = 0; - let isCommentMention = false; - - if (currentUser) { - const rootId = post.root_id || post.id; - const threadIds = postsInThread[rootId] || []; - - for (const pid of threadIds) { - const p = allPosts[pid]; - if (!p) { - continue; - } - - if (p.user_id === currentUser.id) { - threadRepliedToByCurrentUser = true; - } - - if (!isPostEphemeral(p)) { - replyCount += 1; - } - } - - const rootPost = allPosts[rootId]; - - isCommentMention = isPostCommentMention({post, currentUser, threadRepliedToByCurrentUser, rootPost}); - } - - return { - ...post, - isFirstReply, - isLastReply, - previousPostIsComment, - commentedOnPost, - consecutivePostByUser, - replyCount, - isCommentMention, - highlight, - }; -} - function isPostInteractable(post: Post | undefined) { return post && !post.delete_at && @@ -337,81 +205,34 @@ export function getLatestPostToEdit(state: GlobalState, channelId: string, rootI export const getLatestReplyablePostId: (state: GlobalState) => Post['id'] = (state) => getLatestInteractablePostId(state, getCurrentChannelId(state)); -// makeGetPostsInChannel creates a selector that returns up to the given number of posts loaded at the bottom of the -// given channel. It does not include older posts such as those loaded by viewing a thread or a permalink. -export function makeGetPostsInChannel(): (state: GlobalState, channelId: Channel['id'], numPosts: number) => PostWithFormatData[] | undefined | null { - return createSelector( - 'makeGetPostsInChannel', - getAllPosts, - getPostsInThread, - (state: GlobalState, channelId: Channel['id']) => getPostIdsInChannel(state, channelId), - getCurrentUser, - shouldShowJoinLeaveMessages, - (state: GlobalState, channelId: Channel['id'], numPosts: number) => numPosts || Posts.POST_CHUNK_SIZE, - (allPosts, postsInThread, allPostIds, currentUser, showJoinLeave, numPosts) => { - if (!allPostIds) { - return null; +// getPostsInCurrentChannel returns an array of all recent posts loaded at the bottom of the given channel. +// It does not include older posts such as those loaded by viewing a thread or a permalink. +export const getPostsInCurrentChannel: (state: GlobalState) => Post[] | undefined | null = createSelector( + 'getPostsInCurrentChannel', + getAllPosts, + getPostIdsInCurrentChannel, + getCurrentUser, + shouldShowJoinLeaveMessages, + (allPosts, postIds, currentUser, showJoinLeave) => { + if (!postIds) { + return null; + } + + const posts: Post[] = []; + + for (let i = 0; i < postIds.length; i++) { + const post = allPosts[postIds[i]]; + + if (!post || shouldFilterJoinLeavePost(post, showJoinLeave, currentUser ? currentUser.username : '')) { + continue; } - const posts: PostWithFormatData[] = []; + posts.push(post); + } - const postIds = numPosts === -1 ? allPostIds : allPostIds.slice(0, numPosts); - - for (let i = 0; i < postIds.length; i++) { - const post = allPosts[postIds[i]]; - - if (!post || shouldFilterJoinLeavePost(post, showJoinLeave, currentUser ? currentUser.username : '')) { - continue; - } - - const previousPost = allPosts[postIds[i + 1]] || null; - posts.push(formatPostInChannel(post, previousPost, i, allPosts, postsInThread, postIds, currentUser, '')); - } - - return posts; - }, - ); -} - -export function makeGetPostsAroundPost(): (state: GlobalState, postId: Post['id'], channelId: Channel['id']) => PostWithFormatData[] | undefined | null { - const getPostIdsAroundPost = makeGetPostIdsAroundPost(); - const options = { - postsBeforeCount: -1, // Where this is used in the web app, view state is used to determine how far back to display - postsAfterCount: Posts.POST_CHUNK_SIZE / 2, - }; - - return createSelector( - 'makeGetPostsAroundPost', - (state: GlobalState, focusedPostId: string, channelId: string) => getPostIdsAroundPost(state, focusedPostId, channelId, options), - getAllPosts, - getPostsInThread, - (state: GlobalState, focusedPostId) => focusedPostId, - getCurrentUser, - shouldShowJoinLeaveMessages, - (postIds, allPosts, postsInThread, focusedPostId, currentUser, showJoinLeave) => { - if (!postIds || !currentUser) { - return null; - } - - const posts: PostWithFormatData[] = []; - - for (let i = 0; i < postIds.length; i++) { - const post = allPosts[postIds[i]]; - - if (!post || shouldFilterJoinLeavePost(post, showJoinLeave, currentUser.username)) { - continue; - } - - const previousPost = allPosts[postIds[i + 1]] || null; - const formattedPost = formatPostInChannel(post, previousPost, i, allPosts, postsInThread, postIds, currentUser, focusedPostId); - - posts.push(formattedPost); - } - - return posts; - }, - ); -} + return posts; + }, +); // Returns a function that creates a creates a selector that will get the posts for a given thread. // That selector will take a props object (containing a rootId field) as its