From ed68f2e9015f3ac94ef1d5f7bf2941611625c60d Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Thu, 29 Oct 2015 17:45:33 -0400 Subject: Refactoring center channel --- web/react/components/posts_view.jsx | 249 ++++++++++++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 web/react/components/posts_view.jsx (limited to 'web/react/components/posts_view.jsx') diff --git a/web/react/components/posts_view.jsx b/web/react/components/posts_view.jsx new file mode 100644 index 000000000..d9a95af8b --- /dev/null +++ b/web/react/components/posts_view.jsx @@ -0,0 +1,249 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +const UserStore = require('../stores/user_store.jsx'); +const Utils = require('../utils/utils.jsx'); +const Post = require('./post.jsx'); + +export default class PostsView extends React.Component { + constructor(props) { + super(props); + + this.handleScroll = this.handleScroll.bind(this); + this.loadMorePostsTop = this.loadMorePostsTop.bind(this); + this.postsRerendered = this.postsRerendered.bind(this); + this.createPosts = this.createPosts.bind(this); + this.scrollToBottom = this.scrollToBottom.bind(this); + this.handleResize = this.handleResize.bind(this); + } + static get SCROLL_TYPE_FREE() { + return 1; + } + static get SCROLL_TYPE_BOTTOM() { + return 2; + } + handleScroll() { + const atBottom = ((this.refs.postlist.scrollHeight - this.refs.postlist.scrollTop) === this.refs.postlist.clientHeight); + this.props.postListScrolled(atBottom); + } + loadMorePostsTop() { + this.props.loadMorePostsTopClicked(); + } + postsRerendered() { + } + createPosts(posts, order) { + const postCtls = []; + let previousPostDay = new Date(0); + const userId = UserStore.getCurrentId(); + + let renderedLastViewed = false; + + let numToDisplay = this.props.numPostsToDisplay; + if (order.length - 1 < numToDisplay) { + numToDisplay = order.length - 1; + } + + for (let i = numToDisplay; i >= 0; i--) { + const post = posts[order[i]]; + const parentPost = posts[post.parent_id]; + const prevPost = posts[order[i + 1]]; + + let sameUser = false; + let sameRoot = false; + let hideProfilePic = false; + + if (prevPost) { + sameUser = prevPost.user_id === post.user_id && post.create_at - prevPost.create_at <= 1000 * 60 * 5; + + sameRoot = Utils.isComment(post) && (prevPost.id === post.root_id || prevPost.root_id === post.root_id); + + // hide the profile pic if: + // the previous post was made by the same user as the current post, + // the previous post is not a comment, + // the current post is not a comment, + // the current post is not from a webhook + // and the previous post is not from a webhook + if ((prevPost.user_id === post.user_id) && + !Utils.isComment(prevPost) && + !Utils.isComment(post) && + (!post.props || !post.props.from_webhook) && + (!prevPost.props || !prevPost.props.from_webhook)) { + hideProfilePic = true; + } + } + + // 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 + var isLastComment = Utils.isComment(post) && (i === 0 || posts[order[i - 1]].root_id !== post.root_id); + + var postCtl = ( + + ); + + const currentPostDay = Utils.getDateForUnixTicks(post.create_at); + if (currentPostDay.toDateString() !== previousPostDay.toDateString()) { + postCtls.push( +
+
+
{currentPostDay.toDateString()}
+
+ ); + } + + if (post.user_id !== userId && + this.props.messageSeparatorTime !== 0 && + post.create_at > this.props.messageSeparatorTime && + !renderedLastViewed) { + renderedLastViewed = true; + + // Temporary fix to solve ie11 rendering issue + let newSeparatorId = ''; + if (!Utils.isBrowserIE()) { + newSeparatorId = 'new_message_' + post.id; + } + postCtls.push( +
+
+
{'New Messages'}
+
+ ); + } + postCtls.push(postCtl); + previousPostDay = currentPostDay; + } + + return postCtls; + } + scrollToBottom() { + if (this.props.scrollType === PostsView.SCROLL_TYPE_BOTTOM) { + window.requestAnimationFrame(() => { + this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight; + }); + } + } + handleResize() { + this.scrollToBottom(); + } + componentDidMount() { + this.scrollToBottom(); + window.addEventListener('resize', this.handleResize); + } + componentWillUnmount() { + window.removeEventListener('resize', this.handleResize); + } + componentDidUpdate() { + this.scrollToBottom(); + } + shouldComponentUpdate(nextProps) { + if (this.props.isActive !== nextProps.isActive) { + return true; + } + if (this.props.postList !== nextProps.postList) { + return true; + } + if (this.props.scrollPost !== nextProps.scrollPost) { + return true; + } + if (this.props.scrollType !== nextProps.scrollType && nextProps.scrollType !== PostsView.SCROLL_TYPE_FREE) { + return true; + } + if (this.props.numPostsToDisplay !== nextProps.numPostsToDisplay) { + return true; + } + if (this.props.messageSeparatorTime !== nextProps.messageSeparatorTime) { + return true; + } + if (!Utils.areStatesEqual(this.props.postList, nextProps.postList)) { + return true; + } + + return false; + } + render() { + let posts = []; + let order = []; + let moreMessages; + 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 (order.length >= this.props.numPostsToDisplay) { + moreMessages = ( + + {'Load more messages'} + + ); + } else { + moreMessages = this.props.introText; + } + + // Create post elements + postElements = this.createPosts(posts, order); + + // Show ourselves if we are marked active + if (this.props.isActive) { + activeClass = ''; + } + } + + return ( +
+
+
+ {moreMessages} + {postElements} +
+
+
+ ); + } +} +PostsView.defaultProps = { +}; + +PostsView.propTypes = { + isActive: React.PropTypes.bool, + postList: React.PropTypes.object.isRequired, + scrollPost: React.PropTypes.string, + scrollType: React.PropTypes.number, + postListScrolled: React.PropTypes.func.isRequired, + loadMorePostsTopClicked: React.PropTypes.func.isRequired, + numPostsToDisplay: React.PropTypes.number, + introText: React.PropTypes.element, + messageSeparatorTime: React.PropTypes.number +}; -- cgit v1.2.3-1-g7c22 From b9a3ff74dd2b299ae4980922a6dcc55002662517 Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Fri, 30 Oct 2015 14:29:15 -0400 Subject: Hooking up external components --- web/react/components/posts_view.jsx | 70 ++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 9 deletions(-) (limited to 'web/react/components/posts_view.jsx') diff --git a/web/react/components/posts_view.jsx b/web/react/components/posts_view.jsx index d9a95af8b..57e7abd35 100644 --- a/web/react/components/posts_view.jsx +++ b/web/react/components/posts_view.jsx @@ -10,11 +10,16 @@ export default class PostsView extends React.Component { super(props); this.handleScroll = this.handleScroll.bind(this); + this.isAtBottom = this.isAtBottom.bind(this); this.loadMorePostsTop = this.loadMorePostsTop.bind(this); this.postsRerendered = this.postsRerendered.bind(this); this.createPosts = this.createPosts.bind(this); - this.scrollToBottom = this.scrollToBottom.bind(this); + this.updateScrolling = this.updateScrolling.bind(this); this.handleResize = this.handleResize.bind(this); + + this.jumpToPostNode = null; + this.wasAtBottom = true; + this.scrollHeight = 0; } static get SCROLL_TYPE_FREE() { return 1; @@ -22,9 +27,28 @@ export default class PostsView extends React.Component { static get SCROLL_TYPE_BOTTOM() { return 2; } + static get SIDEBAR_OPEN() { + return 3; + } + isAtBottom() { + return ((this.refs.postlist.scrollHeight - this.refs.postlist.scrollTop) === this.refs.postlist.clientHeight); + } handleScroll() { - const atBottom = ((this.refs.postlist.scrollHeight - this.refs.postlist.scrollTop) === this.refs.postlist.clientHeight); - this.props.postListScrolled(atBottom); + // 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))) { + this.jumpToPostNode = childNodes[i]; + break; + } + } + this.wasAtBottom = this.isAtBottom(); + + // --- -------- + + this.props.postListScrolled(this.isAtBottom()); + this.prevScrollHeight = this.refs.postlist.scrollHeight; } loadMorePostsTop() { this.props.loadMorePostsTopClicked(); @@ -134,25 +158,53 @@ export default class PostsView extends React.Component { return postCtls; } - scrollToBottom() { + updateScrolling() { if (this.props.scrollType === PostsView.SCROLL_TYPE_BOTTOM) { window.requestAnimationFrame(() => { this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight; }); + } else if (this.props.scrollType === PostsView.SCROLL_TYPE_POST && this.props.scrollPost) { + window.requestAnimationFrame(() => { + const postNode = ReactDOM.findDOMNode(this.refs[this.props.scrollPost]); + postNode.scrollIntoView(); + if (this.refs.postlist.scrollTop === postNode.offsetTop) { + this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3); + } else { + this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3) + (this.refs.postlist.scrollTop - postNode.offsetTop); + } + }); + } else if (this.props.scrollType === PostsView.SIDEBAR_OPEN) { + // If we are at the bottom then stay there + if (this.wasAtBottom) { + this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight; + } else { + window.requestAnimationFrame(() => { + this.jumpToPostNode.scrollIntoView(); + if (this.refs.postlist.scrollTop === this.jumpToPostNode.offsetTop) { + this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3); + } else { + this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3) + (this.refs.postlist.scrollTop - this.jumpToPostNode.offsetTop); + } + }); + } + } else if (this.refs.postlist.scrollHeight !== this.prevScrollHeight) { + window.requestAnimationFrame(() => { + this.refs.postlist.scrollTop += (this.refs.postlist.scrollHeight - this.prevScrollHeight); + }); } } handleResize() { - this.scrollToBottom(); + this.updateScrolling(); } componentDidMount() { - this.scrollToBottom(); + this.updateScrolling(); window.addEventListener('resize', this.handleResize); } componentWillUnmount() { window.removeEventListener('resize', this.handleResize); } componentDidUpdate() { - this.scrollToBottom(); + this.updateScrolling(); } shouldComponentUpdate(nextProps) { if (this.props.isActive !== nextProps.isActive) { @@ -197,7 +249,7 @@ export default class PostsView extends React.Component { className='more-messages-text theme' href='#' onClick={this.loadMorePostsTop} - > + > {'Load more messages'} ); @@ -238,7 +290,7 @@ PostsView.defaultProps = { PostsView.propTypes = { isActive: React.PropTypes.bool, - postList: React.PropTypes.object.isRequired, + postList: React.PropTypes.object, scrollPost: React.PropTypes.string, scrollType: React.PropTypes.number, postListScrolled: React.PropTypes.func.isRequired, -- cgit v1.2.3-1-g7c22 From 1e7ac66e3bcc796c705ed6a833161cab6bd5b65d Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Mon, 2 Nov 2015 08:37:17 -0500 Subject: Some renaming --- web/react/components/posts_view.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'web/react/components/posts_view.jsx') diff --git a/web/react/components/posts_view.jsx b/web/react/components/posts_view.jsx index 57e7abd35..5cefb4885 100644 --- a/web/react/components/posts_view.jsx +++ b/web/react/components/posts_view.jsx @@ -47,7 +47,7 @@ export default class PostsView extends React.Component { // --- -------- - this.props.postListScrolled(this.isAtBottom()); + this.props.postViewScrolled(this.isAtBottom()); this.prevScrollHeight = this.refs.postlist.scrollHeight; } loadMorePostsTop() { @@ -293,7 +293,7 @@ PostsView.propTypes = { postList: React.PropTypes.object, scrollPost: React.PropTypes.string, scrollType: React.PropTypes.number, - postListScrolled: React.PropTypes.func.isRequired, + postViewScrolled: React.PropTypes.func.isRequired, loadMorePostsTopClicked: React.PropTypes.func.isRequired, numPostsToDisplay: React.PropTypes.number, introText: React.PropTypes.element, -- cgit v1.2.3-1-g7c22 From d4ec6d3bf42d257851304fe9588f2db8bbbefa13 Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Mon, 2 Nov 2015 10:32:00 -0500 Subject: Removing useless resize alert function in PostBody --- web/react/components/posts_view.jsx | 4 ---- 1 file changed, 4 deletions(-) (limited to 'web/react/components/posts_view.jsx') diff --git a/web/react/components/posts_view.jsx b/web/react/components/posts_view.jsx index 5cefb4885..f5a492b85 100644 --- a/web/react/components/posts_view.jsx +++ b/web/react/components/posts_view.jsx @@ -12,7 +12,6 @@ export default class PostsView extends React.Component { this.handleScroll = this.handleScroll.bind(this); this.isAtBottom = this.isAtBottom.bind(this); this.loadMorePostsTop = this.loadMorePostsTop.bind(this); - this.postsRerendered = this.postsRerendered.bind(this); this.createPosts = this.createPosts.bind(this); this.updateScrolling = this.updateScrolling.bind(this); this.handleResize = this.handleResize.bind(this); @@ -53,8 +52,6 @@ export default class PostsView extends React.Component { loadMorePostsTop() { this.props.loadMorePostsTopClicked(); } - postsRerendered() { - } createPosts(posts, order) { const postCtls = []; let previousPostDay = new Date(0); @@ -111,7 +108,6 @@ export default class PostsView extends React.Component { posts={posts} hideProfilePic={hideProfilePic} isLastComment={isLastComment} - resize={this.postsRerendered} /> ); -- cgit v1.2.3-1-g7c22