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() {
super.dispose();
this._room.off("change", this._onRoomChange);
if (this._room.isArchived) {
this._room.release();
}
if (this._clearUnreadTimout) {
this._clearUnreadTimout.abort();
this._clearUnreadTimout = null;

View file

@ -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;
}

View file

@ -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.

View file

@ -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) {