diff --git a/src/client.js b/src/client.js index e35f331f4..d206f6a40 100644 --- a/src/client.js +++ b/src/client.js @@ -2089,7 +2089,7 @@ MatrixClient.prototype.resetNotifTimelineSet = function() { // know about /notifications, so we have no choice but to start paginating // from the current point in time. This may well overlap with historical // notifs which are then inserted into the timeline by /sync responses. - this._notifTimelineSet.resetLiveTimeline('end', true); + this._notifTimelineSet.resetLiveTimeline('end', null); // we could try to paginate a single event at this point in order to get // a more valid pagination token, but it just ends up with an out of order diff --git a/src/models/event-timeline-set.js b/src/models/event-timeline-set.js index 4b46eccf5..16ad21d72 100644 --- a/src/models/event-timeline-set.js +++ b/src/models/event-timeline-set.js @@ -149,13 +149,14 @@ EventTimelineSet.prototype.replaceEventId = function(oldEventId, newEventId) { *
This is used when /sync returns a 'limited' timeline. * * @param {string=} backPaginationToken token for back-paginating the new timeline - * @param {?bool} flush Whether to flush the non-live timelines too. + * @param {string=} forwardPaginationToken token for forward-paginating the old live timeline, + * if absent or null, all timelines are reset. * * @fires module:client~MatrixClient#event:"Room.timelineReset" */ -EventTimelineSet.prototype.resetLiveTimeline = function(backPaginationToken, flush) { +EventTimelineSet.prototype.resetLiveTimeline = function(backPaginationToken, forwardPaginationToken) { // if timeline support is disabled, forget about the old timelines - const resetAllTimelines = !this._timelineSupport || flush; + const resetAllTimelines = !this._timelineSupport || !forwardPaginationToken; let newTimeline; if (resetAllTimelines) { @@ -166,21 +167,35 @@ EventTimelineSet.prototype.resetLiveTimeline = function(backPaginationToken, flu newTimeline = this.addTimeline(); } - // initialise the state in the new timeline from our last known state - const evMap = this._liveTimeline.getState(EventTimeline.FORWARDS).events; - const events = []; - for (const evtype in evMap) { - if (!evMap.hasOwnProperty(evtype)) { - continue; - } - for (const stateKey in evMap[evtype]) { - if (!evMap[evtype].hasOwnProperty(stateKey)) { + // move the state objects over from the old live timeline, then we'll + // keep using the same RoomMember objects for the 'live' set of members + newTimeline._startState = this._liveTimeline._startState; + newTimeline._endState = this._liveTimeline._endState; + + if (!resetAllTimelines) { + // Now regenerate the state for the previously-live timeline, because + // we just stole it and put it on the new live timeline + // (If we're resetting all timelines, don't bother because the old live + // timeline is about to be thrown away anyway. + this._liveTimeline._startState = new RoomState(this._roomId); + this._liveTimeline._endState = new RoomState(this._roomId); + const evMap = this._liveTimeline.getState(EventTimeline.FORWARDS).events; + const events = []; + for (const evtype in evMap) { + if (!evMap.hasOwnProperty(evtype)) { continue; } - events.push(evMap[evtype][stateKey]); + for (const stateKey in evMap[evtype]) { + if (!evMap[evtype].hasOwnProperty(stateKey)) { + continue; + } + events.push(evMap[evtype][stateKey]); + } } + this._liveTimeline.initialiseState(events); + + this._liveTimeline.setPaginationToken(forwardPaginationToken, EventTimeline.FORWARDS); } - newTimeline.initialiseState(events); // make sure we set the pagination token before firing timelineReset, // otherwise clients which start back-paginating will fail, and then get diff --git a/src/models/room.js b/src/models/room.js index a1f88cd7c..6c47dd24c 100644 --- a/src/models/room.js +++ b/src/models/room.js @@ -205,12 +205,12 @@ Room.prototype.getLiveTimeline = function() { *
This is used when /sync returns a 'limited' timeline. * * @param {string=} backPaginationToken token for back-paginating the new timeline - * @param {boolean=} flush True to remove all events in all timelines. If false, only - * the live timeline is reset. + * @param {string=} forwardPaginationToken token for forward-paginating the old live timeline, + * if absent or null, all timelines are reset. */ -Room.prototype.resetLiveTimeline = function(backPaginationToken, flush) { +Room.prototype.resetLiveTimeline = function(backPaginationToken, forwardPaginationToken) { for (let i = 0; i < this._timelineSets.length; i++) { - this._timelineSets[i].resetLiveTimeline(backPaginationToken, flush); + this._timelineSets[i].resetLiveTimeline(backPaginationToken, forwardPaginationToken); } this._fixUpLegacyTimelineFields(); diff --git a/src/sync.js b/src/sync.js index 284b967ef..54536fc6c 100644 --- a/src/sync.js +++ b/src/sync.js @@ -884,14 +884,10 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) { } if (limited) { - // save the old 'next_batch' token as the - // forward-pagination token for the previously-active - // timeline. - room.currentState.paginationToken = syncToken; self._deregisterStateListeners(room); room.resetLiveTimeline( joinObj.timeline.prev_batch, - self.opts.canResetEntireTimeline(room.roomId), + self.opts.canResetEntireTimeline(room.roomId) ? null : syncToken, ); // We have to assume any gap in any timeline is