diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 758d3500dd..af20b25421 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -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 } />