1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-12-23 22:42:10 +03:00

Set the back-pagination token before raising Room.timelineReset

This fixes another race condition on gappy syncs, wherein we weren't
back-paginating back from the start of the gappy sync.
This commit is contained in:
Richard van der Hoff
2016-03-01 13:35:22 +00:00
parent 00af1ce7d2
commit 0034bdf4ad
4 changed files with 80 additions and 34 deletions

View File

@@ -168,9 +168,11 @@ Room.prototype.getLiveTimeline = function() {
*
* <p>This is used when /sync returns a 'limited' timeline.
*
* @param {string=} backPaginationToken token for back-paginating the new timeline
*
* @fires module:client~MatrixClient#event:"Room.timelineReset"
*/
Room.prototype.resetLiveTimeline = function() {
Room.prototype.resetLiveTimeline = function(backPaginationToken) {
var newTimeline;
if (!this._timelineSupport) {
@@ -194,6 +196,11 @@ Room.prototype.resetLiveTimeline = function() {
}
newTimeline.initialiseState(events);
// make sure we set the pagination token before firing timelineReset,
// otherwise clients which start back-paginating will fail, and then get
// stuck without realising that they *can* back-paginate.
newTimeline.setPaginationToken(backPaginationToken, EventTimeline.BACKWARDS);
this._liveTimeline = newTimeline;
this._fixUpLegacyTimelineFields();
this.emit("Room.timelineReset", this);

View File

@@ -28,6 +28,7 @@ var User = require("./models/user");
var Room = require("./models/room");
var utils = require("./utils");
var Filter = require("./filter");
var EventTimeline = require("./models/event-timeline");
var DEBUG = true;
@@ -175,12 +176,14 @@ SyncApi.prototype.syncLeftRooms = function() {
var timelineEvents =
self._mapSyncEventsFormat(leaveObj.timeline, room);
var stateEvents = self._mapSyncEventsFormat(leaveObj.state, room);
var paginationToken = (
leaveObj.timeline.limited ? leaveObj.timeline.prev_batch : null
);
self._processRoomEvents(
room, stateEvents, timelineEvents, paginationToken
);
// set the back-pagination token. Do this *before* adding any
// events so that clients can start back-paginating.
room.getLiveTimeline().setPaginationToken(leaveObj.timeline.prev_batch,
EventTimeline.BACKWARDS);
self._processRoomEvents(room, stateEvents, timelineEvents);
room.recalculate(client.credentials.userId);
client.store.storeRoom(room);
client.emit("Room", room);
@@ -593,9 +596,14 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
joinObj.timeline = joinObj.timeline || {};
var limited = false;
if (joinObj.timeline.limited) {
limited = true;
if (joinObj.isBrandNewRoom) {
// set the back-pagination token. Do this *before* adding any
// events so that clients can start back-paginating.
room.getLiveTimeline().setPaginationToken(
joinObj.timeline.prev_batch, EventTimeline.BACKWARDS);
}
else if (joinObj.timeline.limited) {
var limited = true;
// we've got a limited sync, so we *probably* have a gap in the
// timeline, so should reset. But we might have been peeking or
@@ -638,21 +646,12 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
// timeline.
room.currentState.paginationToken = syncToken;
self._deregisterStateListeners(room);
room.resetLiveTimeline();
room.resetLiveTimeline(joinObj.timeline.prev_batch);
self._registerStateListeners(room);
}
}
// we want to set a new pagination token if this is the first time
// we've made this room or if we're nuking the timeline
var paginationToken = null;
if (joinObj.isBrandNewRoom || limited) {
paginationToken = joinObj.timeline.prev_batch;
}
self._processRoomEvents(
room, stateEvents, timelineEvents, paginationToken
);
self._processRoomEvents(room, stateEvents, timelineEvents);
// XXX: should we be adding ephemeralEvents to the timeline?
// It feels like that for symmetry with room.addAccountData()
@@ -873,11 +872,9 @@ SyncApi.prototype._resolveInvites = function(room) {
* at the *START* of the timeline list if it is supplied.
* @param {?MatrixEvent[]} timelineEventList A list of timeline events. Lower index
* is earlier in time. Higher index is later.
* @param {string=} paginationToken pagination token for going backwards in time.
* This should only be set if this is a new room/timeline.
*/
SyncApi.prototype._processRoomEvents = function(room, stateEventList,
timelineEventList, paginationToken) {
timelineEventList) {
timelineEventList = timelineEventList || [];
var client = this.client;
// "old" and "current" state are the same initially; they
@@ -891,14 +888,6 @@ SyncApi.prototype._processRoomEvents = function(room, stateEventList,
);
var stateEvents = stateEventList;
// Set the pagination token BEFORE adding events to the timeline: it's not
// unreasonable for clients to call scrollback() in response to Room.timeline
// events which addEventsToTimeline will emit-- we want to make sure they use
// the right token if and when they do.
if (paginationToken) {
room.oldState.paginationToken = paginationToken;
}
// set the state of the room to as it was before the timeline executes
//
// XXX: what if we've already seen (some of) the events in the timeline,

View File

@@ -3,6 +3,7 @@ var sdk = require("../..");
var HttpBackend = require("../mock-request");
var utils = require("../test-utils");
var MatrixEvent = sdk.MatrixEvent;
var EventTimeline = sdk.EventTimeline;
describe("MatrixClient syncing", function() {
var baseUrl = "http://localhost.or.something";
@@ -548,5 +549,46 @@ describe("MatrixClient syncing", function() {
return httpBackend.flush();
}).catch(utils.failTest);
});
it("should set the back-pagination token on left rooms", function(done) {
var syncData = {
next_batch: "batch_token",
rooms: {
leave: {}
},
};
syncData.rooms.leave[roomTwo] = {
timeline: {
events: [
utils.mkMessage({
room: roomTwo, user: otherUserId, msg: "hello"
}),
],
prev_batch: "pagTok",
},
};
httpBackend.when("POST", "/filter").respond(200, {
filter_id: "another_id"
});
httpBackend.when("GET", "/sync").respond(200, syncData);
client.syncLeftRooms().then(function() {
var room = client.getRoom(roomTwo);
var tok = room.getLiveTimeline().getPaginationToken(
EventTimeline.BACKWARDS);
expect(tok).toEqual("pagTok");
done();
}).catch(utils.failTest).done();
// first flush the filter request; this will make syncLeftRooms
// make its /sync call
httpBackend.flush("/filter").then(function() {
return httpBackend.flush();
}).catch(utils.failTest);
});
});
});

View File

@@ -376,13 +376,21 @@ describe("Room", function() {
newLiveTimeline.getState(EventTimeline.FORWARDS));
});
it("should emit Room.timelineReset event", function() {
it("should emit Room.timelineReset event and set the correct " +
"pagination token", function() {
var callCount = 0;
room.on("Room.timelineReset", function(emitRoom) {
callCount += 1;
expect(emitRoom).toEqual(room);
// make sure that the pagination token has been set before the
// event is emitted.
var tok = emitRoom.getLiveTimeline()
.getPaginationToken(EventTimeline.BACKWARDS);
expect(tok).toEqual("pagToken");
});
room.resetLiveTimeline();
room.resetLiveTimeline("pagToken");
expect(callCount).toEqual(1);
});