diff --git a/lib/models/event-timeline-set.js b/lib/models/event-timeline-set.js index 4b9a80e4d..83339aa22 100644 --- a/lib/models/event-timeline-set.js +++ b/lib/models/event-timeline-set.js @@ -53,14 +53,12 @@ if (DEBUG) { * map from event_id to timeline and index. * * @constructor - * @param {?String} roomId the roomId of this timelineSet's room, if any * @param {?Room} room the optional room for this timelineSet * @param {Object} opts hash of options inherited from Room. * opts.timelineSupport gives whether timeline support is enabled * opts.filter is the filter object, if any, for this timelineSet. */ -function EventTimelineSet(roomId, room, opts) { - this.roomId = roomId; +function EventTimelineSet(room, opts) { this.room = room; this._timelineSupport = Boolean(opts.timelineSupport); @@ -475,7 +473,6 @@ EventTimelineSet.prototype.addEventToTimeline = function(event, timeline, var data = { timeline: timeline, liveEvent: !toStartOfTimeline && timeline == this._liveTimeline, - timelineSet: this, }; this.emit("Room.timeline", event, this.room, Boolean(toStartOfTimeline), false, data); @@ -483,7 +480,7 @@ EventTimelineSet.prototype.addEventToTimeline = function(event, timeline, /** * Replaces event with ID oldEventId with one with newEventId, if oldEventId is - * recognised. Otherwise, add to the live timeline. + * recognised. Otherwise, add to the live timeline. Used to handle remote echos. * * @param {MatrixEvent} localEvent the new event to be added to the timeline * @param {String} oldEventId the ID of the original event @@ -491,7 +488,7 @@ EventTimelineSet.prototype.addEventToTimeline = function(event, timeline, * * @fires module:client~MatrixClient#event:"Room.timeline" */ -EventTimelineSet.prototype.replaceOrAddEvent = function(localEvent, oldEventId, +EventTimelineSet.prototype.handleRemoteEcho = function(localEvent, oldEventId, newEventId) { // XXX: why don't we infer newEventId from localEvent? var existingTimeline = this._eventIdToTimeline[oldEventId]; @@ -529,7 +526,6 @@ EventTimelineSet.prototype.removeEvent = function(eventId) { delete this._eventIdToTimeline[eventId]; var data = { timeline: timeline, - timelineSet: this, }; this.emit("Room.timeline", removed, this.room, undefined, true, data); } diff --git a/lib/models/event-timeline.js b/lib/models/event-timeline.js index 40d52580d..343b3fbed 100644 --- a/lib/models/event-timeline.js +++ b/lib/models/event-timeline.js @@ -30,7 +30,7 @@ var MatrixEvent = require("./event").MatrixEvent; */ function EventTimeline(eventTimelineSet) { this._eventTimelineSet = eventTimelineSet; - this._roomId = eventTimelineSet.roomId; + this._roomId = eventTimelineSet.room ? eventTimelineSet.room.roomId : null; this._events = []; this._baseIndex = 0; this._startState = new RoomState(this._roomId); diff --git a/lib/models/room-state.js b/lib/models/room-state.js index 1e22b0335..32b3a924e 100644 --- a/lib/models/room-state.js +++ b/lib/models/room-state.js @@ -25,7 +25,8 @@ var RoomMember = require("./room-member"); /** * Construct room state. * @constructor - * @param {string} roomId Required. The ID of the room which has this state. + * @param {?string} roomId Optional. The ID of the room which has this state. + * If none is specified it just tracks paginationTokens, useful for notifTimelineSet * @prop {Object.} members The room member dictionary, keyed * on the user's ID. * @prop {Object.>} events The state diff --git a/lib/models/room.js b/lib/models/room.js index 621ac8493..0a38df71b 100644 --- a/lib/models/room.js +++ b/lib/models/room.js @@ -151,7 +151,7 @@ function Room(roomId, opts) { // all our per-room timeline sets. the first one is the unfiltered ones; // the subsequent ones are the filtered ones in no particular order. - this._timelineSets = [new EventTimelineSet(roomId, this, opts)]; + this._timelineSets = [new EventTimelineSet(this, opts)]; reEmit(this, this.getUnfilteredTimelineSet(), ["Room.timeline"]); this._fixUpLegacyTimelineFields(); @@ -465,7 +465,7 @@ Room.prototype.getOrCreateFilteredTimelineSet = function(filter) { return this._filteredTimelineSets[filter.filterId]; } var opts = Object.assign({ filter: filter }, this._opts); - var timelineSet = new EventTimelineSet(this.roomId, this, opts); + var timelineSet = new EventTimelineSet(this, opts); reEmit(this, timelineSet, ["Room.timeline"]); this._filteredTimelineSets[filter.filterId] = timelineSet; this._timelineSets.push(timelineSet); @@ -483,8 +483,14 @@ Room.prototype.getOrCreateFilteredTimelineSet = function(filter) { timelineSet.addLiveEvent(event); }); + // find the earliest unfiltered timeline + var timeline = unfilteredLiveTimeline; + while (timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS)) { + timeline = timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS); + } + timelineSet.getLiveTimeline().setPaginationToken( - unfilteredLiveTimeline.getPaginationToken(EventTimeline.BACKWARDS), + timeline.getPaginationToken(EventTimeline.BACKWARDS), EventTimeline.BACKWARDS ); @@ -527,25 +533,21 @@ Room.prototype._addLiveEvent = function(event, duplicateStrategy) { if (event.getType() === "m.room.redaction") { var redactId = event.event.redacts; - for (i = 0; i < this._timelineSets.length; i++) { - var timelineSet = this._timelineSets[i]; - // if we know about this event, redact its contents now. - var redactedEvent = timelineSet.findEventById(redactId); - if (redactedEvent) { - redactedEvent.makeRedacted(event); - // FIXME: these should be emitted from EventTimelineSet probably - this.emit("Room.redaction", event, this, timelineSet); + // if we know about this event, redact its contents now. + var redactedEvent = this.getUnfilteredTimelineSet().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. - } - - // FIXME: apply redactions to notification list + // 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. } + // FIXME: apply redactions to notification list + // 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. @@ -687,7 +689,7 @@ Room.prototype._handleRemoteEcho = function(remoteEvent, localEvent) { var timelineSet = this._timelineSets[i]; // if it's already in the timeline, update the timeline map. If it's not, add it. - timelineSet.replaceOrAddEvent(localEvent, oldEventId, newEventId); + timelineSet.handleRemoteEcho(localEvent, oldEventId, newEventId); } this.emit("Room.localEchoUpdated", localEvent, this, @@ -865,11 +867,10 @@ Room.prototype.removeEvents = function(event_ids) { * * @param {String} eventId The id of the event to remove * - * @return {?MatrixEvent} the removed event, or null if the event was not found - * in this room. + * @return {bool} true if the event was removed from any of the room's timeline sets */ Room.prototype.removeEvent = function(eventId) { - var removedAny; + var removedAny = false; for (var i = 0; i < this._timelineSets.length; i++) { var removed = this._timelineSets[i].removeEvent(eventId); if (removed) { diff --git a/spec/unit/event-timeline.spec.js b/spec/unit/event-timeline.spec.js index 25b748648..f1a8f0109 100644 --- a/spec/unit/event-timeline.spec.js +++ b/spec/unit/event-timeline.spec.js @@ -16,7 +16,9 @@ describe("EventTimeline", function() { beforeEach(function() { utils.beforeEach(this); - timeline = new EventTimeline({ roomId: roomId }); + // XXX: this is a horrid hack; should use sinon or something instead + var timelineSet = { room: { roomId: roomId }}; + timeline = new EventTimeline(timelineSet); }); describe("construction", function() { diff --git a/spec/unit/timeline-window.spec.js b/spec/unit/timeline-window.spec.js index ce87f13c0..f0f101ede 100644 --- a/spec/unit/timeline-window.spec.js +++ b/spec/unit/timeline-window.spec.js @@ -18,7 +18,9 @@ function createTimeline(numEvents, baseIndex) { if (numEvents === undefined) { numEvents = 3; } if (baseIndex === undefined) { baseIndex = 1; } - var timeline = new EventTimeline({ roomId: ROOM_ID }); + // XXX: this is a horrid hack + var timelineSet = { room: { roomId: ROOM_ID }}; + var timeline = new EventTimeline(timelineSet); // add the events after the baseIndex first addEventsToTimeline(timeline, numEvents - baseIndex, false);