Merge pull request #1801 from matrix-org/luke/perf-room-list

Improve room list performance when receiving messages
This commit is contained in:
Luke Barnard 2018-03-15 13:33:19 +00:00 committed by GitHub
commit 0d7099fd61
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 78 additions and 22 deletions

View file

@ -624,6 +624,7 @@ var TimelinePanel = React.createClass({
this.props.timelineSet.room.setUnreadNotificationCount('highlight', 0); this.props.timelineSet.room.setUnreadNotificationCount('highlight', 0);
dis.dispatch({ dis.dispatch({
action: 'on_room_read', action: 'on_room_read',
roomId: this.props.timelineSet.room.roomId,
}); });
} }
} }

View file

@ -48,12 +48,33 @@ module.exports = React.createClass({
}; };
}, },
componentWillMount: function() {
MatrixClientPeg.get().on("RoomState.events", this.onRoomStateEvents);
},
componentWillUnmount: function() {
const cli = MatrixClientPeg.get();
if (cli) {
cli.removeListener("RoomState.events", this.onRoomStateEvents);
}
},
componentWillReceiveProps: function(newProps) { componentWillReceiveProps: function(newProps) {
this.setState({ this.setState({
urls: this.getImageUrls(newProps), urls: this.getImageUrls(newProps),
}); });
}, },
onRoomStateEvents: function(ev) {
if (ev.getRoomId() !== this.props.room.roomId ||
ev.getType() !== 'm.room.avatar'
) return;
this.setState({
urls: this.getImageUrls(this.props),
});
},
getImageUrls: function(props) { getImageUrls: function(props) {
return [ return [
ContentRepo.getHttpUriForMxc( ContentRepo.getHttpUriForMxc(

View file

@ -77,9 +77,7 @@ module.exports = React.createClass({
cli.on("Room", this.onRoom); cli.on("Room", this.onRoom);
cli.on("deleteRoom", this.onDeleteRoom); cli.on("deleteRoom", this.onDeleteRoom);
cli.on("Room.name", this.onRoomName);
cli.on("Room.receipt", this.onRoomReceipt); cli.on("Room.receipt", this.onRoomReceipt);
cli.on("RoomState.events", this.onRoomStateEvents);
cli.on("RoomMember.name", this.onRoomMemberName); cli.on("RoomMember.name", this.onRoomMemberName);
cli.on("Event.decrypted", this.onEventDecrypted); cli.on("Event.decrypted", this.onEventDecrypted);
cli.on("accountData", this.onAccountData); cli.on("accountData", this.onAccountData);
@ -161,12 +159,6 @@ module.exports = React.createClass({
}); });
} }
break; break;
case 'on_room_read':
// Force an update because the notif count state is too deep to cause
// an update. This forces the local echo of reading notifs to be
// reflected by the RoomTiles.
this.forceUpdate();
break;
} }
}, },
@ -177,9 +169,7 @@ module.exports = React.createClass({
if (MatrixClientPeg.get()) { if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener("Room", this.onRoom); MatrixClientPeg.get().removeListener("Room", this.onRoom);
MatrixClientPeg.get().removeListener("deleteRoom", this.onDeleteRoom); MatrixClientPeg.get().removeListener("deleteRoom", this.onDeleteRoom);
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
MatrixClientPeg.get().removeListener("Room.receipt", this.onRoomReceipt); MatrixClientPeg.get().removeListener("Room.receipt", this.onRoomReceipt);
MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents);
MatrixClientPeg.get().removeListener("RoomMember.name", this.onRoomMemberName); MatrixClientPeg.get().removeListener("RoomMember.name", this.onRoomMemberName);
MatrixClientPeg.get().removeListener("Event.decrypted", this.onEventDecrypted); MatrixClientPeg.get().removeListener("Event.decrypted", this.onEventDecrypted);
MatrixClientPeg.get().removeListener("accountData", this.onAccountData); MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
@ -243,14 +233,6 @@ module.exports = React.createClass({
} }
}, },
onRoomName: function(room) {
this._delayedRefreshRoomList();
},
onRoomStateEvents: function(ev, state) {
this._delayedRefreshRoomList();
},
onRoomMemberName: function(ev, member) { onRoomMemberName: function(ev, member) {
this._delayedRefreshRoomList(); this._delayedRefreshRoomList();
}, },

View file

@ -21,6 +21,7 @@ const React = require('react');
const ReactDOM = require("react-dom"); const ReactDOM = require("react-dom");
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const classNames = require('classnames'); const classNames = require('classnames');
import dis from '../../../dispatcher';
const MatrixClientPeg = require('../../../MatrixClientPeg'); const MatrixClientPeg = require('../../../MatrixClientPeg');
import DMRoomMap from '../../../utils/DMRoomMap'; import DMRoomMap from '../../../utils/DMRoomMap';
const sdk = require('../../../index'); const sdk = require('../../../index');
@ -58,7 +59,9 @@ module.exports = React.createClass({
hover: false, hover: false,
badgeHover: false, badgeHover: false,
menuDisplayed: false, menuDisplayed: false,
roomName: this.props.room.name,
notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId), notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
notificationCount: this.props.room.getUnreadNotificationCount(),
selected: this.props.room.roomId === RoomViewStore.getRoomId(), selected: this.props.room.roomId === RoomViewStore.getRoomId(),
}); });
}, },
@ -81,6 +84,20 @@ module.exports = React.createClass({
} }
}, },
onRoomTimeline: function(ev, room) {
if (room !== this.props.room) return;
this.setState({
notificationCount: this.props.room.getUnreadNotificationCount(),
});
},
onRoomName: function(room) {
if (room !== this.props.room) return;
this.setState({
roomName: this.props.room.name,
});
},
onAccountData: function(accountDataEvent) { onAccountData: function(accountDataEvent) {
if (accountDataEvent.getType() == 'm.push_rules') { if (accountDataEvent.getType() == 'm.push_rules') {
this.setState({ this.setState({
@ -89,6 +106,21 @@ module.exports = React.createClass({
} }
}, },
onAction: function(payload) {
switch (payload.action) {
// XXX: slight hack in order to zero the notification count when a room
// is read. Ideally this state would be given to this via props (as we
// do with `unread`). This is still better than forceUpdating the entire
// RoomList when a room is read.
case 'on_room_read':
if (payload.roomId !== this.props.room.roomId) break;
this.setState({
notificationCount: this.props.room.getUnreadNotificationCount(),
});
break;
}
},
_onActiveRoomChange: function() { _onActiveRoomChange: function() {
this.setState({ this.setState({
selected: this.props.room.roomId === RoomViewStore.getRoomId(), selected: this.props.room.roomId === RoomViewStore.getRoomId(),
@ -97,15 +129,37 @@ module.exports = React.createClass({
componentWillMount: function() { componentWillMount: function() {
MatrixClientPeg.get().on("accountData", this.onAccountData); MatrixClientPeg.get().on("accountData", this.onAccountData);
MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
MatrixClientPeg.get().on("Room.name", this.onRoomName);
ActiveRoomObserver.addListener(this.props.room.roomId, this._onActiveRoomChange); ActiveRoomObserver.addListener(this.props.room.roomId, this._onActiveRoomChange);
this.dispatcherRef = dis.register(this.onAction);
}, },
componentWillUnmount: function() { componentWillUnmount: function() {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
if (cli) { if (cli) {
MatrixClientPeg.get().removeListener("accountData", this.onAccountData); MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline);
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
} }
ActiveRoomObserver.removeListener(this.props.room.roomId, this._onActiveRoomChange); ActiveRoomObserver.removeListener(this.props.room.roomId, this._onActiveRoomChange);
dis.unregister(this.dispatcherRef);
},
// Do a simple shallow comparison of props and state to avoid unnecessary
// renders. The assumption made here is that only state and props are used
// in rendering this component and children.
//
// RoomList is frequently made to forceUpdate, so this decreases number of
// RoomTile renderings.
shouldComponentUpdate: function(newProps, newState) {
if (Object.keys(newProps).some((k) => newProps[k] !== this.props[k])) {
return true;
}
if (Object.keys(newState).some((k) => newState[k] !== this.state[k])) {
return true;
}
return false;
}, },
onClick: function(ev) { onClick: function(ev) {
@ -174,7 +228,7 @@ module.exports = React.createClass({
const myUserId = MatrixClientPeg.get().credentials.userId; const myUserId = MatrixClientPeg.get().credentials.userId;
const me = this.props.room.currentState.members[myUserId]; const me = this.props.room.currentState.members[myUserId];
const notificationCount = this.props.room.getUnreadNotificationCount(); const notificationCount = this.state.notificationCount;
// var highlightCount = this.props.room.getUnreadNotificationCount("highlight"); // var highlightCount = this.props.room.getUnreadNotificationCount("highlight");
const notifBadges = notificationCount > 0 && this._shouldShowNotifBadge(); const notifBadges = notificationCount > 0 && this._shouldShowNotifBadge();
@ -202,9 +256,7 @@ module.exports = React.createClass({
'mx_RoomTile_badgeButton': this.state.badgeHover || this.state.menuDisplayed, 'mx_RoomTile_badgeButton': this.state.badgeHover || this.state.menuDisplayed,
}); });
// XXX: We should never display raw room IDs, but sometimes the let name = this.state.roomName;
// room name js sdk gives is undefined (cannot repro this -- k)
let name = this.props.room.name || this.props.room.roomId;
name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon
let badge; let badge;