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.
This commit is contained in:
Bruno Windels 2021-05-11 16:09:58 +02:00
parent 965700272b
commit 8b8214cd1b
4 changed files with 38 additions and 6 deletions

View file

@ -81,6 +81,9 @@ export class RoomViewModel extends ViewModel {
dispose() { dispose() {
super.dispose(); super.dispose();
this._room.off("change", this._onRoomChange); this._room.off("change", this._onRoomChange);
if (this._room.isArchived) {
this._room.release();
}
if (this._clearUnreadTimout) { if (this._clearUnreadTimout) {
this._clearUnreadTimout.abort(); this._clearUnreadTimout.abort();
this._clearUnreadTimout = null; this._clearUnreadTimout = null;

View file

@ -56,6 +56,7 @@ export class Session {
this._sessionInfo = sessionInfo; this._sessionInfo = sessionInfo;
this._rooms = new ObservableMap(); this._rooms = new ObservableMap();
this._roomUpdateCallback = (room, params) => this._rooms.update(room.id, params); this._roomUpdateCallback = (room, params) => this._rooms.update(room.id, params);
this._activeArchivedRooms = new Map();
this._invites = new ObservableMap(); this._invites = new ObservableMap();
this._inviteUpdateCallback = (invite, params) => this._invites.update(invite.id, params); this._inviteUpdateCallback = (invite, params) => this._invites.update(invite.id, params);
this._user = new User(sessionInfo.userId); this._user = new User(sessionInfo.userId);
@ -399,18 +400,21 @@ export class Session {
} }
/** @internal */ /** @internal */
createArchivedRoom(roomId) { _createArchivedRoom(roomId) {
return new ArchivedRoom({ const room = new ArchivedRoom({
roomId, roomId,
getSyncToken: this._getSyncToken, getSyncToken: this._getSyncToken,
storage: this._storage, storage: this._storage,
emitCollectionChange: () => {}, emitCollectionChange: () => {},
releaseCallback: () => this._activeArchivedRooms.delete(roomId),
hsApi: this._hsApi, hsApi: this._hsApi,
mediaRepository: this._mediaRepository, mediaRepository: this._mediaRepository,
user: this._user, user: this._user,
createRoomEncryption: this._createRoomEncryption, createRoomEncryption: this._createRoomEncryption,
platform: this._platform platform: this._platform
}); });
this._activeArchivedRooms.set(roomId, room);
return room;
} }
get invites() { get invites() {
@ -641,6 +645,8 @@ export class Session {
return RoomStatus.joined; return RoomStatus.joined;
} else { } else {
const isInvited = !!this._invites.get(roomId); 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) { if (isInvited && isArchived) {
return RoomStatus.invitedAndArchived; return RoomStatus.invitedAndArchived;
} else if (isInvited) { } else if (isInvited) {
@ -668,13 +674,18 @@ export class Session {
loadArchivedRoom(roomId, log = null) { loadArchivedRoom(roomId, log = null) {
return this._platform.logger.wrapOrRun(log, "loadArchivedRoom", async log => { return this._platform.logger.wrapOrRun(log, "loadArchivedRoom", async log => {
log.set("id", roomId); log.set("id", roomId);
const activeArchivedRoom = this._activeArchivedRooms.get(roomId);
if (activeArchivedRoom) {
activeArchivedRoom.retain();
return activeArchivedRoom;
}
const txn = await this._storage.readTxn([ const txn = await this._storage.readTxn([
this._storage.storeNames.archivedRoomSummary, this._storage.storeNames.archivedRoomSummary,
this._storage.storeNames.roomMembers, this._storage.storeNames.roomMembers,
]); ]);
const summary = await txn.archivedRoomSummary.get(roomId); const summary = await txn.archivedRoomSummary.get(roomId);
if (summary) { if (summary) {
const room = this.createArchivedRoom(roomId); const room = this._createArchivedRoom(roomId);
await room.load(summary, txn, log); await room.load(summary, txn, log);
return room; return room;
} }

View file

@ -309,7 +309,10 @@ export class Sync {
_afterSync(sessionState, inviteStates, roomStates, archivedRoomStates, log) { _afterSync(sessionState, inviteStates, roomStates, archivedRoomStates, log) {
log.wrap("session", log => this._session.afterSync(sessionState.changes, log), log.level.Detail); log.wrap("session", log => this._session.afterSync(sessionState.changes, log), log.level.Detail);
for(let ars of archivedRoomStates) { 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) { for(let rs of roomStates) {
log.wrap("room", log => rs.room.afterSync(rs.changes, log), log.level.Detail); 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, // when adding a joined room during incremental sync,
// always create the archived room to write the removal // always create the archived room to write the removal
// of the archived summary // of the archived summary
archivedRoom = this._session.createArchivedRoom(roomId); archivedRoom = await this._session.loadArchivedRoom(roomId, log);
} else if (membership === "leave") { } else if (membership === "leave") {
if (roomState) { if (roomState) {
// we still have a roomState, so we just left it // we still have a roomState, so we just left it
// in this case, create a new archivedRoom // in this case, create a new archivedRoom
archivedRoom = this._session.createArchivedRoom(roomId); archivedRoom = await this._session.loadArchivedRoom(roomId, log);
} else { } else {
// this is an update of an already left room, restore // this is an update of an already left room, restore
// it from storage first, so we can increment it. // it from storage first, so we can increment it.

View file

@ -21,6 +21,10 @@ import {RoomMember} from "./members/RoomMember.js";
export class ArchivedRoom extends BaseRoom { export class ArchivedRoom extends BaseRoom {
constructor(options) { constructor(options) {
super(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. 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. 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; this._kickedBy = null;
} }
retain() {
this._retentionCount += 1;
}
release() {
this._retentionCount -= 1;
if (this._retentionCount === 0) {
this._releaseCallback();
}
}
async _getKickAuthor(sender, txn) { async _getKickAuthor(sender, txn) {
const senderMember = await txn.roomMembers.get(this.id, sender); const senderMember = await txn.roomMembers.get(this.id, sender);
if (senderMember) { if (senderMember) {