You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-29 16:43:09 +03:00
Merge branch 'develop' into markjh/megolm
Conflicts: lib/crypto/algorithms/megolm.js
This commit is contained in:
253
lib/client.js
253
lib/client.js
@@ -146,6 +146,7 @@ function MatrixClient(opts) {
|
||||
this._ongoingScrollbacks = {};
|
||||
this.timelineSupport = Boolean(opts.timelineSupport);
|
||||
this.urlPreviewCache = {};
|
||||
this._notifTimelineSet = null;
|
||||
|
||||
this._crypto = null;
|
||||
if (CRYPTO_ENABLED && opts.sessionStore !== null &&
|
||||
@@ -249,6 +250,24 @@ MatrixClient.prototype.retryImmediately = function() {
|
||||
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
|
||||
// ===========
|
||||
|
||||
@@ -1330,16 +1349,16 @@ function _membershipChange(client, roomId, userId, membership, reason, callback)
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return {module:pushprocessor~PushAction} A dict of actions to perform.
|
||||
*/
|
||||
MatrixClient.prototype.getPushActionsForEvent = function(event) {
|
||||
if (event._pushActions === undefined) {
|
||||
if (!event.getPushActions()) {
|
||||
var pushProcessor = new PushProcessor(this);
|
||||
event._pushActions = pushProcessor.actionsForEvent(event);
|
||||
event.setPushActions(pushProcessor.actionsForEvent(event));
|
||||
}
|
||||
return event._pushActions;
|
||||
return event.getPushActions();
|
||||
};
|
||||
|
||||
// Profile operations
|
||||
@@ -1584,18 +1603,18 @@ MatrixClient.prototype.paginateEventContext = function(eventContext, opts) {
|
||||
/**
|
||||
* Get an EventTimeline for the given event
|
||||
*
|
||||
* <p>If the room object already has the given event in its store, the
|
||||
* <p>If the EventTimelineSet object already has the given event in its store, the
|
||||
* corresponding timeline will be returned. Otherwise, a /context request is
|
||||
* made, and used to construct an EventTimeline.
|
||||
*
|
||||
* @param {Room} room The room to look for the event in
|
||||
* @param {EventTimelineSet} timelineSet The timelineSet to look for the event in
|
||||
* @param {string} eventId The ID of the event to look for
|
||||
*
|
||||
* @return {module:client.Promise} Resolves:
|
||||
* {@link module:models/event-timeline~EventTimeline} including the given
|
||||
* event
|
||||
*/
|
||||
MatrixClient.prototype.getEventTimeline = function(room, eventId) {
|
||||
MatrixClient.prototype.getEventTimeline = function(timelineSet, eventId) {
|
||||
// don't allow any timeline support unless it's been enabled.
|
||||
if (!this.timelineSupport) {
|
||||
throw new Error("timeline support is disabled. Set the 'timelineSupport'" +
|
||||
@@ -1603,13 +1622,13 @@ MatrixClient.prototype.getEventTimeline = function(room, eventId) {
|
||||
" it.");
|
||||
}
|
||||
|
||||
if (room.getTimelineForEvent(eventId)) {
|
||||
return q(room.getTimelineForEvent(eventId));
|
||||
if (timelineSet.getTimelineForEvent(eventId)) {
|
||||
return q(timelineSet.getTimelineForEvent(eventId));
|
||||
}
|
||||
|
||||
var path = utils.encodeUri(
|
||||
"/rooms/$roomId/context/$eventId", {
|
||||
$roomId: room.roomId,
|
||||
$roomId: timelineSet.room.roomId,
|
||||
$eventId: eventId,
|
||||
}
|
||||
);
|
||||
@@ -1626,8 +1645,8 @@ MatrixClient.prototype.getEventTimeline = function(room, eventId) {
|
||||
|
||||
// by the time the request completes, the event might have ended up in
|
||||
// the timeline.
|
||||
if (room.getTimelineForEvent(eventId)) {
|
||||
return room.getTimelineForEvent(eventId);
|
||||
if (timelineSet.getTimelineForEvent(eventId)) {
|
||||
return timelineSet.getTimelineForEvent(eventId);
|
||||
}
|
||||
|
||||
// we start with the last event, since that's the point at which we
|
||||
@@ -1639,21 +1658,22 @@ MatrixClient.prototype.getEventTimeline = function(room, eventId) {
|
||||
.concat(res.events_before);
|
||||
var matrixEvents = utils.map(events, self.getEventMapper());
|
||||
|
||||
var timeline = room.getTimelineForEvent(matrixEvents[0].getId());
|
||||
var timeline = timelineSet.getTimelineForEvent(matrixEvents[0].getId());
|
||||
if (!timeline) {
|
||||
timeline = room.addTimeline();
|
||||
timeline = timelineSet.addTimeline();
|
||||
timeline.initialiseState(utils.map(res.state,
|
||||
self.getEventMapper()));
|
||||
timeline.getState(EventTimeline.FORWARDS).paginationToken = res.end;
|
||||
}
|
||||
room.addEventsToTimeline(matrixEvents, true, timeline, res.start);
|
||||
timelineSet.addEventsToTimeline(matrixEvents, true, timeline, res.start);
|
||||
|
||||
// there is no guarantee that the event ended up in "timeline" (we
|
||||
// might have switched to a neighbouring timeline) - so check the
|
||||
// room's index again. On the other hand, there's no guarantee the
|
||||
// event ended up anywhere, if it was later redacted, so we just
|
||||
// return the timeline we first thought of.
|
||||
return room.getTimelineForEvent(eventId) || timeline;
|
||||
var tl = timelineSet.getTimelineForEvent(eventId) || timeline;
|
||||
return tl;
|
||||
});
|
||||
return promise;
|
||||
};
|
||||
@@ -1665,7 +1685,7 @@ MatrixClient.prototype.getEventTimeline = function(room, eventId) {
|
||||
* @param {module:models/event-timeline~EventTimeline} eventTimeline timeline
|
||||
* object to be updated
|
||||
* @param {Object} [opts]
|
||||
* @param {boolean} [opts.backwards = false] true to fill backwards,
|
||||
* @param {bool} [opts.backwards = false] true to fill backwards,
|
||||
* false to go forwards
|
||||
* @param {number} [opts.limit = 30] number of events to request
|
||||
*
|
||||
@@ -1673,14 +1693,17 @@ MatrixClient.prototype.getEventTimeline = function(room, eventId) {
|
||||
* events and we reached either end of the timeline; else true.
|
||||
*/
|
||||
MatrixClient.prototype.paginateEventTimeline = function(eventTimeline, opts) {
|
||||
var isNotifTimeline = (eventTimeline.getTimelineSet() === this._notifTimelineSet);
|
||||
|
||||
// TODO: we should implement a backoff (as per scrollback()) to deal more
|
||||
// nicely with HTTP errors.
|
||||
opts = opts || {};
|
||||
var backwards = opts.backwards || false;
|
||||
|
||||
var room = this.getRoom(eventTimeline.getRoomId());
|
||||
if (!room) {
|
||||
throw new Error("Unknown room " + eventTimeline.getRoomId());
|
||||
if (isNotifTimeline) {
|
||||
if (!backwards) {
|
||||
throw new Error("paginateNotifTimeline can only paginate backwards");
|
||||
}
|
||||
}
|
||||
|
||||
var dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
|
||||
@@ -1698,39 +1721,131 @@ MatrixClient.prototype.paginateEventTimeline = function(eventTimeline, opts) {
|
||||
return pendingRequest;
|
||||
}
|
||||
|
||||
var path = utils.encodeUri(
|
||||
"/rooms/$roomId/messages", {$roomId: eventTimeline.getRoomId()}
|
||||
);
|
||||
var params = {
|
||||
from: token,
|
||||
limit: ('limit' in opts) ? opts.limit : 30,
|
||||
dir: dir
|
||||
};
|
||||
|
||||
var path, params, promise;
|
||||
var self = this;
|
||||
|
||||
var promise =
|
||||
this._http.authedRequest(undefined, "GET", path, params
|
||||
).then(function(res) {
|
||||
var token = res.end;
|
||||
var matrixEvents = utils.map(res.chunk, self.getEventMapper());
|
||||
room.addEventsToTimeline(matrixEvents, backwards, eventTimeline, token);
|
||||
if (isNotifTimeline) {
|
||||
path = "/notifications";
|
||||
params = {
|
||||
limit: ('limit' in opts) ? opts.limit : 30,
|
||||
only: 'highlight',
|
||||
};
|
||||
|
||||
// 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.end == res.start) {
|
||||
eventTimeline.setPaginationToken(null, dir);
|
||||
if (token && token !== "end") {
|
||||
params.from = token;
|
||||
}
|
||||
return res.end != res.start;
|
||||
}).finally(function() {
|
||||
eventTimeline._paginationRequests[dir] = null;
|
||||
});
|
||||
eventTimeline._paginationRequests[dir] = promise;
|
||||
|
||||
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(
|
||||
PushProcessor.actionListToActionsObject(notification.actions)
|
||||
);
|
||||
event.event.room_id = notification.room_id; // XXX: gutwrenching
|
||||
matrixEvents[i] = event;
|
||||
}
|
||||
|
||||
eventTimeline.getTimelineSet()
|
||||
.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;
|
||||
}
|
||||
else {
|
||||
var room = this.getRoom(eventTimeline.getRoomId());
|
||||
if (!room) {
|
||||
throw new Error("Unknown room " + eventTimeline.getRoomId());
|
||||
}
|
||||
|
||||
path = utils.encodeUri(
|
||||
"/rooms/$roomId/messages", {$roomId: eventTimeline.getRoomId()}
|
||||
);
|
||||
params = {
|
||||
from: token,
|
||||
limit: ('limit' in opts) ? opts.limit : 30,
|
||||
dir: dir
|
||||
};
|
||||
|
||||
var filter = eventTimeline.getFilter();
|
||||
if (filter) {
|
||||
// XXX: it's horrific that /messages' filter parameter doesn't match
|
||||
// /sync's one - see https://matrix.org/jira/browse/SPEC-451
|
||||
params.filter = JSON.stringify(filter.getRoomTimelineFilterComponent());
|
||||
}
|
||||
|
||||
promise =
|
||||
this._http.authedRequest(undefined, "GET", path, params
|
||||
).then(function(res) {
|
||||
var token = res.end;
|
||||
var matrixEvents = utils.map(res.chunk, self.getEventMapper());
|
||||
eventTimeline.getTimelineSet()
|
||||
.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.end == res.start) {
|
||||
eventTimeline.setPaginationToken(null, dir);
|
||||
}
|
||||
return res.end != res.start;
|
||||
}).finally(function() {
|
||||
eventTimeline._paginationRequests[dir] = null;
|
||||
});
|
||||
eventTimeline._paginationRequests[dir] = 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
|
||||
});
|
||||
*/
|
||||
};
|
||||
|
||||
/**
|
||||
* Peek into a room and receive updates about the room. This only works if the
|
||||
* history visibility for the room is world_readable.
|
||||
@@ -2242,6 +2357,54 @@ MatrixClient.prototype.getFilter = function(userId, filterId, allowCached) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} filterName
|
||||
* @param {Filter} filter
|
||||
* @return {Promise<String>} Filter ID
|
||||
*/
|
||||
MatrixClient.prototype.getOrCreateFilter = function(filterName, filter) {
|
||||
|
||||
var filterId = this.store.getFilterIdByName(filterName);
|
||||
var promise = q();
|
||||
var self = this;
|
||||
|
||||
if (filterId) {
|
||||
// check that the existing filter matches our expectations
|
||||
promise = self.getFilter(self.credentials.userId,
|
||||
filterId, true
|
||||
).then(function(existingFilter) {
|
||||
var oldDef = existingFilter.getDefinition();
|
||||
var newDef = filter.getDefinition();
|
||||
|
||||
if (utils.deepCompare(oldDef, newDef)) {
|
||||
// super, just use that.
|
||||
// debuglog("Using existing filter ID %s: %s", filterId,
|
||||
// JSON.stringify(oldDef));
|
||||
return q(filterId);
|
||||
}
|
||||
// debuglog("Existing filter ID %s: %s; new filter: %s",
|
||||
// filterId, JSON.stringify(oldDef), JSON.stringify(newDef));
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
return promise.then(function(existingId) {
|
||||
if (existingId) {
|
||||
return existingId;
|
||||
}
|
||||
|
||||
// create a new filter
|
||||
return self.createFilter(filter.getDefinition()
|
||||
).then(function(createdFilter) {
|
||||
// debuglog("Created new filter ID %s: %s", createdFilter.filterId,
|
||||
// JSON.stringify(createdFilter.getDefinition()));
|
||||
self.store.setFilterIdByName(filterName, createdFilter.filterId);
|
||||
return createdFilter.filterId;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets a bearer token from the Home Server that the user can
|
||||
* present to a third party in order to prove their ownership
|
||||
|
||||
Reference in New Issue
Block a user