Implement filtering within skip list
This commit is contained in:
@@ -8,6 +8,7 @@ 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 } from "./skip-list/filters";
|
||||
import { AsyncStoreWithClient } from "../AsyncStoreWithClient";
|
||||
import SettingsStore from "../../settings/SettingsStore";
|
||||
import { VisibilityProvider } from "../room-list/filters/VisibilityProvider";
|
||||
@@ -16,10 +17,12 @@ import { LISTS_UPDATE_EVENT } from "../room-list/RoomListStore";
|
||||
import { RoomSkipList } from "./skip-list/RoomSkipList";
|
||||
import { RecencySorter } from "./skip-list/sorters/RecencySorter";
|
||||
import { AlphabeticSorter } from "./skip-list/sorters/AlphabeticSorter";
|
||||
import { FavouriteFilter } from "./skip-list/filters/FavouriteFilter";
|
||||
|
||||
export class RoomListStoreV3Class extends AsyncStoreWithClient<EmptyObject> {
|
||||
private roomSkipList?: RoomSkipList;
|
||||
private readonly msc3946ProcessDynamicPredecessor: boolean;
|
||||
private filters: Filter[] = [new FavouriteFilter()];
|
||||
|
||||
public constructor(dispatcher: MatrixDispatcher) {
|
||||
super(dispatcher);
|
||||
@@ -54,7 +57,7 @@ export class RoomListStoreV3Class extends AsyncStoreWithClient<EmptyObject> {
|
||||
protected async onReady(): Promise<any> {
|
||||
if (this.roomSkipList?.initialized || !this.matrixClient) return;
|
||||
const sorter = new RecencySorter(this.matrixClient.getSafeUserId());
|
||||
this.roomSkipList = new RoomSkipList(sorter);
|
||||
this.roomSkipList = new RoomSkipList(sorter, this.filters);
|
||||
const rooms = this.getRooms();
|
||||
this.roomSkipList.seed(rooms);
|
||||
this.emit(LISTS_UPDATE_EVENT);
|
||||
|
||||
@@ -6,6 +6,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import type { Room } from "matrix-js-sdk/src/matrix";
|
||||
import type { Filter, Filters } from "./filters";
|
||||
|
||||
/**
|
||||
* Room skip list stores room nodes.
|
||||
@@ -26,4 +27,18 @@ export class RoomNode {
|
||||
* eg: previous[i] gives the previous room node from this room node in level i.
|
||||
*/
|
||||
public previous: RoomNode[] = [];
|
||||
|
||||
/**
|
||||
* Aggregates all the filters that apply to this room.
|
||||
* eg: if filters[Filter.FavouriteFilter] is true, then this room is a favourite
|
||||
* room.
|
||||
*/
|
||||
public filters: Map<Filters, boolean> = new Map();
|
||||
|
||||
public calculateFilters(filters: Filter[]): void {
|
||||
for (const filter of filters) {
|
||||
const matchesFilter = filter.matches(this.room);
|
||||
this.filters.set(filter.key, matchesFilter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import type { Room } from "matrix-js-sdk/src/matrix";
|
||||
import type { Sorter } from "./sorters";
|
||||
import type { Filter, Filters } from "./filters";
|
||||
import { RoomNode } from "./RoomNode";
|
||||
import { shouldPromote } from "./utils";
|
||||
import { Level } from "./Level";
|
||||
@@ -20,7 +21,10 @@ export class RoomSkipList implements Iterable<Room> {
|
||||
private roomNodeMap: Map<string, RoomNode> = new Map();
|
||||
public initialized: boolean = false;
|
||||
|
||||
public constructor(private sorter: Sorter) {}
|
||||
public constructor(
|
||||
private sorter: Sorter,
|
||||
private filters: Filter[] = [],
|
||||
) {}
|
||||
|
||||
private reset(): void {
|
||||
this.levels = [new Level(0)];
|
||||
@@ -35,6 +39,7 @@ export class RoomSkipList implements Iterable<Room> {
|
||||
const sortedRoomNodes = this.sorter.sort(rooms).map((room) => new RoomNode(room));
|
||||
let currentLevel = this.levels[0];
|
||||
for (const node of sortedRoomNodes) {
|
||||
node.calculateFilters(this.filters);
|
||||
currentLevel.setNext(node);
|
||||
this.roomNodeMap.set(node.room.roomId, node);
|
||||
}
|
||||
@@ -81,6 +86,7 @@ export class RoomSkipList implements Iterable<Room> {
|
||||
this.removeRoom(room);
|
||||
|
||||
const newNode = new RoomNode(room);
|
||||
newNode.calculateFilters(this.filters);
|
||||
this.roomNodeMap.set(room.roomId, newNode);
|
||||
|
||||
/**
|
||||
@@ -159,6 +165,10 @@ export class RoomSkipList implements Iterable<Room> {
|
||||
return new SortedRoomIterator(this.levels[0].head!);
|
||||
}
|
||||
|
||||
public getFiltered(filterKeys: Filters[]): SortedFilteredIterator {
|
||||
return new SortedFilteredIterator(this.levels[0].head!, filterKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of rooms currently in the skip list.
|
||||
*/
|
||||
@@ -179,3 +189,26 @@ class SortedRoomIterator implements Iterator<Room> {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class SortedFilteredIterator implements Iterator<Room> {
|
||||
public constructor(
|
||||
private current: RoomNode,
|
||||
private filterKeys: Filters[],
|
||||
) {}
|
||||
|
||||
public [Symbol.iterator](): SortedFilteredIterator {
|
||||
return this;
|
||||
}
|
||||
|
||||
public next(): IteratorResult<Room> {
|
||||
let current = this.current;
|
||||
while (current && this.filterKeys.some((key) => !current.filters.get(key))) {
|
||||
current = current.next[0];
|
||||
}
|
||||
if (!current) return { value: undefined, done: true };
|
||||
this.current = current.next[0];
|
||||
return {
|
||||
value: current.room,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
20
src/stores/room-list-v3/skip-list/filters/FavouriteFilter.ts
Normal file
20
src/stores/room-list-v3/skip-list/filters/FavouriteFilter.ts
Normal file
@@ -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 { Filters } from ".";
|
||||
import { DefaultTagID } from "../../../room-list/models";
|
||||
|
||||
export class FavouriteFilter implements Filter {
|
||||
public matches(room: Room): boolean {
|
||||
return !!room.tags[DefaultTagID.Favourite];
|
||||
}
|
||||
|
||||
public get key(): Filters.FavouriteFilter {
|
||||
return Filters.FavouriteFilter;
|
||||
}
|
||||
}
|
||||
25
src/stores/room-list-v3/skip-list/filters/index.ts
Normal file
25
src/stores/room-list-v3/skip-list/filters/index.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
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 Filters {
|
||||
FavouriteFilter,
|
||||
}
|
||||
|
||||
export interface Filter {
|
||||
/**
|
||||
* Boolean return value indicates whether this room satisfies
|
||||
* the filter condition.
|
||||
*/
|
||||
matches(room: Room): boolean;
|
||||
|
||||
/**
|
||||
* An unique string used to identify a given filter.
|
||||
*/
|
||||
key: Filters;
|
||||
}
|
||||
Reference in New Issue
Block a user