From 32cc48ff7a714fab5ef802cf4e94358475ebe73b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 16 Jul 2021 08:49:19 +0100 Subject: [PATCH 1/3] Fix issue with room duplication caused by filtering and selecting room using keyboard Wrap sticky room updates in lock to prevent setStickyRoom running in middle of setKnownRooms --- src/stores/room-list/algorithms/Algorithm.ts | 145 ++++++++++--------- 1 file changed, 79 insertions(+), 66 deletions(-) diff --git a/src/stores/room-list/algorithms/Algorithm.ts b/src/stores/room-list/algorithms/Algorithm.ts index f50d112248b..2acce1ecd76 100644 --- a/src/stores/room-list/algorithms/Algorithm.ts +++ b/src/stores/room-list/algorithms/Algorithm.ts @@ -16,8 +16,10 @@ limitations under the License. import { Room } from "matrix-js-sdk/src/models/room"; import { isNullOrUndefined } from "matrix-js-sdk/src/utils"; -import DMRoomMap from "../../../utils/DMRoomMap"; import { EventEmitter } from "events"; +import AwaitLock from "await-lock"; + +import DMRoomMap from "../../../utils/DMRoomMap"; import { arrayDiff, arrayHasDiff } from "../../../utils/arrays"; import { DefaultTagID, RoomUpdateCause, TagID } from "../models"; import { @@ -78,6 +80,7 @@ export class Algorithm extends EventEmitter { } = {}; private allowedByFilter: Map = new Map(); private allowedRoomsByFilters: Set = new Set(); + private stickyLock = new AwaitLock(); /** * Set to true to suspend emissions of algorithm updates. @@ -123,7 +126,12 @@ export class Algorithm extends EventEmitter { * @param val The new room to sticky. */ public async setStickyRoom(val: Room) { - await this.updateStickyRoom(val); + await this.stickyLock.acquireAsync(); + try { + await this.updateStickyRoom(val); + } finally { + this.stickyLock.release(); + } } public getTagSorting(tagId: TagID): SortAlgorithm { @@ -519,82 +527,87 @@ export class Algorithm extends EventEmitter { if (isNullOrUndefined(rooms)) throw new Error(`Array of rooms cannot be null`); if (!this.sortAlgorithms) throw new Error(`Cannot set known rooms without a tag sorting map`); - if (!this.updatesInhibited) { - // We only log this if we're expecting to be publishing updates, which means that - // this could be an unexpected invocation. If we're inhibited, then this is probably - // an intentional invocation. - console.warn("Resetting known rooms, initiating regeneration"); - } - - // Before we go any further we need to clear (but remember) the sticky room to - // avoid accidentally duplicating it in the list. - const oldStickyRoom = this._stickyRoom; - await this.updateStickyRoom(null); + await this.stickyLock.acquireAsync(); + try { + if (!this.updatesInhibited) { + // We only log this if we're expecting to be publishing updates, which means that + // this could be an unexpected invocation. If we're inhibited, then this is probably + // an intentional invocation. + console.warn("Resetting known rooms, initiating regeneration"); + } - this.rooms = rooms; + // Before we go any further we need to clear (but remember) the sticky room to + // avoid accidentally duplicating it in the list. + const oldStickyRoom = this._stickyRoom; + if (oldStickyRoom) await this.updateStickyRoom(null); - const newTags: ITagMap = {}; - for (const tagId in this.sortAlgorithms) { - // noinspection JSUnfilteredForInLoop - newTags[tagId] = []; - } + this.rooms = rooms; - // If we can avoid doing work, do so. - if (!rooms.length) { - await this.generateFreshTags(newTags); // just in case it wants to do something - this.cachedRooms = newTags; - return; - } + const newTags: ITagMap = {}; + for (const tagId in this.sortAlgorithms) { + // noinspection JSUnfilteredForInLoop + newTags[tagId] = []; + } - // Split out the easy rooms first (leave and invite) - const memberships = splitRoomsByMembership(rooms); - for (const room of memberships[EffectiveMembership.Invite]) { - newTags[DefaultTagID.Invite].push(room); - } - for (const room of memberships[EffectiveMembership.Leave]) { - newTags[DefaultTagID.Archived].push(room); - } + // If we can avoid doing work, do so. + if (!rooms.length) { + await this.generateFreshTags(newTags); // just in case it wants to do something + this.cachedRooms = newTags; + return; + } - // Now process all the joined rooms. This is a bit more complicated - for (const room of memberships[EffectiveMembership.Join]) { - const tags = this.getTagsOfJoinedRoom(room); + // Split out the easy rooms first (leave and invite) + const memberships = splitRoomsByMembership(rooms); + for (const room of memberships[EffectiveMembership.Invite]) { + newTags[DefaultTagID.Invite].push(room); + } + for (const room of memberships[EffectiveMembership.Leave]) { + newTags[DefaultTagID.Archived].push(room); + } - let inTag = false; - if (tags.length > 0) { - for (const tag of tags) { - if (!isNullOrUndefined(newTags[tag])) { - newTags[tag].push(room); - inTag = true; + // Now process all the joined rooms. This is a bit more complicated + for (const room of memberships[EffectiveMembership.Join]) { + const tags = this.getTagsOfJoinedRoom(room); + + let inTag = false; + if (tags.length > 0) { + for (const tag of tags) { + if (!isNullOrUndefined(newTags[tag])) { + newTags[tag].push(room); + inTag = true; + } } } - } - if (!inTag) { - if (DMRoomMap.shared().getUserIdForRoomId(room.roomId)) { - newTags[DefaultTagID.DM].push(room); - } else { - newTags[DefaultTagID.Untagged].push(room); + if (!inTag) { + if (DMRoomMap.shared().getUserIdForRoomId(room.roomId)) { + newTags[DefaultTagID.DM].push(room); + } else { + newTags[DefaultTagID.Untagged].push(room); + } } } - } - - await this.generateFreshTags(newTags); - this.cachedRooms = newTags; // this recalculates the filtered rooms for us - this.updateTagsFromCache(); - - // Now that we've finished generation, we need to update the sticky room to what - // it was. It's entirely possible that it changed lists though, so if it did then - // we also have to update the position of it. - if (oldStickyRoom && oldStickyRoom.room) { - await this.updateStickyRoom(oldStickyRoom.room); - if (this._stickyRoom && this._stickyRoom.room) { // just in case the update doesn't go according to plan - if (this._stickyRoom.tag !== oldStickyRoom.tag) { - // We put the sticky room at the top of the list to treat it as an obvious tag change. - this._stickyRoom.position = 0; - this.recalculateStickyRoom(this._stickyRoom.tag); + await this.generateFreshTags(newTags); + + this.cachedRooms = newTags; // this recalculates the filtered rooms for us + this.updateTagsFromCache(); + + // Now that we've finished generation, we need to update the sticky room to what + // it was. It's entirely possible that it changed lists though, so if it did then + // we also have to update the position of it. + if (oldStickyRoom && oldStickyRoom.room) { + await this.updateStickyRoom(oldStickyRoom.room); + if (this._stickyRoom && this._stickyRoom.room) { // just in case the update doesn't go according to plan + if (this._stickyRoom.tag !== oldStickyRoom.tag) { + // We put the sticky room at the top of the list to treat it as an obvious tag change. + this._stickyRoom.position = 0; + this.recalculateStickyRoom(this._stickyRoom.tag); + } } } + } finally { + this.stickyLock.release(); } } @@ -685,9 +698,9 @@ export class Algorithm extends EventEmitter { if (!this.algorithms) throw new Error("Not ready: no algorithms to determine tags from"); // Note: check the isSticky against the room ID just in case the reference is wrong - const isSticky = this._stickyRoom && this._stickyRoom.room && this._stickyRoom.room.roomId === room.roomId; + const isSticky = this._stickyRoom?.room?.roomId === room.roomId; if (cause === RoomUpdateCause.NewRoom) { - const isForLastSticky = this._lastStickyRoom && this._lastStickyRoom.room === room; + const isForLastSticky = this._lastStickyRoom?.room === room; const roomTags = this.roomIdsToTags[room.roomId]; const hasTags = roomTags && roomTags.length > 0; From 191591e80730990c18f9d24c35273cfa2c2fb18d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 16 Jul 2021 09:15:56 +0100 Subject: [PATCH 2/3] Make the critical sections of the RLS synchronous to avoid needing to mutex everything --- src/components/views/rooms/RoomSublist.tsx | 4 +- src/stores/room-list/RoomListStore.ts | 54 ++--- src/stores/room-list/algorithms/Algorithm.ts | 198 ++++++++---------- .../list-ordering/ImportanceAlgorithm.ts | 88 ++++---- .../list-ordering/NaturalAlgorithm.ts | 52 ++--- .../list-ordering/OrderingAlgorithm.ts | 10 +- .../tag-sorting/AlphabeticAlgorithm.ts | 2 +- .../algorithms/tag-sorting/IAlgorithm.ts | 4 +- .../algorithms/tag-sorting/ManualAlgorithm.ts | 2 +- .../algorithms/tag-sorting/RecentAlgorithm.ts | 2 +- .../room-list/algorithms/tag-sorting/index.ts | 4 +- 11 files changed, 190 insertions(+), 230 deletions(-) diff --git a/src/components/views/rooms/RoomSublist.tsx b/src/components/views/rooms/RoomSublist.tsx index fce9e297a18..8d825a2b532 100644 --- a/src/components/views/rooms/RoomSublist.tsx +++ b/src/components/views/rooms/RoomSublist.tsx @@ -408,10 +408,10 @@ export default class RoomSublist extends React.Component { this.setState({ addRoomContextMenuPosition: null }); }; - private onUnreadFirstChanged = async () => { + private onUnreadFirstChanged = () => { const isUnreadFirst = RoomListStore.instance.getListOrder(this.props.tagId) === ListAlgorithm.Importance; const newAlgorithm = isUnreadFirst ? ListAlgorithm.Natural : ListAlgorithm.Importance; - await RoomListStore.instance.setListOrder(this.props.tagId, newAlgorithm); + RoomListStore.instance.setListOrder(this.props.tagId, newAlgorithm); this.forceUpdate(); // because if the sublist doesn't have any changes then we will miss the list order change }; diff --git a/src/stores/room-list/RoomListStore.ts b/src/stores/room-list/RoomListStore.ts index bedbfebd7f0..3913a2220f2 100644 --- a/src/stores/room-list/RoomListStore.ts +++ b/src/stores/room-list/RoomListStore.ts @@ -132,8 +132,8 @@ export class RoomListStoreClass extends AsyncStoreWithClient { // Update any settings here, as some may have happened before we were logically ready. console.log("Regenerating room lists: Startup"); await this.readAndCacheSettingsFromStore(); - await this.regenerateAllLists({ trigger: false }); - await this.handleRVSUpdate({ trigger: false }); // fake an RVS update to adjust sticky room, if needed + this.regenerateAllLists({ trigger: false }); + this.handleRVSUpdate({ trigger: false }); // fake an RVS update to adjust sticky room, if needed this.updateFn.mark(); // we almost certainly want to trigger an update. this.updateFn.trigger(); @@ -150,7 +150,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { await this.updateState({ tagsEnabled, }); - await this.updateAlgorithmInstances(); + this.updateAlgorithmInstances(); } /** @@ -158,23 +158,23 @@ export class RoomListStoreClass extends AsyncStoreWithClient { * @param trigger Set to false to prevent a list update from being sent. Should only * be used if the calling code will manually trigger the update. */ - private async handleRVSUpdate({ trigger = true }) { + private handleRVSUpdate({ trigger = true }) { if (!this.matrixClient) return; // We assume there won't be RVS updates without a client const activeRoomId = RoomViewStore.getRoomId(); if (!activeRoomId && this.algorithm.stickyRoom) { - await this.algorithm.setStickyRoom(null); + this.algorithm.setStickyRoom(null); } else if (activeRoomId) { const activeRoom = this.matrixClient.getRoom(activeRoomId); if (!activeRoom) { console.warn(`${activeRoomId} is current in RVS but missing from client - clearing sticky room`); - await this.algorithm.setStickyRoom(null); + this.algorithm.setStickyRoom(null); } else if (activeRoom !== this.algorithm.stickyRoom) { if (SettingsStore.getValue("advancedRoomListLogging")) { // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`Changing sticky room to ${activeRoomId}`); } - await this.algorithm.setStickyRoom(activeRoom); + this.algorithm.setStickyRoom(activeRoom); } } @@ -226,7 +226,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { console.log("Regenerating room lists: Settings changed"); await this.readAndCacheSettingsFromStore(); - await this.regenerateAllLists({ trigger: false }); // regenerate the lists now + this.regenerateAllLists({ trigger: false }); // regenerate the lists now this.updateFn.trigger(); } } @@ -368,7 +368,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[RoomListDebug] Clearing sticky room due to room upgrade`); } - await this.algorithm.setStickyRoom(null); + this.algorithm.setStickyRoom(null); } // Note: we hit the algorithm instead of our handleRoomUpdate() function to @@ -377,7 +377,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`[RoomListDebug] Removing previous room from room list`); } - await this.algorithm.handleRoomUpdate(prevRoom, RoomUpdateCause.RoomRemoved); + this.algorithm.handleRoomUpdate(prevRoom, RoomUpdateCause.RoomRemoved); } } @@ -433,7 +433,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { return; // don't do anything on new/moved rooms which ought not to be shown } - const shouldUpdate = await this.algorithm.handleRoomUpdate(room, cause); + const shouldUpdate = this.algorithm.handleRoomUpdate(room, cause); if (shouldUpdate) { if (SettingsStore.getValue("advancedRoomListLogging")) { // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 @@ -462,13 +462,13 @@ export class RoomListStoreClass extends AsyncStoreWithClient { // Reset the sticky room before resetting the known rooms so the algorithm // doesn't freak out. - await this.algorithm.setStickyRoom(null); - await this.algorithm.setKnownRooms(rooms); + this.algorithm.setStickyRoom(null); + this.algorithm.setKnownRooms(rooms); // Set the sticky room back, if needed, now that we have updated the store. // This will use relative stickyness to the new room set. if (stickyIsStillPresent) { - await this.algorithm.setStickyRoom(currentSticky); + this.algorithm.setStickyRoom(currentSticky); } // Finally, mark an update and resume updates from the algorithm @@ -477,12 +477,12 @@ export class RoomListStoreClass extends AsyncStoreWithClient { } public async setTagSorting(tagId: TagID, sort: SortAlgorithm) { - await this.setAndPersistTagSorting(tagId, sort); + this.setAndPersistTagSorting(tagId, sort); this.updateFn.trigger(); } - private async setAndPersistTagSorting(tagId: TagID, sort: SortAlgorithm) { - await this.algorithm.setTagSorting(tagId, sort); + private setAndPersistTagSorting(tagId: TagID, sort: SortAlgorithm) { + this.algorithm.setTagSorting(tagId, sort); // TODO: Per-account? https://github.com/vector-im/element-web/issues/14114 localStorage.setItem(`mx_tagSort_${tagId}`, sort); } @@ -520,13 +520,13 @@ export class RoomListStoreClass extends AsyncStoreWithClient { return tagSort; } - public async setListOrder(tagId: TagID, order: ListAlgorithm) { - await this.setAndPersistListOrder(tagId, order); + public setListOrder(tagId: TagID, order: ListAlgorithm) { + this.setAndPersistListOrder(tagId, order); this.updateFn.trigger(); } - private async setAndPersistListOrder(tagId: TagID, order: ListAlgorithm) { - await this.algorithm.setListOrdering(tagId, order); + private setAndPersistListOrder(tagId: TagID, order: ListAlgorithm) { + this.algorithm.setListOrdering(tagId, order); // TODO: Per-account? https://github.com/vector-im/element-web/issues/14114 localStorage.setItem(`mx_listOrder_${tagId}`, order); } @@ -563,7 +563,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { return listOrder; } - private async updateAlgorithmInstances() { + private updateAlgorithmInstances() { // We'll require an update, so mark for one. Marking now also prevents the calls // to setTagSorting and setListOrder from causing triggers. this.updateFn.mark(); @@ -576,10 +576,10 @@ export class RoomListStoreClass extends AsyncStoreWithClient { const listOrder = this.calculateListOrder(tag); if (tagSort !== definedSort) { - await this.setAndPersistTagSorting(tag, tagSort); + this.setAndPersistTagSorting(tag, tagSort); } if (listOrder !== definedOrder) { - await this.setAndPersistListOrder(tag, listOrder); + this.setAndPersistListOrder(tag, listOrder); } } } @@ -632,7 +632,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { * @param trigger Set to false to prevent a list update from being sent. Should only * be used if the calling code will manually trigger the update. */ - public async regenerateAllLists({ trigger = true }) { + public regenerateAllLists({ trigger = true }) { console.warn("Regenerating all room lists"); const rooms = this.getPlausibleRooms(); @@ -656,8 +656,8 @@ export class RoomListStoreClass extends AsyncStoreWithClient { RoomListLayoutStore.instance.ensureLayoutExists(tagId); } - await this.algorithm.populateTags(sorts, orders); - await this.algorithm.setKnownRooms(rooms); + this.algorithm.populateTags(sorts, orders); + this.algorithm.setKnownRooms(rooms); this.initialListsGenerated = true; diff --git a/src/stores/room-list/algorithms/Algorithm.ts b/src/stores/room-list/algorithms/Algorithm.ts index 2acce1ecd76..8574f095d6e 100644 --- a/src/stores/room-list/algorithms/Algorithm.ts +++ b/src/stores/room-list/algorithms/Algorithm.ts @@ -17,7 +17,6 @@ limitations under the License. import { Room } from "matrix-js-sdk/src/models/room"; import { isNullOrUndefined } from "matrix-js-sdk/src/utils"; import { EventEmitter } from "events"; -import AwaitLock from "await-lock"; import DMRoomMap from "../../../utils/DMRoomMap"; import { arrayDiff, arrayHasDiff } from "../../../utils/arrays"; @@ -80,7 +79,6 @@ export class Algorithm extends EventEmitter { } = {}; private allowedByFilter: Map = new Map(); private allowedRoomsByFilters: Set = new Set(); - private stickyLock = new AwaitLock(); /** * Set to true to suspend emissions of algorithm updates. @@ -125,13 +123,8 @@ export class Algorithm extends EventEmitter { * Awaitable version of the sticky room setter. * @param val The new room to sticky. */ - public async setStickyRoom(val: Room) { - await this.stickyLock.acquireAsync(); - try { - await this.updateStickyRoom(val); - } finally { - this.stickyLock.release(); - } + public setStickyRoom(val: Room) { + this.updateStickyRoom(val); } public getTagSorting(tagId: TagID): SortAlgorithm { @@ -139,13 +132,13 @@ export class Algorithm extends EventEmitter { return this.sortAlgorithms[tagId]; } - public async setTagSorting(tagId: TagID, sort: SortAlgorithm) { + public setTagSorting(tagId: TagID, sort: SortAlgorithm) { if (!tagId) throw new Error("Tag ID must be defined"); if (!sort) throw new Error("Algorithm must be defined"); this.sortAlgorithms[tagId] = sort; const algorithm: OrderingAlgorithm = this.algorithms[tagId]; - await algorithm.setSortAlgorithm(sort); + algorithm.setSortAlgorithm(sort); this._cachedRooms[tagId] = algorithm.orderedRooms; this.recalculateFilteredRoomsForTag(tagId); // update filter to re-sort the list this.recalculateStickyRoom(tagId); // update sticky room to make sure it appears if needed @@ -156,7 +149,7 @@ export class Algorithm extends EventEmitter { return this.listAlgorithms[tagId]; } - public async setListOrdering(tagId: TagID, order: ListAlgorithm) { + public setListOrdering(tagId: TagID, order: ListAlgorithm) { if (!tagId) throw new Error("Tag ID must be defined"); if (!order) throw new Error("Algorithm must be defined"); this.listAlgorithms[tagId] = order; @@ -164,7 +157,7 @@ export class Algorithm extends EventEmitter { const algorithm = getListAlgorithmInstance(order, tagId, this.sortAlgorithms[tagId]); this.algorithms[tagId] = algorithm; - await algorithm.setRooms(this._cachedRooms[tagId]); + algorithm.setRooms(this._cachedRooms[tagId]); this._cachedRooms[tagId] = algorithm.orderedRooms; this.recalculateFilteredRoomsForTag(tagId); // update filter to re-sort the list this.recalculateStickyRoom(tagId); // update sticky room to make sure it appears if needed @@ -191,31 +184,25 @@ export class Algorithm extends EventEmitter { } } - private async handleFilterChange() { - await this.recalculateFilteredRooms(); + private handleFilterChange() { + this.recalculateFilteredRooms(); // re-emit the update so the list store can fire an off-cycle update if needed if (this.updatesInhibited) return; this.emit(FILTER_CHANGED); } - private async updateStickyRoom(val: Room) { - try { - return await this.doUpdateStickyRoom(val); - } finally { - this._lastStickyRoom = null; // clear to indicate we're done changing - } + private updateStickyRoom(val: Room) { + this.doUpdateStickyRoom(val); + this._lastStickyRoom = null; // clear to indicate we're done changing } - private async doUpdateStickyRoom(val: Room) { + private doUpdateStickyRoom(val: Room) { if (SpaceStore.spacesEnabled && val?.isSpaceRoom() && val.getMyMembership() !== "invite") { // no-op sticky rooms for spaces - they're effectively virtual rooms val = null; } - // Note throughout: We need async so we can wait for handleRoomUpdate() to do its thing, - // otherwise we risk duplicating rooms. - if (val && !VisibilityProvider.instance.isRoomVisible(val)) { val = null; // the room isn't visible - lie to the rest of this function } @@ -231,7 +218,7 @@ export class Algorithm extends EventEmitter { this._stickyRoom = null; // clear before we go to update the algorithm // Lie to the algorithm and re-add the room to the algorithm - await this.handleRoomUpdate(stickyRoom, RoomUpdateCause.NewRoom); + this.handleRoomUpdate(stickyRoom, RoomUpdateCause.NewRoom); return; } return; @@ -277,10 +264,10 @@ export class Algorithm extends EventEmitter { // referential checks as the references can differ through the lifecycle. if (lastStickyRoom && lastStickyRoom.room && lastStickyRoom.room.roomId !== val.roomId) { // Lie to the algorithm and re-add the room to the algorithm - await this.handleRoomUpdate(lastStickyRoom.room, RoomUpdateCause.NewRoom); + this.handleRoomUpdate(lastStickyRoom.room, RoomUpdateCause.NewRoom); } // Lie to the algorithm and remove the room from it's field of view - await this.handleRoomUpdate(val, RoomUpdateCause.RoomRemoved); + this.handleRoomUpdate(val, RoomUpdateCause.RoomRemoved); // Check for tag & position changes while we're here. We also check the room to ensure // it is still the same room. @@ -470,9 +457,8 @@ export class Algorithm extends EventEmitter { * them. * @param {ITagSortingMap} tagSortingMap The tags to generate. * @param {IListOrderingMap} listOrderingMap The ordering of those tags. - * @returns {Promise<*>} A promise which resolves when complete. */ - public async populateTags(tagSortingMap: ITagSortingMap, listOrderingMap: IListOrderingMap): Promise { + public populateTags(tagSortingMap: ITagSortingMap, listOrderingMap: IListOrderingMap): void { if (!tagSortingMap) throw new Error(`Sorting map cannot be null or empty`); if (!listOrderingMap) throw new Error(`Ordering ma cannot be null or empty`); if (arrayHasDiff(Object.keys(tagSortingMap), Object.keys(listOrderingMap))) { @@ -521,93 +507,87 @@ export class Algorithm extends EventEmitter { * Seeds the Algorithm with a set of rooms. The algorithm will discard all * previously known information and instead use these rooms instead. * @param {Room[]} rooms The rooms to force the algorithm to use. - * @returns {Promise<*>} A promise which resolves when complete. */ - public async setKnownRooms(rooms: Room[]): Promise { + public setKnownRooms(rooms: Room[]): void { if (isNullOrUndefined(rooms)) throw new Error(`Array of rooms cannot be null`); if (!this.sortAlgorithms) throw new Error(`Cannot set known rooms without a tag sorting map`); - await this.stickyLock.acquireAsync(); - try { - if (!this.updatesInhibited) { - // We only log this if we're expecting to be publishing updates, which means that - // this could be an unexpected invocation. If we're inhibited, then this is probably - // an intentional invocation. - console.warn("Resetting known rooms, initiating regeneration"); - } + if (!this.updatesInhibited) { + // We only log this if we're expecting to be publishing updates, which means that + // this could be an unexpected invocation. If we're inhibited, then this is probably + // an intentional invocation. + console.warn("Resetting known rooms, initiating regeneration"); + } - // Before we go any further we need to clear (but remember) the sticky room to - // avoid accidentally duplicating it in the list. - const oldStickyRoom = this._stickyRoom; - if (oldStickyRoom) await this.updateStickyRoom(null); + // Before we go any further we need to clear (but remember) the sticky room to + // avoid accidentally duplicating it in the list. + const oldStickyRoom = this._stickyRoom; + if (oldStickyRoom) this.updateStickyRoom(null); - this.rooms = rooms; + this.rooms = rooms; - const newTags: ITagMap = {}; - for (const tagId in this.sortAlgorithms) { - // noinspection JSUnfilteredForInLoop - newTags[tagId] = []; - } + const newTags: ITagMap = {}; + for (const tagId in this.sortAlgorithms) { + // noinspection JSUnfilteredForInLoop + newTags[tagId] = []; + } - // If we can avoid doing work, do so. - if (!rooms.length) { - await this.generateFreshTags(newTags); // just in case it wants to do something - this.cachedRooms = newTags; - return; - } + // If we can avoid doing work, do so. + if (!rooms.length) { + this.generateFreshTags(newTags); // just in case it wants to do something + this.cachedRooms = newTags; + return; + } - // Split out the easy rooms first (leave and invite) - const memberships = splitRoomsByMembership(rooms); - for (const room of memberships[EffectiveMembership.Invite]) { - newTags[DefaultTagID.Invite].push(room); - } - for (const room of memberships[EffectiveMembership.Leave]) { - newTags[DefaultTagID.Archived].push(room); - } + // Split out the easy rooms first (leave and invite) + const memberships = splitRoomsByMembership(rooms); + for (const room of memberships[EffectiveMembership.Invite]) { + newTags[DefaultTagID.Invite].push(room); + } + for (const room of memberships[EffectiveMembership.Leave]) { + newTags[DefaultTagID.Archived].push(room); + } + + // Now process all the joined rooms. This is a bit more complicated + for (const room of memberships[EffectiveMembership.Join]) { + const tags = this.getTagsOfJoinedRoom(room); - // Now process all the joined rooms. This is a bit more complicated - for (const room of memberships[EffectiveMembership.Join]) { - const tags = this.getTagsOfJoinedRoom(room); - - let inTag = false; - if (tags.length > 0) { - for (const tag of tags) { - if (!isNullOrUndefined(newTags[tag])) { - newTags[tag].push(room); - inTag = true; - } + let inTag = false; + if (tags.length > 0) { + for (const tag of tags) { + if (!isNullOrUndefined(newTags[tag])) { + newTags[tag].push(room); + inTag = true; } } + } - if (!inTag) { - if (DMRoomMap.shared().getUserIdForRoomId(room.roomId)) { - newTags[DefaultTagID.DM].push(room); - } else { - newTags[DefaultTagID.Untagged].push(room); - } + if (!inTag) { + if (DMRoomMap.shared().getUserIdForRoomId(room.roomId)) { + newTags[DefaultTagID.DM].push(room); + } else { + newTags[DefaultTagID.Untagged].push(room); } } + } - await this.generateFreshTags(newTags); - - this.cachedRooms = newTags; // this recalculates the filtered rooms for us - this.updateTagsFromCache(); - - // Now that we've finished generation, we need to update the sticky room to what - // it was. It's entirely possible that it changed lists though, so if it did then - // we also have to update the position of it. - if (oldStickyRoom && oldStickyRoom.room) { - await this.updateStickyRoom(oldStickyRoom.room); - if (this._stickyRoom && this._stickyRoom.room) { // just in case the update doesn't go according to plan - if (this._stickyRoom.tag !== oldStickyRoom.tag) { - // We put the sticky room at the top of the list to treat it as an obvious tag change. - this._stickyRoom.position = 0; - this.recalculateStickyRoom(this._stickyRoom.tag); - } + this.generateFreshTags(newTags); + + this.cachedRooms = newTags; // this recalculates the filtered rooms for us + this.updateTagsFromCache(); + + // Now that we've finished generation, we need to update the sticky room to what + // it was. It's entirely possible that it changed lists though, so if it did then + // we also have to update the position of it. + if (oldStickyRoom && oldStickyRoom.room) { + this.updateStickyRoom(oldStickyRoom.room); + if (this._stickyRoom && this._stickyRoom.room) { // just in case the update doesn't go according to plan + if (this._stickyRoom.tag !== oldStickyRoom.tag) { + // We put the sticky room at the top of the list to treat it as an obvious tag change. + this._stickyRoom.position = 0; + this.recalculateStickyRoom(this._stickyRoom.tag); } } - } finally { - this.stickyLock.release(); } } @@ -665,16 +645,15 @@ export class Algorithm extends EventEmitter { * @param {ITagMap} updatedTagMap The tag map which needs populating. Each tag * will already have the rooms which belong to it - they just need ordering. Must * be mutated in place. - * @returns {Promise<*>} A promise which resolves when complete. */ - private async generateFreshTags(updatedTagMap: ITagMap): Promise { + private generateFreshTags(updatedTagMap: ITagMap): void { if (!this.algorithms) throw new Error("Not ready: no algorithms to determine tags from"); for (const tag of Object.keys(updatedTagMap)) { const algorithm: OrderingAlgorithm = this.algorithms[tag]; if (!algorithm) throw new Error(`No algorithm for ${tag}`); - await algorithm.setRooms(updatedTagMap[tag]); + algorithm.setRooms(updatedTagMap[tag]); updatedTagMap[tag] = algorithm.orderedRooms; } } @@ -686,11 +665,10 @@ export class Algorithm extends EventEmitter { * may no-op this request if no changes are required. * @param {Room} room The room which might have affected sorting. * @param {RoomUpdateCause} cause The reason for the update being triggered. - * @returns {Promise} A promise which resolve to true or false - * depending on whether or not getOrderedRooms() should be called after - * processing. + * @returns {Promise} A boolean of whether or not getOrderedRooms() + * should be called after processing. */ - public async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise { + public handleRoomUpdate(room: Room, cause: RoomUpdateCause): boolean { if (SettingsStore.getValue("advancedRoomListLogging")) { // TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602 console.log(`Handle room update for ${room.roomId} called with cause ${cause}`); @@ -757,7 +735,7 @@ export class Algorithm extends EventEmitter { } const algorithm: OrderingAlgorithm = this.algorithms[rmTag]; if (!algorithm) throw new Error(`No algorithm for ${rmTag}`); - await algorithm.handleRoomUpdate(room, RoomUpdateCause.RoomRemoved); + algorithm.handleRoomUpdate(room, RoomUpdateCause.RoomRemoved); this._cachedRooms[rmTag] = algorithm.orderedRooms; this.recalculateFilteredRoomsForTag(rmTag); // update filter to re-sort the list this.recalculateStickyRoom(rmTag); // update sticky room to make sure it moves if needed @@ -769,7 +747,7 @@ export class Algorithm extends EventEmitter { } const algorithm: OrderingAlgorithm = this.algorithms[addTag]; if (!algorithm) throw new Error(`No algorithm for ${addTag}`); - await algorithm.handleRoomUpdate(room, RoomUpdateCause.NewRoom); + algorithm.handleRoomUpdate(room, RoomUpdateCause.NewRoom); this._cachedRooms[addTag] = algorithm.orderedRooms; } @@ -802,7 +780,7 @@ export class Algorithm extends EventEmitter { }; } else { // We have to clear the lock as the sticky room change will trigger updates. - await this.setStickyRoom(room); + this.setStickyRoom(room); } } } @@ -865,7 +843,7 @@ export class Algorithm extends EventEmitter { const algorithm: OrderingAlgorithm = this.algorithms[tag]; if (!algorithm) throw new Error(`No algorithm for ${tag}`); - await algorithm.handleRoomUpdate(room, cause); + algorithm.handleRoomUpdate(room, cause); this._cachedRooms[tag] = algorithm.orderedRooms; // Flag that we've done something diff --git a/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts b/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts index 80bdf74afb6..1d35df331de 100644 --- a/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts +++ b/src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm.ts @@ -94,15 +94,15 @@ export class ImportanceAlgorithm extends OrderingAlgorithm { return state.color; } - public async setRooms(rooms: Room[]): Promise { + public setRooms(rooms: Room[]): void { if (this.sortingAlgorithm === SortAlgorithm.Manual) { - this.cachedOrderedRooms = await sortRoomsWithAlgorithm(rooms, this.tagId, this.sortingAlgorithm); + this.cachedOrderedRooms = sortRoomsWithAlgorithm(rooms, this.tagId, this.sortingAlgorithm); } else { // Every other sorting type affects the categories, not the whole tag. const categorized = this.categorizeRooms(rooms); for (const category of Object.keys(categorized)) { const roomsToOrder = categorized[category]; - categorized[category] = await sortRoomsWithAlgorithm(roomsToOrder, this.tagId, this.sortingAlgorithm); + categorized[category] = sortRoomsWithAlgorithm(roomsToOrder, this.tagId, this.sortingAlgorithm); } const newlyOrganized: Room[] = []; @@ -118,12 +118,12 @@ export class ImportanceAlgorithm extends OrderingAlgorithm { } } - private async handleSplice(room: Room, cause: RoomUpdateCause): Promise { + private handleSplice(room: Room, cause: RoomUpdateCause): boolean { if (cause === RoomUpdateCause.NewRoom) { const category = this.getRoomCategory(room); this.alterCategoryPositionBy(category, 1, this.indices); this.cachedOrderedRooms.splice(this.indices[category], 0, room); // splice in the new room (pre-adjusted) - await this.sortCategory(category); + this.sortCategory(category); } else if (cause === RoomUpdateCause.RoomRemoved) { const roomIdx = this.getRoomIndex(room); if (roomIdx === -1) { @@ -141,55 +141,49 @@ export class ImportanceAlgorithm extends OrderingAlgorithm { return true; } - public async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise { - try { - await this.updateLock.acquireAsync(); - - if (cause === RoomUpdateCause.NewRoom || cause === RoomUpdateCause.RoomRemoved) { - return this.handleSplice(room, cause); - } + public handleRoomUpdate(room: Room, cause: RoomUpdateCause): boolean { + if (cause === RoomUpdateCause.NewRoom || cause === RoomUpdateCause.RoomRemoved) { + return this.handleSplice(room, cause); + } - if (cause !== RoomUpdateCause.Timeline && cause !== RoomUpdateCause.ReadReceipt) { - throw new Error(`Unsupported update cause: ${cause}`); - } + if (cause !== RoomUpdateCause.Timeline && cause !== RoomUpdateCause.ReadReceipt) { + throw new Error(`Unsupported update cause: ${cause}`); + } - const category = this.getRoomCategory(room); - if (this.sortingAlgorithm === SortAlgorithm.Manual) { - return; // Nothing to do here. - } + const category = this.getRoomCategory(room); + if (this.sortingAlgorithm === SortAlgorithm.Manual) { + return; // Nothing to do here. + } - const roomIdx = this.getRoomIndex(room); - if (roomIdx === -1) { - throw new Error(`Room ${room.roomId} has no index in ${this.tagId}`); - } + const roomIdx = this.getRoomIndex(room); + if (roomIdx === -1) { + throw new Error(`Room ${room.roomId} has no index in ${this.tagId}`); + } - // Try to avoid doing array operations if we don't have to: only move rooms within - // the categories if we're jumping categories - const oldCategory = this.getCategoryFromIndices(roomIdx, this.indices); - if (oldCategory !== category) { - // Move the room and update the indices - this.moveRoomIndexes(1, oldCategory, category, this.indices); - this.cachedOrderedRooms.splice(roomIdx, 1); // splice out the old index (fixed position) - this.cachedOrderedRooms.splice(this.indices[category], 0, room); // splice in the new room (pre-adjusted) - // Note: if moveRoomIndexes() is called after the splice then the insert operation - // will happen in the wrong place. Because we would have already adjusted the index - // for the category, we don't need to determine how the room is moving in the list. - // If we instead tried to insert before updating the indices, we'd have to determine - // whether the room was moving later (towards IDLE) or earlier (towards RED) from its - // current position, as it'll affect the category's start index after we remove the - // room from the array. - } + // Try to avoid doing array operations if we don't have to: only move rooms within + // the categories if we're jumping categories + const oldCategory = this.getCategoryFromIndices(roomIdx, this.indices); + if (oldCategory !== category) { + // Move the room and update the indices + this.moveRoomIndexes(1, oldCategory, category, this.indices); + this.cachedOrderedRooms.splice(roomIdx, 1); // splice out the old index (fixed position) + this.cachedOrderedRooms.splice(this.indices[category], 0, room); // splice in the new room (pre-adjusted) + // Note: if moveRoomIndexes() is called after the splice then the insert operation + // will happen in the wrong place. Because we would have already adjusted the index + // for the category, we don't need to determine how the room is moving in the list. + // If we instead tried to insert before updating the indices, we'd have to determine + // whether the room was moving later (towards IDLE) or earlier (towards RED) from its + // current position, as it'll affect the category's start index after we remove the + // room from the array. + } - // Sort the category now that we've dumped the room in - await this.sortCategory(category); + // Sort the category now that we've dumped the room in + this.sortCategory(category); - return true; // change made - } finally { - await this.updateLock.release(); - } + return true; // change made } - private async sortCategory(category: NotificationColor) { + private sortCategory(category: NotificationColor) { // This should be relatively quick because the room is usually inserted at the top of the // category, and most popular sorting algorithms will deal with trying to keep the active // room at the top/start of the category. For the few algorithms that will have to move the @@ -201,7 +195,7 @@ export class ImportanceAlgorithm extends OrderingAlgorithm { const startIdx = this.indices[category]; const numSort = nextCategoryStartIdx - startIdx; // splice() returns up to the max, so MAX_SAFE_INT is fine const unsortedSlice = this.cachedOrderedRooms.splice(startIdx, numSort); - const sorted = await sortRoomsWithAlgorithm(unsortedSlice, this.tagId, this.sortingAlgorithm); + const sorted = sortRoomsWithAlgorithm(unsortedSlice, this.tagId, this.sortingAlgorithm); this.cachedOrderedRooms.splice(startIdx, 0, ...sorted); } diff --git a/src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm.ts b/src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm.ts index cc2a28d8922..91182dee167 100644 --- a/src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm.ts +++ b/src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm.ts @@ -29,42 +29,32 @@ export class NaturalAlgorithm extends OrderingAlgorithm { super(tagId, initialSortingAlgorithm); } - public async setRooms(rooms: Room[]): Promise { - this.cachedOrderedRooms = await sortRoomsWithAlgorithm(rooms, this.tagId, this.sortingAlgorithm); + public setRooms(rooms: Room[]): void { + this.cachedOrderedRooms = sortRoomsWithAlgorithm(rooms, this.tagId, this.sortingAlgorithm); } - public async handleRoomUpdate(room, cause): Promise { - try { - await this.updateLock.acquireAsync(); - - const isSplice = cause === RoomUpdateCause.NewRoom || cause === RoomUpdateCause.RoomRemoved; - const isInPlace = cause === RoomUpdateCause.Timeline || cause === RoomUpdateCause.ReadReceipt; - if (!isSplice && !isInPlace) { - throw new Error(`Unsupported update cause: ${cause}`); - } + public handleRoomUpdate(room, cause): boolean { + const isSplice = cause === RoomUpdateCause.NewRoom || cause === RoomUpdateCause.RoomRemoved; + const isInPlace = cause === RoomUpdateCause.Timeline || cause === RoomUpdateCause.ReadReceipt; + if (!isSplice && !isInPlace) { + throw new Error(`Unsupported update cause: ${cause}`); + } - if (cause === RoomUpdateCause.NewRoom) { - this.cachedOrderedRooms.push(room); - } else if (cause === RoomUpdateCause.RoomRemoved) { - const idx = this.getRoomIndex(room); - if (idx >= 0) { - this.cachedOrderedRooms.splice(idx, 1); - } else { - console.warn(`Tried to remove unknown room from ${this.tagId}: ${room.roomId}`); - } + if (cause === RoomUpdateCause.NewRoom) { + this.cachedOrderedRooms.push(room); + } else if (cause === RoomUpdateCause.RoomRemoved) { + const idx = this.getRoomIndex(room); + if (idx >= 0) { + this.cachedOrderedRooms.splice(idx, 1); + } else { + console.warn(`Tried to remove unknown room from ${this.tagId}: ${room.roomId}`); } + } - // TODO: Optimize this to avoid useless operations: https://github.com/vector-im/element-web/issues/14457 - // For example, we can skip updates to alphabetic (sometimes) and manually ordered tags - this.cachedOrderedRooms = await sortRoomsWithAlgorithm( - this.cachedOrderedRooms, - this.tagId, - this.sortingAlgorithm, - ); + // TODO: Optimize this to avoid useless operations: https://github.com/vector-im/element-web/issues/14457 + // For example, we can skip updates to alphabetic (sometimes) and manually ordered tags + this.cachedOrderedRooms = sortRoomsWithAlgorithm(this.cachedOrderedRooms, this.tagId, this.sortingAlgorithm); - return true; - } finally { - await this.updateLock.release(); - } + return true; } } diff --git a/src/stores/room-list/algorithms/list-ordering/OrderingAlgorithm.ts b/src/stores/room-list/algorithms/list-ordering/OrderingAlgorithm.ts index c47a35523c2..23a8e33a415 100644 --- a/src/stores/room-list/algorithms/list-ordering/OrderingAlgorithm.ts +++ b/src/stores/room-list/algorithms/list-ordering/OrderingAlgorithm.ts @@ -26,7 +26,6 @@ import AwaitLock from "await-lock"; export abstract class OrderingAlgorithm { protected cachedOrderedRooms: Room[]; protected sortingAlgorithm: SortAlgorithm; - protected readonly updateLock = new AwaitLock(); protected constructor(protected tagId: TagID, initialSortingAlgorithm: SortAlgorithm) { // noinspection JSIgnoredPromiseFromCall @@ -45,21 +44,20 @@ export abstract class OrderingAlgorithm { * @param newAlgorithm The new algorithm. Must be defined. * @returns Resolves when complete. */ - public async setSortAlgorithm(newAlgorithm: SortAlgorithm) { + public setSortAlgorithm(newAlgorithm: SortAlgorithm) { if (!newAlgorithm) throw new Error("A sorting algorithm must be defined"); this.sortingAlgorithm = newAlgorithm; // Force regeneration of the rooms - await this.setRooms(this.orderedRooms); + this.setRooms(this.orderedRooms); } /** * Sets the rooms the algorithm should be handling, implying a reconstruction * of the ordering. * @param rooms The rooms to use going forward. - * @returns Resolves when complete. */ - public abstract setRooms(rooms: Room[]): Promise; + public abstract setRooms(rooms: Room[]): void; /** * Handle a room update. The Algorithm will only call this for causes which @@ -69,7 +67,7 @@ export abstract class OrderingAlgorithm { * @param cause The cause of the update. * @returns True if the update requires the Algorithm to update the presentation layers. */ - public abstract handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise; + public abstract handleRoomUpdate(room: Room, cause: RoomUpdateCause): boolean; protected getRoomIndex(room: Room): number { let roomIdx = this.cachedOrderedRooms.indexOf(room); diff --git a/src/stores/room-list/algorithms/tag-sorting/AlphabeticAlgorithm.ts b/src/stores/room-list/algorithms/tag-sorting/AlphabeticAlgorithm.ts index b016a4256c9..45f6eaf8436 100644 --- a/src/stores/room-list/algorithms/tag-sorting/AlphabeticAlgorithm.ts +++ b/src/stores/room-list/algorithms/tag-sorting/AlphabeticAlgorithm.ts @@ -23,7 +23,7 @@ import { compare } from "../../../../utils/strings"; * Sorts rooms according to the browser's determination of alphabetic. */ export class AlphabeticAlgorithm implements IAlgorithm { - public async sortRooms(rooms: Room[], tagId: TagID): Promise { + public sortRooms(rooms: Room[], tagId: TagID): Room[] { return rooms.sort((a, b) => { return compare(a.name, b.name); }); diff --git a/src/stores/room-list/algorithms/tag-sorting/IAlgorithm.ts b/src/stores/room-list/algorithms/tag-sorting/IAlgorithm.ts index 6c22ee0c9c3..588bbbffc9c 100644 --- a/src/stores/room-list/algorithms/tag-sorting/IAlgorithm.ts +++ b/src/stores/room-list/algorithms/tag-sorting/IAlgorithm.ts @@ -25,7 +25,7 @@ export interface IAlgorithm { * Sorts the given rooms according to the sorting rules of the algorithm. * @param {Room[]} rooms The rooms to sort. * @param {TagID} tagId The tag ID in which the rooms are being sorted. - * @returns {Promise} Resolves to the sorted rooms. + * @returns {Room[]} Returns the sorted rooms. */ - sortRooms(rooms: Room[], tagId: TagID): Promise; + sortRooms(rooms: Room[], tagId: TagID): Room[]; } diff --git a/src/stores/room-list/algorithms/tag-sorting/ManualAlgorithm.ts b/src/stores/room-list/algorithms/tag-sorting/ManualAlgorithm.ts index b8c03576330..9be8ba52622 100644 --- a/src/stores/room-list/algorithms/tag-sorting/ManualAlgorithm.ts +++ b/src/stores/room-list/algorithms/tag-sorting/ManualAlgorithm.ts @@ -22,7 +22,7 @@ import { IAlgorithm } from "./IAlgorithm"; * Sorts rooms according to the tag's `order` property on the room. */ export class ManualAlgorithm implements IAlgorithm { - public async sortRooms(rooms: Room[], tagId: TagID): Promise { + public sortRooms(rooms: Room[], tagId: TagID): Room[] { const getOrderProp = (r: Room) => r.tags[tagId].order || 0; return rooms.sort((a, b) => { return getOrderProp(a) - getOrderProp(b); diff --git a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts index 49cfd9e5204..f47458d1b1b 100644 --- a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts +++ b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts @@ -97,7 +97,7 @@ export const sortRooms = (rooms: Room[]): Room[] => { * useful to the user. */ export class RecentAlgorithm implements IAlgorithm { - public async sortRooms(rooms: Room[], tagId: TagID): Promise { + public sortRooms(rooms: Room[], tagId: TagID): Room[] { return sortRooms(rooms); } } diff --git a/src/stores/room-list/algorithms/tag-sorting/index.ts b/src/stores/room-list/algorithms/tag-sorting/index.ts index c22865f5ba1..368c76f1110 100644 --- a/src/stores/room-list/algorithms/tag-sorting/index.ts +++ b/src/stores/room-list/algorithms/tag-sorting/index.ts @@ -46,8 +46,8 @@ export function getSortingAlgorithmInstance(algorithm: SortAlgorithm): IAlgorith * @param {Room[]} rooms The rooms to sort. * @param {TagID} tagId The tag in which the sorting is occurring. * @param {SortAlgorithm} algorithm The algorithm to use for sorting. - * @returns {Promise} Resolves to the sorted rooms. + * @returns {Room[]} Returns the sorted rooms. */ -export function sortRoomsWithAlgorithm(rooms: Room[], tagId: TagID, algorithm: SortAlgorithm): Promise { +export function sortRoomsWithAlgorithm(rooms: Room[], tagId: TagID, algorithm: SortAlgorithm): Room[] { return getSortingAlgorithmInstance(algorithm).sortRooms(rooms, tagId); } From 05028f1997c8731ef6a64d52e811522f61869dad Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 16 Jul 2021 09:22:25 +0100 Subject: [PATCH 3/3] remove unused import --- .../room-list/algorithms/list-ordering/OrderingAlgorithm.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/stores/room-list/algorithms/list-ordering/OrderingAlgorithm.ts b/src/stores/room-list/algorithms/list-ordering/OrderingAlgorithm.ts index 23a8e33a415..9d7b5f9ddb5 100644 --- a/src/stores/room-list/algorithms/list-ordering/OrderingAlgorithm.ts +++ b/src/stores/room-list/algorithms/list-ordering/OrderingAlgorithm.ts @@ -17,7 +17,6 @@ limitations under the License. import { Room } from "matrix-js-sdk/src/models/room"; import { RoomUpdateCause, TagID } from "../../models"; import { SortAlgorithm } from "../models"; -import AwaitLock from "await-lock"; /** * Represents a list ordering algorithm. Subclasses should populate the