Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 8672b97

Browse files
committed
Improve room list performance when receiving messages
In summary this makes RoomTiles (and RoomAvatars) do more for themselves in terms of reacting individually to state changes in the js-sdk. Instead of force updating the entire room list for avatar changes and room name changes, do this in the RoomTile and RoomAvatar instead. This increases the number of listeners listening to the matrix client, but allows us to properly implement a shouldComponentUpdate for RoomTile (because the avatar, name and notification count are now in component state)
1 parent 59bb5ce commit 8672b97

File tree

4 files changed

+75
-22
lines changed

4 files changed

+75
-22
lines changed

src/components/structures/TimelinePanel.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,7 @@ var TimelinePanel = React.createClass({
624624
this.props.timelineSet.room.setUnreadNotificationCount('highlight', 0);
625625
dis.dispatch({
626626
action: 'on_room_read',
627+
roomId: this.props.timelineSet.room.roomId,
627628
});
628629
}
629630
}

src/components/views/avatars/RoomAvatar.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,30 @@ module.exports = React.createClass({
4848
};
4949
},
5050

51+
componentWillMount: function() {
52+
MatrixClientPeg.get().on("RoomState.events", this.onRoomStateEvents);
53+
},
54+
55+
componentWillUnmount: function() {
56+
MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents);
57+
},
58+
5159
componentWillReceiveProps: function(newProps) {
5260
this.setState({
5361
urls: this.getImageUrls(newProps),
5462
});
5563
},
5664

65+
onRoomStateEvents: function(ev) {
66+
if (ev.getRoomId() !== this.props.room.roomId ||
67+
ev.getType() !== 'm.room.avatar'
68+
) return;
69+
70+
this.setState({
71+
urls: this.getImageUrls(this.props),
72+
});
73+
},
74+
5775
getImageUrls: function(props) {
5876
return [
5977
ContentRepo.getHttpUriForMxc(

src/components/views/rooms/RoomList.js

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,7 @@ module.exports = React.createClass({
7777

7878
cli.on("Room", this.onRoom);
7979
cli.on("deleteRoom", this.onDeleteRoom);
80-
cli.on("Room.name", this.onRoomName);
8180
cli.on("Room.receipt", this.onRoomReceipt);
82-
cli.on("RoomState.events", this.onRoomStateEvents);
8381
cli.on("RoomMember.name", this.onRoomMemberName);
8482
cli.on("Event.decrypted", this.onEventDecrypted);
8583
cli.on("accountData", this.onAccountData);
@@ -161,12 +159,6 @@ module.exports = React.createClass({
161159
});
162160
}
163161
break;
164-
case 'on_room_read':
165-
// Force an update because the notif count state is too deep to cause
166-
// an update. This forces the local echo of reading notifs to be
167-
// reflected by the RoomTiles.
168-
this.forceUpdate();
169-
break;
170162
}
171163
},
172164

@@ -177,9 +169,7 @@ module.exports = React.createClass({
177169
if (MatrixClientPeg.get()) {
178170
MatrixClientPeg.get().removeListener("Room", this.onRoom);
179171
MatrixClientPeg.get().removeListener("deleteRoom", this.onDeleteRoom);
180-
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
181172
MatrixClientPeg.get().removeListener("Room.receipt", this.onRoomReceipt);
182-
MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents);
183173
MatrixClientPeg.get().removeListener("RoomMember.name", this.onRoomMemberName);
184174
MatrixClientPeg.get().removeListener("Event.decrypted", this.onEventDecrypted);
185175
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
@@ -243,14 +233,6 @@ module.exports = React.createClass({
243233
}
244234
},
245235

246-
onRoomName: function(room) {
247-
this._delayedRefreshRoomList();
248-
},
249-
250-
onRoomStateEvents: function(ev, state) {
251-
this._delayedRefreshRoomList();
252-
},
253-
254236
onRoomMemberName: function(ev, member) {
255237
this._delayedRefreshRoomList();
256238
},

src/components/views/rooms/RoomTile.js

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const React = require('react');
2121
const ReactDOM = require("react-dom");
2222
import PropTypes from 'prop-types';
2323
const classNames = require('classnames');
24+
import dis from '../../../dispatcher';
2425
const MatrixClientPeg = require('../../../MatrixClientPeg');
2526
import DMRoomMap from '../../../utils/DMRoomMap';
2627
const sdk = require('../../../index');
@@ -58,7 +59,9 @@ module.exports = React.createClass({
5859
hover: false,
5960
badgeHover: false,
6061
menuDisplayed: false,
62+
roomName: this.props.room.name,
6163
notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
64+
notificationCount: this.props.room.getUnreadNotificationCount(),
6265
selected: this.props.room.roomId === RoomViewStore.getRoomId(),
6366
});
6467
},
@@ -81,6 +84,20 @@ module.exports = React.createClass({
8184
}
8285
},
8386

87+
onRoomTimeline: function(ev, room) {
88+
if (room !== this.props.room) return;
89+
this.setState({
90+
notificationCount: this.props.room.getUnreadNotificationCount(),
91+
});
92+
},
93+
94+
onRoomName: function(room) {
95+
if (room !== this.props.room) return;
96+
this.setState({
97+
roomName: this.props.room.name,
98+
});
99+
},
100+
84101
onAccountData: function(accountDataEvent) {
85102
if (accountDataEvent.getType() == 'm.push_rules') {
86103
this.setState({
@@ -89,6 +106,21 @@ module.exports = React.createClass({
89106
}
90107
},
91108

109+
onAction: function(payload) {
110+
switch (payload.action) {
111+
// XXX: slight hack in order to zero the notification count when a room
112+
// is read. Ideally this state would be given to this via props (as we
113+
// do with `unread`). This is still better than forceUpdating the entire
114+
// RoomList when a room is read.
115+
case 'on_room_read':
116+
if (payload.roomId !== this.props.room.roomId) break;
117+
this.setState({
118+
notificationCount: this.props.room.getUnreadNotificationCount(),
119+
});
120+
break;
121+
}
122+
},
123+
92124
_onActiveRoomChange: function() {
93125
this.setState({
94126
selected: this.props.room.roomId === RoomViewStore.getRoomId(),
@@ -97,15 +129,37 @@ module.exports = React.createClass({
97129

98130
componentWillMount: function() {
99131
MatrixClientPeg.get().on("accountData", this.onAccountData);
132+
MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
133+
MatrixClientPeg.get().on("Room.name", this.onRoomName);
100134
ActiveRoomObserver.addListener(this.props.room.roomId, this._onActiveRoomChange);
135+
this.dispatcherRef = dis.register(this.onAction);
101136
},
102137

103138
componentWillUnmount: function() {
104139
const cli = MatrixClientPeg.get();
105140
if (cli) {
106141
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
142+
MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline);
143+
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
107144
}
108145
ActiveRoomObserver.removeListener(this.props.room.roomId, this._onActiveRoomChange);
146+
dis.unregister(this.dispatcherRef);
147+
},
148+
149+
// Do a simple shallow comparison of props and state to avoid unnecessary
150+
// renders. The assumption made here is that only state and props are used
151+
// in rendering this component and children.
152+
//
153+
// RoomList is frequently made to forceUpdate, so this decreases number of
154+
// RoomTile renderings.
155+
shouldComponentUpdate: function(newProps, newState) {
156+
if (Object.keys(newProps).some((k) => newProps[k] !== this.props[k])) {
157+
return true;
158+
}
159+
if (Object.keys(newState).some((k) => newState[k] !== this.state[k])) {
160+
return true;
161+
}
162+
return false;
109163
},
110164

111165
onClick: function(ev) {
@@ -174,7 +228,7 @@ module.exports = React.createClass({
174228
const myUserId = MatrixClientPeg.get().credentials.userId;
175229
const me = this.props.room.currentState.members[myUserId];
176230

177-
const notificationCount = this.props.room.getUnreadNotificationCount();
231+
const notificationCount = this.state.notificationCount;
178232
// var highlightCount = this.props.room.getUnreadNotificationCount("highlight");
179233

180234
const notifBadges = notificationCount > 0 && this._shouldShowNotifBadge();
@@ -202,9 +256,7 @@ module.exports = React.createClass({
202256
'mx_RoomTile_badgeButton': this.state.badgeHover || this.state.menuDisplayed,
203257
});
204258

205-
// XXX: We should never display raw room IDs, but sometimes the
206-
// room name js sdk gives is undefined (cannot repro this -- k)
207-
let name = this.props.room.name || this.props.room.roomId;
259+
let name = this.state.roomName;
208260
name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon
209261

210262
let badge;

0 commit comments

Comments
 (0)