You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-28 05:03:59 +03:00
maintain the global notification timeline set.
* track notifTimelineSet on MatrixClient * stop Rooms from tracking notifTimelineSet as they don't need to * Implement client.paginateNotifTimelineSet * make Events store their pushActions properly * insert live notifs directly into the notifTimelineSet in /sync, ordering by origin_server_ts.
This commit is contained in:
109
lib/client.js
109
lib/client.js
@@ -146,6 +146,7 @@ function MatrixClient(opts) {
|
|||||||
this._ongoingScrollbacks = {};
|
this._ongoingScrollbacks = {};
|
||||||
this.timelineSupport = Boolean(opts.timelineSupport);
|
this.timelineSupport = Boolean(opts.timelineSupport);
|
||||||
this.urlPreviewCache = {};
|
this.urlPreviewCache = {};
|
||||||
|
this._notifTimelineSet = null;
|
||||||
|
|
||||||
this._crypto = null;
|
this._crypto = null;
|
||||||
if (CRYPTO_ENABLED && opts.sessionStore !== null &&
|
if (CRYPTO_ENABLED && opts.sessionStore !== null &&
|
||||||
@@ -249,6 +250,24 @@ MatrixClient.prototype.retryImmediately = function() {
|
|||||||
return this._syncApi.retryImmediately();
|
return this._syncApi.retryImmediately();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the global notification EventTimelineSet, if any
|
||||||
|
*
|
||||||
|
* @return {EventTimelineSet} the globl notification EventTimelineSet
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.getNotifTimelineSet = function() {
|
||||||
|
return this._notifTimelineSet;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the global notification EventTimelineSet
|
||||||
|
*
|
||||||
|
* @param {EventTimelineSet} notifTimelineSet
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.setNotifTimelineSet = function(notifTimelineSet) {
|
||||||
|
this._notifTimelineSet = notifTimelineSet;
|
||||||
|
};
|
||||||
|
|
||||||
// Crypto bits
|
// Crypto bits
|
||||||
// ===========
|
// ===========
|
||||||
|
|
||||||
@@ -1365,16 +1384,16 @@ function _membershipChange(client, roomId, userId, membership, reason, callback)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain a dict of actions which should be performed for this event according
|
* Obtain a dict of actions which should be performed for this event according
|
||||||
* to the push rules for this user.
|
* to the push rules for this user. Caches the dict on the event.
|
||||||
* @param {MatrixEvent} event The event to get push actions for.
|
* @param {MatrixEvent} event The event to get push actions for.
|
||||||
* @return {module:pushprocessor~PushAction} A dict of actions to perform.
|
* @return {module:pushprocessor~PushAction} A dict of actions to perform.
|
||||||
*/
|
*/
|
||||||
MatrixClient.prototype.getPushActionsForEvent = function(event) {
|
MatrixClient.prototype.getPushActionsForEvent = function(event) {
|
||||||
if (event._pushActions === undefined) {
|
if (!event.getPushActions()) {
|
||||||
var pushProcessor = new PushProcessor(this);
|
var pushProcessor = new PushProcessor(this);
|
||||||
event._pushActions = pushProcessor.actionsForEvent(event);
|
event.setPushActions(pushProcessor.actionsForEvent(event));
|
||||||
}
|
}
|
||||||
return event._pushActions;
|
return event.getPushActions();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Profile operations
|
// Profile operations
|
||||||
@@ -1774,6 +1793,88 @@ MatrixClient.prototype.paginateEventTimeline = function(eventTimeline, opts) {
|
|||||||
return promise;
|
return promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take an EventTimeline, and backfill results from the notifications API.
|
||||||
|
* In future, the notifications API should probably be replaced by /messages
|
||||||
|
* with a custom filter or something - so we don't feel too bad about this being
|
||||||
|
* cargoculted from paginateEventTimeLine.
|
||||||
|
*
|
||||||
|
* @param {module:models/event-timeline~EventTimeline} eventTimeline timeline
|
||||||
|
* object to be updated
|
||||||
|
* @param {Object} [opts]
|
||||||
|
* @param {boolean} [opts.backwards = false] true to fill backwards,
|
||||||
|
* false to go forwards. <b>Forwards is not implemented yet!</b>
|
||||||
|
* @param {number} [opts.limit = 30] number of events to request
|
||||||
|
*
|
||||||
|
* @return {module:client.Promise} Resolves to a boolean: false if there are no
|
||||||
|
* events and we reached either end of the timeline; else true.
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.paginateNotifTimeline = function(eventTimeline, opts) {
|
||||||
|
// TODO: we should implement a backoff (as per scrollback()) to deal more
|
||||||
|
// nicely with HTTP errors.
|
||||||
|
opts = opts || {};
|
||||||
|
var backwards = opts.backwards || false;
|
||||||
|
|
||||||
|
if (!backwards) {
|
||||||
|
throw new Error("paginateNotifTimeline can only paginate backwards");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventTimeline.getRoomId()) {
|
||||||
|
throw new Error("paginateNotifTimeline should never be called on a " +
|
||||||
|
"timeline associated with a room");
|
||||||
|
}
|
||||||
|
|
||||||
|
var dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
|
||||||
|
|
||||||
|
var token = eventTimeline.getPaginationToken(dir);
|
||||||
|
|
||||||
|
var pendingRequest = eventTimeline._paginationRequests[dir];
|
||||||
|
if (pendingRequest) {
|
||||||
|
// already a request in progress - return the existing promise
|
||||||
|
return pendingRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = "/notifications";
|
||||||
|
var params = {
|
||||||
|
from: token,
|
||||||
|
limit: ('limit' in opts) ? opts.limit : 30,
|
||||||
|
only: 'highlight',
|
||||||
|
};
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
var promise =
|
||||||
|
this._http.authedRequestWithPrefix(undefined, "GET", path, params,
|
||||||
|
undefined, httpApi.PREFIX_UNSTABLE
|
||||||
|
).then(function(res) {
|
||||||
|
var token = res.next_token;
|
||||||
|
var matrixEvents = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < res.notifications.length; i++) {
|
||||||
|
var notification = res.notifications[i];
|
||||||
|
var event = self.getEventMapper()(notification.event);
|
||||||
|
event.setPushActions(notification.actions);
|
||||||
|
matrixEvents[i] = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
eventTimeline.getEventTimelineSet()
|
||||||
|
.addEventsToTimeline(matrixEvents, backwards, eventTimeline, token);
|
||||||
|
|
||||||
|
// if we've hit the end of the timeline, we need to stop trying to
|
||||||
|
// paginate. We need to keep the 'forwards' token though, to make sure
|
||||||
|
// we can recover from gappy syncs.
|
||||||
|
if (backwards && !res.next_token) {
|
||||||
|
eventTimeline.setPaginationToken(null, dir);
|
||||||
|
}
|
||||||
|
return res.next_token ? true : false;
|
||||||
|
}).finally(function() {
|
||||||
|
eventTimeline._paginationRequests[dir] = null;
|
||||||
|
});
|
||||||
|
eventTimeline._paginationRequests[dir] = promise;
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Peek into a room and receive updates about the room. This only works if the
|
* Peek into a room and receive updates about the room. This only works if the
|
||||||
* history visibility for the room is world_readable.
|
* history visibility for the room is world_readable.
|
||||||
|
|||||||
@@ -100,6 +100,14 @@ EventTimeline.prototype.getFilter = function() {
|
|||||||
return this._eventTimelineSet.getFilter();
|
return this._eventTimelineSet.getFilter();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the timelineSet for this timeline
|
||||||
|
* @return {EventTimelineSet} timelineSet
|
||||||
|
*/
|
||||||
|
EventTimeline.prototype.getTimelineSet = function() {
|
||||||
|
return this._eventTimelineSet;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the base index.
|
* Get the base index.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ module.exports.MatrixEvent = function MatrixEvent(event, clearEvent) {
|
|||||||
this.forwardLooking = true;
|
this.forwardLooking = true;
|
||||||
|
|
||||||
this._clearEvent = clearEvent || {};
|
this._clearEvent = clearEvent || {};
|
||||||
|
this._pushActions = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.MatrixEvent.prototype = {
|
module.exports.MatrixEvent.prototype = {
|
||||||
@@ -294,6 +295,24 @@ module.exports.MatrixEvent.prototype = {
|
|||||||
isRedacted: function() {
|
isRedacted: function() {
|
||||||
return Boolean(this.getUnsigned().redacted_because);
|
return Boolean(this.getUnsigned().redacted_because);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the push actions, if known, for this event
|
||||||
|
*
|
||||||
|
* @return {?Object} push actions
|
||||||
|
*/
|
||||||
|
getPushActions: function() {
|
||||||
|
return this._pushActions;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the push actions for this event.
|
||||||
|
*
|
||||||
|
* @param {Object} pushActions push actions
|
||||||
|
*/
|
||||||
|
setPushActions: function(pushActions) {
|
||||||
|
this._pushActions = pushActions;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -149,21 +149,18 @@ function Room(roomId, opts) {
|
|||||||
|
|
||||||
this._notificationCounts = {};
|
this._notificationCounts = {};
|
||||||
|
|
||||||
// all our per-room timeline lists. the first one is the unfiltered ones;
|
// all our per-room timeline sets. the first one is the unfiltered ones;
|
||||||
// the subsequent ones are the filtered ones in no particular order.
|
// the subsequent ones are the filtered ones in no particular order.
|
||||||
this._timelineSets = [new EventTimelineSet(roomId, this, opts)];
|
this._timelineSets = [new EventTimelineSet(roomId, this, opts)];
|
||||||
reEmit(this, this._timelineSets[0], ["Room.timeline"]);
|
reEmit(this, this._timelineSets[0], ["Room.timeline"]);
|
||||||
|
|
||||||
this._fixUpLegacyTimelineFields();
|
this._fixUpLegacyTimelineFields();
|
||||||
|
|
||||||
// any filtered timeline lists we're maintaining for this room
|
// any filtered timeline sets we're maintaining for this room
|
||||||
this._filteredTimelineSets = {
|
this._filteredTimelineSets = {
|
||||||
// filter_id: timelineSet
|
// filter_id: timelineSet
|
||||||
};
|
};
|
||||||
|
|
||||||
// a reference to our shared notification timeline list
|
|
||||||
this._notifTimelineSet = opts.notifTimelineSet;
|
|
||||||
|
|
||||||
if (this._opts.pendingEventOrdering == "detached") {
|
if (this._opts.pendingEventOrdering == "detached") {
|
||||||
this._pendingEventList = [];
|
this._pendingEventList = [];
|
||||||
}
|
}
|
||||||
@@ -241,14 +238,6 @@ Room.prototype.getTimelineSets = function() {
|
|||||||
return this._timelineSets;
|
return this._timelineSets;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the shared notification timeline set
|
|
||||||
* @return {EventTimelineSet} notification timeline set
|
|
||||||
*/
|
|
||||||
Room.prototype.getNotifTimelineSet = function() {
|
|
||||||
return this._notifTimelineSet;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the timeline which contains the given event from the unfiltered set, if any
|
* Get the timeline which contains the given event from the unfiltered set, if any
|
||||||
*
|
*
|
||||||
@@ -261,7 +250,7 @@ Room.prototype.getTimelineForEvent = function(eventId) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new timeline to this room's unfiltered timeline list
|
* Add a new timeline to this room's unfiltered timeline set
|
||||||
*
|
*
|
||||||
* @return {module:models/event-timeline~EventTimeline} newly-created timeline
|
* @return {module:models/event-timeline~EventTimeline} newly-created timeline
|
||||||
*/
|
*/
|
||||||
@@ -563,18 +552,11 @@ Room.prototype._addLiveEvent = function(event, duplicateStrategy) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add to our timeline lists
|
// add to our timeline sets
|
||||||
for (i = 0; i < this._timelineSets.length; i++) {
|
for (i = 0; i < this._timelineSets.length; i++) {
|
||||||
this._timelineSets[i].addLiveEvent(event, duplicateStrategy);
|
this._timelineSets[i].addLiveEvent(event, duplicateStrategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add to notification timeline list, if any
|
|
||||||
if (this._notifTimelineSet) {
|
|
||||||
if (event.isNotification()) {
|
|
||||||
this._notifTimelineSet.addLiveEvent(event, duplicateStrategy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// synthesize and inject implicit read receipts
|
// synthesize and inject implicit read receipts
|
||||||
// Done after adding the event because otherwise the app would get a read receipt
|
// Done after adding the event because otherwise the app would get a read receipt
|
||||||
// pointing to an event that wasn't yet in the timeline
|
// pointing to an event that wasn't yet in the timeline
|
||||||
@@ -648,8 +630,6 @@ Room.prototype.addPendingEvent = function(event, txnId) {
|
|||||||
timelineSet.getLiveTimeline(), false);
|
timelineSet.getLiveTimeline(), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// notifications are receive-only, so we don't need to worry
|
|
||||||
// about this._notifTimelineSet.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emit("Room.localEchoUpdated", event, this, null, null);
|
this.emit("Room.localEchoUpdated", event, this, null, null);
|
||||||
|
|||||||
22
lib/sync.js
22
lib/sync.js
@@ -655,6 +655,8 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.notifEvents = [];
|
||||||
|
|
||||||
// Handle invites
|
// Handle invites
|
||||||
inviteRooms.forEach(function(inviteObj) {
|
inviteRooms.forEach(function(inviteObj) {
|
||||||
var room = inviteObj.room;
|
var room = inviteObj.room;
|
||||||
@@ -788,6 +790,16 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
|
|||||||
timelineEvents.forEach(function(e) { client.emit("event", e); });
|
timelineEvents.forEach(function(e) { client.emit("event", e); });
|
||||||
accountDataEvents.forEach(function(e) { client.emit("event", e); });
|
accountDataEvents.forEach(function(e) { client.emit("event", e); });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// update the notification timeline, if appropriate
|
||||||
|
if (this.notifEvents.length) {
|
||||||
|
this.notifEvents.sort(function(a, b) {
|
||||||
|
return a.getTs() - b.getTs();
|
||||||
|
});
|
||||||
|
this.notifEvents.forEach(function(event) {
|
||||||
|
client.getNotifTimelineSet().addLiveEvent(event);
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -976,6 +988,16 @@ 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
|
||||||
|
if (client.getNotifTimelineSet()) {
|
||||||
|
for (var i = 0; i < timelineEventList.length; i++) {
|
||||||
|
var pushActions = client.getPushActionsForEvent(timelineEventList[i]);
|
||||||
|
if (pushActions && pushActions.notify) {
|
||||||
|
this.notifEvents.push(timelineEventList[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// execute the timeline events, this will begin to diverge the current state
|
// execute the timeline events, this will begin to diverge the current state
|
||||||
// if the timeline has any state events in it.
|
// if the timeline has any state events in it.
|
||||||
room.addLiveEvents(timelineEventList);
|
room.addLiveEvents(timelineEventList);
|
||||||
|
|||||||
@@ -254,7 +254,12 @@ TimelineWindow.prototype.paginate = function(direction, size, makeRequest,
|
|||||||
|
|
||||||
debuglog("TimelineWindow: starting request");
|
debuglog("TimelineWindow: starting request");
|
||||||
var self = this;
|
var self = this;
|
||||||
var prom = this._client.paginateEventTimeline(tl.timeline, {
|
|
||||||
|
var paginateTimeline = tl.timeline.getRoomId() ?
|
||||||
|
this._client.paginateEventTimeline :
|
||||||
|
this._client.paginateNotifTimeline;
|
||||||
|
|
||||||
|
var prom = paginateTimeline.call(this._client, tl.timeline, {
|
||||||
backwards: direction == EventTimeline.BACKWARDS,
|
backwards: direction == EventTimeline.BACKWARDS,
|
||||||
limit: size
|
limit: size
|
||||||
}).finally(function() {
|
}).finally(function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user