summaryrefslogtreecommitdiffstats
path: root/webapp
diff options
context:
space:
mode:
authorJoram Wilander <jwawilander@gmail.com>2016-05-27 16:01:28 -0400
committerChristopher Speller <crspeller@gmail.com>2016-05-27 16:01:28 -0400
commit6399a94ce221be3d15e7132654c28cd953075ec6 (patch)
tree4b1927fdd8374e8bd3cb809ecb720f2689043358 /webapp
parentca9f348be6bf62fc888df9a710c9af155872528e (diff)
downloadchat-6399a94ce221be3d15e7132654c28cd953075ec6.tar.gz
chat-6399a94ce221be3d15e7132654c28cd953075ec6.tar.bz2
chat-6399a94ce221be3d15e7132654c28cd953075ec6.zip
PLT-2672 Refactored posts view with caching (#3054)
* Refactored posts view to use view controller design * Add post view caching * Required updates after rebase * Fixed bug where current channel not set yet was causing breakage
Diffstat (limited to 'webapp')
-rw-r--r--webapp/components/channel_view.jsx4
-rw-r--r--webapp/components/permalink_view.jsx4
-rw-r--r--webapp/components/post_view/components/floating_timestamp.jsx (renamed from webapp/components/floating_timestamp.jsx)0
-rw-r--r--webapp/components/post_view/components/pending_post_options.jsx (renamed from webapp/components/pending_post_actions.jsx)6
-rw-r--r--webapp/components/post_view/components/post.jsx (renamed from webapp/components/post.jsx)41
-rw-r--r--webapp/components/post_view/components/post_attachment.jsx (renamed from webapp/components/post_attachment.jsx)0
-rw-r--r--webapp/components/post_view/components/post_attachment_list.jsx (renamed from webapp/components/post_attachment_list.jsx)0
-rw-r--r--webapp/components/post_view/components/post_attachment_oembed.jsx (renamed from webapp/components/post_attachment_oembed.jsx)0
-rw-r--r--webapp/components/post_view/components/post_body.jsx (renamed from webapp/components/post_body.jsx)6
-rw-r--r--webapp/components/post_view/components/post_body_additional_content.jsx (renamed from webapp/components/post_body_additional_content.jsx)2
-rw-r--r--webapp/components/post_view/components/post_header.jsx (renamed from webapp/components/post_header.jsx)7
-rw-r--r--webapp/components/post_view/components/post_image.jsx (renamed from webapp/components/post_image.jsx)0
-rw-r--r--webapp/components/post_view/components/post_info.jsx (renamed from webapp/components/post_info.jsx)2
-rw-r--r--webapp/components/post_view/components/post_list.jsx (renamed from webapp/components/posts_view.jsx)344
-rw-r--r--webapp/components/post_view/components/providers.json (renamed from webapp/components/providers.json)0
-rw-r--r--webapp/components/post_view/components/scroll_to_bottom_arrows.jsx37
-rw-r--r--webapp/components/post_view/post_focus_view_controller.jsx (renamed from webapp/components/post_focus_view.jsx)79
-rw-r--r--webapp/components/post_view/post_view_cache.jsx85
-rw-r--r--webapp/components/post_view/post_view_controller.jsx264
-rw-r--r--webapp/components/posts_view_container.jsx217
-rw-r--r--webapp/components/rhs_comment.jsx4
-rw-r--r--webapp/components/rhs_root_post.jsx12
-rw-r--r--webapp/components/search_results_item.jsx3
-rw-r--r--webapp/stores/notification_store.jsx3
-rw-r--r--webapp/utils/constants.jsx14
-rw-r--r--webapp/utils/post_utils.jsx32
-rw-r--r--webapp/utils/utils.jsx28
27 files changed, 640 insertions, 554 deletions
diff --git a/webapp/components/channel_view.jsx b/webapp/components/channel_view.jsx
index 6511d960a..ff101bca7 100644
--- a/webapp/components/channel_view.jsx
+++ b/webapp/components/channel_view.jsx
@@ -7,7 +7,7 @@ import React from 'react';
import ChannelHeader from 'components/channel_header.jsx';
import FileUploadOverlay from 'components/file_upload_overlay.jsx';
import CreatePost from 'components/create_post.jsx';
-import PostsViewContainer from 'components/posts_view_container.jsx';
+import PostViewCache from 'components/post_view/post_view_cache.jsx';
import ChannelStore from 'stores/channel_store.jsx';
@@ -70,7 +70,7 @@ export default class ChannelView extends React.Component {
<ChannelHeader
channelId={this.state.channelId}
/>
- <PostsViewContainer/>
+ <PostViewCache/>
<div
className='post-create__container'
id='post-create'
diff --git a/webapp/components/permalink_view.jsx b/webapp/components/permalink_view.jsx
index c2019cb49..8f443bc05 100644
--- a/webapp/components/permalink_view.jsx
+++ b/webapp/components/permalink_view.jsx
@@ -5,7 +5,7 @@ import $ from 'jquery';
import React from 'react';
import ChannelHeader from 'components/channel_header.jsx';
-import PostFocusView from 'components/post_focus_view.jsx';
+import PostFocusViewController from 'components/post_view/post_focus_view_controller.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import TeamStore from 'stores/team_store.jsx';
@@ -70,7 +70,7 @@ export default class PermalinkView extends React.Component {
<ChannelHeader
channelId={this.state.channelId}
/>
- <PostFocusView/>
+ <PostFocusViewController/>
<div
id='archive-link-home'
>
diff --git a/webapp/components/floating_timestamp.jsx b/webapp/components/post_view/components/floating_timestamp.jsx
index 8974c62c5..8974c62c5 100644
--- a/webapp/components/floating_timestamp.jsx
+++ b/webapp/components/post_view/components/floating_timestamp.jsx
diff --git a/webapp/components/pending_post_actions.jsx b/webapp/components/post_view/components/pending_post_options.jsx
index 7528ef207..536ec541c 100644
--- a/webapp/components/pending_post_actions.jsx
+++ b/webapp/components/post_view/components/pending_post_options.jsx
@@ -4,7 +4,7 @@
import PostStore from 'stores/post_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
+import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
@@ -16,7 +16,7 @@ import {FormattedMessage} from 'react-intl';
import React from 'react';
-export default class PendingPostActions extends React.Component {
+export default class PendingPostOptions extends React.Component {
constructor(props) {
super(props);
this.retryPost = this.retryPost.bind(this);
@@ -87,6 +87,6 @@ export default class PendingPostActions extends React.Component {
}
}
-PendingPostActions.propTypes = {
+PendingPostOptions.propTypes = {
post: React.PropTypes.object
};
diff --git a/webapp/components/post.jsx b/webapp/components/post_view/components/post.jsx
index 2b28d442c..0bf4680fe 100644
--- a/webapp/components/post.jsx
+++ b/webapp/components/post_view/components/post.jsx
@@ -8,7 +8,8 @@ import Constants from 'utils/constants.jsx';
const ActionTypes = Constants.ActionTypes;
import * as Utils from 'utils/utils.jsx';
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
+import * as PostUtils from 'utils/post_utils.jsx';
+import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
import React from 'react';
@@ -55,7 +56,7 @@ export default class Post extends React.Component {
return true;
}
- if (this.getCommentCount(nextProps) !== this.getCommentCount(this.props)) {
+ if (nextProps.commentCount !== this.props.commentCount) {
return true;
}
@@ -77,30 +78,9 @@ export default class Post extends React.Component {
return false;
}
- getCommentCount(props) {
- const post = props.post;
- const parentPost = props.parentPost;
- const posts = props.posts;
-
- let commentCount = 0;
- let commentRootId;
- if (parentPost) {
- commentRootId = post.root_id;
- } else {
- commentRootId = post.id;
- }
- for (const postId in posts) {
- if (posts[postId].root_id === commentRootId) {
- commentCount += 1;
- }
- }
-
- return commentCount;
- }
render() {
const post = this.props.post;
const parentPost = this.props.parentPost;
- const posts = this.props.posts;
const mattermostLogo = Constants.MATTERMOST_ICON_SVG;
if (!post.props) {
@@ -112,7 +92,7 @@ export default class Post extends React.Component {
type = 'Comment';
}
- const commentCount = this.getCommentCount(this.props);
+ const commentCount = this.props.commentCount;
let rootUser;
if (this.props.sameRoot) {
@@ -129,7 +109,7 @@ export default class Post extends React.Component {
}
let currentUserCss = '';
- if (this.props.currentUser.id === post.user_id && !post.props.from_webhook && !Utils.isSystemMessage(post)) {
+ if (this.props.currentUser.id === post.user_id && !post.props.from_webhook && !PostUtils.isSystemMessage(post)) {
currentUserCss = 'current--user';
}
@@ -151,19 +131,19 @@ export default class Post extends React.Component {
}
let systemMessageClass = '';
- if (Utils.isSystemMessage(post)) {
+ if (PostUtils.isSystemMessage(post)) {
systemMessageClass = 'post--system';
}
let profilePic = (
<img
- src={Utils.getProfilePicSrcForPost(post, timestamp)}
+ src={PostUtils.getProfilePicSrcForPost(post, timestamp)}
height='36'
width='36'
/>
);
- if (Utils.isSystemMessage(post)) {
+ if (PostUtils.isSystemMessage(post)) {
profilePic = (
<span
className='icon'
@@ -207,7 +187,6 @@ export default class Post extends React.Component {
post={post}
sameRoot={this.props.sameRoot}
parentPost={parentPost}
- posts={posts}
handleCommentClick={this.handleCommentClick}
compactDisplay={this.props.compactDisplay}
/>
@@ -221,7 +200,6 @@ export default class Post extends React.Component {
Post.propTypes = {
post: React.PropTypes.object.isRequired,
- posts: React.PropTypes.object,
parentPost: React.PropTypes.object,
user: React.PropTypes.object,
sameUser: React.PropTypes.bool,
@@ -233,5 +211,6 @@ Post.propTypes = {
hasProfiles: React.PropTypes.bool,
currentUser: React.PropTypes.object.isRequired,
center: React.PropTypes.bool,
- compactDisplay: React.PropTypes.bool
+ compactDisplay: React.PropTypes.bool,
+ commentCount: React.PropTypes.number
};
diff --git a/webapp/components/post_attachment.jsx b/webapp/components/post_view/components/post_attachment.jsx
index 8b5ff91f2..8b5ff91f2 100644
--- a/webapp/components/post_attachment.jsx
+++ b/webapp/components/post_view/components/post_attachment.jsx
diff --git a/webapp/components/post_attachment_list.jsx b/webapp/components/post_view/components/post_attachment_list.jsx
index 7da9efbee..7da9efbee 100644
--- a/webapp/components/post_attachment_list.jsx
+++ b/webapp/components/post_view/components/post_attachment_list.jsx
diff --git a/webapp/components/post_attachment_oembed.jsx b/webapp/components/post_view/components/post_attachment_oembed.jsx
index 359c7cc35..359c7cc35 100644
--- a/webapp/components/post_attachment_oembed.jsx
+++ b/webapp/components/post_view/components/post_attachment_oembed.jsx
diff --git a/webapp/components/post_body.jsx b/webapp/components/post_view/components/post_body.jsx
index 415052d96..3c57db0cd 100644
--- a/webapp/components/post_body.jsx
+++ b/webapp/components/post_view/components/post_body.jsx
@@ -1,13 +1,13 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import FileAttachmentList from './file_attachment_list.jsx';
+import FileAttachmentList from 'components/file_attachment_list.jsx';
import UserStore from 'stores/user_store.jsx';
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
import * as TextFormatting from 'utils/text_formatting.jsx';
import PostBodyAdditionalContent from './post_body_additional_content.jsx';
-import PendingPostActions from './pending_post_actions.jsx';
+import PendingPostOptions from './pending_post_options.jsx';
import {FormattedMessage} from 'react-intl';
@@ -111,7 +111,7 @@ export default class PostBody extends React.Component {
let loading;
if (post.state === Constants.POST_FAILED) {
postClass += ' post--fail';
- loading = <PendingPostActions post={this.props.post}/>;
+ loading = <PendingPostOptions post={this.props.post}/>;
} else if (post.state === Constants.POST_LOADING) {
postClass += ' post-waiting';
loading = (
diff --git a/webapp/components/post_body_additional_content.jsx b/webapp/components/post_view/components/post_body_additional_content.jsx
index cdb735b47..deabaaa9b 100644
--- a/webapp/components/post_body_additional_content.jsx
+++ b/webapp/components/post_view/components/post_body_additional_content.jsx
@@ -4,7 +4,7 @@
import PostAttachmentList from './post_attachment_list.jsx';
import PostAttachmentOEmbed from './post_attachment_oembed.jsx';
import PostImage from './post_image.jsx';
-import YoutubeVideo from './youtube_video.jsx';
+import YoutubeVideo from 'components/youtube_video.jsx';
import Constants from 'utils/constants.jsx';
import OEmbedProviders from './providers.json';
diff --git a/webapp/components/post_header.jsx b/webapp/components/post_view/components/post_header.jsx
index 6fae092e5..3e7650d7f 100644
--- a/webapp/components/post_header.jsx
+++ b/webapp/components/post_view/components/post_header.jsx
@@ -1,9 +1,10 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import UserProfile from './user_profile.jsx';
+import UserProfile from 'components/user_profile.jsx';
import PostInfo from './post_info.jsx';
-import * as Utils from 'utils/utils.jsx';
+
+import * as PostUtils from 'utils/post_utils.jsx';
import Constants from 'utils/constants.jsx';
@@ -33,7 +34,7 @@ export default class PostHeader extends React.Component {
}
botIndicator = <li className='col col__name bot-indicator'>{Constants.BOT_NAME}</li>;
- } else if (Utils.isSystemMessage(post)) {
+ } else if (PostUtils.isSystemMessage(post)) {
userProfile = (
<UserProfile
user={{}}
diff --git a/webapp/components/post_image.jsx b/webapp/components/post_view/components/post_image.jsx
index d1d1a6c7a..d1d1a6c7a 100644
--- a/webapp/components/post_image.jsx
+++ b/webapp/components/post_view/components/post_image.jsx
diff --git a/webapp/components/post_info.jsx b/webapp/components/post_view/components/post_info.jsx
index e3b80e45c..cc8c0a842 100644
--- a/webapp/components/post_info.jsx
+++ b/webapp/components/post_view/components/post_info.jsx
@@ -3,7 +3,7 @@
import $ from 'jquery';
import * as Utils from 'utils/utils.jsx';
-import TimeSince from './time_since.jsx';
+import TimeSince from 'components/time_since.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
import TeamStore from 'stores/team_store.jsx';
import UserStore from 'stores/user_store.jsx';
diff --git a/webapp/components/posts_view.jsx b/webapp/components/post_view/components/post_list.jsx
index 64da4e67c..288609cd9 100644
--- a/webapp/components/posts_view.jsx
+++ b/webapp/components/post_view/components/post_list.jsx
@@ -5,30 +5,28 @@ import $ from 'jquery';
import Post from './post.jsx';
import FloatingTimestamp from './floating_timestamp.jsx';
+import ScrollToBottomArrows from './scroll_to_bottom_arrows.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
-import PreferenceStore from 'stores/preference_store.jsx';
-import UserStore from 'stores/user_store.jsx';
-
import {createChannelIntroMessage} from 'utils/channel_intro_messages.jsx';
import * as Utils from 'utils/utils.jsx';
+import * as PostUtils from 'utils/post_utils.jsx';
import DelayedAction from 'utils/delayed_action.jsx';
import Constants from 'utils/constants.jsx';
-const Preferences = Constants.Preferences;
+const ScrollTypes = Constants.ScrollTypes;
import {FormattedDate, FormattedMessage} from 'react-intl';
import React from 'react';
import ReactDOM from 'react-dom';
-export default class PostsView extends React.Component {
+export default class PostList extends React.Component {
constructor(props) {
super(props);
- this.updateState = this.updateState.bind(this);
this.handleScroll = this.handleScroll.bind(this);
this.handleScrollStop = this.handleScrollStop.bind(this);
this.isAtBottom = this.isAtBottom.bind(this);
@@ -39,7 +37,6 @@ export default class PostsView extends React.Component {
this.handleResize = this.handleResize.bind(this);
this.scrollToBottom = this.scrollToBottom.bind(this);
this.scrollToBottomAnimated = this.scrollToBottomAnimated.bind(this);
- this.onUserChange = this.onUserChange.bind(this);
this.jumpToPostNode = null;
this.wasAtBottom = true;
@@ -47,62 +44,31 @@ export default class PostsView extends React.Component {
this.scrollStopAction = new DelayedAction(this.handleScrollStop);
- let profiles = UserStore.getProfiles();
- if (props.channel && props.channel.type === Constants.DM_CHANNEL) {
- profiles = Object.assign({}, profiles, UserStore.getDirectProfiles());
+ if (props.channel) {
+ this.introText = createChannelIntroMessage(props.channel);
+ } else {
+ this.introText = this.getArchivesIntroMessage();
}
this.state = {
- displayNameType: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', 'false'),
- centerPosts: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.CHANNEL_DISPLAY_MODE, Preferences.CHANNEL_DISPLAY_MODE_DEFAULT) === Preferences.CHANNEL_DISPLAY_MODE_CENTERED,
- compactPosts: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.MESSAGE_DISPLAY, Preferences.MESSAGE_DISPLAY_DEFAULT) === Preferences.MESSAGE_DISPLAY_COMPACT,
isScrolling: false,
- topPostId: null,
- currentUser: UserStore.getCurrentUser(),
- profiles
+ topPostId: null
};
}
- static get SCROLL_TYPE_FREE() {
- return 1;
- }
- static get SCROLL_TYPE_BOTTOM() {
- return 2;
- }
- static get SCROLL_TYPE_SIDEBAR_OPEN() {
- return 3;
- }
- static get SCROLL_TYPE_NEW_MESSAGE() {
- return 4;
- }
- static get SCROLL_TYPE_POST() {
- return 5;
- }
- updateState() {
- this.setState({
- displayNameType: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', 'false'),
- centerPosts: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.CHANNEL_DISPLAY_MODE, Preferences.CHANNEL_DISPLAY_MODE_DEFAULT) === Preferences.CHANNEL_DISPLAY_MODE_CENTERED,
- compactPosts: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.MESSAGE_DISPLAY, Preferences.MESSAGE_DISPLAY_DEFAULT) === Preferences.MESSAGE_DISPLAY_COMPACT
- });
- }
- onUserChange() {
- let profiles = UserStore.getProfiles();
- if (this.props.channel && this.props.channel.type === Constants.DM_CHANNEL) {
- profiles = Object.assign({}, profiles, UserStore.getDirectProfiles());
- }
- this.setState({currentUser: UserStore.getCurrentUser(), profiles: JSON.parse(JSON.stringify(profiles))});
- }
+
isAtBottom() {
// consider the view to be at the bottom if it's within this many pixels of the bottom
const atBottomMargin = 10;
return this.refs.postlist.clientHeight + this.refs.postlist.scrollTop >= this.refs.postlist.scrollHeight - atBottomMargin;
}
+
handleScroll() {
// HACK FOR RHS -- REMOVE WHEN RHS DIES
const childNodes = this.refs.postlistcontent.childNodes;
for (let i = 0; i < childNodes.length; i++) {
// If the node is 1/3 down the page
- if (childNodes[i].offsetTop > (this.refs.postlist.scrollTop + (this.refs.postlist.offsetHeight / 3))) {
+ if (childNodes[i].offsetTop > (this.refs.postlist.scrollTop + (this.refs.postlist.offsetHeight / Constants.SCROLL_PAGE_FRACTION))) {
this.jumpToPostNode = childNodes[i];
break;
}
@@ -114,7 +80,7 @@ export default class PostsView extends React.Component {
// --- --------
- this.props.postViewScrolled(this.isAtBottom());
+ this.props.postListScrolled(this.isAtBottom());
this.prevScrollHeight = this.refs.postlist.scrollHeight;
this.prevOffsetTop = this.jumpToPostNode.offsetTop;
@@ -126,16 +92,18 @@ export default class PostsView extends React.Component {
});
}
- this.scrollStopAction.fireAfter(2000);
+ this.scrollStopAction.fireAfter(Constants.SCROLL_DELAY);
}
+
handleScrollStop() {
this.setState({
isScrolling: false
});
}
+
updateFloatingTimestamp() {
// skip this in non-mobile view since that's when the timestamp is visible
- if ($(window).width() > 768) {
+ if (!Utils.isMobile()) {
return;
}
@@ -143,7 +111,7 @@ export default class PostsView extends React.Component {
// iterate through posts starting at the bottom since users are more likely to be viewing newer posts
for (let i = 0; i < this.props.postList.order.length; i++) {
const id = this.props.postList.order[i];
- const element = ReactDOM.findDOMNode(this.refs[id]);
+ const element = this.refs[id];
if (!element || element.offsetTop + element.clientHeight <= this.refs.postlist.scrollTop) {
// this post is off the top of the screen so the last one is at the top of the screen
@@ -167,17 +135,20 @@ export default class PostsView extends React.Component {
}
}
}
+
loadMorePostsTop() {
- this.props.loadMorePostsTopClicked();
+ GlobalActions.emitLoadMorePostsEvent();
}
+
loadMorePostsBottom() {
- this.props.loadMorePostsBottomClicked();
+ GlobalActions.emitLoadMorePostsFocusedBottomEvent();
}
+
createPosts(posts, order) {
const postCtls = [];
let previousPostDay = new Date(0);
- const userId = this.state.currentUser.id;
- const profiles = this.state.profiles || {};
+ const userId = this.props.currentUser.id;
+ const profiles = this.props.profiles || {};
let renderedLastViewed = false;
@@ -185,7 +156,7 @@ export default class PostsView extends React.Component {
const post = posts[order[i]];
const parentPost = posts[post.parent_id];
const prevPost = posts[order[i + 1]];
- const postUserId = Utils.isSystemMessage(post) ? '' : post.user_id;
+ const postUserId = PostUtils.isSystemMessage(post) ? '' : post.user_id;
// If the post is a comment whose parent has been deleted, don't add it to the list.
if (parentPost && parentPost.state === Constants.POST_DELETED) {
@@ -197,11 +168,11 @@ export default class PostsView extends React.Component {
let hideProfilePic = false;
if (prevPost) {
- const postIsComment = Utils.isComment(post);
- const prevPostIsComment = Utils.isComment(prevPost);
+ const postIsComment = PostUtils.isComment(post);
+ const prevPostIsComment = PostUtils.isComment(prevPost);
const postFromWebhook = Boolean(post.props && post.props.from_webhook);
const prevPostFromWebhook = Boolean(prevPost.props && prevPost.props.from_webhook);
- const prevPostUserId = Utils.isSystemMessage(prevPost) ? '' : prevPost.user_id;
+ const prevPostUserId = PostUtils.isSystemMessage(prevPost) ? '' : prevPost.user_id;
// consider posts from the same user if:
// the previous post was made by the same user as the current post,
@@ -209,7 +180,7 @@ export default class PostsView extends React.Component {
// the current post is not from a webhook
// the previous post is not from a webhook
if (prevPostUserId === postUserId &&
- post.create_at - prevPost.create_at <= 1000 * 60 * 5 &&
+ post.create_at - prevPost.create_at <= Constants.POST_COLLAPSE_TIMEOUT &&
!postFromWebhook && !prevPostFromWebhook) {
sameUser = true;
}
@@ -246,7 +217,7 @@ export default class PostsView extends React.Component {
// check if it's the last comment in a consecutive string of comments on the same post
// it is the last comment if it is last post in the channel or the next post has a different root post
- const isLastComment = Utils.isComment(post) && (i === 0 || posts[order[i - 1]].root_id !== post.root_id);
+ const isLastComment = PostUtils.isComment(post) && (i === 0 || posts[order[i - 1]].root_id !== post.root_id);
const keyPrefix = post.id ? post.id : i;
@@ -254,11 +225,24 @@ export default class PostsView extends React.Component {
let profile;
if (userId === post.user_id) {
- profile = this.state.currentUser;
+ profile = this.props.currentUser;
} else {
profile = profiles[post.user_id];
}
+ let commentCount = 0;
+ let commentRootId;
+ if (parentPost) {
+ commentRootId = post.root_id;
+ } else {
+ commentRootId = post.id;
+ }
+ for (const postId in posts) {
+ if (posts[postId].root_id === commentRootId) {
+ commentCount += 1;
+ }
+ }
+
const postCtl = (
<Post
key={keyPrefix + 'postKey'}
@@ -267,16 +251,15 @@ export default class PostsView extends React.Component {
sameRoot={sameRoot}
post={post}
parentPost={parentPost}
- posts={posts}
hideProfilePic={hideProfilePic}
isLastComment={isLastComment}
shouldHighlight={shouldHighlight}
- onClick={() => GlobalActions.emitPostFocusEvent(post.id)} //eslint-disable-line no-loop-func
- displayNameType={this.state.displayNameType}
+ displayNameType={this.props.displayNameType}
user={profile}
- currentUser={this.state.currentUser}
- center={this.state.centerPosts}
- compactDisplay={this.state.compactPosts}
+ currentUser={this.props.currentUser}
+ center={this.props.displayPostsInCenter}
+ commentCount={commentCount}
+ compactDisplay={this.props.compactDisplay}
/>
);
@@ -302,8 +285,8 @@ export default class PostsView extends React.Component {
}
if (postUserId !== userId &&
- this.props.messageSeparatorTime !== 0 &&
- post.create_at > this.props.messageSeparatorTime &&
+ this.props.lastViewed !== 0 &&
+ post.create_at > this.props.lastViewed &&
!renderedLastViewed) {
renderedLastViewed = true;
@@ -337,10 +320,11 @@ export default class PostsView extends React.Component {
return postCtls;
}
+
updateScrolling() {
- if (this.props.scrollType === PostsView.SCROLL_TYPE_BOTTOM) {
+ if (this.props.scrollType === ScrollTypes.BOTTOM) {
this.scrollToBottom();
- } else if (this.props.scrollType === PostsView.SCROLL_TYPE_NEW_MESSAGE) {
+ } else if (this.props.scrollType === ScrollTypes.NEW_MESSAGE) {
window.setTimeout(window.requestAnimationFrame(() => {
// If separator exists scroll to it. Otherwise scroll to bottom.
if (this.refs.newMessageSeparator) {
@@ -350,7 +334,7 @@ export default class PostsView extends React.Component {
this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight;
}
}), 0);
- } else if (this.props.scrollType === PostsView.SCROLL_TYPE_POST && this.props.scrollPostId) {
+ } else if (this.props.scrollType === ScrollTypes.POST && this.props.scrollPostId) {
window.requestAnimationFrame(() => {
const postNode = ReactDOM.findDOMNode(this.refs[this.props.scrollPostId]);
if (postNode == null) {
@@ -358,12 +342,12 @@ export default class PostsView extends React.Component {
}
postNode.scrollIntoView();
if (this.refs.postlist.scrollTop === postNode.offsetTop) {
- this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3);
+ this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / Constants.SCROLL_PAGE_FRACTION);
} else {
- this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3) + (this.refs.postlist.scrollTop - postNode.offsetTop);
+ this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / Constants.SCROLL_PAGE_FRACTION) + (this.refs.postlist.scrollTop - postNode.offsetTop);
}
});
- } else if (this.props.scrollType === PostsView.SCROLL_TYPE_SIDEBAR_OPEN) {
+ } else if (this.props.scrollType === ScrollTypes.SIDEBAR_OPEN) {
// If we are at the bottom then stay there
if (this.wasAtBottom) {
this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight;
@@ -371,9 +355,9 @@ export default class PostsView extends React.Component {
window.requestAnimationFrame(() => {
this.jumpToPostNode.scrollIntoView();
if (this.refs.postlist.scrollTop === this.jumpToPostNode.offsetTop) {
- this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3);
+ this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / Constants.SCROLL_PAGE_FRACTION);
} else {
- this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3) + (this.refs.postlist.scrollTop - this.jumpToPostNode.offsetTop);
+ this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / Constants.SCROLL_PAGE_FRACTION) + (this.refs.postlist.scrollTop - this.jumpToPostNode.offsetTop);
}
});
}
@@ -386,14 +370,17 @@ export default class PostsView extends React.Component {
});
}
}
+
handleResize() {
this.updateScrolling();
}
+
scrollToBottom() {
window.requestAnimationFrame(() => {
this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight;
});
}
+
scrollToBottomAnimated() {
var postList = $(this.refs.postlist);
postList.animate({scrollTop: this.refs.postlist.scrollHeight}, '500');
@@ -417,145 +404,76 @@ export default class PostsView extends React.Component {
this.updateScrolling();
}
- if (this.props.isActive) {
- PreferenceStore.addChangeListener(this.updateState);
- UserStore.addChangeListener(this.onUserChange);
- }
-
- if (this.props.channel) {
- this.introText = createChannelIntroMessage(this.props.channel);
- } else {
- this.introText = this.getArchivesIntroMessage();
- }
-
window.addEventListener('resize', this.handleResize);
}
+
componentWillUnmount() {
window.removeEventListener('resize', this.handleResize);
this.scrollStopAction.cancel();
- PreferenceStore.removeChangeListener(this.updateState);
- UserStore.removeChangeListener(this.onUserChange);
}
+
componentDidUpdate() {
if (this.props.postList != null) {
this.updateScrolling();
}
}
- componentWillReceiveProps(nextProps) {
- if (!this.props.isActive && nextProps.isActive) {
- this.updateState();
- PreferenceStore.addChangeListener(this.updateState);
- UserStore.addChangeListener(this.onUserChange);
- } else if (this.props.isActive && !nextProps.isActive) {
- PreferenceStore.removeChangeListener(this.updateState);
- UserStore.removeChangeListener(this.onUserChange);
- }
- }
- shouldComponentUpdate(nextProps, nextState) {
- if (this.props.isActive !== nextProps.isActive) {
- return true;
- }
- if (this.props.postList !== nextProps.postList) {
- return true;
- }
- if (this.props.scrollPostId !== nextProps.scrollPostId) {
- return true;
- }
- if (this.props.scrollType !== nextProps.scrollType && nextProps.scrollType !== PostsView.SCROLL_TYPE_FREE) {
- return true;
- }
- if (this.props.messageSeparatorTime !== nextProps.messageSeparatorTime) {
- return true;
- }
- if (!Utils.areObjectsEqual(this.props.postList, nextProps.postList)) {
- return true;
- }
- if (nextState.displayNameType !== this.state.displayNameType) {
- return true;
- }
- if (this.state.topPostId !== nextState.topPostId) {
- return true;
- }
- if (this.state.isScrolling !== nextState.isScrolling) {
- return true;
- }
- if (this.state.centerPosts !== nextState.centerPosts) {
- return true;
- }
- if (this.state.compactPosts !== nextState.compactPosts) {
- return true;
- }
- if (!Utils.areObjectsEqual(this.state.profiles, nextState.profiles)) {
- return true;
- }
- return false;
- }
render() {
- let posts = [];
- let order = [];
- let moreMessagesTop;
- let moreMessagesBottom;
- let postElements;
- let activeClass = 'inactive';
- if (this.props.postList != null) {
- posts = this.props.postList.posts;
- order = this.props.postList.order;
-
- // Create intro message or top loadmore link
- if (this.props.showMoreMessagesTop) {
- moreMessagesTop = (
- <a
- ref='loadmoretop'
- className='more-messages-text theme'
- href='#'
- onClick={this.loadMorePostsTop}
- >
- <FormattedMessage
- id='posts_view.loadMore'
- defaultMessage='Load more messages'
- />
- </a>
- );
- } else {
- moreMessagesTop = this.introText;
- }
+ if (this.props.postList == null) {
+ return <div/>;
+ }
- // Give option to load more posts at bottom if nessisary
- if (this.props.showMoreMessagesBottom) {
- moreMessagesBottom = (
- <a
- ref='loadmorebottom'
- className='more-messages-text theme'
- href='#'
- onClick={this.loadMorePostsBottom}
- >
- <FormattedMessage id='posts_view.loadMore'/>
- </a>
- );
- } else {
- moreMessagesBottom = null;
- }
+ const posts = this.props.postList.posts;
+ const order = this.props.postList.order;
- // Create post elements
- postElements = this.createPosts(posts, order);
+ // Create intro message or top loadmore link
+ let moreMessagesTop;
+ if (this.props.showMoreMessagesTop) {
+ moreMessagesTop = (
+ <a
+ ref='loadmoretop'
+ className='more-messages-text theme'
+ href='#'
+ onClick={this.loadMorePostsTop}
+ >
+ <FormattedMessage
+ id='posts_view.loadMore'
+ defaultMessage='Load more messages'
+ />
+ </a>
+ );
+ } else {
+ moreMessagesTop = this.introText;
+ }
- // Show ourselves if we are marked active
- if (this.props.isActive) {
- activeClass = '';
- }
+ // Give option to load more posts at bottom if necessary
+ let moreMessagesBottom;
+ if (this.props.showMoreMessagesBottom) {
+ moreMessagesBottom = (
+ <a
+ ref='loadmorebottom'
+ className='more-messages-text theme'
+ href='#'
+ onClick={this.loadMorePostsBottom}
+ >
+ <FormattedMessage id='posts_view.loadMore'/>
+ </a>
+ );
}
+ // Create post elements
+ const postElements = this.createPosts(posts, order);
+
let topPostCreateAt = 0;
if (this.state.topPostId && this.props.postList.posts[this.state.topPostId]) {
topPostCreateAt = this.props.postList.posts[this.state.topPostId].create_at;
}
return (
- <div className={activeClass}>
+ <div>
<FloatingTimestamp
isScrolling={this.state.isScrolling}
- isMobile={$(window).width() > 768}
+ isMobile={Utils.isMobile()}
createAt={topPostCreateAt}
/>
<ScrollToBottomArrows
@@ -583,48 +501,24 @@ export default class PostsView extends React.Component {
);
}
}
-PostsView.defaultProps = {
+
+PostList.defaultProps = {
+ lastViewed: 0
};
-PostsView.propTypes = {
- isActive: React.PropTypes.bool,
+PostList.propTypes = {
postList: React.PropTypes.object,
+ profiles: React.PropTypes.object,
+ channel: React.PropTypes.object,
+ currentUser: React.PropTypes.object,
scrollPostId: React.PropTypes.string,
scrollType: React.PropTypes.number,
- postViewScrolled: React.PropTypes.func.isRequired,
- loadMorePostsTopClicked: React.PropTypes.func.isRequired,
- loadMorePostsBottomClicked: React.PropTypes.func.isRequired,
+ postListScrolled: React.PropTypes.func.isRequired,
showMoreMessagesTop: React.PropTypes.bool,
showMoreMessagesBottom: React.PropTypes.bool,
- channel: React.PropTypes.object,
- messageSeparatorTime: React.PropTypes.number,
+ lastViewed: React.PropTypes.number,
postsToHighlight: React.PropTypes.object,
+ displayNameType: React.PropTypes.string,
+ displayPostsInCenter: React.PropTypes.bool,
compactDisplay: React.PropTypes.bool
};
-
-function ScrollToBottomArrows({isScrolling, atBottom, onClick}) {
- // only show on mobile
- if ($(window).width() > 768) {
- return <noscript/>;
- }
-
- let className = 'post-list__arrows';
- if (isScrolling && !atBottom) {
- className += ' scrolling';
- }
-
- return (
- <div
- className={className}
- onClick={onClick}
- >
- <span dangerouslySetInnerHTML={{__html: Constants.SCROLL_BOTTOM_ICON}}/>
- </div>
- );
-}
-
-ScrollToBottomArrows.propTypes = {
- isScrolling: React.PropTypes.bool.isRequired,
- atBottom: React.PropTypes.bool.isRequired,
- onClick: React.PropTypes.func.isRequired
-};
diff --git a/webapp/components/providers.json b/webapp/components/post_view/components/providers.json
index b5899c225..b5899c225 100644
--- a/webapp/components/providers.json
+++ b/webapp/components/post_view/components/providers.json
diff --git a/webapp/components/post_view/components/scroll_to_bottom_arrows.jsx b/webapp/components/post_view/components/scroll_to_bottom_arrows.jsx
new file mode 100644
index 000000000..461ca3358
--- /dev/null
+++ b/webapp/components/post_view/components/scroll_to_bottom_arrows.jsx
@@ -0,0 +1,37 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import $ from 'jquery';
+
+import Constants from 'utils/constants.jsx';
+
+import React from 'react';
+
+export default class ScrollToBottomArrows extends React.Component {
+ render() {
+ // only show on mobile
+ if ($(window).width() > 768) {
+ return <noscript/>;
+ }
+
+ let className = 'post-list__arrows';
+ if (this.props.isScrolling && !this.props.atBottom) {
+ className += ' scrolling';
+ }
+
+ return (
+ <div
+ className={className}
+ onClick={this.props.onClick}
+ >
+ <span dangerouslySetInnerHTML={{__html: Constants.SCROLL_BOTTOM_ICON}}/>
+ </div>
+ );
+ }
+}
+
+ScrollToBottomArrows.propTypes = {
+ isScrolling: React.PropTypes.bool.isRequired,
+ atBottom: React.PropTypes.bool.isRequired,
+ onClick: React.PropTypes.func.isRequired
+};
diff --git a/webapp/components/post_focus_view.jsx b/webapp/components/post_view/post_focus_view_controller.jsx
index 30a2f9d72..7c1da6566 100644
--- a/webapp/components/post_focus_view.jsx
+++ b/webapp/components/post_view/post_focus_view_controller.jsx
@@ -1,11 +1,15 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import PostsView from './posts_view.jsx';
+import PostList from './components/post_list.jsx';
+import LoadingScreen from 'components/loading_screen.jsx';
import PostStore from 'stores/post_store.jsx';
+import UserStore from 'stores/user_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';
-import * as GlobalActions from 'actions/global_actions.jsx';
+
+import Constants from 'utils/constants.jsx';
+const ScrollTypes = Constants.ScrollTypes;
import React from 'react';
@@ -15,17 +19,24 @@ export default class PostFocusView extends React.Component {
this.onChannelChange = this.onChannelChange.bind(this);
this.onPostsChange = this.onPostsChange.bind(this);
- this.handlePostsViewScroll = this.handlePostsViewScroll.bind(this);
- this.loadMorePostsTop = this.loadMorePostsTop.bind(this);
- this.loadMorePostsBottom = this.loadMorePostsBottom.bind(this);
+ this.onUserChange = this.onUserChange.bind(this);
+ this.onPostListScroll = this.onPostListScroll.bind(this);
const focusedPostId = PostStore.getFocusedPostId();
+ const channel = ChannelStore.getCurrent();
+ let profiles = UserStore.getProfiles();
+ if (channel && channel.type === Constants.DM_CHANNEL) {
+ profiles = Object.assign({}, profiles, UserStore.getDirectProfiles());
+ }
+
this.state = {
- scrollType: PostsView.SCROLL_TYPE_POST,
+ postList: PostStore.getVisiblePosts(focusedPostId),
+ currentUser: UserStore.getCurrentUser(),
+ profiles,
+ scrollType: ScrollTypes.POST,
currentChannel: ChannelStore.getCurrentId().slice(),
scrollPostId: focusedPostId,
- postList: PostStore.getVisiblePosts(focusedPostId),
atTop: PostStore.getVisibilityAtTop(focusedPostId),
atBottom: PostStore.getVisibilityAtBottom(focusedPostId)
};
@@ -34,11 +45,13 @@ export default class PostFocusView extends React.Component {
componentDidMount() {
ChannelStore.addChangeListener(this.onChannelChange);
PostStore.addChangeListener(this.onPostsChange);
+ UserStore.addChangeListener(this.onUserChange);
}
componentWillUnmount() {
ChannelStore.removeChangeListener(this.onChannelChange);
PostStore.removeChangeListener(this.onPostsChange);
+ UserStore.removeChangeListener(this.onUserChange);
}
onChannelChange() {
@@ -46,7 +59,7 @@ export default class PostFocusView extends React.Component {
if (this.state.currentChannel !== currentChannel) {
this.setState({
currentChannel: currentChannel.slice(),
- scrollType: PostsView.SCROLL_TYPE_POST
+ scrollType: ScrollTypes.POST
});
}
}
@@ -65,42 +78,50 @@ export default class PostFocusView extends React.Component {
});
}
- handlePostsViewScroll() {
- this.setState({scrollType: PostsView.SCROLL_TYPE_FREE});
- }
-
- loadMorePostsTop() {
- GlobalActions.emitLoadMorePostsFocusedTopEvent();
+ onUserChange() {
+ const channel = ChannelStore.getCurrent();
+ let profiles = UserStore.getProfiles();
+ if (channel && channel.type === Constants.DM_CHANNEL) {
+ profiles = Object.assign({}, profiles, UserStore.getDirectProfiles());
+ }
+ this.setState({currentUser: UserStore.getCurrentUser(), profiles: JSON.parse(JSON.stringify(profiles))});
}
- loadMorePostsBottom() {
- GlobalActions.emitLoadMorePostsFocusedBottomEvent();
+ onPostListScroll() {
+ this.setState({scrollType: ScrollTypes.FREE});
}
render() {
const postsToHighlight = {};
postsToHighlight[this.state.scrollPostId] = true;
- if (!this.state.postList) {
- return null;
- }
-
- return (
- <div id='post-list'>
- <PostsView
- key={'postfocusview'}
- isActive={true}
+ let content;
+ if (this.state.postList == null) {
+ content = (
+ <LoadingScreen
+ position='absolute'
+ key='loading'
+ />
+ );
+ } else {
+ content = (
+ <PostList
postList={this.state.postList}
+ currentUser={this.state.currentUser}
+ profiles={this.state.profiles}
scrollType={this.state.scrollType}
scrollPostId={this.state.scrollPostId}
- postViewScrolled={this.handlePostsViewScroll}
- loadMorePostsTopClicked={this.loadMorePostsTop}
- loadMorePostsBottomClicked={this.loadMorePostsBottom}
+ postListScrolled={this.onPostListScroll}
showMoreMessagesTop={!this.state.atTop}
showMoreMessagesBottom={!this.state.atBottom}
- messageSeparatorTime={0}
postsToHighlight={postsToHighlight}
/>
+ );
+ }
+
+ return (
+ <div id='post-list'>
+ {content}
</div>
);
}
diff --git a/webapp/components/post_view/post_view_cache.jsx b/webapp/components/post_view/post_view_cache.jsx
new file mode 100644
index 000000000..8876ae461
--- /dev/null
+++ b/webapp/components/post_view/post_view_cache.jsx
@@ -0,0 +1,85 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information
+
+import PostViewController from './post_view_controller.jsx';
+
+import ChannelStore from 'stores/channel_store.jsx';
+
+import React from 'react';
+
+const MAXIMUM_CACHED_VIEWS = 5;
+
+export default class PostViewCache extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.onChannelChange = this.onChannelChange.bind(this);
+
+ const channel = ChannelStore.getCurrent();
+
+ this.state = {
+ currentChannelId: channel.id,
+ channels: [channel]
+ };
+ }
+
+ componentDidMount() {
+ ChannelStore.addChangeListener(this.onChannelChange);
+ }
+
+ componentWillUnmount() {
+ ChannelStore.removeChangeListener(this.onChannelChange);
+ }
+
+ onChannelChange() {
+ const channels = Object.assign([], this.state.channels);
+ const currentChannel = ChannelStore.getCurrent();
+
+ if (currentChannel == null) {
+ return;
+ }
+
+ // make sure current channel really changed
+ if (currentChannel.id === this.state.currentChannelId) {
+ return;
+ }
+
+ if (channels.length > MAXIMUM_CACHED_VIEWS) {
+ channels.shift();
+ }
+
+ const index = channels.map((c) => c.id).indexOf(currentChannel.id);
+ if (index !== -1) {
+ channels.splice(index, 1);
+ }
+
+ channels.push(currentChannel);
+
+ this.setState({
+ currentChannelId: currentChannel.id,
+ channels
+ });
+ }
+
+ render() {
+ const channels = this.state.channels;
+ const currentChannelId = this.state.currentChannelId;
+
+ let postViews = [];
+ for (let i = 0; i < channels.length; i++) {
+ postViews.push(
+ <PostViewController
+ key={'postviewcontroller_' + channels[i].id}
+ channel={channels[i]}
+ active={channels[i].id === currentChannelId}
+ />
+ );
+ }
+
+ return (
+ <div id='post-list'>
+ {postViews}
+ </div>
+ );
+ }
+}
diff --git a/webapp/components/post_view/post_view_controller.jsx b/webapp/components/post_view/post_view_controller.jsx
new file mode 100644
index 000000000..0898a9ce6
--- /dev/null
+++ b/webapp/components/post_view/post_view_controller.jsx
@@ -0,0 +1,264 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import PostList from './components/post_list.jsx';
+import LoadingScreen from 'components/loading_screen.jsx';
+
+import PreferenceStore from 'stores/preference_store.jsx';
+import UserStore from 'stores/user_store.jsx';
+import PostStore from 'stores/post_store.jsx';
+import ChannelStore from 'stores/channel_store.jsx';
+
+import * as Utils from 'utils/utils.jsx';
+
+import Constants from 'utils/constants.jsx';
+const Preferences = Constants.Preferences;
+const ScrollTypes = Constants.ScrollTypes;
+
+import React from 'react';
+
+export default class PostViewController extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.onPreferenceChange = this.onPreferenceChange.bind(this);
+ this.onUserChange = this.onUserChange.bind(this);
+ this.onPostsChange = this.onPostsChange.bind(this);
+ this.onPostsViewJumpRequest = this.onPostsViewJumpRequest.bind(this);
+ this.onPostListScroll = this.onPostListScroll.bind(this);
+ this.onActivate = this.onActivate.bind(this);
+ this.onDeactivate = this.onDeactivate.bind(this);
+
+ const channel = props.channel;
+ let profiles = UserStore.getProfiles();
+ if (channel && channel.type === Constants.DM_CHANNEL) {
+ profiles = Object.assign({}, profiles, UserStore.getDirectProfiles());
+ }
+
+ let lastViewed = Number.MAX_VALUE;
+ const member = ChannelStore.getMember(channel.id);
+ if (member != null) {
+ lastViewed = member.last_viewed_at;
+ }
+
+ this.state = {
+ channel,
+ postList: PostStore.getVisiblePosts(channel.id),
+ currentUser: UserStore.getCurrentUser(),
+ profiles,
+ atTop: PostStore.getVisibilityAtTop(channel.id),
+ lastViewed,
+ scrollType: ScrollTypes.BOTTOM,
+ displayNameType: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', 'false'),
+ displayPostsInCenter: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.CHANNEL_DISPLAY_MODE, Preferences.CHANNEL_DISPLAY_MODE_DEFAULT) === Preferences.CHANNEL_DISPLAY_MODE_CENTERED,
+ compactDisplay: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.MESSAGE_DISPLAY, Preferences.MESSAGE_DISPLAY_DEFAULT) === Preferences.MESSAGE_DISPLAY_COMPACT
+ };
+ }
+
+ componentDidMount() {
+ if (this.props.active) {
+ this.onActivate();
+ }
+ }
+
+ componentWillUnmount() {
+ if (this.props.active) {
+ this.onDeactivate();
+ }
+ }
+
+ onPreferenceChange() {
+ this.setState({
+ displayNameType: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', 'false'),
+ displayPostsInCenter: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.CHANNEL_DISPLAY_MODE, Preferences.CHANNEL_DISPLAY_MODE_DEFAULT) === Preferences.CHANNEL_DISPLAY_MODE_CENTERED,
+ compactDisplay: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.MESSAGE_DISPLAY, Preferences.MESSAGE_DISPLAY_DEFAULT) === Preferences.MESSAGE_DISPLAY_COMPACT
+ });
+ }
+
+ onUserChange() {
+ const channel = this.state.channel;
+ let profiles = UserStore.getProfiles();
+ if (channel && channel.type === Constants.DM_CHANNEL) {
+ profiles = Object.assign({}, profiles, UserStore.getDirectProfiles());
+ }
+ this.setState({currentUser: UserStore.getCurrentUser(), profiles: JSON.parse(JSON.stringify(profiles))});
+ }
+
+ onPostsChange() {
+ this.setState({
+ postList: JSON.parse(JSON.stringify(PostStore.getVisiblePosts(this.state.channel.id))),
+ atTop: PostStore.getVisibilityAtTop(this.state.channel.id)
+ });
+ }
+
+ onActivate() {
+ PreferenceStore.addChangeListener(this.onPreferenceChange);
+ UserStore.addChangeListener(this.onUserChange);
+ PostStore.addChangeListener(this.onPostsChange);
+ PostStore.addPostsViewJumpListener(this.onPostsViewJumpRequest);
+ }
+
+ onDeactivate() {
+ PreferenceStore.removeChangeListener(this.onPreferenceChange);
+ UserStore.removeChangeListener(this.onUserChange);
+ PostStore.removeChangeListener(this.onPostsChange);
+ PostStore.removePostsViewJumpListener(this.onPostsViewJumpRequest);
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (this.props.active && !nextProps.active) {
+ this.onDeactivate();
+ } else if (!this.props.active && nextProps.active) {
+ this.onActivate();
+
+ const channel = nextProps.channel;
+
+ let lastViewed = Number.MAX_VALUE;
+ const member = ChannelStore.getMember(channel.id);
+ if (member != null) {
+ lastViewed = member.last_viewed_at;
+ }
+
+ let profiles = UserStore.getProfiles();
+ if (channel && channel.type === Constants.DM_CHANNEL) {
+ profiles = Object.assign({}, profiles, UserStore.getDirectProfiles());
+ }
+
+ this.setState({
+ channel,
+ lastViewed,
+ profiles: JSON.parse(JSON.stringify(profiles)),
+ postList: JSON.parse(JSON.stringify(PostStore.getVisiblePosts(channel.id))),
+ displayNameType: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', 'false'),
+ displayPostsInCenter: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.CHANNEL_DISPLAY_MODE, Preferences.CHANNEL_DISPLAY_MODE_DEFAULT) === Preferences.CHANNEL_DISPLAY_MODE_CENTERED,
+ compactDisplay: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.MESSAGE_DISPLAY, Preferences.MESSAGE_DISPLAY_DEFAULT) === Preferences.MESSAGE_DISPLAY_COMPACT,
+ scrollType: ScrollTypes.BOTTOM
+ });
+ }
+ }
+
+ onPostsViewJumpRequest(type, postId) {
+ switch (type) {
+ case Constants.PostsViewJumpTypes.BOTTOM:
+ this.setState({scrollType: ScrollTypes.BOTTOM});
+ break;
+ case Constants.PostsViewJumpTypes.POST:
+ this.setState({
+ scrollType: ScrollTypes.POST,
+ scrollPostId: postId
+ });
+ break;
+ case Constants.PostsViewJumpTypes.SIDEBAR_OPEN:
+ this.setState({scrollType: ScrollTypes.SIDEBAR_OPEN});
+ break;
+ }
+ }
+
+ onPostListScroll(atBottom) {
+ if (atBottom) {
+ this.setState({scrollType: ScrollTypes.BOTTOM});
+ } else {
+ this.setState({scrollType: ScrollTypes.FREE});
+ }
+ }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ if (nextProps.active !== this.props.active) {
+ return true;
+ }
+
+ if (nextState.atTop !== this.state.atTop) {
+ return true;
+ }
+
+ if (nextState.displayNameType !== this.state.displayNameType) {
+ return true;
+ }
+
+ if (nextState.displayPostsInCenter !== this.state.displayPostsInCenter) {
+ return true;
+ }
+
+ if (nextState.compactDisplay !== this.state.compactDisplay) {
+ return true;
+ }
+
+ if (nextState.lastViewed !== this.state.lastViewed) {
+ return true;
+ }
+
+ if (nextState.showMoreMessagesTop !== this.state.showMoreMessagesTop) {
+ return true;
+ }
+
+ if (nextState.scrollType !== this.state.scrollType) {
+ return true;
+ }
+
+ if (nextState.scrollPostId !== this.state.scrollPostId) {
+ return true;
+ }
+
+ if (nextProps.channel.id !== this.props.channel.id) {
+ return true;
+ }
+
+ if (!Utils.areObjectsEqual(nextState.currentUser, this.state.currentUser)) {
+ return true;
+ }
+
+ if (!Utils.areObjectsEqual(nextState.postList, this.state.postList)) {
+ return true;
+ }
+
+ if (!Utils.areObjectsEqual(nextState.profiles, this.state.profiles)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ render() {
+ let content;
+ if (this.state.postList == null) {
+ content = (
+ <LoadingScreen
+ position='absolute'
+ key='loading'
+ />
+ );
+ } else {
+ content = (
+ <PostList
+ postList={this.state.postList}
+ profiles={this.state.profiles}
+ channel={this.state.channel}
+ currentUser={this.state.currentUser}
+ showMoreMessagesTop={!this.state.atTop}
+ scrollType={this.state.scrollType}
+ scrollPostId={this.state.scrollPostId}
+ postListScrolled={this.onPostListScroll}
+ displayNameType={this.state.displayNameType}
+ displayPostsInCenter={this.state.displayPostsInCenter}
+ compactDisplay={this.state.compactDisplay}
+ />
+ );
+ }
+
+ let activeClass = '';
+ if (!this.props.active) {
+ activeClass = 'inactive';
+ }
+
+ return (
+ <div className={activeClass}>
+ {content}
+ </div>
+ );
+ }
+}
+
+PostViewController.propTypes = {
+ channel: React.PropTypes.object,
+ active: React.PropTypes.bool
+};
diff --git a/webapp/components/posts_view_container.jsx b/webapp/components/posts_view_container.jsx
deleted file mode 100644
index 3f8a44cc3..000000000
--- a/webapp/components/posts_view_container.jsx
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import $ from 'jquery';
-
-import PostsView from './posts_view.jsx';
-import LoadingScreen from './loading_screen.jsx';
-
-import ChannelStore from 'stores/channel_store.jsx';
-import PostStore from 'stores/post_store.jsx';
-
-import * as GlobalActions from 'actions/global_actions.jsx';
-
-import Constants from 'utils/constants.jsx';
-
-import React from 'react';
-
-const MAXIMUM_CACHED_VIEWS = 3;
-
-export default class PostsViewContainer extends React.Component {
- constructor() {
- super();
-
- this.onChannelChange = this.onChannelChange.bind(this);
- this.onChannelLeave = this.onChannelLeave.bind(this);
- this.onPostsChange = this.onPostsChange.bind(this);
- this.handlePostsViewScroll = this.handlePostsViewScroll.bind(this);
- this.loadMorePostsTop = this.loadMorePostsTop.bind(this);
- this.handlePostsViewJumpRequest = this.handlePostsViewJumpRequest.bind(this);
-
- const currentChannelId = ChannelStore.getCurrentId();
- const state = {
- scrollType: PostsView.SCROLL_TYPE_BOTTOM,
- scrollPost: null
- };
- if (currentChannelId) {
- let lastViewed = Date.now();
- const member = ChannelStore.getMember(currentChannelId);
- if (member) {
- lastViewed = member.last_viewed_at;
- }
-
- Object.assign(state, {
- currentChannelIndex: 0,
- channels: [currentChannelId],
- postLists: [this.getChannelPosts(currentChannelId)],
- atTop: [PostStore.getVisibilityAtTop(currentChannelId)],
- currentLastViewed: lastViewed
- });
- } else {
- Object.assign(state, {
- currentChannelIndex: null,
- channels: [],
- postLists: [],
- atTop: [],
- currentLastViewed: Date.now()
- });
- }
-
- state.showInviteModal = false;
- this.state = state;
- }
- componentDidMount() {
- ChannelStore.addChangeListener(this.onChannelChange);
- ChannelStore.addLeaveListener(this.onChannelLeave);
- PostStore.addChangeListener(this.onPostsChange);
- PostStore.addPostsViewJumpListener(this.handlePostsViewJumpRequest);
- $('body').addClass('app__body');
- }
- componentWillUnmount() {
- ChannelStore.removeChangeListener(this.onChannelChange);
- ChannelStore.removeLeaveListener(this.onChannelLeave);
- PostStore.removeChangeListener(this.onPostsChange);
- PostStore.removePostsViewJumpListener(this.handlePostsViewJumpRequest);
- $('body').removeClass('app__body');
- }
- handlePostsViewJumpRequest(type, post) {
- switch (type) {
- case Constants.PostsViewJumpTypes.BOTTOM:
- this.setState({scrollType: PostsView.SCROLL_TYPE_BOTTOM});
- break;
- case Constants.PostsViewJumpTypes.POST:
- this.setState({
- scrollType: PostsView.SCROLL_TYPE_POST,
- scrollPost: post
- });
- break;
- case Constants.PostsViewJumpTypes.SIDEBAR_OPEN:
- this.setState({scrollType: PostsView.SCROLL_TYPE_SIDEBAR_OPEN});
- break;
- }
- }
- onChannelChange() {
- const postLists = this.state.postLists.slice();
- const atTop = this.state.atTop.slice();
- const channels = this.state.channels.slice();
- const channelId = ChannelStore.getCurrentId();
-
- // Has the channel really changed?
- if (channelId === channels[this.state.currentChannelIndex]) {
- return;
- }
-
- let lastViewed = Number.MAX_VALUE;
- const member = ChannelStore.getMember(channelId);
- if (member != null) {
- lastViewed = member.last_viewed_at;
- }
-
- let newIndex = channels.indexOf(channelId);
- if (newIndex === -1) {
- if (channels.length >= MAXIMUM_CACHED_VIEWS) {
- channels.shift();
- atTop.shift();
- postLists.shift();
- }
-
- newIndex = channels.length;
- channels.push(channelId);
- atTop[newIndex] = PostStore.getVisibilityAtTop(channelId);
- }
-
- // make sure we have the latest posts from the store
- postLists[newIndex] = this.getChannelPosts(channelId);
-
- this.setState({
- currentChannelIndex: newIndex,
- currentLastViewed: lastViewed,
- scrollType: PostsView.SCROLL_TYPE_NEW_MESSAGE,
- channels,
- postLists,
- atTop});
- }
- onChannelLeave(id) {
- const postLists = this.state.postLists.slice();
- const channels = this.state.channels.slice();
- const atTop = this.state.atTop.slice();
- const index = channels.indexOf(id);
- if (index !== -1) {
- postLists.splice(index, 1);
- channels.splice(index, 1);
- atTop.splice(index, 1);
- }
- this.setState({channels, postLists, atTop});
- }
- onPostsChange() {
- const channels = this.state.channels;
- const postLists = this.state.postLists.slice();
- const atTop = this.state.atTop.slice();
- const currentChannelId = channels[this.state.currentChannelIndex];
- const newPostsView = this.getChannelPosts(currentChannelId);
-
- postLists[this.state.currentChannelIndex] = newPostsView;
- atTop[this.state.currentChannelIndex] = PostStore.getVisibilityAtTop(currentChannelId);
- this.setState({postLists, atTop});
- }
- getChannelPosts(id) {
- return PostStore.getVisiblePosts(id);
- }
- loadMorePostsTop() {
- GlobalActions.emitLoadMorePostsEvent();
- }
- handlePostsViewScroll(atBottom) {
- if (atBottom) {
- this.setState({scrollType: PostsView.SCROLL_TYPE_BOTTOM});
- } else {
- this.setState({scrollType: PostsView.SCROLL_TYPE_FREE});
- }
- }
- render() {
- const postLists = this.state.postLists;
- const channels = this.state.channels;
- const currentChannelId = channels[this.state.currentChannelIndex];
- const channel = ChannelStore.get(currentChannelId);
-
- if (!channel) {
- return null;
- }
-
- const postListCtls = [];
- for (let i = 0; i < channels.length; i++) {
- const isActive = (channels[i] === currentChannelId);
- postListCtls.push(
- <PostsView
- key={'postsviewkey' + channels[i]}
- isActive={isActive}
- postList={postLists[i]}
- scrollType={this.state.scrollType}
- scrollPostId={this.state.scrollPost}
- postViewScrolled={this.handlePostsViewScroll}
- loadMorePostsTopClicked={this.loadMorePostsTop}
- loadMorePostsBottomClicked={() => {
- // Do Nothing
- }}
- showMoreMessagesTop={!this.state.atTop[this.state.currentChannelIndex]}
- showMoreMessagesBottom={false}
- channel={channel}
- messageSeparatorTime={this.state.currentLastViewed}
- />
- );
- if (!postLists[i] && isActive) {
- postListCtls.push(
- <LoadingScreen
- position='absolute'
- key='loading'
- />
- );
- }
- }
-
- return (
- <div id='post-list'>
- {postListCtls}
- </div>
- );
- }
-}
diff --git a/webapp/components/rhs_comment.jsx b/webapp/components/rhs_comment.jsx
index a771803b8..a980a8227 100644
--- a/webapp/components/rhs_comment.jsx
+++ b/webapp/components/rhs_comment.jsx
@@ -3,7 +3,7 @@
import UserProfile from './user_profile.jsx';
import FileAttachmentList from './file_attachment_list.jsx';
-import PendingPostActions from './pending_post_actions.jsx';
+import PendingPostOptions from 'components/post_view/components/pending_post_options.jsx';
import TeamStore from 'stores/team_store.jsx';
import UserStore from 'stores/user_store.jsx';
@@ -168,7 +168,7 @@ export default class RhsComment extends React.Component {
if (post.state === Constants.POST_FAILED) {
postClass += ' post-fail';
- loading = <PendingPostActions post={this.props.post}/>;
+ loading = <PendingPostOptions post={this.props.post}/>;
} else if (post.state === Constants.POST_LOADING) {
postClass += ' post-waiting';
loading = (
diff --git a/webapp/components/rhs_root_post.jsx b/webapp/components/rhs_root_post.jsx
index 02fc4fc59..051d68f34 100644
--- a/webapp/components/rhs_root_post.jsx
+++ b/webapp/components/rhs_root_post.jsx
@@ -6,11 +6,13 @@ import UserProfile from './user_profile.jsx';
import UserStore from 'stores/user_store.jsx';
import TeamStore from 'stores/team_store.jsx';
import * as TextFormatting from 'utils/text_formatting.jsx';
-import * as Utils from 'utils/utils.jsx';
import FileAttachmentList from './file_attachment_list.jsx';
-import PostBodyAdditionalContent from './post_body_additional_content.jsx';
+import PostBodyAdditionalContent from 'components/post_view/components/post_body_additional_content.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
+import * as Utils from 'utils/utils.jsx';
+import * as PostUtils from 'utils/post_utils.jsx';
+
import Constants from 'utils/constants.jsx';
import {FormattedMessage, FormattedDate} from 'react-intl';
@@ -56,7 +58,7 @@ export default class RhsRootPost extends React.Component {
}
var systemMessageClass = '';
- if (Utils.isSystemMessage(post)) {
+ if (PostUtils.isSystemMessage(post)) {
systemMessageClass = 'post--system';
}
@@ -188,7 +190,7 @@ export default class RhsRootPost extends React.Component {
}
botIndicator = <li className='col col__name bot-indicator'>{'BOT'}</li>;
- } else if (Utils.isSystemMessage(post)) {
+ } else if (PostUtils.isSystemMessage(post)) {
userProfile = (
<UserProfile
user={{}}
@@ -202,7 +204,7 @@ export default class RhsRootPost extends React.Component {
const profilePic = (
<img
className='post-profile-img'
- src={Utils.getProfilePicSrcForPost(post, timestamp)}
+ src={PostUtils.getProfilePicSrcForPost(post, timestamp)}
height='36'
width='36'
/>
diff --git a/webapp/components/search_results_item.jsx b/webapp/components/search_results_item.jsx
index 708b148d8..3fe13878e 100644
--- a/webapp/components/search_results_item.jsx
+++ b/webapp/components/search_results_item.jsx
@@ -10,6 +10,7 @@ import * as GlobalActions from 'actions/global_actions.jsx';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import * as TextFormatting from 'utils/text_formatting.jsx';
import * as Utils from 'utils/utils.jsx';
+import * as PostUtils from 'utils/post_utils.jsx';
import Constants from 'utils/constants.jsx';
const ActionTypes = Constants.ActionTypes;
@@ -93,7 +94,7 @@ export default class SearchResultsItem extends React.Component {
<div className='post__content'>
<div className='post__img'>
<img
- src={Utils.getProfilePicSrcForPost(post, timestamp)}
+ src={PostUtils.getProfilePicSrcForPost(post, timestamp)}
height='36'
width='36'
/>
diff --git a/webapp/stores/notification_store.jsx b/webapp/stores/notification_store.jsx
index 7d7039780..202b24432 100644
--- a/webapp/stores/notification_store.jsx
+++ b/webapp/stores/notification_store.jsx
@@ -7,6 +7,7 @@ import Constants from 'utils/constants.jsx';
import UserStore from './user_store.jsx';
import ChannelStore from './channel_store.jsx';
import * as Utils from 'utils/utils.jsx';
+import * as PostUtils from 'utils/post_utils.jsx';
const ActionTypes = Constants.ActionTypes;
const CHANGE_EVENT = 'change';
@@ -26,7 +27,7 @@ class NotificationStoreClass extends EventEmitter {
handleRecievedPost(post, msgProps) {
// Send desktop notification
- if ((UserStore.getCurrentId() !== post.user_id || post.props.from_webhook === 'true') && !Utils.isSystemMessage(post)) {
+ if ((UserStore.getCurrentId() !== post.user_id || post.props.from_webhook === 'true') && !PostUtils.isSystemMessage(post)) {
let mentions = [];
if (msgProps.mentions) {
mentions = JSON.parse(msgProps.mentions);
diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx
index 0e2ae07ea..f1af112a9 100644
--- a/webapp/utils/constants.jsx
+++ b/webapp/utils/constants.jsx
@@ -161,6 +161,14 @@ export default {
EPHEMERAL_MESSAGE: 'ephemeral_message'
},
+ ScrollTypes: {
+ FREE: 1,
+ BOTTOM: 2,
+ SIDEBBAR_OPEN: 3,
+ NEW_MESSAGE: 4,
+ POST: 5
+ },
+
//SPECIAL_MENTIONS: ['all', 'channel'],
SPECIAL_MENTIONS: ['channel'],
CHARACTER_LIMIT: 4000,
@@ -204,6 +212,9 @@ export default {
WEB_VIDEO_HEIGHT: 480,
MOBILE_VIDEO_WIDTH: 480,
MOBILE_VIDEO_HEIGHT: 360,
+ MOBILE_SCREEN_WIDTH: 768,
+ SCROLL_DELAY: 2000,
+ SCROLL_PAGE_FRACTION: 3,
DEFAULT_CHANNEL: 'town-square',
DEFAULT_CHANNEL_UI_NAME: 'Town Square',
OFFTOPIC_CHANNEL: 'off-topic',
@@ -738,5 +749,6 @@ export default {
DEFAULT_WEBHOOK_LOGO: logoWebhook,
MHPNS: 'https://push.mattermost.com',
MTPNS: 'http://push-test.mattermost.com',
- BOT_NAME: 'BOT'
+ BOT_NAME: 'BOT',
+ POST_COLLAPSE_TIMEOUT: 1000 * 60 * 5 // five minutes
};
diff --git a/webapp/utils/post_utils.jsx b/webapp/utils/post_utils.jsx
new file mode 100644
index 000000000..f5111d72d
--- /dev/null
+++ b/webapp/utils/post_utils.jsx
@@ -0,0 +1,32 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import Client from 'utils/web_client.jsx';
+
+import Constants from 'utils/constants.jsx';
+
+export function isSystemMessage(post) {
+ return post.type && (post.type.lastIndexOf(Constants.SYSTEM_MESSAGE_PREFIX) === 0);
+}
+
+export function isComment(post) {
+ if ('root_id' in post) {
+ return post.root_id !== '' && post.root_id != null;
+ }
+ return false;
+}
+
+export function getProfilePicSrcForPost(post, timestamp) {
+ let src = Client.getUsersRoute() + '/' + post.user_id + '/image?time=' + timestamp;
+ if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
+ if (post.props.override_icon_url) {
+ src = post.props.override_icon_url;
+ } else {
+ src = Constants.DEFAULT_WEBHOOK_LOGO;
+ }
+ } else if (isSystemMessage(post)) {
+ src = Constants.SYSTEM_MESSAGE_PROFILE_IMAGE;
+ }
+
+ return src;
+}
diff --git a/webapp/utils/utils.jsx b/webapp/utils/utils.jsx
index 978f231df..9b0e370bf 100644
--- a/webapp/utils/utils.jsx
+++ b/webapp/utils/utils.jsx
@@ -909,14 +909,7 @@ export function isValidUsername(name) {
}
export function isMobile() {
- return window.innerWidth <= 768;
-}
-
-export function isComment(post) {
- if ('root_id' in post) {
- return post.root_id !== '' && post.root_id != null;
- }
- return false;
+ return window.innerWidth <= Constants.MOBILE_SCREEN_WIDTH;
}
export function getDirectTeammate(channelId) {
@@ -1315,10 +1308,6 @@ export function isFeatureEnabled(feature) {
return PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, Constants.FeatureTogglePrefix + feature.label);
}
-export function isSystemMessage(post) {
- return post.type && (post.type.lastIndexOf(Constants.SYSTEM_MESSAGE_PREFIX) === 0);
-}
-
export function fillArray(value, length) {
const arr = [];
@@ -1379,18 +1368,3 @@ export function localizeMessage(id, defaultMessage) {
export function mod(a, b) {
return ((a % b) + b) % b;
}
-
-export function getProfilePicSrcForPost(post, timestamp) {
- let src = Client.getUsersRoute() + '/' + post.user_id + '/image?time=' + timestamp;
- if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
- if (post.props.override_icon_url) {
- src = post.props.override_icon_url;
- } else {
- src = Constants.DEFAULT_WEBHOOK_LOGO;
- }
- } else if (isSystemMessage(post)) {
- src = Constants.SYSTEM_MESSAGE_PROFILE_IMAGE;
- }
-
- return src;
-}