1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-12-01 04:43:29 +03:00

Merge branch 'develop' into matthew/syjs-28

This commit is contained in:
Matthew Hodgson
2016-04-18 01:34:11 +01:00
15 changed files with 396 additions and 294 deletions

View File

@@ -26,6 +26,18 @@ var utils = require("../utils");
var ContentRepo = require("../content-repo");
var EventTimeline = require("./event-timeline");
// var DEBUG = false;
var DEBUG = true;
if (DEBUG) {
// using bind means that we get to keep useful line numbers in the console
var debuglog = console.log.bind(console);
} else {
var debuglog = function() {};
}
function synthesizeReceipt(userId, event, receiptType) {
// console.log("synthesizing receipt for "+event.getId());
// This is really ugly because JS has no way to express an object literal
@@ -416,8 +428,8 @@ Room.prototype.addTimeline = function() {
* (oldest) instead of the end (newest) of the timeline. If true, the oldest
* event will be the <b>last</b> element of 'events'.
*
* @param {module:models/event-timeline~EventTimeline=} timeline timeline to
* add events to. If not given, events will be added to the live timeline
* @param {module:models/event-timeline~EventTimeline} timeline timeline to
* add events to.
*
* @param {string=} paginationToken token for the next batch of events
*
@@ -427,13 +439,16 @@ Room.prototype.addTimeline = function() {
Room.prototype.addEventsToTimeline = function(events, toStartOfTimeline,
timeline, paginationToken) {
if (!timeline) {
timeline = this._liveTimeline;
throw new Error(
"'timeline' not specified for Room.addEventsToTimeline"
);
}
if (!toStartOfTimeline && timeline == this._liveTimeline) {
// special treatment for live events
this._addLiveEvents(events);
return;
throw new Error(
"Room.addEventsToTimeline cannot be used for adding events to " +
"the live timeline - use Room.addLiveEvents instead"
);
}
var direction = toStartOfTimeline ? EventTimeline.BACKWARDS :
@@ -529,7 +544,7 @@ Room.prototype.addEventsToTimeline = function(events, toStartOfTimeline,
lastEventWasNew = false;
if (existingTimeline == timeline) {
console.log("Event " + eventId + " already in timeline " + timeline);
debuglog("Event " + eventId + " already in timeline " + timeline);
continue;
}
@@ -545,10 +560,10 @@ Room.prototype.addEventsToTimeline = function(events, toStartOfTimeline,
// that would happen, so I'm going to ignore it for now.
//
if (existingTimeline == neighbour) {
console.log("Event " + eventId + " in neighbouring timeline - " +
debuglog("Event " + eventId + " in neighbouring timeline - " +
"switching to " + existingTimeline);
} else {
console.log("Event " + eventId + " already in a different " +
debuglog("Event " + eventId + " already in a different " +
"timeline " + existingTimeline);
}
timeline = existingTimeline;
@@ -601,69 +616,95 @@ Room.prototype._addEventToTimeline = function(event, timeline, toStartOfTimeline
/**
* Add some events to the end of this room's live timeline. Will fire
* "Room.timeline" for each event added.
* Add an event to the end of this room's live timeline. Will fire
* "Room.timeline"..
*
* @param {MatrixEvent[]} events A list of events to add.
* @param {MatrixEvent} event Event to be added
* @param {string?} duplicateStrategy 'ignore' or 'replace'
* @fires module:client~MatrixClient#event:"Room.timeline"
* @private
*/
Room.prototype._addLiveEvents = function(events) {
// var now = Date.now();
for (var i = 0; i < events.length; i++) {
if (events[i].getType() === "m.room.redaction") {
var redactId = events[i].event.redacts;
Room.prototype._addLiveEvent = function(event, duplicateStrategy) {
if (event.getType() === "m.room.redaction") {
var redactId = event.event.redacts;
// if we know about this event, redact its contents now.
var redactedEvent = this.findEventById(redactId);
if (redactedEvent) {
redactedEvent.makeRedacted(events[i]);
this.emit("Room.redaction", events[i], this);
// if we know about this event, redact its contents now.
var redactedEvent = this.findEventById(redactId);
if (redactedEvent) {
redactedEvent.makeRedacted(event);
this.emit("Room.redaction", event, this);
// TODO: we stash user displaynames (among other things) in
// RoomMember objects which are then attached to other events
// (in the sender and target fields). We should get those
// RoomMember objects to update themselves when the events that
// they are based on are changed.
// TODO: we stash user displaynames (among other things) in
// RoomMember objects which are then attached to other events
// (in the sender and target fields). We should get those
// RoomMember objects to update themselves when the events that
// they are based on are changed.
}
// NB: We continue to add the redaction event to the timeline so
// clients can say "so and so redacted an event" if they wish to. Also
// this may be needed to trigger an update.
}
if (event.getUnsigned().transaction_id) {
var existingEvent = this._txnToEvent[event.getUnsigned().transaction_id];
if (existingEvent) {
// remote echo of an event we sent earlier
this._handleRemoteEcho(event, existingEvent);
return;
}
}
var timeline = this._eventIdToTimeline[event.getId()];
if (timeline) {
if (duplicateStrategy === "replace") {
debuglog("Room._addLiveEvent: replacing duplicate event " +
event.getId());
var tlEvents = timeline.getEvents();
for (var j = 0; j < tlEvents.length; j++) {
if (tlEvents[j].getId() === event.getId()) {
// still need to set the right metadata on this event
setEventMetadata(
event,
timeline.getState(EventTimeline.FORWARDS),
false
);
if (!tlEvents[j].encryptedType) {
tlEvents[j] = event;
}
// XXX: we need to fire an event when this happens.
break;
}
}
// NB: We continue to add the redaction event to the timeline so
// clients can say "so and so redacted an event" if they wish to. Also
// this may be needed to trigger an update.
} else {
debuglog("Room._addLiveEvent: ignoring duplicate event " +
event.getId());
}
return;
}
if (events[i].getUnsigned().transaction_id) {
var existingEvent = this._txnToEvent[events[i].getUnsigned().transaction_id];
if (existingEvent) {
// remote echo of an event we sent earlier
this._handleRemoteEcho(events[i], existingEvent);
continue;
}
}
// TODO: pass through filter to see if this should be added to the timeline.
this._addEventToTimeline(event, this._liveTimeline, false);
if (!this._eventIdToTimeline[events[i].getId()]) {
// TODO: pass through filter to see if this should be added to the timeline.
this._addEventToTimeline(events[i], this._liveTimeline, false);
}
// synthesize and inject implicit read receipts
// 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
if (event.sender) {
this.addReceipt(synthesizeReceipt(
event.sender.userId, event, "m.read"
), true);
// synthesize and inject implicit read receipts
// 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
if (events[i].sender) {
this.addReceipt(synthesizeReceipt(
events[i].sender.userId, events[i], "m.read"
), true);
// also, any live events from a user should be taken as implicit
// presence information: evidence that they are currently active.
// ...except in a world where we use 'user.currentlyActive' to reduce
// presence spam, this isn't very useful - we'll get a transition when
// they are no longer currently active anyway. so comment it out for now.
// also, any live events from a user should be taken as implicit
// presence information: evidence that they are currently active.
// ...except in a world where we use 'user.currentlyActive' to reduce
// presence spam, this isn't very useful - we'll get a transition when
// they are no longer currently active anyway. so comment it out for now.
// var user = this.currentState.getMember(events[i].sender.userId);
// user.lastActiveAgo = 0;
// user.lastPresenceTs = now;
}
// var user = this.currentState.getMember(events[i].sender.userId);
// user.lastActiveAgo = 0;
// user.lastPresenceTs = Date.now();
}
};
@@ -866,20 +907,36 @@ Room.prototype.updatePendingEvent = function(event, newStatus, newEventId) {
* Add some events to this room. This can include state events, message
* events and typing notifications. These events are treated as "live" so
* they will go to the end of the timeline.
*
* @param {MatrixEvent[]} events A list of events to add.
*
* @param {string} duplicateStrategy Optional. Applies to events in the
* timeline only. If this is not specified, no duplicate suppression is
* performed (this improves performance). If this is 'replace' then if a
* duplicate is encountered, the event passed to this function will replace the
* existing event in the timeline. If this is 'ignore', then the event passed to
* timeline only. If this is 'replace' then if a duplicate is encountered, the
* event passed to this function will replace the existing event in the
* timeline. If this is not specified, or is 'ignore', then the event passed to
* this function will be ignored entirely, preserving the existing event in the
* timeline. Events are identical based on their event ID <b>only</b>.
*
* @throws If <code>duplicateStrategy</code> is not falsey, 'replace' or 'ignore'.
*/
Room.prototype.addEvents = function(events, duplicateStrategy) {
Room.prototype.addLiveEvents = function(events, duplicateStrategy) {
if (duplicateStrategy && ["replace", "ignore"].indexOf(duplicateStrategy) === -1) {
throw new Error("duplicateStrategy MUST be either 'replace' or 'ignore'");
}
// sanity check that the live timeline is still live
if (this._liveTimeline.getPaginationToken(EventTimeline.FORWARDS)) {
throw new Error(
"live timeline is no longer live - it has a pagination token (" +
this._liveTimeline.getPaginationToken(EventTimeline.FORWARDS) + ")"
);
}
if (this._liveTimeline.getNeighbouringTimeline(EventTimeline.FORWARDS)) {
throw new Error(
"live timeline is no longer live - it has a neighbouring timeline"
);
}
for (var i = 0; i < events.length; i++) {
if (events[i].getType() === "m.typing") {
this.currentState.setTypingEvent(events[i]);
@@ -890,41 +947,9 @@ Room.prototype.addEvents = function(events, duplicateStrategy) {
// N.B. account_data is added directly by /sync to avoid
// having to maintain an event.isAccountData() here
else {
var timeline = this._eventIdToTimeline[events[i].getId()];
if (timeline && duplicateStrategy) {
// is there a duplicate?
var shouldIgnore = false;
var tlEvents = timeline.getEvents();
for (var j = 0; j < tlEvents.length; j++) {
if (tlEvents[j].getId() === events[i].getId()) {
if (duplicateStrategy === "replace") {
// still need to set the right metadata on this event
setEventMetadata(
events[i],
timeline.getState(EventTimeline.FORWARDS),
false
);
if (!tlEvents[j].encryptedType) {
tlEvents[j] = events[i];
}
// skip the insert so we don't add this event twice.
// Don't break in case we replace multiple events.
shouldIgnore = true;
}
else if (duplicateStrategy === "ignore") {
shouldIgnore = true;
break; // stop searching, we're skipping the insert
}
}
}
if (shouldIgnore) {
continue; // skip the insertion of this event.
}
}
// TODO: We should have a filter to say "only add state event
// types X Y Z to the timeline".
this._addLiveEvents([events[i]]);
this._addLiveEvent(events[i], duplicateStrategy);
}
}
};