From 8a976ef24b3444b0ce2c73fac84bb538eecea588 Mon Sep 17 00:00:00 2001 From: RMidhunSuresh Date: Wed, 14 Jul 2021 12:59:44 +0530 Subject: [PATCH] Make powerLevels observable Signed-off-by: RMidhunSuresh --- .../session/rightpanel/MemberListViewModel.js | 4 +- .../session/rightpanel/RightPanelViewModel.js | 3 +- src/matrix/room/BaseRoom.js | 44 ++++++++++++++++++- src/matrix/room/timeline/Timeline.js | 29 ++---------- 4 files changed, 50 insertions(+), 30 deletions(-) diff --git a/src/domain/session/rightpanel/MemberListViewModel.js b/src/domain/session/rightpanel/MemberListViewModel.js index 0a6c4add..bc54ad3e 100644 --- a/src/domain/session/rightpanel/MemberListViewModel.js +++ b/src/domain/session/rightpanel/MemberListViewModel.js @@ -6,8 +6,10 @@ import {Disambiguator} from "./disambiguator.js"; export class MemberListViewModel extends ViewModel { constructor(options) { super(options); + const powerLevels = options.powerLevelsObservable.get(); + // We should subscribe to the observable here so that we can resort when pl changes this.memberTileViewModels = this._mapTileViewModels(this._filterJoinedMembers(options.members)) - .sortValues(createMemberComparator(options.powerLevels)); + .sortValues(createMemberComparator(powerLevels)); this.nameDisambiguator = new Disambiguator(); this.mediaRepository = options.mediaRepository; } diff --git a/src/domain/session/rightpanel/RightPanelViewModel.js b/src/domain/session/rightpanel/RightPanelViewModel.js index 9486c042..29755e8d 100644 --- a/src/domain/session/rightpanel/RightPanelViewModel.js +++ b/src/domain/session/rightpanel/RightPanelViewModel.js @@ -14,7 +14,8 @@ export class RightPanelViewModel extends ViewModel { async _getMemberArguments() { const list = await this._room.loadMemberList(); const room = this._room; - return {members: list.members, powerLevels: room.powerLevels, mediaRepository: room.mediaRepository}; + const powerLevelsObservable = await this._room.observePowerLevels(); + return {members: list.members, powerLevelsObservable, mediaRepository: room.mediaRepository}; } _setupNavigation() { diff --git a/src/matrix/room/BaseRoom.js b/src/matrix/room/BaseRoom.js index ac5fa59e..ddeb063b 100644 --- a/src/matrix/room/BaseRoom.js +++ b/src/matrix/room/BaseRoom.js @@ -28,6 +28,9 @@ import {EventEntry} from "./timeline/entries/EventEntry.js"; import {ObservedEventMap} from "./ObservedEventMap.js"; import {DecryptionSource} from "../e2ee/common.js"; import {ensureLogItem} from "../../logging/utils.js"; +import {TimelineReader} from "./timeline/persistence/TimelineReader.js"; +import {PowerLevels} from "./timeline/PowerLevels.js"; +import {RetainedObservableValue} from "../../observable/ObservableValue.js"; const EVENT_ENCRYPTED_TYPE = "m.room.encrypted"; @@ -388,8 +391,44 @@ export class BaseRoom extends EventEmitter { return this._summary.data.membership; } - get powerLevels() { - return this._timeline.powerLevels; + async loadPowerLevels() { + const timelineReader = new TimelineReader({ + roomId: this._roomId, + storage: this._storage, + fragmentIdComparer: this._fragmentIdComparer + }); + const txn = await this._storage.readTxn( + timelineReader.readTxnStores.concat(this._storage.storeNames.roomMembers, this._storage.storeNames.roomState) + ); + const powerLevelsState = await txn.roomState.get(this._roomId, "m.room.power_levels", ""); + if (powerLevelsState) { + return new PowerLevels({ + powerLevelEvent: powerLevelsState.event, + ownUserId: this._user.id, + membership: this.membership + }); + } + const createState = await txn.roomState.get(this._roomId, "m.room.create", ""); + if (createState) { + return new PowerLevels({ + createEvent: createState.event, + ownUserId: this._user.id, + membership: this.membership + }); + } else { + const membership = this.membership; + return new PowerLevels({ownUserId: this._user.id, membership}); + } + } + + async observePowerLevels() { + let observable = this._powerLevels; + if (!observable) { + const powerLevels = await this.loadPowerLevels(); + observable = new RetainedObservableValue(powerLevels, () => { this._powerLevels = null; }); + this._powerLevels = observable; + } + return observable; } enableSessionBackup(sessionBackup) { @@ -433,6 +472,7 @@ export class BaseRoom extends EventEmitter { }, clock: this._platform.clock, logger: this._platform.logger, + powerLevelsObservable: await this.observePowerLevels() }); try { if (this._roomEncryption) { diff --git a/src/matrix/room/timeline/Timeline.js b/src/matrix/room/timeline/Timeline.js index 3d82a284..11a49f55 100644 --- a/src/matrix/room/timeline/Timeline.js +++ b/src/matrix/room/timeline/Timeline.js @@ -21,12 +21,11 @@ import {Direction} from "./Direction.js"; import {TimelineReader} from "./persistence/TimelineReader.js"; import {PendingEventEntry} from "./entries/PendingEventEntry.js"; import {RoomMember} from "../members/RoomMember.js"; -import {PowerLevels} from "./PowerLevels.js"; import {getRelation, ANNOTATION_RELATION_TYPE} from "./relations.js"; import {REDACTION_TYPE} from "../common.js"; export class Timeline { - constructor({roomId, storage, closeCallback, fragmentIdComparer, pendingEvents, clock}) { + constructor({roomId, storage, closeCallback, fragmentIdComparer, pendingEvents, clock, powerLevelsObservable}) { this._roomId = roomId; this._storage = storage; this._closeCallback = closeCallback; @@ -44,7 +43,8 @@ export class Timeline { }); this._readerRequest = null; this._allEntries = null; - this._powerLevels = null; + this._powerLevels = powerLevelsObservable.get(); + this._disposables.track(powerLevelsObservable.subscribe(powerLevels => this._powerLevels = powerLevels)); } /** @package */ @@ -66,7 +66,6 @@ export class Timeline { // as they should only populate once the view subscribes to it // if they are populated already, the sender profile would be empty - this._powerLevels = await this._loadPowerLevels(membership, txn); // 30 seems to be a good amount to fill the entire screen const readerRequest = this._disposables.track(this._timelineReader.readFromEnd(30, txn, log)); try { @@ -78,28 +77,6 @@ export class Timeline { // txn should be assumed to have finished here, as decryption will close it. } - async _loadPowerLevels(membership, txn) { - // TODO: update power levels as state is updated - const powerLevelsState = await txn.roomState.get(this._roomId, "m.room.power_levels", ""); - if (powerLevelsState) { - return new PowerLevels({ - powerLevelEvent: powerLevelsState.event, - ownUserId: this._ownMember.userId, - membership - }); - } - const createState = await txn.roomState.get(this._roomId, "m.room.create", ""); - if (createState) { - return new PowerLevels({ - createEvent: createState.event, - ownUserId: this._ownMember.userId, - membership - }); - } else { - return new PowerLevels({ownUserId: this._ownMember.userId, membership}); - } - } - _setupEntries(timelineEntries) { this._remoteEntries.setManySorted(timelineEntries); if (this._pendingEvents) {