summaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2015-10-30 14:29:15 -0400
committerChristopher Speller <crspeller@gmail.com>2015-11-02 08:16:57 -0500
commitb9a3ff74dd2b299ae4980922a6dcc55002662517 (patch)
tree882abf9d49107d604f7c94e5739d63c84d742315 /web
parented68f2e9015f3ac94ef1d5f7bf2941611625c60d (diff)
downloadchat-b9a3ff74dd2b299ae4980922a6dcc55002662517.tar.gz
chat-b9a3ff74dd2b299ae4980922a6dcc55002662517.tar.bz2
chat-b9a3ff74dd2b299ae4980922a6dcc55002662517.zip
Hooking up external components
Diffstat (limited to 'web')
-rw-r--r--web/react/components/create_post.jsx1
-rw-r--r--web/react/components/post_info.jsx18
-rw-r--r--web/react/components/post_list_container.jsx25
-rw-r--r--web/react/components/posts_view.jsx70
-rw-r--r--web/react/components/sidebar_right.jsx65
-rw-r--r--web/react/components/updating_time_since_counter.jsx50
-rw-r--r--web/react/pages/channel.jsx4
-rw-r--r--web/react/stores/post_store.jsx29
-rw-r--r--web/react/utils/constants.jsx5
-rw-r--r--web/sass-files/sass/partials/_base.scss12
10 files changed, 217 insertions, 62 deletions
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
index cdbc3bc6d..eb702bf7c 100644
--- a/web/react/components/create_post.jsx
+++ b/web/react/components/create_post.jsx
@@ -176,6 +176,7 @@ export default class CreatePost extends React.Component {
PostStore.storePendingPost(post);
PostStore.storeDraft(channel.id, null);
+ PostStore.jumpPostListBottom();
this.setState({messageText: '', submitting: false, postError: null, previews: [], serverError: null});
Client.createPost(post, channel,
diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx
index ddda48e06..202b043ce 100644
--- a/web/react/components/post_info.jsx
+++ b/web/react/components/post_info.jsx
@@ -3,10 +3,9 @@
var UserStore = require('../stores/user_store.jsx');
var utils = require('../utils/utils.jsx');
+var UpdatingTimeSinceCounter = require('./updating_time_since_counter.jsx');
var Constants = require('../utils/constants.jsx');
-var Tooltip = ReactBootstrap.Tooltip;
-var OverlayTrigger = ReactBootstrap.OverlayTrigger;
export default class PostInfo extends React.Component {
constructor(props) {
@@ -144,21 +143,12 @@ export default class PostInfo extends React.Component {
var dropdown = this.createDropdown();
- let tooltip = <Tooltip id={post.id + 'tooltip'}>{`${utils.displayDate(post.create_at)} at ${utils.displayTime(post.create_at)}`}</Tooltip>;
-
return (
<ul className='post-header post-info'>
<li className='post-header-col'>
- <OverlayTrigger
- delayShow={500}
- container={this}
- placement='top'
- overlay={tooltip}
- >
- <time className='post-profile-time'>
- {utils.displayDateTime(post.create_at)}
- </time>
- </OverlayTrigger>
+ <UpdatingTimeSinceCounter
+ eventTime={post.create_at}
+ />
</li>
<li className='post-header-col post-header__reply'>
<div className='dropdown'>
diff --git a/web/react/components/post_list_container.jsx b/web/react/components/post_list_container.jsx
index 5e12f0e2b..90468ab66 100644
--- a/web/react/components/post_list_container.jsx
+++ b/web/react/components/post_list_container.jsx
@@ -25,10 +25,12 @@ export default class PostListContainer extends React.Component {
this.loadMorePostsTop = this.loadMorePostsTop.bind(this);
this.postsLoaded = this.postsLoaded.bind(this);
this.postsLoadedFailure = this.postsLoadedFailure.bind(this);
+ this.handlePostListJumpRequest = this.handlePostListJumpRequest.bind(this);
const currentChannelId = ChannelStore.getCurrentId();
const state = {
scrollType: PostsView.SCROLL_TYPE_BOTTOM,
+ scrollPost: null,
numPostsToDisplay: Constants.POST_CHUNK_SIZE
};
if (currentChannelId) {
@@ -51,11 +53,29 @@ export default class PostListContainer extends React.Component {
ChannelStore.addChangeListener(this.onChannelChange);
ChannelStore.addLeaveListener(this.onChannelLeave);
PostStore.addChangeListener(this.onPostsChange);
+ PostStore.addPostListJumpListener(this.handlePostListJumpRequest);
}
componentWillUnmount() {
ChannelStore.removeChangeListener(this.onChannelChange);
ChannelStore.removeLeaveListener(this.onChannelLeave);
PostStore.removeChangeListener(this.onPostsChange);
+ PostStore.removePostListJumpListener(this.handlePostListJumpRequest);
+ }
+ handlePostListJumpRequest(type, post) {
+ switch (type) {
+ case Constants.PostListJumpTypes.BOTTOM:
+ this.setState({scrollType: PostsView.SCROLL_TYPE_BOTTOM});
+ break;
+ case Constants.PostListJumpTypes.POST:
+ this.setState({
+ scrollType: PostsView.SCROLL_TYPE_POST,
+ scrollPost: post
+ });
+ break;
+ case Constants.PostListJumpTypes.SIDEBAR_OPEN:
+ this.setState({scrollType: PostsView.SIDEBAR_OPEN});
+ break;
+ }
}
onChannelChange() {
const postLists = Object.assign({}, this.state.postLists);
@@ -70,7 +90,7 @@ export default class PostListContainer extends React.Component {
PostStore.clearUnseenDeletedPosts(channelId);
let lastViewed = Number.MAX_VALUE;
- let member = ChannelStore.getMember(channelId);
+ const member = ChannelStore.getMember(channelId);
if (member != null) {
lastViewed = member.last_viewed_at;
}
@@ -219,10 +239,11 @@ export default class PostListContainer extends React.Component {
isActive={isActive}
postList={postLists[i]}
scrollType={this.state.scrollType}
+ scrollPost={this.state.scrollPost}
postListScrolled={this.handlePostListScroll}
loadMorePostsTopClicked={this.loadMorePostsTop}
numPostsToDisplay={this.state.numPostsToDisplay}
- introText={channel ? createChannelIntroMessage(channel) : []}
+ introText={channel ? createChannelIntroMessage(channel) : null}
messageSeparatorTime={this.state.currentLastViewed}
/>
);
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'}
</a>
);
@@ -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,
diff --git a/web/react/components/sidebar_right.jsx b/web/react/components/sidebar_right.jsx
index 2ec2b5bbf..020db6d88 100644
--- a/web/react/components/sidebar_right.jsx
+++ b/web/react/components/sidebar_right.jsx
@@ -20,23 +20,48 @@ export default class SidebarRight extends React.Component {
this.onSelectedChange = this.onSelectedChange.bind(this);
this.onSearchChange = this.onSearchChange.bind(this);
+ this.doStrangeThings = this.doStrangeThings.bind(this);
+
this.state = getStateFromStores();
}
componentDidMount() {
SearchStore.addSearchChangeListener(this.onSearchChange);
PostStore.addSelectedPostChangeListener(this.onSelectedChange);
+ this.doStrangeThings();
}
componentWillUnmount() {
SearchStore.removeSearchChangeListener(this.onSearchChange);
PostStore.removeSelectedPostChangeListener(this.onSelectedChange);
}
- componentDidUpdate() {
- if (this.plScrolledToBottom) {
- var postHolder = $('.post-list-holder-by-time').not('.inactive');
- postHolder.scrollTop(postHolder[0].scrollHeight);
- } else {
- $('.top-visible-post')[0].scrollIntoView();
+ componentWillUpdate() {
+ PostStore.jumpPostListSidebarOpen();
+ }
+ doStrangeThings() {
+ // We should have a better way to do this stuff
+ // Hence the function name.
+ $('.inner__wrap').removeClass('.move--right');
+ $('.inner__wrap').addClass('move--left');
+ $('.sidebar--left').removeClass('move--right');
+ $('.sidebar--right').addClass('move--left');
+
+ //$('.sidebar--right').prepend('<div class="sidebar__overlay"></div>');
+
+ if (!(this.state.search_visible || this.state.post_right_visible)) {
+ $('.inner__wrap').removeClass('move--left').removeClass('move--right');
+ $('.sidebar--right').removeClass('move--left');
+ return (
+ <div></div>
+ );
}
+
+ /*setTimeout(() => {
+ $('.sidebar__overlay').fadeOut('200', () => {
+ $('.sidebar__overlay').remove();
+ });
+ }, 500);*/
+ }
+ componentDidUpdate() {
+ this.doStrangeThings();
}
onSelectedChange(fromSearch) {
var newState = getStateFromStores(fromSearch);
@@ -52,34 +77,6 @@ export default class SidebarRight extends React.Component {
}
}
render() {
- var postHolder = $('.post-list-holder-by-time').not('.inactive');
- if (postHolder[0]) {
- const position = postHolder.scrollTop() + postHolder.height() + 14;
- const bottom = postHolder[0].scrollHeight;
- this.plScrolledToBottom = position >= bottom;
- } else {
- this.plScrolledToBottom = true;
- }
-
- if (!(this.state.search_visible || this.state.post_right_visible)) {
- $('.inner__wrap').removeClass('move--left').removeClass('move--right');
- $('.sidebar--right').removeClass('move--left');
- return (
- <div></div>
- );
- }
-
- $('.inner__wrap').removeClass('.move--right').addClass('move--left');
- $('.sidebar--left').removeClass('move--right');
- $('.sidebar--right').addClass('move--left');
- $('.sidebar--right').prepend('<div class="sidebar__overlay"></div>');
-
- setTimeout(() => {
- $('.sidebar__overlay').fadeOut('200', function fadeOverlay() {
- $(this).remove();
- });
- }, 500);
-
var content = '';
if (this.state.search_visible) {
diff --git a/web/react/components/updating_time_since_counter.jsx b/web/react/components/updating_time_since_counter.jsx
new file mode 100644
index 000000000..d06ffb842
--- /dev/null
+++ b/web/react/components/updating_time_since_counter.jsx
@@ -0,0 +1,50 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+var Utils = require('../utils/utils.jsx');
+
+var Tooltip = ReactBootstrap.Tooltip;
+var OverlayTrigger = ReactBootstrap.OverlayTrigger;
+
+export default class UpdatingTimeSinceCounter extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ componentDidMount() {
+ this.intervalId = setInterval(() => {
+ this.forceUpdate();
+ }, 30000);
+ }
+ componentWillUnmount() {
+ clearInterval(this.intervalId);
+ }
+ render() {
+ const displayDate = Utils.displayDate(this.props.eventTime);
+ const displayTime = Utils.displayTime(this.props.eventTime);
+
+ const tooltip = (
+ <Tooltip id={'time-since-tooltip-' + this.props.eventTime}>
+ {displayDate + ' at ' + displayTime}
+ </Tooltip>
+ );
+
+ return (
+ <OverlayTrigger
+ delayShow={400}
+ placement='top'
+ overlay={tooltip}
+ >
+ <time className='post-profile-time'>
+ {Utils.displayDateTime(this.props.eventTime)}
+ </time>
+ </OverlayTrigger>
+ );
+ }
+}
+UpdatingTimeSinceCounter.defaultProps = {
+ eventTime: 0
+};
+
+UpdatingTimeSinceCounter.propTypes = {
+ eventTime: React.PropTypes.number.isRequired
+};
diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx
index 464561742..067dcde50 100644
--- a/web/react/pages/channel.jsx
+++ b/web/react/pages/channel.jsx
@@ -55,9 +55,7 @@ function setupChannelPage(props) {
);
ReactDOM.render(
- <ChannelView
-
- />,
+ <ChannelView/>,
document.getElementById('channel_view')
);
diff --git a/web/react/stores/post_store.jsx b/web/react/stores/post_store.jsx
index 8f4e30e7c..19b200ac8 100644
--- a/web/react/stores/post_store.jsx
+++ b/web/react/stores/post_store.jsx
@@ -14,6 +14,7 @@ var ActionTypes = Constants.ActionTypes;
var CHANGE_EVENT = 'change';
var SELECTED_POST_CHANGE_EVENT = 'selected_post_change';
var EDIT_POST_EVENT = 'edit_post';
+var POST_LIST_JUMP_EVENT = 'post_list_jump';
class PostStoreClass extends EventEmitter {
constructor() {
@@ -31,6 +32,10 @@ class PostStoreClass extends EventEmitter {
this.addEditPostListener = this.addEditPostListener.bind(this);
this.removeEditPostListener = this.removeEditPostListener.bind(this);
+ this.emitPostListJump = this.emitPostListJump.bind(this);
+ this.addPostListJumpListener = this.addPostListJumpListener.bind(this);
+ this.removePostListJumpListener = this.removePostListJumpListener.bind(this);
+
this.getCurrentPosts = this.getCurrentPosts.bind(this);
this.storePosts = this.storePosts.bind(this);
this.pStorePosts = this.pStorePosts.bind(this);
@@ -100,6 +105,30 @@ class PostStoreClass extends EventEmitter {
this.removeListener(EDIT_POST_EVENT, callback);
}
+ emitPostListJump(type, post) {
+ this.emit(POST_LIST_JUMP_EVENT, type, post);
+ }
+
+ addPostListJumpListener(callback) {
+ this.on(POST_LIST_JUMP_EVENT, callback);
+ }
+
+ removePostListJumpListener(callback) {
+ this.removeListener(POST_LIST_JUMP_EVENT, callback);
+ }
+
+ jumpPostListBottom() {
+ this.emitPostListJump(Constants.PostListJumpTypes.BOTTOM, null);
+ }
+
+ jumpPostListToPost(post) {
+ this.emitPostListJump(Constants.PostListJumpTypes.POST, post);
+ }
+
+ jumpPostListSidebarOpen() {
+ this.emitPostListJump(Constants.PostListJumpTypes.SIDEBAR_OPEN, null);
+ }
+
getCurrentPosts() {
var currentId = ChannelStore.getCurrentId();
diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx
index 1593f6706..c97e4d982 100644
--- a/web/react/utils/constants.jsx
+++ b/web/react/utils/constants.jsx
@@ -350,5 +350,10 @@ module.exports = {
ruby: 'Ruby',
java: 'Java',
ini: 'ini'
+ },
+ PostListJumpTypes: {
+ BOTTOM: 1,
+ POST: 2,
+ SIDEBAR_OPEN: 3
}
};
diff --git a/web/sass-files/sass/partials/_base.scss b/web/sass-files/sass/partials/_base.scss
index a9f46a815..c286927a2 100644
--- a/web/sass-files/sass/partials/_base.scss
+++ b/web/sass-files/sass/partials/_base.scss
@@ -8,6 +8,18 @@ body {
background: $body-bg;
position: relative;
height: 100%;
+ &.white {
+ background: #fff;
+ > .container-fluid {
+ overflow: auto;
+ }
+ .inner__wrap {
+ > .row.content {
+ min-height: 100%;
+ margin-bottom: -89px;
+ }
+ }
+ }
}
.inner__wrap {