1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-29 16:43:09 +03:00

make /notification pagination actually work

This commit is contained in:
Matthew Hodgson
2016-09-09 02:08:39 +01:00
parent f959e1a134
commit 2e4c362ccd
3 changed files with 66 additions and 11 deletions

View File

@@ -1793,6 +1793,38 @@ MatrixClient.prototype.paginateEventTimeline = function(eventTimeline, opts) {
return promise; return promise;
}; };
/**
* Reset the notifTimelineSet entirely, paginating in some historical notifs as
* a starting point for subsequent pagination.
*/
MatrixClient.prototype.resetNotifTimelineSet = function() {
if (!this._notifTimelineSet) {
return;
}
// FIXME: This thing is a total hack, and results in duplicate events being
// added to the timeline both from /sync and /notifications, and lots of
// slow and wasteful processing and pagination. The correct solution is to
// extend /messages or /search or something to filter on notifications.
// use the fictitious token 'end'. in practice we would ideally give it
// the oldest backwards pagination token from /sync, but /sync doesn't
// 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);
// 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
// timeline. given what a mess this is and given we're going to have duplicate
// events anyway, just leave it with the dummy token for now.
/*
this.paginateNotifTimeline(this._notifTimelineSet.getLiveTimeline(), {
backwards: true,
limit: 1
});
*/
};
/** /**
* Take an EventTimeline, and backfill results from the notifications API. * Take an EventTimeline, and backfill results from the notifications API.
@@ -1853,11 +1885,14 @@ MatrixClient.prototype.paginateNotifTimeline = function(eventTimeline, opts) {
for (var i = 0; i < res.notifications.length; i++) { for (var i = 0; i < res.notifications.length; i++) {
var notification = res.notifications[i]; var notification = res.notifications[i];
var event = self.getEventMapper()(notification.event); var event = self.getEventMapper()(notification.event);
event.setPushActions(notification.actions); event.setPushActions(
PushProcessor.actionListToActionsObject(notification.actions)
);
event.event.room_id = notification.room_id; // XXX: gutwrenching
matrixEvents[i] = event; matrixEvents[i] = event;
} }
eventTimeline.getEventTimelineSet() eventTimeline.getTimelineSet()
.addEventsToTimeline(matrixEvents, backwards, eventTimeline, token); .addEventsToTimeline(matrixEvents, backwards, eventTimeline, token);
// if we've hit the end of the timeline, we need to stop trying to // if we've hit the end of the timeline, we need to stop trying to

View File

@@ -149,11 +149,12 @@ EventTimelineSet.prototype.replaceEventId = function(oldEventId, newEventId) {
* <p>This is used when /sync returns a 'limited' timeline. * <p>This is used when /sync returns a 'limited' timeline.
* *
* @param {string=} backPaginationToken token for back-paginating the new timeline * @param {string=} backPaginationToken token for back-paginating the new timeline
* @param {?bool} flush Whether to flush the non-live timelines too.
*/ */
EventTimelineSet.prototype.resetLiveTimeline = function(backPaginationToken) { EventTimelineSet.prototype.resetLiveTimeline = function(backPaginationToken, flush) {
var newTimeline; var newTimeline;
if (!this._timelineSupport) { if (!this._timelineSupport || flush) {
// if timeline support is disabled, forget about the old timelines // if timeline support is disabled, forget about the old timelines
newTimeline = new EventTimeline(this); newTimeline = new EventTimeline(this);
this._timelines = [newTimeline]; this._timelines = [newTimeline];

View File

@@ -72,6 +72,7 @@ function SyncApi(client, opts) {
this._running = false; this._running = false;
this._keepAliveTimer = null; this._keepAliveTimer = null;
this._connectionReturnedDefer = null; this._connectionReturnedDefer = null;
this._notifEvents = []; // accumulator of sync events in the current sync response
} }
/** /**
@@ -392,6 +393,16 @@ SyncApi.prototype.sync = function() {
client.getOrCreateFilter( client.getOrCreateFilter(
getFilterName(client.credentials.userId), filter getFilterName(client.credentials.userId), filter
).done(function(filterId) { ).done(function(filterId) {
// prepare the notif timeline for pagination before we receive
// sync responses. Technically there are two small races here:
// 1: the reset will execute async and may race with the /sync.
// 2: the reset may return before the sync, and the room may go
// so fast that in the gap before the sync returns we may miss some
// notifs. This is incredibly unlikely however.
// The right solution would be to tie /sync pagination tokens into
// notifications.
client.resetNotifTimelineSet();
self._sync({ filterId: filterId }); self._sync({ filterId: filterId });
}, function(err) { }, function(err) {
self._startKeepAlives().done(function() { self._startKeepAlives().done(function() {
@@ -655,7 +666,7 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
} }
} }
this.notifEvents = []; this._notifEvents = [];
// Handle invites // Handle invites
inviteRooms.forEach(function(inviteObj) { inviteRooms.forEach(function(inviteObj) {
@@ -742,6 +753,12 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
room.currentState.paginationToken = syncToken; room.currentState.paginationToken = syncToken;
self._deregisterStateListeners(room); self._deregisterStateListeners(room);
room.resetLiveTimeline(joinObj.timeline.prev_batch); room.resetLiveTimeline(joinObj.timeline.prev_batch);
// XXX: for now we have to assume any gap in any timeline is
// reason to stop incrementally tracking notifications and
// reset the timeline.
client.resetNotifTimelineSet();
self._registerStateListeners(room); self._registerStateListeners(room);
} }
} }
@@ -792,11 +809,11 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
}); });
// update the notification timeline, if appropriate // update the notification timeline, if appropriate
if (this.notifEvents.length) { if (this._notifEvents.length) {
this.notifEvents.sort(function(a, b) { this._notifEvents.sort(function(a, b) {
return a.getTs() - b.getTs(); return a.getTs() - b.getTs();
}); });
this.notifEvents.forEach(function(event) { this._notifEvents.forEach(function(event) {
client.getNotifTimelineSet().addLiveEvent(event); client.getNotifTimelineSet().addLiveEvent(event);
}); });
} }
@@ -988,12 +1005,14 @@ SyncApi.prototype._processRoomEvents = function(room, stateEventList,
// may make notifications appear which should have the right name. // may make notifications appear which should have the right name.
room.recalculate(this.client.credentials.userId); room.recalculate(this.client.credentials.userId);
// gather our notifications into this.notifEvents // gather our notifications into this._notifEvents
if (client.getNotifTimelineSet()) { if (client.getNotifTimelineSet()) {
for (var i = 0; i < timelineEventList.length; i++) { for (var i = 0; i < timelineEventList.length; i++) {
var pushActions = client.getPushActionsForEvent(timelineEventList[i]); var pushActions = client.getPushActionsForEvent(timelineEventList[i]);
if (pushActions && pushActions.notify) { if (pushActions && pushActions.notify &&
this.notifEvents.push(timelineEventList[i]); pushActions.tweaks && pushActions.tweaks.highlight)
{
this._notifEvents.push(timelineEventList[i]);
} }
} }
} }