From 8b8214cd1b7c67f534641b2d78953f280462e6a2 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 11 May 2021 16:09:58 +0200 Subject: [PATCH] reference count archived rooms and keep track of active ones so we don't create two instances for the same id, one for sync, and one for displaying, and hence updates from sync being pushed on a different instance than the one displaying, and not updating the view. --- src/domain/session/room/RoomViewModel.js | 3 +++ src/matrix/Session.js | 17 ++++++++++++++--- src/matrix/Sync.js | 9 ++++++--- src/matrix/room/ArchivedRoom.js | 15 +++++++++++++++ 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/domain/session/room/RoomViewModel.js b/src/domain/session/room/RoomViewModel.js index 9c0dfd65..7e8591af 100644 --- a/src/domain/session/room/RoomViewModel.js +++ b/src/domain/session/room/RoomViewModel.js @@ -81,6 +81,9 @@ export class RoomViewModel extends ViewModel { dispose() { super.dispose(); this._room.off("change", this._onRoomChange); + if (this._room.isArchived) { + this._room.release(); + } if (this._clearUnreadTimout) { this._clearUnreadTimout.abort(); this._clearUnreadTimout = null; diff --git a/src/matrix/Session.js b/src/matrix/Session.js index 20f303fb..527b7e6c 100644 --- a/src/matrix/Session.js +++ b/src/matrix/Session.js @@ -56,6 +56,7 @@ export class Session { this._sessionInfo = sessionInfo; this._rooms = new ObservableMap(); this._roomUpdateCallback = (room, params) => this._rooms.update(room.id, params); + this._activeArchivedRooms = new Map(); this._invites = new ObservableMap(); this._inviteUpdateCallback = (invite, params) => this._invites.update(invite.id, params); this._user = new User(sessionInfo.userId); @@ -399,18 +400,21 @@ export class Session { } /** @internal */ - createArchivedRoom(roomId) { - return new ArchivedRoom({ + _createArchivedRoom(roomId) { + const room = new ArchivedRoom({ roomId, getSyncToken: this._getSyncToken, storage: this._storage, emitCollectionChange: () => {}, + releaseCallback: () => this._activeArchivedRooms.delete(roomId), hsApi: this._hsApi, mediaRepository: this._mediaRepository, user: this._user, createRoomEncryption: this._createRoomEncryption, platform: this._platform }); + this._activeArchivedRooms.set(roomId, room); + return room; } get invites() { @@ -641,6 +645,8 @@ export class Session { return RoomStatus.joined; } else { const isInvited = !!this._invites.get(roomId); + const txn = await this._storage.readTxn([this._storage.storeNames.archivedRoomSummary]); + const isArchived = await txn.archivedRoomSummary.has(roomId); if (isInvited && isArchived) { return RoomStatus.invitedAndArchived; } else if (isInvited) { @@ -668,13 +674,18 @@ export class Session { loadArchivedRoom(roomId, log = null) { return this._platform.logger.wrapOrRun(log, "loadArchivedRoom", async log => { log.set("id", roomId); + const activeArchivedRoom = this._activeArchivedRooms.get(roomId); + if (activeArchivedRoom) { + activeArchivedRoom.retain(); + return activeArchivedRoom; + } const txn = await this._storage.readTxn([ this._storage.storeNames.archivedRoomSummary, this._storage.storeNames.roomMembers, ]); const summary = await txn.archivedRoomSummary.get(roomId); if (summary) { - const room = this.createArchivedRoom(roomId); + const room = this._createArchivedRoom(roomId); await room.load(summary, txn, log); return room; } diff --git a/src/matrix/Sync.js b/src/matrix/Sync.js index cde134e4..ac0da4b2 100644 --- a/src/matrix/Sync.js +++ b/src/matrix/Sync.js @@ -309,7 +309,10 @@ export class Sync { _afterSync(sessionState, inviteStates, roomStates, archivedRoomStates, log) { log.wrap("session", log => this._session.afterSync(sessionState.changes, log), log.level.Detail); for(let ars of archivedRoomStates) { - log.wrap("archivedRoom", log => ars.archivedRoom.afterSync(ars.changes, log), log.level.Detail); + log.wrap("archivedRoom", log => { + ars.archivedRoom.afterSync(ars.changes, log); + ars.archivedRoom.release(); + }, log.level.Detail); } for(let rs of roomStates) { log.wrap("room", log => rs.room.afterSync(rs.changes, log), log.level.Detail); @@ -407,12 +410,12 @@ export class Sync { // when adding a joined room during incremental sync, // always create the archived room to write the removal // of the archived summary - archivedRoom = this._session.createArchivedRoom(roomId); + archivedRoom = await this._session.loadArchivedRoom(roomId, log); } else if (membership === "leave") { if (roomState) { // we still have a roomState, so we just left it // in this case, create a new archivedRoom - archivedRoom = this._session.createArchivedRoom(roomId); + archivedRoom = await this._session.loadArchivedRoom(roomId, log); } else { // this is an update of an already left room, restore // it from storage first, so we can increment it. diff --git a/src/matrix/room/ArchivedRoom.js b/src/matrix/room/ArchivedRoom.js index a7f6525e..fa95270b 100644 --- a/src/matrix/room/ArchivedRoom.js +++ b/src/matrix/room/ArchivedRoom.js @@ -21,6 +21,10 @@ import {RoomMember} from "./members/RoomMember.js"; export class ArchivedRoom extends BaseRoom { constructor(options) { super(options); + // archived rooms are reference counted, + // as they are not kept in memory when not needed + this._releaseCallback = options.releaseCallback; + this._retentionCount = 1; /** Some details from our own member event when being kicked or banned. We can't get this from the member store, because we don't store the reason field there. @@ -29,6 +33,17 @@ export class ArchivedRoom extends BaseRoom { this._kickedBy = null; } + retain() { + this._retentionCount += 1; + } + + release() { + this._retentionCount -= 1; + if (this._retentionCount === 0) { + this._releaseCallback(); + } + } + async _getKickAuthor(sender, txn) { const senderMember = await txn.roomMembers.get(this.id, sender); if (senderMember) {