diff --git a/components/post_view/post/index.js b/components/post_view/post/index.js index c7bc19b9b58e..893c71dc667f 100644 --- a/components/post_view/post/index.js +++ b/components/post_view/post/index.js @@ -3,21 +3,20 @@ import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; -import {createSelector} from 'reselect'; import {Posts} from 'mattermost-redux/constants'; import {getChannel} from 'mattermost-redux/selectors/entities/channels'; import {getPost, makeIsPostCommentMention} from 'mattermost-redux/selectors/entities/posts'; import {get} from 'mattermost-redux/selectors/entities/preferences'; import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; -import {isPostEphemeral, isSystemMessage} from 'mattermost-redux/utils/post_utils'; +import {isSystemMessage} from 'mattermost-redux/utils/post_utils'; import {markPostAsUnread} from 'actions/post_actions'; import {selectPost, selectPostCard} from 'actions/views/rhs'; import {isArchivedChannel} from 'utils/channel_utils'; import {Preferences} from 'utils/constants'; -import {makeCreateAriaLabelForPost} from 'utils/post_utils.jsx'; +import {makeCreateAriaLabelForPost, makeGetReplyCount} from 'utils/post_utils.jsx'; import Post from './post.jsx'; @@ -37,21 +36,6 @@ export function isFirstReply(post, previousPost) { return false; } -export function makeGetReplyCount() { - return createSelector( - (state) => state.entities.posts.posts, - (state, post) => state.entities.posts.postsInThread[post.root_id || post.id], - (allPosts, postIds) => { - if (!postIds) { - return 0; - } - - // Count the number of non-ephemeral posts in the thread - return postIds.map((id) => allPosts[id]).filter((post) => post && !isPostEphemeral(post)).length; - } - ); -} - function makeMapStateToProps() { const getReplyCount = makeGetReplyCount(); const isPostCommentMention = makeIsPostCommentMention(); diff --git a/components/post_view/post/index.test.js b/components/post_view/post/index.test.js index 39bdbbc8656a..7e6cd362efb7 100644 --- a/components/post_view/post/index.test.js +++ b/components/post_view/post/index.test.js @@ -1,9 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Posts} from 'mattermost-redux/constants'; - -import {isFirstReply, makeGetReplyCount} from './index'; +import {isFirstReply} from './index'; describe('isFirstReply', () => { for (const testCase of [ @@ -61,89 +59,3 @@ describe('isFirstReply', () => { }); } }); - -describe('makeGetReplyCount', () => { - test('should return the number of comments when called on a root post', () => { - const getReplyCount = makeGetReplyCount(); - - const state = { - entities: { - posts: { - posts: { - post1: {id: 'post1'}, - post2: {id: 'post2', root_id: 'post1'}, - post3: {id: 'post3', root_id: 'post1'}, - }, - postsInThread: { - post1: ['post2', 'post3'], - }, - }, - }, - }; - const post = state.entities.posts.posts.post1; - - expect(getReplyCount(state, post)).toBe(2); - }); - - test('should return the number of comments when called on a comment', () => { - const getReplyCount = makeGetReplyCount(); - - const state = { - entities: { - posts: { - posts: { - post1: {id: 'post1'}, - post2: {id: 'post2', root_id: 'post1'}, - post3: {id: 'post3', root_id: 'post1'}, - }, - postsInThread: { - post1: ['post2', 'post3'], - }, - }, - }, - }; - const post = state.entities.posts.posts.post3; - - expect(getReplyCount(state, post)).toBe(2); - }); - - test('should return 0 when called on a post without comments', () => { - const getReplyCount = makeGetReplyCount(); - - const state = { - entities: { - posts: { - posts: { - post1: {id: 'post1'}, - }, - postsInThread: {}, - }, - }, - }; - const post = state.entities.posts.posts.post1; - - expect(getReplyCount(state, post)).toBe(0); - }); - - test('should not count ephemeral comments', () => { - const getReplyCount = makeGetReplyCount(); - - const state = { - entities: { - posts: { - posts: { - post1: {id: 'post1'}, - post2: {id: 'post2', root_id: 'post1', type: Posts.POST_TYPES.EPHEMERAL}, - post3: {id: 'post3', root_id: 'post1'}, - }, - postsInThread: { - post1: ['post2', 'post3'], - }, - }, - }, - }; - const post = state.entities.posts.posts.post1; - - expect(getReplyCount(state, post)).toBe(1); - }); -}); diff --git a/components/search_results_item/index.js b/components/search_results_item/index.js index 4bf787609a43..30691f3a4deb 100644 --- a/components/search_results_item/index.js +++ b/components/search_results_item/index.js @@ -3,6 +3,7 @@ import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; + import {getChannel} from 'mattermost-redux/selectors/entities/channels'; import {getConfig} from 'mattermost-redux/selectors/entities/general'; import {getUser} from 'mattermost-redux/selectors/entities/users'; @@ -18,12 +19,13 @@ import { setRhsExpanded, } from 'actions/views/rhs'; -import {makeCreateAriaLabelForPost} from 'utils/post_utils.jsx'; +import {makeCreateAriaLabelForPost, makeGetReplyCount} from 'utils/post_utils.jsx'; import {getDirectTeammate, getDisplayNameByUser} from 'utils/utils.jsx'; import SearchResultsItem from './search_results_item.jsx'; function mapStateToProps() { + const getReplyCount = makeGetReplyCount(); const createAriaLabelForPost = makeCreateAriaLabelForPost(); const getCommentCountForPost = makeGetCommentCountForPost(); @@ -48,7 +50,8 @@ function mapStateToProps() { isFlagged: isPostFlagged(post.id, preferences), isBot: user ? user.is_bot : false, directTeammate, - displayName: getDisplayNameByUser(state, directTeammate) + displayName: getDisplayNameByUser(state, directTeammate), + replyCount: getReplyCount(state, post), }; }; } diff --git a/components/search_results_item/search_results_item.jsx b/components/search_results_item/search_results_item.jsx index 36d2f75b181b..c7e477a23b85 100644 --- a/components/search_results_item/search_results_item.jsx +++ b/components/search_results_item/search_results_item.jsx @@ -109,6 +109,11 @@ class SearchResultsItem extends React.PureComponent { intl: intlShape.isRequired, directTeammate: PropTypes.string.isRequired, displayName: PropTypes.string.isRequired, + + /** + * The number of replies in the same thread as this post + */ + replyCount: PropTypes.number, }; static defaultProps = { @@ -307,8 +312,10 @@ class SearchResultsItem extends React.PureComponent { item.indexOf(PostListRowListIds.START_OF_NEW_MESSAGES) === 0 ); } + +export function makeGetReplyCount() { + return createSelector( + (state) => state.entities.posts.posts, + (state, post) => state.entities.posts.postsInThread[post.root_id || post.id], + (allPosts, postIds) => { + if (!postIds) { + return 0; + } + + // Count the number of non-ephemeral posts in the thread + return postIds.map((id) => allPosts[id]).filter((post) => post && !isPostEphemeral(post)).length; + } + ); +} diff --git a/utils/post_utils.test.jsx b/utils/post_utils.test.jsx index 6d2677050760..43a5c1d9ce72 100644 --- a/utils/post_utils.test.jsx +++ b/utils/post_utils.test.jsx @@ -4,6 +4,7 @@ import assert from 'assert'; import {createIntl} from 'react-intl'; +import {Posts} from 'mattermost-redux/constants'; import * as PostUtils from 'utils/post_utils.jsx'; import {PostListRowListIds} from 'utils/constants'; @@ -741,3 +742,89 @@ describe('PostUtils.splitMessageBasedOnCaretPosition', () => { assert.equal('st Message', stringPieces.lastPiece); }); }); + +describe('makeGetReplyCount', () => { + test('should return the number of comments when called on a root post', () => { + const getReplyCount = PostUtils.makeGetReplyCount(); + + const state = { + entities: { + posts: { + posts: { + post1: {id: 'post1'}, + post2: {id: 'post2', root_id: 'post1'}, + post3: {id: 'post3', root_id: 'post1'}, + }, + postsInThread: { + post1: ['post2', 'post3'], + }, + }, + }, + }; + const post = state.entities.posts.posts.post1; + + expect(getReplyCount(state, post)).toBe(2); + }); + + test('should return the number of comments when called on a comment', () => { + const getReplyCount = PostUtils.makeGetReplyCount(); + + const state = { + entities: { + posts: { + posts: { + post1: {id: 'post1'}, + post2: {id: 'post2', root_id: 'post1'}, + post3: {id: 'post3', root_id: 'post1'}, + }, + postsInThread: { + post1: ['post2', 'post3'], + }, + }, + }, + }; + const post = state.entities.posts.posts.post3; + + expect(getReplyCount(state, post)).toBe(2); + }); + + test('should return 0 when called on a post without comments', () => { + const getReplyCount = PostUtils.makeGetReplyCount(); + + const state = { + entities: { + posts: { + posts: { + post1: {id: 'post1'}, + }, + postsInThread: {}, + }, + }, + }; + const post = state.entities.posts.posts.post1; + + expect(getReplyCount(state, post)).toBe(0); + }); + + test('should not count ephemeral comments', () => { + const getReplyCount = PostUtils.makeGetReplyCount(); + + const state = { + entities: { + posts: { + posts: { + post1: {id: 'post1'}, + post2: {id: 'post2', root_id: 'post1', type: Posts.POST_TYPES.EPHEMERAL}, + post3: {id: 'post3', root_id: 'post1'}, + }, + postsInThread: { + post1: ['post2', 'post3'], + }, + }, + }, + }; + const post = state.entities.posts.posts.post1; + + expect(getReplyCount(state, post)).toBe(1); + }); +});