From cecbcd941e33a21fd80461100b3bd9875833c628 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Thu, 1 Apr 2021 10:59:16 +0100 Subject: [PATCH 1/4] Persist unsent messages for subsequent sessions --- src/models/room.js | 68 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/src/models/room.js b/src/models/room.js index 1f19cf19833..04a8d57148f 100644 --- a/src/models/room.js +++ b/src/models/room.js @@ -189,6 +189,17 @@ export function Room(roomId, client, myUserId, opts) { if (this._opts.pendingEventOrdering == "detached") { this._pendingEventList = []; + const serializedPendingEventList = client._sessionStore.store.getItem(pendingEventsKey(this.roomId)); + if (serializedPendingEventList) { + const self = this; + JSON.parse(serializedPendingEventList) + .forEach(serializedEvent => { + const event = new MatrixEvent(serializedEvent); + event.setStatus(EventStatus.NOT_SENT); + const txnId = client.makeTxnId(); + self.addPendingEvent(event, txnId); + }); + } } // read by megolm; boolean value - null indicates "use global value" @@ -205,6 +216,14 @@ export function Room(roomId, client, myUserId, opts) { } } +/** + * @param {string} roomId ID of the current room + * @returns {string} Storage key to retrieve pending events + */ +function pendingEventsKey(roomId) { + return `mx_pending_events_${roomId}`; +} + utils.inherits(Room, EventEmitter); /** @@ -357,6 +376,39 @@ Room.prototype.getPendingEvents = function() { return this._pendingEventList; }; +/** + * Removes a pending event for this room + * + * @param {string} eventId + * @return {boolean} True if an element was removed. + */ +Room.prototype.removePendingEvent = function(eventId) { + if (this._opts.pendingEventOrdering !== "detached") { + throw new Error( + "Cannot call removePendingEvent with pendingEventOrdering == " + + this._opts.pendingEventOrdering); + } + + const removed = utils.removeElement( + this._pendingEventList, + function(ev) { + return ev.getId() == eventId; + }, false, + ); + + const { store } = this._client._sessionStore; + if (this._pendingEventList.length > 0) { + store.setItem( + pendingEventsKey(this.roomId), + JSON.stringify(this._pendingEventList), + ); + } else { + store.removeItem(pendingEventsKey(this.roomId)); + } + + return removed; +}; + /** * Check whether the pending event list contains a given event by ID. * If pending event ordering is not "detached" then this returns false. @@ -1192,7 +1244,7 @@ Room.prototype._addLiveEvent = function(event, duplicateStrategy, fromCache) { * unique transaction id. */ Room.prototype.addPendingEvent = function(event, txnId) { - if (event.status !== EventStatus.SENDING) { + if (event.status !== EventStatus.SENDING && event.status !== EventStatus.NOT_SENT) { throw new Error("addPendingEvent called on an event with status " + event.status); } @@ -1219,7 +1271,11 @@ Room.prototype.addPendingEvent = function(event, txnId) { event.setStatus(EventStatus.NOT_SENT); } this._pendingEventList.push(event); - + const { store } = this._client._sessionStore; + store.setItem( + pendingEventsKey(this.roomId), + JSON.stringify(this._pendingEventList), + ); if (event.isRelation()) { // For pending events, add them to the relations collection immediately. // (The alternate case below already covers this as part of adding to @@ -1310,12 +1366,7 @@ Room.prototype._handleRemoteEcho = function(remoteEvent, localEvent) { // if it's in the pending list, remove it if (this._pendingEventList) { - utils.removeElement( - this._pendingEventList, - function(ev) { - return ev.getId() == oldEventId; - }, false, - ); + this.removePendingEvent(oldEventId); } // replace the event source (this will preserve the plaintext payload if @@ -1421,6 +1472,7 @@ Room.prototype.updatePendingEvent = function(event, newStatus, newEventId) { for (let i = 0; i < this._timelineSets.length; i++) { this._timelineSets[i].replaceEventId(oldEventId, newEventId); } + this.removePendingEvent(event.event.event_id); } else if (newStatus == EventStatus.CANCELLED) { // remove it from the pending event list, or the timeline. if (this._pendingEventList) { From f29a24a915f805077ff85782f9b3e8972c52ddeb Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Thu, 1 Apr 2021 15:22:13 +0100 Subject: [PATCH 2/4] specify TestClient when testing room model --- spec/unit/room.spec.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/spec/unit/room.spec.js b/spec/unit/room.spec.js index 5e2f9f98473..85f72ab9ed9 100644 --- a/spec/unit/room.spec.js +++ b/spec/unit/room.spec.js @@ -3,6 +3,7 @@ import {EventStatus, MatrixEvent} from "../../src/models/event"; import {EventTimeline} from "../../src/models/event-timeline"; import {RoomState} from "../../src/models/room-state"; import {Room} from "../../src/models/room"; +import {TestClient} from "../TestClient"; describe("Room", function() { const roomId = "!foo:bar"; @@ -1176,7 +1177,10 @@ describe("Room", function() { describe("addPendingEvent", function() { it("should add pending events to the pendingEventList if " + "pendingEventOrdering == 'detached'", function() { - const room = new Room(roomId, null, userA, { + const client = (new TestClient( + "@alice:example.com", "alicedevice", + )).client; + const room = new Room(roomId, client, userA, { pendingEventOrdering: "detached", }); const eventA = utils.mkMessage({ @@ -1226,7 +1230,10 @@ describe("Room", function() { describe("updatePendingEvent", function() { it("should remove cancelled events from the pending list", function() { - const room = new Room(roomId, null, userA, { + const client = (new TestClient( + "@alice:example.com", "alicedevice", + )).client; + const room = new Room(roomId, client, userA, { pendingEventOrdering: "detached", }); const eventA = utils.mkMessage({ From 0534a4ed1ba8b14b655a068cf4290123ca0005d9 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Thu, 1 Apr 2021 17:23:55 +0100 Subject: [PATCH 3/4] prevent removePendingEvent being called when not in detached mode --- src/models/room.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/models/room.js b/src/models/room.js index 04a8d57148f..b0482706f29 100644 --- a/src/models/room.js +++ b/src/models/room.js @@ -123,6 +123,8 @@ export function Room(roomId, client, myUserId, opts) { opts = opts || {}; opts.pendingEventOrdering = opts.pendingEventOrdering || "chronological"; + this._client = client; + // In some cases, we add listeners for every displayed Matrix event, so it's // common to have quite a few more than the default limit. this.setMaxListeners(100); @@ -208,7 +210,6 @@ export function Room(roomId, client, myUserId, opts) { this._summaryHeroes = null; // awaited by getEncryptionTargetMembers while room members are loading - this._client = client; if (!this._opts.lazyLoadMembers) { this._membersPromise = Promise.resolve(); } else { @@ -1472,7 +1473,9 @@ Room.prototype.updatePendingEvent = function(event, newStatus, newEventId) { for (let i = 0; i < this._timelineSets.length; i++) { this._timelineSets[i].replaceEventId(oldEventId, newEventId); } - this.removePendingEvent(event.event.event_id); + if (this._opts.pendingEventOrdering === "detached") { + this.removePendingEvent(event.event.event_id); + } } else if (newStatus == EventStatus.CANCELLED) { // remove it from the pending event list, or the timeline. if (this._pendingEventList) { From 26ef33e4f3356fecf814c715bf2e84bc27d22b9d Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Thu, 1 Apr 2021 17:29:21 +0100 Subject: [PATCH 4/4] No this assign --- src/models/room.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/models/room.js b/src/models/room.js index b0482706f29..302d571a246 100644 --- a/src/models/room.js +++ b/src/models/room.js @@ -193,13 +193,12 @@ export function Room(roomId, client, myUserId, opts) { this._pendingEventList = []; const serializedPendingEventList = client._sessionStore.store.getItem(pendingEventsKey(this.roomId)); if (serializedPendingEventList) { - const self = this; JSON.parse(serializedPendingEventList) .forEach(serializedEvent => { const event = new MatrixEvent(serializedEvent); event.setStatus(EventStatus.NOT_SENT); const txnId = client.makeTxnId(); - self.addPendingEvent(event, txnId); + this.addPendingEvent(event, txnId); }); } }