diff --git a/app/actions/views/emoji.js b/app/actions/views/emoji.js
index 6692fe113..bc2d75825 100644
--- a/app/actions/views/emoji.js
+++ b/app/actions/views/emoji.js
@@ -2,16 +2,16 @@
// See License.txt for license information.
import {addReaction} from 'mattermost-redux/actions/posts';
-import {getPostsInCurrentChannel, makeGetPostsForThread} from 'mattermost-redux/selectors/entities/posts';
+import {getPostIdsInCurrentChannel, makeGetPostIdsForThread} from 'mattermost-redux/selectors/entities/posts';
-const getPostsForThread = makeGetPostsForThread();
+const getPostIdsForThread = makeGetPostIdsForThread();
export function addReactionToLatestPost(emoji, rootId) {
return async (dispatch, getState) => {
const state = getState();
- const posts = rootId ? getPostsForThread(state, {rootId}) : getPostsInCurrentChannel(state);
- const lastPost = posts[0];
+ const postIds = rootId ? getPostIdsForThread(state, rootId) : getPostIdsInCurrentChannel(state);
+ const lastPostId = postIds[0];
- dispatch(addReaction(lastPost.id, emoji));
+ dispatch(addReaction(lastPostId, emoji));
};
}
diff --git a/app/components/post/index.js b/app/components/post/index.js
index a564abf45..a9f60fc2e 100644
--- a/app/components/post/index.js
+++ b/app/components/post/index.js
@@ -18,11 +18,38 @@ function mapStateToProps(state, ownProps) {
const {config, license} = state.entities.general;
const roles = getCurrentUserId(state) ? getCurrentUserRoles(state) : '';
+ let isFirstReply = true;
+ let isLastReply = true;
+ let commentedOnPost = null;
+ if (ownProps.renderReplies && post && post.root_id) {
+ if (ownProps.previousPostId) {
+ const previousPost = getPost(state, ownProps.previousPostId);
+
+ if (previousPost && (previousPost.id === post.root_id || previousPost.root_id === post.root_id)) {
+ // Previous post is root post or previous post is in same thread
+ isFirstReply = false;
+ } else {
+ // Last post is not a comment on the same message
+ commentedOnPost = getPost(state, post.root_id);
+ }
+ }
+
+ if (ownProps.nextPostId) {
+ const nextPost = getPost(state, ownProps.nextPostId);
+
+ if (nextPost && nextPost.root_id === post.root_id) {
+ isLastReply = false;
+ }
+ }
+ }
+
return {
- post,
config,
currentUserId: getCurrentUserId(state),
- highlight: ownProps.highlight,
+ post,
+ isFirstReply,
+ isLastReply,
+ commentedOnPost,
license,
roles,
theme: getTheme(state)
diff --git a/app/components/post/post.js b/app/components/post/post.js
index 3ab5ea62d..0fb39cdd0 100644
--- a/app/components/post/post.js
+++ b/app/components/post/post.js
@@ -41,6 +41,7 @@ class Post extends PureComponent {
intl: intlShape.isRequired,
style: ViewPropTypes.style,
post: PropTypes.object,
+ postId: PropTypes.string.isRequired, // Used by container // eslint-disable-line no-unused-prop-types
renderReplies: PropTypes.bool,
isFirstReply: PropTypes.bool,
isLastReply: PropTypes.bool,
@@ -65,15 +66,27 @@ class Post extends PureComponent {
const {config, license, currentUserId, roles, post} = props;
this.editDisableAction = new DelayedAction(this.handleEditDisable);
- this.state = {
- canEdit: canEditPost(config, license, currentUserId, post, this.editDisableAction),
- canDelete: canDeletePost(config, license, currentUserId, post, isAdmin(roles), isSystemAdmin(roles))
- };
+ if (post) {
+ this.state = {
+ canEdit: canEditPost(config, license, currentUserId, post, this.editDisableAction),
+ canDelete: canDeletePost(config, license, currentUserId, post, isAdmin(roles), isSystemAdmin(roles))
+ };
+ } else {
+ this.state = {
+ canEdit: false,
+ canDelete: false
+ };
+ }
}
componentWillReceiveProps(nextProps) {
- const {config, license, currentUserId, roles, post} = nextProps;
- if (post) {
+ if (nextProps.config !== this.props.config ||
+ nextProps.license !== this.props.license ||
+ nextProps.currentUserId !== this.props.currentUserId ||
+ nextProps.post !== this.props.post ||
+ nextProps.roles !== this.props.roles) {
+ const {config, license, currentUserId, roles, post} = nextProps;
+
this.setState({
canEdit: canEditPost(config, license, currentUserId, post, this.editDisableAction),
canDelete: canDeletePost(config, license, currentUserId, post, isAdmin(roles), isSystemAdmin(roles))
diff --git a/app/components/post_list/index.js b/app/components/post_list/index.js
index 61db323c5..96c09e38e 100644
--- a/app/components/post_list/index.js
+++ b/app/components/post_list/index.js
@@ -5,13 +5,20 @@ import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {refreshChannelWithRetry} from 'app/actions/views/channel';
+import {makePreparePostIdsForPostList} from 'app/selectors/post_list';
+
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import PostList from './post_list';
-function mapStateToProps(state) {
- return {
- theme: getTheme(state)
+function makeMapStateToProps() {
+ const preparePostIds = makePreparePostIdsForPostList();
+
+ return (state, ownProps) => {
+ return {
+ postIds: preparePostIds(state, ownProps),
+ theme: getTheme(state)
+ };
};
}
@@ -23,4 +30,4 @@ function mapDispatchToProps(dispatch) {
};
}
-export default connect(mapStateToProps, mapDispatchToProps)(PostList);
+export default connect(makeMapStateToProps, mapDispatchToProps)(PostList);
diff --git a/app/components/post_list/post_list.js b/app/components/post_list/post_list.js
index e068db993..d8a9a928c 100644
--- a/app/components/post_list/post_list.js
+++ b/app/components/post_list/post_list.js
@@ -7,19 +7,16 @@ import {
StyleSheet,
View
} from 'react-native';
-import FlatList from 'app/components/inverted_flat_list';
-
-import {General} from 'mattermost-redux/constants';
-import {addDatesToPostList} from 'mattermost-redux/utils/post_utils';
import ChannelIntro from 'app/components/channel_intro';
+import FlatList from 'app/components/inverted_flat_list';
import Post from 'app/components/post';
+import {DATE_LINE, START_OF_NEW_MESSAGES} from 'app/selectors/post_list';
+
import DateHeader from './date_header';
import LoadMorePosts from './load_more_posts';
import NewMessagesDivider from './new_messages_divider';
-const LOAD_MORE_POSTS = 'load-more-posts';
-
export default class PostList extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
@@ -27,14 +24,15 @@ export default class PostList extends PureComponent {
}).isRequired,
channelId: PropTypes.string,
currentUserId: PropTypes.string,
+ highlightPostId: PropTypes.string,
indicateNewMessages: PropTypes.bool,
isSearchResult: PropTypes.bool,
- lastViewedAt: PropTypes.number,
+ lastViewedAt: PropTypes.number, // Used by container // eslint-disable-line no-unused-prop-types
loadMore: PropTypes.func,
navigator: PropTypes.object,
onPostPress: PropTypes.func,
onRefresh: PropTypes.func,
- posts: PropTypes.array.isRequired,
+ postIds: PropTypes.array.isRequired,
renderReplies: PropTypes.bool,
showLoadMore: PropTypes.bool,
shouldRenderReplyButton: PropTypes.bool,
@@ -52,22 +50,13 @@ export default class PostList extends PureComponent {
}
}
- getPostsWithDates = () => {
- const {posts, indicateNewMessages, currentUserId, lastViewedAt, showLoadMore} = this.props;
- const list = addDatesToPostList(posts, {indicateNewMessages, currentUserId, lastViewedAt});
- if (showLoadMore) {
- return [...list, LOAD_MORE_POSTS];
- }
+ getItem = (data, index) => data[index];
- return list;
- };
+ getItemCount = (data) => data.length;
keyExtractor = (item) => {
- if (item instanceof Date) {
- return item.getTime();
- }
-
- return item.id || item;
+ // All keys are strings (either post IDs or special keys)
+ return item;
};
onRefresh = () => {
@@ -86,19 +75,26 @@ export default class PostList extends PureComponent {
}
};
- renderChannelIntro = () => {
- const {channelId, navigator, showLoadMore} = this.props;
-
- // FIXME: Only show the channel intro when we are at the very start of the channel
- if (channelId && !showLoadMore) {
+ renderItem = ({item, index}) => {
+ if (item === START_OF_NEW_MESSAGES) {
return (
-
-
-
+
);
+ } else if (item.indexOf(DATE_LINE) === 0) {
+ const date = item.substring(DATE_LINE.length);
+ return this.renderDateHeader(new Date(date));
}
- return null;
+ const postId = item;
+
+ // Remember that the list is rendered with item 0 at the bottom so the "previous" post
+ // comes after this one in the list
+ const previousPostId = index < this.props.postIds.length - 1 ? this.props.postIds[index + 1] : null;
+ const nextPostId = index > 0 ? this.props.postIds[index - 1] : null;
+
+ return this.renderPost(postId, previousPostId, nextPostId);
};
renderDateHeader = (date) => {
@@ -110,32 +106,9 @@ export default class PostList extends PureComponent {
);
};
- renderItem = ({item}) => {
- if (item instanceof Date) {
- return this.renderDateHeader(item);
- }
- if (item === General.START_OF_NEW_MESSAGES) {
- return (
-
- );
- }
- if (item === LOAD_MORE_POSTS && this.props.showLoadMore) {
- return (
-
- );
- }
-
- return this.renderPost(item);
- };
-
- getItem = (data, index) => data[index];
-
- getItemCount = (data) => data.length;
-
- renderPost = (post) => {
+ renderPost = (postId, previousPostId, nextPostId) => {
const {
+ highlightPostId,
isSearchResult,
navigator,
onPostPress,
@@ -145,22 +118,42 @@ export default class PostList extends PureComponent {
return (
);
};
+ renderFooter = () => {
+ if (this.props.showLoadMore) {
+ return ;
+ } else if (this.props.channelId) {
+ // FIXME: Only show the channel intro when we are at the very start of the channel
+ return (
+
+
+
+ );
+ }
+
+ return null;
+ };
+
render() {
- const {channelId, loadMore, theme} = this.props;
+ const {
+ channelId,
+ highlightPostId,
+ loadMore,
+ postIds,
+ theme
+ } = this.props;
const refreshControl = {
refreshing: false
@@ -170,15 +163,15 @@ export default class PostList extends PureComponent {
refreshControl.onRefresh = this.onRefresh;
}
- const data = this.getPostsWithDates();
return (
p.id === ownProps.focusedPostId) : -1;
- let posts = [];
-
- if (focusedPostIndex !== -1) {
- const desiredPostIndexBefore = focusedPostIndex - 5;
- const minPostIndex = desiredPostIndexBefore < 0 ? 0 : desiredPostIndexBefore;
- posts = [...postsAroundPost].splice(minPostIndex, 10);
- }
+ const postIds = getPostIdsAroundPost(state, post.id, post.channel_id, {postsBeforeCount: 5, postsAfterCount: 5});
return {
- ...ownProps,
channelId: post.channel_id,
currentUserId: getCurrentUserId(state),
- posts
+ postIds
};
};
}
diff --git a/app/components/search_preview/search_preview.js b/app/components/search_preview/search_preview.js
index 96c2b53d4..69d35117f 100644
--- a/app/components/search_preview/search_preview.js
+++ b/app/components/search_preview/search_preview.js
@@ -39,15 +39,16 @@ export default class SearchPreview extends PureComponent {
channelId: PropTypes.string,
channelName: PropTypes.string,
currentUserId: PropTypes.string.isRequired,
+ focusedPostId: PropTypes.string.isRequired,
navigator: PropTypes.object,
onClose: PropTypes.func,
onPress: PropTypes.func,
- posts: PropTypes.array,
+ postIds: PropTypes.array,
theme: PropTypes.object.isRequired
};
static defaultProps = {
- posts: []
+ postIds: []
};
state = {
@@ -57,7 +58,7 @@ export default class SearchPreview extends PureComponent {
componentWillReceiveProps(nextProps) {
const {animationEnded, showPosts} = this.state;
- if (animationEnded && !showPosts && nextProps.posts.length) {
+ if (animationEnded && !showPosts && nextProps.postIds.length) {
this.setState({showPosts: true});
}
}
@@ -82,24 +83,31 @@ export default class SearchPreview extends PureComponent {
showPostList = () => {
this.setState({animationEnded: true});
- if (!this.state.showPosts && this.props.posts.length) {
+ if (!this.state.showPosts && this.props.postIds.length) {
this.setState({showPosts: true});
}
};
render() {
- const {channelName, currentUserId, posts, theme} = this.props;
+ const {
+ channelName,
+ currentUserId,
+ focusedPostId,
+ postIds,
+ theme
+ } = this.props;
const style = getStyleSheet(theme);
let postList;
if (this.state.showPosts) {
postList = (
= props.postVisibility
+ visiblePostIds: this.getVisiblePostIds(props),
+ showLoadMore: props.postIds.length >= props.postVisibility
};
}
componentWillReceiveProps(nextProps) {
- const {posts: nextPosts} = nextProps;
+ const {postIds: nextPostIds} = nextProps;
- const showLoadMore = nextPosts.length >= nextProps.postVisibility;
- let visiblePosts = this.state.visiblePosts;
+ const showLoadMore = nextPostIds.length >= nextProps.postVisibility;
+ let visiblePostIds = this.state.visiblePostIds;
- if (nextPosts !== this.props.posts || nextProps.postVisibility !== this.props.postVisibility) {
- visiblePosts = this.getVisiblePosts(nextProps);
+ if (nextPostIds !== this.props.postIds || nextProps.postVisibility !== this.props.postVisibility) {
+ visiblePostIds = this.getVisiblePostIds(nextProps);
}
this.setState({
showLoadMore,
- visiblePosts
+ visiblePostIds
});
}
- getVisiblePosts = (props) => {
- return props.posts.slice(0, props.postVisibility);
+ getVisiblePostIds = (props) => {
+ return props.postIds.slice(0, props.postVisibility);
};
goToThread = (post) => {
@@ -130,17 +130,17 @@ class ChannelPostList extends PureComponent {
currentUserId,
lastViewedAt,
navigator,
- posts,
+ postIds,
theme
} = this.props;
const {
showLoadMore,
- visiblePosts
+ visiblePostIds
} = this.state;
let component;
- if (!posts.length && channelRefreshingFailed) {
+ if (!postIds.length && channelRefreshingFailed) {
component = (
getMyPosts(state, props.postIds),
+ (state, props) => props.lastViewedAt,
+ getCurrentUserId,
+ shouldShowJoinLeaveMessages,
+ (posts, lastViewedAt, currentUserId, showJoinLeave) => {
+ const out = [];
+
+ let lastDate = null;
+ let addedNewMessagesIndicator = false;
+
+ const filterOptions = {showJoinLeave};
+
+ // Remember that we're iterating through the posts from newest to oldest
+ for (const post of posts) {
+ if (post.state === Posts.POST_DELETED && post.user_id === currentUserId) {
+ continue;
+ }
+
+ // Filter out join/leave messages if necessary
+ if (shouldFilterPost(post, filterOptions)) {
+ continue;
+ }
+
+ // Only add the new messages line if a lastViewedAt time is set
+ if (lastViewedAt && !addedNewMessagesIndicator) {
+ const postIsUnread = post.create_at > lastViewedAt;
+
+ if (postIsUnread) {
+ out.push(START_OF_NEW_MESSAGES);
+ addedNewMessagesIndicator = true;
+ }
+ }
+
+ // Push on a date header if the last post was on a different day than the current one
+ const postDate = new Date(post.create_at);
+
+ if (lastDate && lastDate.toDateString() !== postDate.toDateString()) {
+ out.push(DATE_LINE + lastDate.toDateString());
+ }
+
+ lastDate = postDate;
+
+ out.push(post.id);
+ }
+
+ // Push on the date header for the oldest post
+ if (lastDate) {
+ out.push(DATE_LINE + lastDate.toDateString());
+ }
+
+ return out;
+ }
+ );
+}
diff --git a/test/app/selectors/post_list.test.js b/test/app/selectors/post_list.test.js
new file mode 100644
index 000000000..01b527513
--- /dev/null
+++ b/test/app/selectors/post_list.test.js
@@ -0,0 +1,288 @@
+// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import assert from 'assert';
+
+import {
+ DATE_LINE,
+ makePreparePostIdsForPostList,
+ START_OF_NEW_MESSAGES
+} from 'app/selectors/post_list';
+
+import {Posts, Preferences} from 'mattermost-redux/constants';
+import {getPreferenceKey} from 'mattermost-redux/utils/preference_utils';
+
+describe('Selectors.PostList', () => {
+ describe('makePreparePostIdsForPostList', () => {
+ it('filter join/leave posts', () => {
+ const preparePostIdsForPostList = makePreparePostIdsForPostList();
+
+ let state = {
+ entities: {
+ posts: {
+ posts: {
+ 1001: {id: '1001', create_at: 0},
+ 1002: {id: '1002', create_at: 1, type: Posts.POST_TYPES.JOIN_CHANNEL}
+ }
+ },
+ preferences: {
+ myPreferences: {}
+ },
+ users: {
+ currentUserId: '1234'
+ }
+ }
+ };
+ const lastViewedAt = Number.POSITIVE_INFINITY;
+ const postIds = ['1001', '1002'];
+
+ // Defaults to show post
+ let now = preparePostIdsForPostList(state, {postIds, lastViewedAt});
+ assert.ok(now.indexOf('1001') !== -1);
+ assert.ok(now.indexOf('1002') !== -1);
+
+ // Show join/leave posts
+ state = {
+ ...state,
+ entities: {
+ ...state.entities,
+ preferences: {
+ ...state.entities.preferences,
+ myPreferences: {
+ ...state.entities.preferences.myPreferences,
+ [getPreferenceKey(Preferences.CATEGORY_ADVANCED_SETTINGS, Preferences.ADVANCED_FILTER_JOIN_LEAVE)]: {
+ category: Preferences.CATEGORY_ADVANCED_SETTINGS,
+ name: Preferences.ADVANCED_FILTER_JOIN_LEAVE,
+ value: 'true'
+ }
+ }
+ }
+ }
+ };
+
+ now = preparePostIdsForPostList(state, {postIds, lastViewedAt});
+ assert.ok(now.indexOf('1001') !== -1);
+ assert.ok(now.indexOf('1002') !== -1);
+
+ // Hide join/leave posts
+ state = {
+ ...state,
+ entities: {
+ ...state.entities,
+ preferences: {
+ ...state.entities.preferences,
+ myPreferences: {
+ ...state.entities.preferences.myPreferences,
+ [getPreferenceKey(Preferences.CATEGORY_ADVANCED_SETTINGS, Preferences.ADVANCED_FILTER_JOIN_LEAVE)]: {
+ category: Preferences.CATEGORY_ADVANCED_SETTINGS,
+ name: Preferences.ADVANCED_FILTER_JOIN_LEAVE,
+ value: 'false'
+ }
+ }
+ }
+ }
+ };
+
+ now = preparePostIdsForPostList(state, {postIds, lastViewedAt});
+ assert.ok(now.indexOf('1001') !== -1);
+ assert.ok(now.indexOf('1002') === -1);
+ });
+
+ it('memoization', () => {
+ const preparePostIdsForPostList = makePreparePostIdsForPostList();
+
+ // Posts 7 hours apart so they should appear on multiple days
+ const initialPosts = {
+ 1001: {id: '1001', create_at: 1 * 60 * 60 * 1000},
+ 1002: {id: '1002', create_at: (1 * 60 * 60 * 1000) + 5},
+ 1003: {id: '1003', create_at: (1 * 60 * 60 * 1000) + 10},
+ 1004: {id: '1004', create_at: 25 * 60 * 60 * 1000},
+ 1005: {id: '1005', create_at: (25 * 60 * 60 * 1000) + 5},
+ 1006: {id: '1006', create_at: (25 * 60 * 60 * 1000) + 10, type: Posts.POST_TYPES.JOIN_CHANNEL}
+ };
+ let state = {
+ entities: {
+ posts: {
+ posts: initialPosts
+ },
+ preferences: {
+ myPreferences: {
+ [getPreferenceKey(Preferences.CATEGORY_ADVANCED_SETTINGS, Preferences.ADVANCED_FILTER_JOIN_LEAVE)]: {
+ category: Preferences.CATEGORY_ADVANCED_SETTINGS,
+ name: Preferences.ADVANCED_FILTER_JOIN_LEAVE,
+ value: 'true'
+ }
+ }
+ },
+ users: {
+ currentUserId: '1234'
+ }
+ }
+ };
+
+ let postIds = ['1001', '1003', '1004', '1006'];
+ let lastViewedAt = initialPosts['1001'].create_at + 1;
+
+ let now = preparePostIdsForPostList(state, {postIds, lastViewedAt});
+ assert.ok(now.indexOf('1001') !== -1);
+ assert.ok(now.indexOf('1002') === -1);
+ assert.ok(now.indexOf('1003') !== -1);
+ assert.ok(now.indexOf('1004') !== -1);
+ assert.ok(now.indexOf('1005') === -1);
+ assert.ok(now.indexOf('1006') !== -1);
+ assert.ok(now.findIndex((id) => id.startsWith(DATE_LINE)) !== -1);
+ assert.ok(now.indexOf(START_OF_NEW_MESSAGES) !== -1);
+
+ // No changes
+ let prev = now;
+ now = preparePostIdsForPostList(state, {postIds, lastViewedAt});
+ assert.equal(now, prev);
+ assert.ok(now.indexOf('1001') !== -1);
+ assert.ok(now.indexOf('1002') === -1);
+ assert.ok(now.indexOf('1003') !== -1);
+ assert.ok(now.indexOf('1004') !== -1);
+ assert.ok(now.indexOf('1005') === -1);
+ assert.ok(now.indexOf('1006') !== -1);
+ assert.ok(now.findIndex((id) => id.startsWith(DATE_LINE)) !== -1);
+ assert.ok(now.indexOf(START_OF_NEW_MESSAGES) !== -1);
+
+ // lastViewedAt changed slightly
+ lastViewedAt = initialPosts['1001'].create_at + 2;
+
+ prev = now;
+ now = preparePostIdsForPostList(state, {postIds, lastViewedAt});
+ assert.equal(now, prev);
+ assert.ok(now.indexOf('1001') !== -1);
+ assert.ok(now.indexOf('1002') === -1);
+ assert.ok(now.indexOf('1003') !== -1);
+ assert.ok(now.indexOf('1004') !== -1);
+ assert.ok(now.indexOf('1005') === -1);
+ assert.ok(now.indexOf('1006') !== -1);
+ assert.ok(now.findIndex((id) => id.startsWith(DATE_LINE)) !== -1);
+ assert.ok(now.indexOf(START_OF_NEW_MESSAGES) !== -1);
+
+ // lastViewedAt changed a lot
+ lastViewedAt += initialPosts['1003'].create_at + 1;
+
+ prev = now;
+ now = preparePostIdsForPostList(state, {postIds, lastViewedAt});
+ assert.notEqual(now, prev);
+ assert.ok(now.indexOf('1001') !== -1);
+ assert.ok(now.indexOf('1002') === -1);
+ assert.ok(now.indexOf('1003') !== -1);
+ assert.ok(now.indexOf('1004') !== -1);
+ assert.ok(now.indexOf('1005') === -1);
+ assert.ok(now.indexOf('1006') !== -1);
+ assert.ok(now.findIndex((id) => id.startsWith(DATE_LINE)) !== -1);
+ assert.ok(now.indexOf(START_OF_NEW_MESSAGES) !== -1);
+
+ prev = now;
+ now = preparePostIdsForPostList(state, {postIds, lastViewedAt});
+ assert.equal(now, prev);
+
+ // postIds changed, but still shallowly equal
+ postIds = [...postIds];
+
+ prev = now;
+ now = preparePostIdsForPostList(state, {postIds, lastViewedAt});
+ assert.equal(now, prev);
+ assert.ok(now.indexOf('1001') !== -1);
+ assert.ok(now.indexOf('1002') === -1);
+ assert.ok(now.indexOf('1003') !== -1);
+ assert.ok(now.indexOf('1004') !== -1);
+ assert.ok(now.indexOf('1005') === -1);
+ assert.ok(now.indexOf('1006') !== -1);
+ assert.ok(now.findIndex((id) => id.startsWith(DATE_LINE)) !== -1);
+ assert.ok(now.indexOf(START_OF_NEW_MESSAGES) !== -1);
+
+ // Post changed, not in postIds
+ state = {
+ ...state,
+ entities: {
+ ...state.entities,
+ posts: {
+ ...state.entities.posts,
+ posts: {
+ ...state.entities.posts.posts,
+ 1007: {id: '1007', create_at: 7 * 60 * 60 * 7 * 1000}
+ }
+ }
+ }
+ };
+
+ prev = now;
+ now = preparePostIdsForPostList(state, {postIds, lastViewedAt});
+ assert.equal(now, prev);
+ assert.ok(now.indexOf('1001') !== -1);
+ assert.ok(now.indexOf('1002') === -1);
+ assert.ok(now.indexOf('1003') !== -1);
+ assert.ok(now.indexOf('1004') !== -1);
+ assert.ok(now.indexOf('1005') === -1);
+ assert.ok(now.indexOf('1006') !== -1);
+ assert.ok(now.findIndex((id) => id.startsWith(DATE_LINE)) !== -1);
+ assert.ok(now.indexOf(START_OF_NEW_MESSAGES) !== -1);
+
+ // Post changed, in postIds
+ state = {
+ ...state,
+ entities: {
+ ...state.entities,
+ posts: {
+ ...state.entities.posts,
+ posts: {
+ ...state.entities.posts.posts,
+ 1006: {...state.entities.posts.posts['1006'], message: 'abcd'}
+ }
+ }
+ }
+ };
+
+ prev = now;
+ now = preparePostIdsForPostList(state, {postIds, lastViewedAt});
+ assert.equal(now, prev);
+ assert.ok(now.indexOf('1001') !== -1);
+ assert.ok(now.indexOf('1002') === -1);
+ assert.ok(now.indexOf('1003') !== -1);
+ assert.ok(now.indexOf('1004') !== -1);
+ assert.ok(now.indexOf('1005') === -1);
+ assert.ok(now.indexOf('1006') !== -1);
+ assert.ok(now.findIndex((id) => id.startsWith(DATE_LINE)) !== -1);
+ assert.ok(now.indexOf(START_OF_NEW_MESSAGES) !== -1);
+
+ // Filter changed
+ state = {
+ ...state,
+ entities: {
+ ...state.entities,
+ preferences: {
+ ...state.entities.preferences,
+ myPreferences: {
+ ...state.entities.preferences.myPreferences,
+ [getPreferenceKey(Preferences.CATEGORY_ADVANCED_SETTINGS, Preferences.ADVANCED_FILTER_JOIN_LEAVE)]: {
+ category: Preferences.CATEGORY_ADVANCED_SETTINGS,
+ name: Preferences.ADVANCED_FILTER_JOIN_LEAVE,
+ value: 'false'
+ }
+ }
+ }
+ }
+ };
+
+ prev = now;
+ now = preparePostIdsForPostList(state, {postIds, lastViewedAt});
+ assert.notEqual(now, prev);
+ assert.ok(now.indexOf('1001') !== -1);
+ assert.ok(now.indexOf('1002') === -1);
+ assert.ok(now.indexOf('1003') !== -1);
+ assert.ok(now.indexOf('1004') !== -1);
+ assert.ok(now.indexOf('1005') === -1);
+ assert.ok(now.indexOf('1006') === -1);
+ assert.ok(now.findIndex((id) => id.startsWith(DATE_LINE)) !== -1);
+ assert.ok(now.indexOf(START_OF_NEW_MESSAGES) !== -1);
+
+ prev = now;
+ now = preparePostIdsForPostList(state, {postIds, lastViewedAt});
+ assert.equal(now, prev);
+ });
+ });
+});
diff --git a/yarn.lock b/yarn.lock
index c9a49d530..a7863aace 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3878,7 +3878,7 @@ makeerror@1.0.x:
mattermost-redux@mattermost/mattermost-redux#master:
version "0.0.1"
- resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/18fa0a2dc2d0648d0346e01f71f096aa835096ac"
+ resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/f6d97c8a49e9402fcb5d72604751c9a5ae6d7e5a"
dependencies:
deep-equal "1.0.1"
harmony-reflect "1.5.1"