1
0
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:
Mark Haines
2016-09-15 17:10:02 +01:00
19 changed files with 1673 additions and 687 deletions

View File

@@ -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