From 8986612ae43c550d717d76a8064f49cca89ab909 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Thu, 20 Feb 2025 12:42:47 +0530 Subject: [PATCH] WIP: Draft implementation of RLS --- src/@types/global.d.ts | 2 + src/stores/room-list-v3/RoomListStoreV3.ts | 133 ++++++++++++++++++ .../room-list-v3/filters/AllRoomsFilter.ts | 20 +++ .../room-list-v3/filters/FavouriteFilter.ts | 21 +++ src/stores/room-list-v3/filters/index.ts | 20 +++ .../room-list-v3/sorters/AlphabeticSorter.ts | 18 +++ .../room-list-v3/sorters/RecencySorter.ts | 16 +++ src/stores/room-list-v3/sorters/index.ts | 12 ++ 8 files changed, 242 insertions(+) create mode 100644 src/stores/room-list-v3/RoomListStoreV3.ts create mode 100644 src/stores/room-list-v3/filters/AllRoomsFilter.ts create mode 100644 src/stores/room-list-v3/filters/FavouriteFilter.ts create mode 100644 src/stores/room-list-v3/filters/index.ts create mode 100644 src/stores/room-list-v3/sorters/AlphabeticSorter.ts create mode 100644 src/stores/room-list-v3/sorters/RecencySorter.ts create mode 100644 src/stores/room-list-v3/sorters/index.ts diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 1df84ad344..154a6504c6 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -47,6 +47,7 @@ import { type DeepReadonly } from "./common"; import type MatrixChat from "../components/structures/MatrixChat"; import { type InitialCryptoSetupStore } from "../stores/InitialCryptoSetupStore"; import { type ModuleApiType } from "../modules/Api.ts"; +import type RoomListStoreV3 from "../stores/room-list-v3/RoomListStoreV3.ts"; /* eslint-disable @typescript-eslint/naming-convention */ @@ -99,6 +100,7 @@ declare global { mxToastStore: ToastStore; mxDeviceListener: DeviceListener; mxRoomListStore: RoomListStore; + mxRoomListStoreV3: RoomListStoreV3; mxRoomListLayoutStore: RoomListLayoutStore; mxPlatformPeg: PlatformPeg; mxIntegrationManagers: typeof IntegrationManagers; diff --git a/src/stores/room-list-v3/RoomListStoreV3.ts b/src/stores/room-list-v3/RoomListStoreV3.ts new file mode 100644 index 0000000000..41095f96f7 --- /dev/null +++ b/src/stores/room-list-v3/RoomListStoreV3.ts @@ -0,0 +1,133 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import type { EmptyObject, Room } from "matrix-js-sdk/src/matrix"; +import type { MatrixDispatcher } from "../../dispatcher/dispatcher"; +import type { ActionPayload } from "../../dispatcher/payloads"; +import type { Filter, FilterKey } from "./filters"; +import type { Sorter } from "./sorters"; +import { AsyncStoreWithClient } from "../AsyncStoreWithClient"; +import SettingsStore from "../../settings/SettingsStore"; +import { VisibilityProvider } from "../room-list/filters/VisibilityProvider"; +import defaultDispatcher from "../../dispatcher/dispatcher"; +import { LISTS_UPDATE_EVENT } from "../room-list/RoomListStore"; +import { AllRoomsFilter } from "./filters/AllRoomsFilter"; +import { FavouriteFilter } from "./filters/FavouriteFilter"; +import { RecencySorter } from "./sorters/RecencySorter"; + +export class RoomListStoreV3Class extends AsyncStoreWithClient { + /** + * This is the unsorted, unfiltered raw list of rooms from the js-sdk. + */ + private rooms: Room[] = []; + + private readonly msc3946ProcessDynamicPredecessor: boolean; + + /** + * Mapping from FilterKey | string to a set of Rooms + */ + private filteredRooms: Map> = new Map(); + private sortedRooms: Room[] = []; + + private readonly filters: Filter[] = [new AllRoomsFilter(), new FavouriteFilter()]; + private sorter: Sorter = new RecencySorter(); + + public constructor(dispatcher: MatrixDispatcher) { + super(dispatcher); + this.msc3946ProcessDynamicPredecessor = SettingsStore.getValue("feature_dynamic_room_predecessors"); + } + + public setSorter(sorter: Sorter): void { + this.sorter = sorter; + } + + public getFilteredRooms(filters: FilterKey[]): Set | null { + const sets = filters.map((f) => this.filteredRooms.get(f)).filter((s) => !!s); + if (!sets.length) return null; + if (sets.length === 1) return sets[0]; + // Find the intersection of these filtered sets + const intersection = new Set(); + const [firstSet, ...otherSets] = sets; + for (const room of firstSet) { + if (!otherSets.some((set) => !set.has(room))) intersection.add(room); + } + return intersection; + } + + public getSortedFilteredRooms(filters: FilterKey[]): Array { + const filteredSet = this.getFilteredRooms(filters); + if (!filteredSet) return this.sortedRooms; + return this.sortedRooms?.filter((room) => filteredSet.has(room)); + } + + protected async onReady(): Promise { + const rooms = this.fetchRoomsFromSdk(); + if (!rooms) return; + this.rooms = rooms; + } + + protected async onAction(payload: ActionPayload): Promise { + if ( + ![ + "MatrixActions.Room.receipt", + "MatrixActions.Room.tags", + "MatrixActions.Room.timeline", + "MatrixActions.Event.decrypted", + "MatrixActions.accountData", + "MatrixActions.Room.myMembership", + ].includes(payload.action) + ) + return; + setTimeout(() => { + this.recalculate(); + }); + } + + private recalculate(): void { + const t0 = performance.now(); + this.fetchRoomsFromSdk(); + this.filterRooms(); + this.sortRooms(); + const t1 = performance.now(); + console.log("RLS Performance, time taken = ", t1 - t0); + this.emit(LISTS_UPDATE_EVENT); + } + + private filterRooms(): void { + for (const filter of this.filters) { + const rooms = filter.filter(this.rooms); + this.filteredRooms.set(filter.key, new Set(rooms)); + } + } + + private sortRooms(): void { + this.sortedRooms = this.sorter.sort(this.rooms); + } + + private fetchRoomsFromSdk(): Room[] | null { + if (!this.matrixClient) return null; + let rooms = this.matrixClient.getVisibleRooms(this.msc3946ProcessDynamicPredecessor); + rooms = rooms.filter((r) => VisibilityProvider.instance.isRoomVisible(r)); + return rooms; + } +} + +export default class RoomListStoreV3 { + private static internalInstance: RoomListStoreV3Class; + + public static get instance(): RoomListStoreV3Class { + if (!RoomListStoreV3.internalInstance) { + const instance = new RoomListStoreV3Class(defaultDispatcher); + instance.start(); + RoomListStoreV3.internalInstance = instance; + } + + return this.internalInstance; + } +} + +window.mxRoomListStoreV3 = RoomListStoreV3.instance; diff --git a/src/stores/room-list-v3/filters/AllRoomsFilter.ts b/src/stores/room-list-v3/filters/AllRoomsFilter.ts new file mode 100644 index 0000000000..bfe7968dfd --- /dev/null +++ b/src/stores/room-list-v3/filters/AllRoomsFilter.ts @@ -0,0 +1,20 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import type { Room } from "matrix-js-sdk/src/matrix"; +import type { Filter } from "."; +import { FilterKeysEnum } from "."; + +export class AllRoomsFilter implements Filter { + public filter(rooms: Room[]): Room[] { + return rooms; + } + + public get key(): FilterKeysEnum.All { + return FilterKeysEnum.All; + } +} diff --git a/src/stores/room-list-v3/filters/FavouriteFilter.ts b/src/stores/room-list-v3/filters/FavouriteFilter.ts new file mode 100644 index 0000000000..7458ef8ddc --- /dev/null +++ b/src/stores/room-list-v3/filters/FavouriteFilter.ts @@ -0,0 +1,21 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import type { Room } from "matrix-js-sdk/src/matrix"; +import type { Filter } from "."; +import { FilterKeysEnum } from "."; +import { DefaultTagID } from "../../room-list/models"; + +export class FavouriteFilter implements Filter { + public filter(rooms: Room[]): Room[] { + return rooms.filter((room) => !!room.tags[DefaultTagID.Favourite]); + } + + public get key(): FilterKeysEnum.Favorite { + return FilterKeysEnum.Favorite; + } +} diff --git a/src/stores/room-list-v3/filters/index.ts b/src/stores/room-list-v3/filters/index.ts new file mode 100644 index 0000000000..866b7b7b36 --- /dev/null +++ b/src/stores/room-list-v3/filters/index.ts @@ -0,0 +1,20 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import type { Room } from "matrix-js-sdk/src/matrix"; + +export const enum FilterKeysEnum { + All, + Favorite, +} + +export type FilterKey = FilterKeysEnum | string; + +export interface Filter { + key: FilterKey; + filter(rooms: Room[]): Room[]; +} diff --git a/src/stores/room-list-v3/sorters/AlphabeticSorter.ts b/src/stores/room-list-v3/sorters/AlphabeticSorter.ts new file mode 100644 index 0000000000..1161f183ac --- /dev/null +++ b/src/stores/room-list-v3/sorters/AlphabeticSorter.ts @@ -0,0 +1,18 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import type { Room } from "matrix-js-sdk/src/matrix"; +import type { Sorter } from "."; + +export class AlphabeticSorter implements Sorter { + public sort(rooms: Room[]): Room[] { + const collator = new Intl.Collator(); + return rooms.sort((a, b) => { + return collator.compare(a.name, b.name); + }); + } +} diff --git a/src/stores/room-list-v3/sorters/RecencySorter.ts b/src/stores/room-list-v3/sorters/RecencySorter.ts new file mode 100644 index 0000000000..c1d922ba03 --- /dev/null +++ b/src/stores/room-list-v3/sorters/RecencySorter.ts @@ -0,0 +1,16 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import type { Room } from "matrix-js-sdk/src/matrix"; +import type { Sorter } from "."; +import { sortRooms } from "../../room-list/algorithms/tag-sorting/RecentAlgorithm"; + +export class RecencySorter implements Sorter { + public sort(rooms: Room[]): Room[] { + return sortRooms(rooms); + } +} diff --git a/src/stores/room-list-v3/sorters/index.ts b/src/stores/room-list-v3/sorters/index.ts new file mode 100644 index 0000000000..56243fbe6b --- /dev/null +++ b/src/stores/room-list-v3/sorters/index.ts @@ -0,0 +1,12 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import type { Room } from "matrix-js-sdk/src/matrix"; + +export interface Sorter { + sort(rooms: Room[]): Room[]; +}