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
Refactor the addition of events to rooms
... and add some sanity checks Two things here: 1. Clean up the Room API for adding new events to the timeline. Where before we had addEvents and addEventsToTimeline, whose purposes were unclear, we now have addLiveEvents which must be used for adding events to the end of the live timeline, and addEventsToTimeline which should be used for pagination (either back-pagination of the live timeline, or pagination of an old timeline). 2. Add some sanity checks for the live timeline. Today we have seen problems where somehow the live timeline had gained a forward pagination token, or the live timeline had got joined to another timeline, leading to much confusion - and I would like to notice these sooner.
This commit is contained in:
@@ -2131,7 +2131,7 @@ MatrixClient.prototype.scrollback = function(room, limit, callback) {
|
|||||||
return self._http.authedRequest(callback, "GET", path, params);
|
return self._http.authedRequest(callback, "GET", path, params);
|
||||||
}).done(function(res) {
|
}).done(function(res) {
|
||||||
var matrixEvents = utils.map(res.chunk, _PojoToMatrixEventMapper(self));
|
var matrixEvents = utils.map(res.chunk, _PojoToMatrixEventMapper(self));
|
||||||
room.addEventsToTimeline(matrixEvents, true);
|
room.addEventsToTimeline(matrixEvents, true, room.getLiveTimeline());
|
||||||
room.oldState.paginationToken = res.end;
|
room.oldState.paginationToken = res.end;
|
||||||
if (res.chunk.length === 0) {
|
if (res.chunk.length === 0) {
|
||||||
room.oldState.paginationToken = null;
|
room.oldState.paginationToken = null;
|
||||||
|
|||||||
@@ -26,6 +26,18 @@ var utils = require("../utils");
|
|||||||
var ContentRepo = require("../content-repo");
|
var ContentRepo = require("../content-repo");
|
||||||
var EventTimeline = require("./event-timeline");
|
var EventTimeline = require("./event-timeline");
|
||||||
|
|
||||||
|
|
||||||
|
// var DEBUG_SCROLL = false;
|
||||||
|
var DEBUG_SCROLL = true;
|
||||||
|
|
||||||
|
if (DEBUG_SCROLL) {
|
||||||
|
// 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) {
|
function synthesizeReceipt(userId, event, receiptType) {
|
||||||
// console.log("synthesizing receipt for "+event.getId());
|
// console.log("synthesizing receipt for "+event.getId());
|
||||||
// This is really ugly because JS has no way to express an object literal
|
// 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
|
* (oldest) instead of the end (newest) of the timeline. If true, the oldest
|
||||||
* event will be the <b>last</b> element of 'events'.
|
* event will be the <b>last</b> element of 'events'.
|
||||||
*
|
*
|
||||||
* @param {module:models/event-timeline~EventTimeline=} timeline timeline to
|
* @param {module:models/event-timeline~EventTimeline} timeline timeline to
|
||||||
* add events to. If not given, events will be added to the live timeline
|
* add events to.
|
||||||
*
|
*
|
||||||
* @param {string=} paginationToken token for the next batch of events
|
* @param {string=} paginationToken token for the next batch of events
|
||||||
*
|
*
|
||||||
@@ -427,13 +439,14 @@ Room.prototype.addTimeline = function() {
|
|||||||
Room.prototype.addEventsToTimeline = function(events, toStartOfTimeline,
|
Room.prototype.addEventsToTimeline = function(events, toStartOfTimeline,
|
||||||
timeline, paginationToken) {
|
timeline, paginationToken) {
|
||||||
if (!timeline) {
|
if (!timeline) {
|
||||||
timeline = this._liveTimeline;
|
throw new Error("'timeline' not specified for "+
|
||||||
|
"Room.addEventsToTimeline");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!toStartOfTimeline && timeline == this._liveTimeline) {
|
if (!toStartOfTimeline && timeline == this._liveTimeline) {
|
||||||
// special treatment for live events
|
throw new Error("Room.addEventsToTimeline cannot be used for adding " +
|
||||||
this._addLiveEvents(events);
|
"events to the live timeline - use Room.addLiveEvents " +
|
||||||
return;
|
"instead");
|
||||||
}
|
}
|
||||||
|
|
||||||
var direction = toStartOfTimeline ? EventTimeline.BACKWARDS :
|
var direction = toStartOfTimeline ? EventTimeline.BACKWARDS :
|
||||||
@@ -529,7 +542,7 @@ Room.prototype.addEventsToTimeline = function(events, toStartOfTimeline,
|
|||||||
lastEventWasNew = false;
|
lastEventWasNew = false;
|
||||||
|
|
||||||
if (existingTimeline == timeline) {
|
if (existingTimeline == timeline) {
|
||||||
console.log("Event " + eventId + " already in timeline " + timeline);
|
debuglog("Event " + eventId + " already in timeline " + timeline);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,10 +558,10 @@ Room.prototype.addEventsToTimeline = function(events, toStartOfTimeline,
|
|||||||
// that would happen, so I'm going to ignore it for now.
|
// that would happen, so I'm going to ignore it for now.
|
||||||
//
|
//
|
||||||
if (existingTimeline == neighbour) {
|
if (existingTimeline == neighbour) {
|
||||||
console.log("Event " + eventId + " in neighbouring timeline - " +
|
debuglog("Event " + eventId + " in neighbouring timeline - " +
|
||||||
"switching to " + existingTimeline);
|
"switching to " + existingTimeline);
|
||||||
} else {
|
} else {
|
||||||
console.log("Event " + eventId + " already in a different " +
|
debuglog("Event " + eventId + " already in a different " +
|
||||||
"timeline " + existingTimeline);
|
"timeline " + existingTimeline);
|
||||||
}
|
}
|
||||||
timeline = existingTimeline;
|
timeline = existingTimeline;
|
||||||
@@ -601,23 +614,23 @@ Room.prototype._addEventToTimeline = function(event, timeline, toStartOfTimeline
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add some events to the end of this room's live timeline. Will fire
|
* Add an event to the end of this room's live timeline. Will fire
|
||||||
* "Room.timeline" for each event added.
|
* "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"
|
* @fires module:client~MatrixClient#event:"Room.timeline"
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
Room.prototype._addLiveEvents = function(events) {
|
Room.prototype._addLiveEvent = function(event, duplicateStrategy) {
|
||||||
for (var i = 0; i < events.length; i++) {
|
if (event.getType() === "m.room.redaction") {
|
||||||
if (events[i].getType() === "m.room.redaction") {
|
var redactId = event.event.redacts;
|
||||||
var redactId = events[i].event.redacts;
|
|
||||||
|
|
||||||
// if we know about this event, redact its contents now.
|
// if we know about this event, redact its contents now.
|
||||||
var redactedEvent = this.findEventById(redactId);
|
var redactedEvent = this.findEventById(redactId);
|
||||||
if (redactedEvent) {
|
if (redactedEvent) {
|
||||||
redactedEvent.makeRedacted(events[i]);
|
redactedEvent.makeRedacted(event);
|
||||||
this.emit("Room.redaction", events[i], this);
|
this.emit("Room.redaction", event, this);
|
||||||
|
|
||||||
// TODO: we stash user displaynames (among other things) in
|
// TODO: we stash user displaynames (among other things) in
|
||||||
// RoomMember objects which are then attached to other events
|
// RoomMember objects which are then attached to other events
|
||||||
@@ -631,29 +644,56 @@ Room.prototype._addLiveEvents = function(events) {
|
|||||||
// this may be needed to trigger an update.
|
// this may be needed to trigger an update.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (events[i].getUnsigned().transaction_id) {
|
if (event.getUnsigned().transaction_id) {
|
||||||
var existingEvent = this._txnToEvent[events[i].getUnsigned().transaction_id];
|
var existingEvent = this._txnToEvent[event.getUnsigned().transaction_id];
|
||||||
if (existingEvent) {
|
if (existingEvent) {
|
||||||
// remote echo of an event we sent earlier
|
// remote echo of an event we sent earlier
|
||||||
this._handleRemoteEcho(events[i], existingEvent);
|
this._handleRemoteEcho(event, existingEvent);
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._eventIdToTimeline[events[i].getId()]) {
|
var timeline = this._eventIdToTimeline[event.getId()];
|
||||||
// TODO: pass through filter to see if this should be added to the timeline.
|
if (timeline) {
|
||||||
this._addEventToTimeline(events[i], this._liveTimeline, false);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debuglog("Room._addLiveEvent: ignoring duplicate event "+
|
||||||
|
event.getId());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: pass through filter to see if this should be added to the timeline.
|
||||||
|
this._addEventToTimeline(event, this._liveTimeline, false);
|
||||||
|
|
||||||
// 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
|
||||||
if (events[i].sender) {
|
if (event.sender) {
|
||||||
this.addReceipt(synthesizeReceipt(
|
this.addReceipt(synthesizeReceipt(
|
||||||
events[i].sender.userId, events[i], "m.read"
|
event.sender.userId, event, "m.read"
|
||||||
), true);
|
), true);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -855,20 +895,36 @@ Room.prototype.updatePendingEvent = function(event, newStatus, newEventId) {
|
|||||||
* Add some events to this room. This can include state events, message
|
* Add some events to this room. This can include state events, message
|
||||||
* events and typing notifications. These events are treated as "live" so
|
* events and typing notifications. These events are treated as "live" so
|
||||||
* they will go to the end of the timeline.
|
* they will go to the end of the timeline.
|
||||||
|
*
|
||||||
* @param {MatrixEvent[]} events A list of events to add.
|
* @param {MatrixEvent[]} events A list of events to add.
|
||||||
|
*
|
||||||
* @param {string} duplicateStrategy Optional. Applies to events in the
|
* @param {string} duplicateStrategy Optional. Applies to events in the
|
||||||
* timeline only. If this is not specified, no duplicate suppression is
|
* timeline only. If this is 'replace' then if a duplicate is encountered, the
|
||||||
* performed (this improves performance). If this is 'replace' then if a
|
* event passed to this function will replace the existing event in the
|
||||||
* duplicate is encountered, the event passed to this function will replace the
|
* timeline. If this is not specified, or is 'ignore', then the event passed to
|
||||||
* existing event in the timeline. If this is 'ignore', then the event passed to
|
|
||||||
* this function will be ignored entirely, preserving the existing event in the
|
* this function will be ignored entirely, preserving the existing event in the
|
||||||
* timeline. Events are identical based on their event ID <b>only</b>.
|
* timeline. Events are identical based on their event ID <b>only</b>.
|
||||||
|
*
|
||||||
* @throws If <code>duplicateStrategy</code> is not falsey, 'replace' or 'ignore'.
|
* @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) {
|
if (duplicateStrategy && ["replace", "ignore"].indexOf(duplicateStrategy) === -1) {
|
||||||
throw new Error("duplicateStrategy MUST be either 'replace' or 'ignore'");
|
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++) {
|
for (var i = 0; i < events.length; i++) {
|
||||||
if (events[i].getType() === "m.typing") {
|
if (events[i].getType() === "m.typing") {
|
||||||
this.currentState.setTypingEvent(events[i]);
|
this.currentState.setTypingEvent(events[i]);
|
||||||
@@ -879,41 +935,9 @@ Room.prototype.addEvents = function(events, duplicateStrategy) {
|
|||||||
// N.B. account_data is added directly by /sync to avoid
|
// N.B. account_data is added directly by /sync to avoid
|
||||||
// having to maintain an event.isAccountData() here
|
// having to maintain an event.isAccountData() here
|
||||||
else {
|
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
|
// TODO: We should have a filter to say "only add state event
|
||||||
// types X Y Z to the timeline".
|
// types X Y Z to the timeline".
|
||||||
this._addLiveEvents([events[i]]);
|
this._addLiveEvent(events[i], duplicateStrategy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -373,7 +373,7 @@ WebStorageStore.prototype.scrollback = function(room, limit) {
|
|||||||
);
|
);
|
||||||
room.addEventsToTimeline(utils.map(scrollback, function(e) {
|
room.addEventsToTimeline(utils.map(scrollback, function(e) {
|
||||||
return new MatrixEvent(e);
|
return new MatrixEvent(e);
|
||||||
}), true);
|
}), true, room.getLiveTimeline());
|
||||||
|
|
||||||
this._tokens[room.storageToken] = {
|
this._tokens[room.storageToken] = {
|
||||||
earliestIndex: earliestIndex
|
earliestIndex: earliestIndex
|
||||||
@@ -594,7 +594,7 @@ function loadRoom(store, roomId, numEvents, tokenArray) {
|
|||||||
index--;
|
index--;
|
||||||
}
|
}
|
||||||
// add events backwards to diverge old state correctly.
|
// add events backwards to diverge old state correctly.
|
||||||
room.addEventsToTimeline(recentEvents.reverse(), true);
|
room.addEventsToTimeline(recentEvents.reverse(), true, room.getLiveTimeline());
|
||||||
room.oldState.paginationToken = currentStateMap.pagination_token;
|
room.oldState.paginationToken = currentStateMap.pagination_token;
|
||||||
// set the token data to let us know which index this room instance is at
|
// set the token data to let us know which index this room instance is at
|
||||||
// for scrollback.
|
// for scrollback.
|
||||||
|
|||||||
@@ -262,7 +262,8 @@ SyncApi.prototype.peek = function(roomId) {
|
|||||||
// will overwrite the pagination token, so make sure it overwrites
|
// will overwrite the pagination token, so make sure it overwrites
|
||||||
// it with the right thing.
|
// it with the right thing.
|
||||||
peekRoom.addEventsToTimeline(messages.reverse(), true,
|
peekRoom.addEventsToTimeline(messages.reverse(), true,
|
||||||
undefined, response.messages.start);
|
peekRoom.getLiveTimeline(),
|
||||||
|
response.messages.start);
|
||||||
|
|
||||||
client.store.storeRoom(peekRoom);
|
client.store.storeRoom(peekRoom);
|
||||||
client.emit("Room", peekRoom);
|
client.emit("Room", peekRoom);
|
||||||
@@ -328,7 +329,7 @@ SyncApi.prototype._peekPoll = function(roomId, token) {
|
|||||||
return e.room_id === roomId;
|
return e.room_id === roomId;
|
||||||
}).map(self.client.getEventMapper());
|
}).map(self.client.getEventMapper());
|
||||||
var room = self.client.getRoom(roomId);
|
var room = self.client.getRoom(roomId);
|
||||||
room.addEvents(events);
|
room.addLiveEvents(events);
|
||||||
self._peekPoll(roomId, res.end);
|
self._peekPoll(roomId, res.end);
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
console.error("[%s] Peek poll failed: %s", roomId, err);
|
console.error("[%s] Peek poll failed: %s", roomId, err);
|
||||||
@@ -717,7 +718,7 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
|
|||||||
// XXX: should we be adding ephemeralEvents to the timeline?
|
// XXX: should we be adding ephemeralEvents to the timeline?
|
||||||
// It feels like that for symmetry with room.addAccountData()
|
// It feels like that for symmetry with room.addAccountData()
|
||||||
// there should be a room.addEphemeralEvents() or similar.
|
// there should be a room.addEphemeralEvents() or similar.
|
||||||
room.addEvents(ephemeralEvents);
|
room.addLiveEvents(ephemeralEvents);
|
||||||
|
|
||||||
// we deliberately don't add accountData to the timeline
|
// we deliberately don't add accountData to the timeline
|
||||||
room.addAccountData(accountDataEvents);
|
room.addAccountData(accountDataEvents);
|
||||||
@@ -993,7 +994,7 @@ SyncApi.prototype._processRoomEvents = function(room, stateEventList,
|
|||||||
|
|
||||||
// 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.addEventsToTimeline(timelineEventList);
|
room.addLiveEvents(timelineEventList);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ describe("MatrixClient", function() {
|
|||||||
it("should no-op if you've already joined a room", function() {
|
it("should no-op if you've already joined a room", function() {
|
||||||
var roomId = "!foo:bar";
|
var roomId = "!foo:bar";
|
||||||
var room = new Room(roomId);
|
var room = new Room(roomId);
|
||||||
room.addEvents([
|
room.addLiveEvents([
|
||||||
utils.mkMembership({
|
utils.mkMembership({
|
||||||
user: userId, room: roomId, mship: "join", event: true
|
user: userId, room: roomId, mship: "join", event: true
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ describe("Room", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("addEvents", function() {
|
describe("addLiveEvents", function() {
|
||||||
var events = [
|
var events = [
|
||||||
utils.mkMessage({
|
utils.mkMessage({
|
||||||
room: roomId, user: userA, msg: "changing room name", event: true
|
room: roomId, user: userA, msg: "changing room name", event: true
|
||||||
@@ -100,12 +100,12 @@ describe("Room", function() {
|
|||||||
user_ids: [userA]
|
user_ids: [userA]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
room.addEvents([typing]);
|
room.addLiveEvents([typing]);
|
||||||
expect(room.currentState.setTypingEvent).toHaveBeenCalledWith(typing);
|
expect(room.currentState.setTypingEvent).toHaveBeenCalledWith(typing);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw if duplicateStrategy isn't 'replace' or 'ignore'", function() {
|
it("should throw if duplicateStrategy isn't 'replace' or 'ignore'", function() {
|
||||||
expect(function() { room.addEvents(events, "foo"); }).toThrow();
|
expect(function() { room.addLiveEvents(events, "foo"); }).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should replace a timeline event if dupe strategy is 'replace'", function() {
|
it("should replace a timeline event if dupe strategy is 'replace'", function() {
|
||||||
@@ -114,9 +114,9 @@ describe("Room", function() {
|
|||||||
room: roomId, user: userA, msg: "dupe", event: true
|
room: roomId, user: userA, msg: "dupe", event: true
|
||||||
});
|
});
|
||||||
dupe.event.event_id = events[0].getId();
|
dupe.event.event_id = events[0].getId();
|
||||||
room.addEvents(events);
|
room.addLiveEvents(events);
|
||||||
expect(room.timeline[0]).toEqual(events[0]);
|
expect(room.timeline[0]).toEqual(events[0]);
|
||||||
room.addEvents([dupe], "replace");
|
room.addLiveEvents([dupe], "replace");
|
||||||
expect(room.timeline[0]).toEqual(dupe);
|
expect(room.timeline[0]).toEqual(dupe);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -126,39 +126,13 @@ describe("Room", function() {
|
|||||||
room: roomId, user: userA, msg: "dupe", event: true
|
room: roomId, user: userA, msg: "dupe", event: true
|
||||||
});
|
});
|
||||||
dupe.event.event_id = events[0].getId();
|
dupe.event.event_id = events[0].getId();
|
||||||
room.addEvents(events);
|
room.addLiveEvents(events);
|
||||||
expect(room.timeline[0]).toEqual(events[0]);
|
expect(room.timeline[0]).toEqual(events[0]);
|
||||||
room.addEvents([dupe], "ignore");
|
room.addLiveEvents([dupe], "ignore");
|
||||||
expect(room.timeline[0]).toEqual(events[0]);
|
expect(room.timeline[0]).toEqual(events[0]);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
describe("addEventsToTimeline", function() {
|
it("should emit 'Room.timeline' events",
|
||||||
var events = [
|
|
||||||
utils.mkMessage({
|
|
||||||
room: roomId, user: userA, msg: "changing room name", event: true
|
|
||||||
}),
|
|
||||||
utils.mkEvent({
|
|
||||||
type: "m.room.name", room: roomId, user: userA, event: true,
|
|
||||||
content: { name: "New Room Name" }
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
it("should be able to add events to the end", function() {
|
|
||||||
room.addEventsToTimeline(events);
|
|
||||||
expect(room.timeline.length).toEqual(2);
|
|
||||||
expect(room.timeline[0]).toEqual(events[0]);
|
|
||||||
expect(room.timeline[1]).toEqual(events[1]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should be able to add events to the start", function() {
|
|
||||||
room.addEventsToTimeline(events, true);
|
|
||||||
expect(room.timeline.length).toEqual(2);
|
|
||||||
expect(room.timeline[0]).toEqual(events[1]);
|
|
||||||
expect(room.timeline[1]).toEqual(events[0]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should emit 'Room.timeline' events when added to the end",
|
|
||||||
function() {
|
function() {
|
||||||
var callCount = 0;
|
var callCount = 0;
|
||||||
room.on("Room.timeline", function(event, emitRoom, toStart) {
|
room.on("Room.timeline", function(event, emitRoom, toStart) {
|
||||||
@@ -168,99 +142,10 @@ describe("Room", function() {
|
|||||||
expect(emitRoom).toEqual(room);
|
expect(emitRoom).toEqual(room);
|
||||||
expect(toStart).toBeFalsy();
|
expect(toStart).toBeFalsy();
|
||||||
});
|
});
|
||||||
room.addEventsToTimeline(events);
|
room.addLiveEvents(events);
|
||||||
expect(callCount).toEqual(2);
|
expect(callCount).toEqual(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should emit 'Room.timeline' events when added to the start",
|
|
||||||
function() {
|
|
||||||
var callCount = 0;
|
|
||||||
room.on("Room.timeline", function(event, emitRoom, toStart) {
|
|
||||||
callCount += 1;
|
|
||||||
expect(room.timeline.length).toEqual(callCount);
|
|
||||||
expect(event).toEqual(events[callCount - 1]);
|
|
||||||
expect(emitRoom).toEqual(room);
|
|
||||||
expect(toStart).toBe(true);
|
|
||||||
});
|
|
||||||
room.addEventsToTimeline(events, true);
|
|
||||||
expect(callCount).toEqual(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should set event.sender for new and old events", function() {
|
|
||||||
var sentinel = {
|
|
||||||
userId: userA,
|
|
||||||
membership: "join",
|
|
||||||
name: "Alice"
|
|
||||||
};
|
|
||||||
var oldSentinel = {
|
|
||||||
userId: userA,
|
|
||||||
membership: "join",
|
|
||||||
name: "Old Alice"
|
|
||||||
};
|
|
||||||
room.currentState.getSentinelMember.andCallFake(function(uid) {
|
|
||||||
if (uid === userA) {
|
|
||||||
return sentinel;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
room.oldState.getSentinelMember.andCallFake(function(uid) {
|
|
||||||
if (uid === userA) {
|
|
||||||
return oldSentinel;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
var newEv = utils.mkEvent({
|
|
||||||
type: "m.room.name", room: roomId, user: userA, event: true,
|
|
||||||
content: { name: "New Room Name" }
|
|
||||||
});
|
|
||||||
var oldEv = utils.mkEvent({
|
|
||||||
type: "m.room.name", room: roomId, user: userA, event: true,
|
|
||||||
content: { name: "Old Room Name" }
|
|
||||||
});
|
|
||||||
room.addEventsToTimeline([newEv]);
|
|
||||||
expect(newEv.sender).toEqual(sentinel);
|
|
||||||
room.addEventsToTimeline([oldEv], true);
|
|
||||||
expect(oldEv.sender).toEqual(oldSentinel);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should set event.target for new and old m.room.member events",
|
|
||||||
function() {
|
|
||||||
var sentinel = {
|
|
||||||
userId: userA,
|
|
||||||
membership: "join",
|
|
||||||
name: "Alice"
|
|
||||||
};
|
|
||||||
var oldSentinel = {
|
|
||||||
userId: userA,
|
|
||||||
membership: "join",
|
|
||||||
name: "Old Alice"
|
|
||||||
};
|
|
||||||
room.currentState.getSentinelMember.andCallFake(function(uid) {
|
|
||||||
if (uid === userA) {
|
|
||||||
return sentinel;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
room.oldState.getSentinelMember.andCallFake(function(uid) {
|
|
||||||
if (uid === userA) {
|
|
||||||
return oldSentinel;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
var newEv = utils.mkMembership({
|
|
||||||
room: roomId, mship: "invite", user: userB, skey: userA, event: true
|
|
||||||
});
|
|
||||||
var oldEv = utils.mkMembership({
|
|
||||||
room: roomId, mship: "ban", user: userB, skey: userA, event: true
|
|
||||||
});
|
|
||||||
room.addEventsToTimeline([newEv]);
|
|
||||||
expect(newEv.target).toEqual(sentinel);
|
|
||||||
room.addEventsToTimeline([oldEv], true);
|
|
||||||
expect(oldEv.target).toEqual(oldSentinel);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call setStateEvents on the right RoomState with the right " +
|
it("should call setStateEvents on the right RoomState with the right " +
|
||||||
"forwardLooking value for new events", function() {
|
"forwardLooking value for new events", function() {
|
||||||
var events = [
|
var events = [
|
||||||
@@ -274,7 +159,7 @@ describe("Room", function() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
room.addEventsToTimeline(events);
|
room.addLiveEvents(events);
|
||||||
expect(room.currentState.setStateEvents).toHaveBeenCalledWith(
|
expect(room.currentState.setStateEvents).toHaveBeenCalledWith(
|
||||||
[events[0]]
|
[events[0]]
|
||||||
);
|
);
|
||||||
@@ -286,33 +171,6 @@ describe("Room", function() {
|
|||||||
expect(room.oldState.setStateEvents).not.toHaveBeenCalled();
|
expect(room.oldState.setStateEvents).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it("should call setStateEvents on the right RoomState with the right " +
|
|
||||||
"forwardLooking value for old events", function() {
|
|
||||||
var events = [
|
|
||||||
utils.mkMembership({
|
|
||||||
room: roomId, mship: "invite", user: userB, skey: userA, event: true
|
|
||||||
}),
|
|
||||||
utils.mkEvent({
|
|
||||||
type: "m.room.name", room: roomId, user: userB, event: true,
|
|
||||||
content: {
|
|
||||||
name: "New room"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
room.addEventsToTimeline(events, true);
|
|
||||||
expect(room.oldState.setStateEvents).toHaveBeenCalledWith(
|
|
||||||
[events[0]]
|
|
||||||
);
|
|
||||||
expect(room.oldState.setStateEvents).toHaveBeenCalledWith(
|
|
||||||
[events[1]]
|
|
||||||
);
|
|
||||||
expect(events[0].forwardLooking).toBe(false);
|
|
||||||
expect(events[1].forwardLooking).toBe(false);
|
|
||||||
expect(room.currentState.setStateEvents).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should synthesize read receipts for the senders of events", function() {
|
it("should synthesize read receipts for the senders of events", function() {
|
||||||
var sentinel = {
|
var sentinel = {
|
||||||
userId: userA,
|
userId: userA,
|
||||||
@@ -325,7 +183,7 @@ describe("Room", function() {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
room.addEventsToTimeline(events);
|
room.addLiveEvents(events);
|
||||||
expect(room.getEventReadUpTo(userA)).toEqual(events[1].getId());
|
expect(room.getEventReadUpTo(userA)).toEqual(events[1].getId());
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -370,13 +228,155 @@ describe("Room", function() {
|
|||||||
expect(room.timeline.length).toEqual(1);
|
expect(room.timeline.length).toEqual(1);
|
||||||
|
|
||||||
// then the remoteEvent
|
// then the remoteEvent
|
||||||
room.addEventsToTimeline([remoteEvent]);
|
room.addLiveEvents([remoteEvent]);
|
||||||
expect(room.timeline.length).toEqual(1);
|
expect(room.timeline.length).toEqual(1);
|
||||||
|
|
||||||
expect(callCount).toEqual(2);
|
expect(callCount).toEqual(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("addEventsToTimeline", function() {
|
||||||
|
var events = [
|
||||||
|
utils.mkMessage({
|
||||||
|
room: roomId, user: userA, msg: "changing room name", event: true
|
||||||
|
}),
|
||||||
|
utils.mkEvent({
|
||||||
|
type: "m.room.name", room: roomId, user: userA, event: true,
|
||||||
|
content: { name: "New Room Name" }
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
it("should not be able to add events to the end", function() {
|
||||||
|
expect(function() {
|
||||||
|
room.addEventsToTimeline(events, false, room.getLiveTimeline())
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be able to add events to the start", function() {
|
||||||
|
room.addEventsToTimeline(events, true, room.getLiveTimeline());
|
||||||
|
expect(room.timeline.length).toEqual(2);
|
||||||
|
expect(room.timeline[0]).toEqual(events[1]);
|
||||||
|
expect(room.timeline[1]).toEqual(events[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should emit 'Room.timeline' events when added to the start",
|
||||||
|
function() {
|
||||||
|
var callCount = 0;
|
||||||
|
room.on("Room.timeline", function(event, emitRoom, toStart) {
|
||||||
|
callCount += 1;
|
||||||
|
expect(room.timeline.length).toEqual(callCount);
|
||||||
|
expect(event).toEqual(events[callCount - 1]);
|
||||||
|
expect(emitRoom).toEqual(room);
|
||||||
|
expect(toStart).toBe(true);
|
||||||
|
});
|
||||||
|
room.addEventsToTimeline(events, true, room.getLiveTimeline());
|
||||||
|
expect(callCount).toEqual(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("event metadata handling", function() {
|
||||||
|
it("should set event.sender for new and old events", function() {
|
||||||
|
var sentinel = {
|
||||||
|
userId: userA,
|
||||||
|
membership: "join",
|
||||||
|
name: "Alice"
|
||||||
|
};
|
||||||
|
var oldSentinel = {
|
||||||
|
userId: userA,
|
||||||
|
membership: "join",
|
||||||
|
name: "Old Alice"
|
||||||
|
};
|
||||||
|
room.currentState.getSentinelMember.andCallFake(function(uid) {
|
||||||
|
if (uid === userA) {
|
||||||
|
return sentinel;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
room.oldState.getSentinelMember.andCallFake(function(uid) {
|
||||||
|
if (uid === userA) {
|
||||||
|
return oldSentinel;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
var newEv = utils.mkEvent({
|
||||||
|
type: "m.room.name", room: roomId, user: userA, event: true,
|
||||||
|
content: { name: "New Room Name" }
|
||||||
|
});
|
||||||
|
var oldEv = utils.mkEvent({
|
||||||
|
type: "m.room.name", room: roomId, user: userA, event: true,
|
||||||
|
content: { name: "Old Room Name" }
|
||||||
|
});
|
||||||
|
room.addLiveEvents([newEv]);
|
||||||
|
expect(newEv.sender).toEqual(sentinel);
|
||||||
|
room.addEventsToTimeline([oldEv], true, room.getLiveTimeline());
|
||||||
|
expect(oldEv.sender).toEqual(oldSentinel);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set event.target for new and old m.room.member events",
|
||||||
|
function() {
|
||||||
|
var sentinel = {
|
||||||
|
userId: userA,
|
||||||
|
membership: "join",
|
||||||
|
name: "Alice"
|
||||||
|
};
|
||||||
|
var oldSentinel = {
|
||||||
|
userId: userA,
|
||||||
|
membership: "join",
|
||||||
|
name: "Old Alice"
|
||||||
|
};
|
||||||
|
room.currentState.getSentinelMember.andCallFake(function(uid) {
|
||||||
|
if (uid === userA) {
|
||||||
|
return sentinel;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
room.oldState.getSentinelMember.andCallFake(function(uid) {
|
||||||
|
if (uid === userA) {
|
||||||
|
return oldSentinel;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
var newEv = utils.mkMembership({
|
||||||
|
room: roomId, mship: "invite", user: userB, skey: userA, event: true
|
||||||
|
});
|
||||||
|
var oldEv = utils.mkMembership({
|
||||||
|
room: roomId, mship: "ban", user: userB, skey: userA, event: true
|
||||||
|
});
|
||||||
|
room.addLiveEvents([newEv]);
|
||||||
|
expect(newEv.target).toEqual(sentinel);
|
||||||
|
room.addEventsToTimeline([oldEv], true, room.getLiveTimeline());
|
||||||
|
expect(oldEv.target).toEqual(oldSentinel);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call setStateEvents on the right RoomState with the right " +
|
||||||
|
"forwardLooking value for old events", function() {
|
||||||
|
var events = [
|
||||||
|
utils.mkMembership({
|
||||||
|
room: roomId, mship: "invite", user: userB, skey: userA, event: true
|
||||||
|
}),
|
||||||
|
utils.mkEvent({
|
||||||
|
type: "m.room.name", room: roomId, user: userB, event: true,
|
||||||
|
content: {
|
||||||
|
name: "New room"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
room.addEventsToTimeline(events, true, room.getLiveTimeline());
|
||||||
|
expect(room.oldState.setStateEvents).toHaveBeenCalledWith(
|
||||||
|
[events[0]]
|
||||||
|
);
|
||||||
|
expect(room.oldState.setStateEvents).toHaveBeenCalledWith(
|
||||||
|
[events[1]]
|
||||||
|
);
|
||||||
|
expect(events[0].forwardLooking).toBe(false);
|
||||||
|
expect(events[1].forwardLooking).toBe(false);
|
||||||
|
expect(room.currentState.setStateEvents).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
var resetTimelineTests = function(timelineSupport) {
|
var resetTimelineTests = function(timelineSupport) {
|
||||||
var events = [
|
var events = [
|
||||||
utils.mkMessage({
|
utils.mkMessage({
|
||||||
@@ -397,11 +397,11 @@ describe("Room", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should copy state from previous timeline", function() {
|
it("should copy state from previous timeline", function() {
|
||||||
room.addEventsToTimeline([events[0], events[1]]);
|
room.addLiveEvents([events[0], events[1]]);
|
||||||
expect(room.getLiveTimeline().getEvents().length).toEqual(2);
|
expect(room.getLiveTimeline().getEvents().length).toEqual(2);
|
||||||
room.resetLiveTimeline();
|
room.resetLiveTimeline();
|
||||||
|
|
||||||
room.addEventsToTimeline([events[2]]);
|
room.addLiveEvents([events[2]]);
|
||||||
var oldState = room.getLiveTimeline().getState(EventTimeline.BACKWARDS);
|
var oldState = room.getLiveTimeline().getState(EventTimeline.BACKWARDS);
|
||||||
var newState = room.getLiveTimeline().getState(EventTimeline.FORWARDS);
|
var newState = room.getLiveTimeline().getState(EventTimeline.FORWARDS);
|
||||||
expect(room.getLiveTimeline().getEvents().length).toEqual(1);
|
expect(room.getLiveTimeline().getEvents().length).toEqual(1);
|
||||||
@@ -410,11 +410,11 @@ describe("Room", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should reset the legacy timeline fields", function() {
|
it("should reset the legacy timeline fields", function() {
|
||||||
room.addEventsToTimeline([events[0], events[1]]);
|
room.addLiveEvents([events[0], events[1]]);
|
||||||
expect(room.timeline.length).toEqual(2);
|
expect(room.timeline.length).toEqual(2);
|
||||||
room.resetLiveTimeline();
|
room.resetLiveTimeline();
|
||||||
|
|
||||||
room.addEventsToTimeline([events[2]]);
|
room.addLiveEvents([events[2]]);
|
||||||
var newLiveTimeline = room.getLiveTimeline();
|
var newLiveTimeline = room.getLiveTimeline();
|
||||||
expect(room.timeline).toEqual(newLiveTimeline.getEvents());
|
expect(room.timeline).toEqual(newLiveTimeline.getEvents());
|
||||||
expect(room.oldState).toEqual(
|
expect(room.oldState).toEqual(
|
||||||
@@ -443,7 +443,7 @@ describe("Room", function() {
|
|||||||
|
|
||||||
it("should " + (timelineSupport ? "remember" : "forget") +
|
it("should " + (timelineSupport ? "remember" : "forget") +
|
||||||
" old timelines", function() {
|
" old timelines", function() {
|
||||||
room.addEventsToTimeline([events[0]]);
|
room.addLiveEvents([events[0]]);
|
||||||
expect(room.timeline.length).toEqual(1);
|
expect(room.timeline.length).toEqual(1);
|
||||||
var firstLiveTimeline = room.getLiveTimeline();
|
var firstLiveTimeline = room.getLiveTimeline();
|
||||||
room.resetLiveTimeline();
|
room.resetLiveTimeline();
|
||||||
@@ -477,7 +477,7 @@ describe("Room", function() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
it("should handle events in the same timeline", function() {
|
it("should handle events in the same timeline", function() {
|
||||||
room.addEventsToTimeline(events);
|
room.addLiveEvents(events);
|
||||||
|
|
||||||
expect(room.compareEventOrdering(events[0].getId(),
|
expect(room.compareEventOrdering(events[0].getId(),
|
||||||
events[1].getId()))
|
events[1].getId()))
|
||||||
@@ -496,7 +496,7 @@ describe("Room", function() {
|
|||||||
room.getLiveTimeline().setNeighbouringTimeline(oldTimeline, 'b');
|
room.getLiveTimeline().setNeighbouringTimeline(oldTimeline, 'b');
|
||||||
|
|
||||||
room.addEventsToTimeline([events[0]], false, oldTimeline);
|
room.addEventsToTimeline([events[0]], false, oldTimeline);
|
||||||
room.addEventsToTimeline([events[1]]);
|
room.addLiveEvents([events[1]]);
|
||||||
|
|
||||||
expect(room.compareEventOrdering(events[0].getId(),
|
expect(room.compareEventOrdering(events[0].getId(),
|
||||||
events[1].getId()))
|
events[1].getId()))
|
||||||
@@ -510,7 +510,7 @@ describe("Room", function() {
|
|||||||
var oldTimeline = room.addTimeline();
|
var oldTimeline = room.addTimeline();
|
||||||
|
|
||||||
room.addEventsToTimeline([events[0]], false, oldTimeline);
|
room.addEventsToTimeline([events[0]], false, oldTimeline);
|
||||||
room.addEventsToTimeline([events[1]]);
|
room.addLiveEvents([events[1]]);
|
||||||
|
|
||||||
expect(room.compareEventOrdering(events[0].getId(),
|
expect(room.compareEventOrdering(events[0].getId(),
|
||||||
events[1].getId()))
|
events[1].getId()))
|
||||||
@@ -521,7 +521,7 @@ describe("Room", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should return null for unknown events", function() {
|
it("should return null for unknown events", function() {
|
||||||
room.addEventsToTimeline(events);
|
room.addLiveEvents(events);
|
||||||
|
|
||||||
expect(room.compareEventOrdering(events[0].getId(), "xxx"))
|
expect(room.compareEventOrdering(events[0].getId(), "xxx"))
|
||||||
.toBe(null);
|
.toBe(null);
|
||||||
@@ -1068,7 +1068,7 @@ describe("Room", function() {
|
|||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
room.addEventsToTimeline(events);
|
room.addLiveEvents(events);
|
||||||
var ts = 13787898424;
|
var ts = 13787898424;
|
||||||
|
|
||||||
// check it initialises correctly
|
// check it initialises correctly
|
||||||
@@ -1159,9 +1159,9 @@ describe("Room", function() {
|
|||||||
var eventC = utils.mkMessage({
|
var eventC = utils.mkMessage({
|
||||||
room: roomId, user: userA, msg: "remote 2", event: true
|
room: roomId, user: userA, msg: "remote 2", event: true
|
||||||
});
|
});
|
||||||
room.addEvents([eventA]);
|
room.addLiveEvents([eventA]);
|
||||||
room.addPendingEvent(eventB, "TXN1");
|
room.addPendingEvent(eventB, "TXN1");
|
||||||
room.addEvents([eventC]);
|
room.addLiveEvents([eventC]);
|
||||||
expect(room.timeline).toEqual(
|
expect(room.timeline).toEqual(
|
||||||
[eventA, eventC]
|
[eventA, eventC]
|
||||||
);
|
);
|
||||||
@@ -1185,9 +1185,9 @@ describe("Room", function() {
|
|||||||
var eventC = utils.mkMessage({
|
var eventC = utils.mkMessage({
|
||||||
room: roomId, user: userA, msg: "remote 2", event: true
|
room: roomId, user: userA, msg: "remote 2", event: true
|
||||||
});
|
});
|
||||||
room.addEvents([eventA]);
|
room.addLiveEvents([eventA]);
|
||||||
room.addPendingEvent(eventB, "TXN1");
|
room.addPendingEvent(eventB, "TXN1");
|
||||||
room.addEvents([eventC]);
|
room.addLiveEvents([eventC]);
|
||||||
expect(room.timeline).toEqual(
|
expect(room.timeline).toEqual(
|
||||||
[eventA, eventB, eventC]
|
[eventA, eventB, eventC]
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user