Add post list container to hold mounted post lists for faster rendering/channel switching.

Этот коммит содержится в:
JoramWilander
2015-09-08 10:11:57 -04:00
родитель 5b3ee66dc3
Коммит 5c0680397d
8 изменённых файлов: 251 добавлений и 107 удалений

Просмотреть файл

@@ -18,8 +18,8 @@ var ActionTypes = Constants.ActionTypes;
import {strings} from '../utils/config.js';
export default class PostList extends React.Component {
constructor() {
super();
constructor(props) {
super(props);
this.gotMorePosts = false;
this.scrolled = false;
@@ -27,6 +27,7 @@ export default class PostList extends React.Component {
this.seenNewMessages = false;
this.isUserScroll = true;
this.userHasSeenNew = false;
this.loadInProgress = false;
this.onChange = this.onChange.bind(this);
this.onTimeChange = this.onTimeChange.bind(this);
@@ -34,22 +35,19 @@ export default class PostList extends React.Component {
this.createChannelIntroMessage = this.createChannelIntroMessage.bind(this);
this.loadMorePosts = this.loadMorePosts.bind(this);
this.loadFirstPosts = this.loadFirstPosts.bind(this);
this.activate = this.activate.bind(this);
this.deactivate = this.deactivate.bind(this);
this.resize = this.resize.bind(this);
this.state = this.getStateFromStores();
this.state = this.getStateFromStores(props.channelId);
this.state.numToDisplay = Constants.POST_CHUNK_SIZE;
this.state.isFirstLoadComplete = false;
}
getStateFromStores() {
var channel = ChannelStore.getCurrent();
if (channel == null) {
channel = {};
}
var postList = PostStore.getCurrentPosts();
getStateFromStores(id) {
var postList = PostStore.getPosts(id);
if (postList != null) {
var deletedPosts = PostStore.getUnseenDeletedPosts(channel.id);
var deletedPosts = PostStore.getUnseenDeletedPosts(id);
if (deletedPosts && Object.keys(deletedPosts).length > 0) {
for (var pid in deletedPosts) {
@@ -70,7 +68,7 @@ export default class PostList extends React.Component {
});
}
var pendingPostList = PostStore.getPendingPosts(channel.id);
var pendingPostList = PostStore.getPendingPosts(id);
if (pendingPostList) {
postList.order = pendingPostList.order.concat(postList.order);
@@ -82,43 +80,42 @@ export default class PostList extends React.Component {
}
}
var lastViewed = Number.MAX_VALUE;
if (ChannelStore.getCurrentMember() != null) {
lastViewed = ChannelStore.getCurrentMember().last_viewed_at;
}
return {
postList: postList,
channel: channel,
lastViewed: lastViewed
postList: postList
};
}
componentDidMount() {
if (this.props.isActive) {
this.activate();
this.loadFirstPosts(this.props.channelId);
}
}
componentWillUnmount() {
this.deactivate();
}
activate() {
this.gotMorePosts = false;
this.scrolled = false;
this.prevScrollTop = 0;
this.seenNewMessages = false;
this.isUserScroll = true;
this.userHasSeenNew = false;
PostStore.clearUnseenDeletedPosts(this.props.channelId);
PostStore.addChangeListener(this.onChange);
ChannelStore.addChangeListener(this.onChange);
UserStore.addStatusesChangeListener(this.onTimeChange);
SocketStore.addChangeListener(this.onSocketChange);
var postHolder = $('.post-list-holder-by-time');
$('.modal').on('show.bs.modal', function onShow() {
$('.modal-body').css('overflow-y', 'auto');
$('.modal-body').css('max-height', $(window).height() * 0.7);
});
$(window).resize(function resize() {
if ($('#create_post').length > 0) {
var height = $(window).height() - $('#create_post').height() - $('#error_bar').outerHeight() - 50;
postHolder.css('height', height + 'px');
}
var postHolder = $(React.findDOMNode(this.refs.postlist));
$(window).on('resize.' + this.props.channelId, function resize() {
this.resize();
if (!this.scrolled) {
this.scrollToBottom();
}
}.bind(this));
postHolder.scroll(function scroll() {
postHolder.on('scroll', function scroll() {
var position = postHolder.scrollTop() + postHolder.height() + 14;
var bottom = postHolder[0].scrollHeight;
@@ -134,41 +131,25 @@ export default class PostList extends React.Component {
this.isUserScroll = true;
}.bind(this));
$('body').on('click.userpopover', function popOver(e) {
if ($(e.target).attr('data-toggle') !== 'popover' &&
$(e.target).parents('.popover.in').length === 0) {
$('.user-popover').popover('hide');
}
});
$('.post-list__content div .post').removeClass('post--last');
$('.post-list__content div:last-child .post').addClass('post--last');
$('body').on('mouseenter mouseleave', '.post', function mouseOver(ev) {
if (ev.type === 'mouseenter') {
$(this).parent('div').prev('.date-separator, .new-separator').addClass('hovered--after');
$(this).parent('div').next('.date-separator, .new-separator').addClass('hovered--before');
} else {
$(this).parent('div').prev('.date-separator, .new-separator').removeClass('hovered--after');
$(this).parent('div').next('.date-separator, .new-separator').removeClass('hovered--before');
}
});
$('body').on('mouseenter mouseleave', '.post.post--comment.same--root', function mouseOver(ev) {
if (ev.type === 'mouseenter') {
$(this).parent('div').prev('.date-separator, .new-separator').addClass('hovered--comment');
$(this).parent('div').next('.date-separator, .new-separator').addClass('hovered--comment');
} else {
$(this).parent('div').prev('.date-separator, .new-separator').removeClass('hovered--comment');
$(this).parent('div').next('.date-separator, .new-separator').removeClass('hovered--comment');
}
});
this.scrollToBottom();
if (this.state.channel.id != null) {
this.loadFirstPosts(this.state.channel.id);
if (!this.state.isFirstLoadComplete) {
this.loadFirstPosts(this.props.channelId);
}
this.resize();
this.onChange();
this.scrollToBottom();
}
deactivate() {
PostStore.removeChangeListener(this.onChange);
UserStore.removeStatusesChangeListener(this.onTimeChange);
SocketStore.removeChangeListener(this.onSocketChange);
$('body').off('click.userpopover');
$(window).off('resize.' + this.props.channelId);
var postHolder = $(React.findDOMNode(this.refs.postlist));
postHolder.off('scroll');
}
componentDidUpdate(prevProps, prevState) {
$('.post-list__content div .post').removeClass('post--last');
@@ -187,7 +168,7 @@ export default class PostList extends React.Component {
var firstPost = posts[order[0]] || {};
var isNewPost = oldOrder.indexOf(order[0]) === -1;
if (this.state.channel.id !== prevState.channel.id) {
if (this.props.isActive && !prevProps.isActive) {
this.scrollToBottom();
} else if (oldOrder.length === 0) {
this.scrollToBottom();
@@ -201,37 +182,43 @@ export default class PostList extends React.Component {
} else if (isNewPost &&
userId === firstPost.user_id &&
!utils.isComment(firstPost)) {
this.state.lastViewed = utils.getTimestamp();
this.scrollToBottom(true);
// the user clicked 'load more messages'
} else if (this.gotMorePosts) {
var lastPost = oldPosts[oldOrder[prevState.numToDisplay]];
$('#' + lastPost.id)[0].scrollIntoView();
this.gotMorePosts = false;
} else {
this.scrollTo(this.prevScrollTop);
}
}
componentWillUpdate() {
var postHolder = $('.post-list-holder-by-time');
var postHolder = $(React.findDOMNode(this.refs.postlist));
this.prevScrollTop = postHolder.scrollTop();
}
componentWillUnmount() {
PostStore.removeChangeListener(this.onChange);
ChannelStore.removeChangeListener(this.onChange);
UserStore.removeStatusesChangeListener(this.onTimeChange);
SocketStore.removeChangeListener(this.onSocketChange);
$('body').off('click.userpopover');
$('.modal').off('show.bs.modal');
componentWillReceiveProps(nextProps) {
if (nextProps.isActive === true && this.props.isActive === false) {
this.activate();
} else if (nextProps.isActive === false && this.props.isActive === true) {
this.deactivate();
}
}
resize() {
const postHolder = $(React.findDOMNode(this.refs.postlist));
if ($('#create_post').length > 0) {
const height = $(window).height() - $('#create_post').height() - $('#error_bar').outerHeight() - 50;
postHolder.css('height', height + 'px');
}
}
scrollTo(val) {
this.isUserScroll = false;
var postHolder = $('.post-list-holder-by-time');
var postHolder = $(React.findDOMNode(this.refs.postlist));
postHolder[0].scrollTop = val;
}
scrollToBottom(force) {
this.isUserScroll = false;
var postHolder = $('.post-list-holder-by-time');
var postHolder = $(React.findDOMNode(this.refs.postlist));
if ($('#new_message')[0] && !this.userHasSeenNew && !force) {
$('#new_message')[0].scrollIntoView();
} else {
@@ -241,34 +228,32 @@ export default class PostList extends React.Component {
}
}
loadFirstPosts(id) {
if (this.loadInProgress) {
return;
}
if (this.props.channelId == null) {
return;
}
this.loadInProgress = true;
Client.getPosts(
id,
PostStore.getLatestUpdate(id),
function success() {
this.loadInProgress = false;
this.setState({isFirstLoadComplete: true});
}.bind(this),
function fail() {
this.loadInProgress = false;
this.setState({isFirstLoadComplete: true});
}.bind(this)
);
}
onChange() {
var newState = this.getStateFromStores();
// Special case where the channel wasn't yet set in componentDidMount
if (!this.state.isFirstLoadComplete && this.state.channel.id == null && newState.channel.id != null) {
this.loadFirstPosts(newState.channel.id);
}
if (!utils.areStatesEqual(newState, this.state)) {
if (this.state.channel.id !== newState.channel.id) {
PostStore.clearUnseenDeletedPosts(this.state.channel.id);
this.userHasSeenNew = false;
newState.numToDisplay = Constants.POST_CHUNK_SIZE;
} else {
newState.lastViewed = this.state.lastViewed;
}
var newState = this.getStateFromStores(this.props.channelId);
if (!utils.areStatesEqual(newState.postList, this.state.postList)) {
this.setState(newState);
}
}
@@ -424,7 +409,7 @@ export default class PostList extends React.Component {
}
}
var members = ChannelStore.getCurrentExtraInfo().members;
var members = ChannelStore.getExtraInfo(channel.id).members;
for (var i = 0; i < members.length; i++) {
if (members[i].roles.indexOf('admin') > -1) {
return members[i].username;
@@ -488,6 +473,11 @@ export default class PostList extends React.Component {
var userId = UserStore.getCurrentId();
var renderedLastViewed = false;
var lastViewed = Number.MAX_VALUE;
if (ChannelStore.getMember(this.props.channelId) != null) {
lastViewed = ChannelStore.getMember(this.props.channelId).last_viewed_at;
}
var numToDisplay = this.state.numToDisplay;
if (order.length - 1 < numToDisplay) {
@@ -543,7 +533,7 @@ export default class PostList extends React.Component {
);
}
if (post.user_id !== userId && post.create_at > this.state.lastViewed && !renderedLastViewed) {
if (post.user_id !== userId && post.create_at > lastViewed && !renderedLastViewed) {
renderedLastViewed = true;
// Temporary fix to solve ie10/11 rendering issue
@@ -577,7 +567,7 @@ export default class PostList extends React.Component {
var posts = this.state.postList.posts;
var order = this.state.postList.order;
var channelId = this.state.channel.id;
var channelId = this.props.channelId;
$(React.findDOMNode(this.refs.loadmore)).text('Retrieving more messages...');
@@ -619,7 +609,7 @@ export default class PostList extends React.Component {
render() {
var order = [];
var posts;
var channel = this.state.channel;
var channel = ChannelStore.get(this.props.channelId);
if (this.state.postList != null) {
posts = this.state.postList.posts;
@@ -628,7 +618,7 @@ export default class PostList extends React.Component {
var moreMessages = <p className='beginning-messages-text'>Beginning of Channel</p>;
if (channel != null) {
if (order.length > this.state.numToDisplay) {
if (order.length >= this.state.numToDisplay) {
moreMessages = (
<a
ref='loadmore'
@@ -655,10 +645,15 @@ export default class PostList extends React.Component {
/>);
}
var activeClass = '';
if (!this.props.isActive) {
activeClass = 'inactive';
}
return (
<div
ref='postlist'
className='post-list-holder-by-time'
className={'post-list-holder-by-time ' + activeClass}
>
<div className='post-list__table'>
<div className='post-list__content'>
@@ -670,3 +665,12 @@ export default class PostList extends React.Component {
);
}
}
PostList.defaultProps = {
isActive: false,
channelId: null
};
PostList.propTypes = {
isActive: React.PropTypes.bool,
channelId: React.PropTypes.string
};