Merge pull request #3867 from oswaldoacauan/chat-performance-improvement
[HTML5] Chat performance improvement
This commit is contained in:
commit
8363d76f98
@ -107,9 +107,8 @@ export default injectIntl(createContainer(({ params, intl }) => {
|
||||
handleClosePrivateChat: chatID => ChatService.closePrivateChat(chatID),
|
||||
|
||||
handleSendMessage: message => {
|
||||
ChatService.updateScrollPosition(chatID, null);
|
||||
let sentMessage = ChatService.sendMessage(chatID, message);
|
||||
ChatService.updateScrollPosition(chatID, null); //null so its scrolls to bottom
|
||||
// ChatService.updateUnreadMessage(chatID, sentMessage.from_time);
|
||||
},
|
||||
|
||||
handleScrollUpdate: position => ChatService.updateScrollPosition(chatID, position),
|
||||
|
@ -18,7 +18,7 @@ const intlMessages = defineMessages({
|
||||
emptyLogLabel: {
|
||||
id: 'app.chat.emptyLogLabel',
|
||||
description: 'aria-label used when chat log is empty',
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
class MessageList extends Component {
|
||||
@ -73,7 +73,6 @@ class MessageList extends Component {
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
|
||||
if (this.props.chatId !== nextProps.chatId) {
|
||||
this.shouldScrollBottom = false;
|
||||
return;
|
||||
@ -86,7 +85,8 @@ class MessageList extends Component {
|
||||
//Compare with <1 to account for the chance scrollArea.scrollTop is a float
|
||||
//value in some browsers.
|
||||
this.shouldScrollBottom = position === scrollArea.scrollHeight ||
|
||||
(scrollArea.scrollHeight - position < 1);
|
||||
(scrollArea.scrollHeight - position < 1) ||
|
||||
nextProps.scrollPosition === null;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedTime } from 'react-intl';
|
||||
import cx from 'classnames';
|
||||
import _ from 'lodash';
|
||||
|
||||
import UserAvatar from '/imports/ui/components/user-avatar/component';
|
||||
import Message from './message/component';
|
||||
@ -16,10 +17,74 @@ const propTypes = {
|
||||
const defaultProps = {
|
||||
};
|
||||
|
||||
const eventsToBeBound = [
|
||||
'scroll',
|
||||
'resize',
|
||||
];
|
||||
|
||||
const isElementInViewport = (el) => {
|
||||
const rect = el.getBoundingClientRect();
|
||||
const clientHeight = window.innerHeight || document.documentElement.clientHeight;
|
||||
const prefetchHeight = 125;
|
||||
|
||||
return (
|
||||
rect.top >= -(prefetchHeight) &&
|
||||
rect.bottom <= clientHeight + prefetchHeight
|
||||
);
|
||||
};
|
||||
|
||||
export default class MessageListItem extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
pendingChanges: false,
|
||||
preventRender: true,
|
||||
};
|
||||
|
||||
this.handleMessageInViewport = _.debounce(this.handleMessageInViewport.bind(this), 50);
|
||||
}
|
||||
|
||||
handleMessageInViewport() {
|
||||
window.requestAnimationFrame(() => {
|
||||
const node = this.refs.item;
|
||||
this.setState({ preventRender: !isElementInViewport(node) });
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const scrollArea = document.getElementById(this.props.chatAreaId);
|
||||
eventsToBeBound.forEach(
|
||||
e => scrollArea.addEventListener(e, this.handleMessageInViewport, false)
|
||||
);
|
||||
|
||||
this.handleMessageInViewport();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const scrollArea = document.getElementById(this.props.chatAreaId);
|
||||
eventsToBeBound.forEach(
|
||||
e => scrollArea.removeEventListener(e, this.handleMessageInViewport, false)
|
||||
);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (prevState.preventRender && !this.state.preventRender && this.state.pendingChanges) {
|
||||
this.setState({ pendingChanges: false });
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (this.state.pendingChanges) return;
|
||||
|
||||
const hasNewMessage = this.props.messages.length !== nextProps.messages.length;
|
||||
const hasUserChanged = !_.isEqual(this.props.user, nextProps.user);
|
||||
|
||||
this.setState({ pendingChanges: hasNewMessage || hasUserChanged });
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return !nextState.preventRender && nextState.pendingChanges;
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -36,32 +101,34 @@ export default class MessageListItem extends Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.item}>
|
||||
<div className={styles.avatar}>
|
||||
<UserAvatar user={user} />
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.meta}>
|
||||
<div className={user.isOnline ? styles.name : styles.logout} aria-label={user.name}>
|
||||
<span>{user.name}</span>
|
||||
{user.isOnline ? null : <span className={styles.offline}>(offline)</span>}
|
||||
</div>
|
||||
<time className={styles.time} dateTime={dateTime}>
|
||||
<FormattedTime value={dateTime}/>
|
||||
</time>
|
||||
<div className={styles.item}>
|
||||
<div className={styles.wrapper} ref="item">
|
||||
<div className={styles.avatar}>
|
||||
<UserAvatar user={user} />
|
||||
</div>
|
||||
<div className={styles.messages}>
|
||||
{messages.map((message, i) => (
|
||||
<Message
|
||||
className={styles.message}
|
||||
key={message.id}
|
||||
text={message.text}
|
||||
time={message.time}
|
||||
chatAreaId={this.props.chatAreaId}
|
||||
lastReadMessageTime={this.props.lastReadMessageTime}
|
||||
handleReadMessage={this.props.handleReadMessage}
|
||||
/>
|
||||
))}
|
||||
<div className={styles.content}>
|
||||
<div className={styles.meta}>
|
||||
<div className={!user.isOnline ? styles.name : styles.logout}>
|
||||
<span>{user.name}</span>
|
||||
{user.isOnline ? null : <span className={styles.offline}>(offline)</span>}
|
||||
</div>
|
||||
<time className={styles.time} dateTime={dateTime}>
|
||||
<FormattedTime value={dateTime}/>
|
||||
</time>
|
||||
</div>
|
||||
<div className={styles.messages}>
|
||||
{messages.map((message, i) => (
|
||||
<Message
|
||||
className={styles.message}
|
||||
key={message.id}
|
||||
text={message.text}
|
||||
time={message.time}
|
||||
chatAreaId={this.props.chatAreaId}
|
||||
lastReadMessageTime={this.props.lastReadMessageTime}
|
||||
handleReadMessage={this.props.handleReadMessage}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -75,7 +142,7 @@ export default class MessageListItem extends Component {
|
||||
|
||||
return (
|
||||
<div className={cx(styles.item, styles.systemMessage)}>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.content} ref="item">
|
||||
<div className={styles.messages}>
|
||||
{messages.map((message, i) => (
|
||||
<Message
|
||||
|
@ -1,17 +1,20 @@
|
||||
@import "../../../../stylesheets/variables/_all";
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
flex: 1;
|
||||
margin-bottom: $line-height-computed;
|
||||
font-size: $font-size-base * .90;
|
||||
margin-bottom: $line-height-computed;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.systemMessage {
|
||||
|
||||
.item + &,
|
||||
|
Loading…
Reference in New Issue
Block a user