Improve perf of refreshing room list. Show spinner when loading left rooms.

When the JS SDK encounters a new room it will emit a flurry of events for things
like state and room members. Refreshing the room list on each event is bad for
performance. This is okay initially because the room list is only shown after
the first sync, but when getting archived rooms it locks up for 15-30s as it
thrashes. Add a 1s cap to refreshRoomList() which means that it will refresh
*AT MOST* once every second. If it has been >1s since the last refresh it will
immediately refresh. If it has been <1s it will wait the difference.
This commit is contained in:
Kegan Dougal 2015-12-18 15:13:59 +00:00
parent 22635f251d
commit 711fdd25af

View file

@ -38,6 +38,7 @@ module.exports = React.createClass({
getInitialState: function() {
return {
activityMap: null,
isLoadingLeftRooms: false,
lists: {},
}
},
@ -89,20 +90,24 @@ module.exports = React.createClass({
},
onRoom: function(room) {
this.refreshRoomList();
this._delayedRefreshRoomList();
},
onDeleteRoom: function(roomId) {
this.refreshRoomList();
this._delayedRefreshRoomList();
},
onArchivedHeaderClick: function(isHidden) {
if (!isHidden) {
var self = this;
this.setState({ isLoadingLeftRooms: true });
// we don't care about the response since it comes down via "Room"
// events.
MatrixClientPeg.get().syncLeftRooms().catch(function(err) {
console.error("Failed to sync left rooms: %s", err);
console.error(err);
}).finally(function() {
self.setState({ isLoadingLeftRooms: false });
});
}
},
@ -143,22 +148,57 @@ module.exports = React.createClass({
},
onRoomName: function(room) {
this.refreshRoomList();
this._delayedRefreshRoomList();
},
onRoomTags: function(event, room) {
this.refreshRoomList();
this._delayedRefreshRoomList();
},
onRoomStateEvents: function(ev, state) {
setTimeout(this.refreshRoomList, 0);
this._delayedRefreshRoomList();
},
onRoomMemberName: function(ev, member) {
setTimeout(this.refreshRoomList, 0);
this._delayedRefreshRoomList();
},
_delayedRefreshRoomList: function() {
// There can be 1000s of JS SDK events when rooms are initially synced;
// we don't want to do lots of work rendering until things have settled.
// Therefore, keep a 1s refresh buffer which will refresh the room list
// at MOST once every 1s to prevent thrashing.
var MAX_REFRESH_INTERVAL_MS = 1000;
var self = this;
if (!self._lastRefreshRoomListTs) {
self.refreshRoomList(); // first refresh evar
}
else {
var timeWaitedMs = Date.now() - self._lastRefreshRoomListTs;
if (timeWaitedMs > MAX_REFRESH_INTERVAL_MS) {
clearTimeout(self._refreshRoomListTimerId);
self._refreshRoomListTimerId = null;
self.refreshRoomList(); // refreshed more than MAX_REFRESH_INTERVAL_MS ago
}
else {
// refreshed less than MAX_REFRESH_INTERVAL_MS ago, wait the difference
// if we aren't already waiting. If we are waiting then NOP, it will
// fire soon, promise!
if (!self._refreshRoomListTimerId) {
self._refreshRoomListTimerId = setTimeout(function() {
self.refreshRoomList();
}, 10 + MAX_REFRESH_INTERVAL_MS - timeWaitedMs); // 10 is a buffer amount
}
}
}
},
refreshRoomList: function() {
// console.log("DEBUG: Refresh room list delta=%s ms",
// (!this._lastRefreshRoomListTs ? "-" : (Date.now() - this._lastRefreshRoomListTs))
// );
// TODO: rather than bluntly regenerating and re-sorting everything
// every time we see any kind of room change from the JS SDK
// we could do incremental updates on our copy of the state
@ -166,6 +206,7 @@ module.exports = React.createClass({
// us re-rendering all the sublists every time anything changes anywhere
// in the state of the client.
this.setState(this.getRoomLists());
this._lastRefreshRoomListTs = Date.now();
},
getRoomLists: function() {
@ -320,6 +361,8 @@ module.exports = React.createClass({
selectedRoom={ self.props.selectedRoom }
collapsed={ self.props.collapsed }
alwaysShowHeader={ true }
startAsHidden={ true }
showSpinner={ self.state.isLoadingLeftRooms }
onHeaderClick= { self.onArchivedHeaderClick } />
</div>
</GeminiScrollbar>