Room List Store: Implement rest of the primary filters (#29444)
* Implement rest of the primary filters * Support the new filters in the store
This commit is contained in:
@@ -25,6 +25,14 @@ import { EffectiveMembership, getEffectiveMembership, getEffectiveMembershipTag
|
||||
import SpaceStore from "../spaces/SpaceStore";
|
||||
import { UPDATE_HOME_BEHAVIOUR, UPDATE_SELECTED_SPACE } from "../spaces";
|
||||
import { FavouriteFilter } from "./skip-list/filters/FavouriteFilter";
|
||||
import { UnreadFilter } from "./skip-list/filters/UnreadFilter";
|
||||
import { PeopleFilter } from "./skip-list/filters/PeopleFilter";
|
||||
import { RoomsFilter } from "./skip-list/filters/RoomsFilter";
|
||||
|
||||
/**
|
||||
* These are the filters passed to the room skip list.
|
||||
*/
|
||||
const FILTERS = [new FavouriteFilter(), new UnreadFilter(), new PeopleFilter(), new RoomsFilter()];
|
||||
|
||||
/**
|
||||
* This store allows for fast retrieval of the room list in a sorted and filtered manner.
|
||||
@@ -96,7 +104,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, [new FavouriteFilter()]);
|
||||
this.roomSkipList = new RoomSkipList(sorter, FILTERS);
|
||||
const rooms = this.getRooms();
|
||||
await SpaceStore.instance.storeReadyPromise;
|
||||
this.roomSkipList.seed(rooms);
|
||||
|
||||
21
src/stores/room-list-v3/skip-list/filters/PeopleFilter.ts
Normal file
21
src/stores/room-list-v3/skip-list/filters/PeopleFilter.ts
Normal file
@@ -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 { FilterKey } from ".";
|
||||
import DMRoomMap from "../../../../utils/DMRoomMap";
|
||||
|
||||
export class PeopleFilter implements Filter {
|
||||
public matches(room: Room): boolean {
|
||||
// Match rooms that are DMs
|
||||
return !!DMRoomMap.shared().getUserIdForRoomId(room.roomId);
|
||||
}
|
||||
|
||||
public get key(): FilterKey.PeopleFilter {
|
||||
return FilterKey.PeopleFilter;
|
||||
}
|
||||
}
|
||||
21
src/stores/room-list-v3/skip-list/filters/RoomsFilter.ts
Normal file
21
src/stores/room-list-v3/skip-list/filters/RoomsFilter.ts
Normal file
@@ -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 { FilterKey } from ".";
|
||||
import DMRoomMap from "../../../../utils/DMRoomMap";
|
||||
|
||||
export class RoomsFilter implements Filter {
|
||||
public matches(room: Room): boolean {
|
||||
// This should filter rooms that are not DMs
|
||||
return !DMRoomMap.shared().getUserIdForRoomId(room.roomId);
|
||||
}
|
||||
|
||||
public get key(): FilterKey.RoomsFilter {
|
||||
return FilterKey.RoomsFilter;
|
||||
}
|
||||
}
|
||||
20
src/stores/room-list-v3/skip-list/filters/UnreadFilter.ts
Normal file
20
src/stores/room-list-v3/skip-list/filters/UnreadFilter.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 { FilterKey } from ".";
|
||||
import { RoomNotificationStateStore } from "../../../notifications/RoomNotificationStateStore";
|
||||
|
||||
export class UnreadFilter implements Filter {
|
||||
public matches(room: Room): boolean {
|
||||
return RoomNotificationStateStore.instance.getRoomState(room).isUnread;
|
||||
}
|
||||
|
||||
public get key(): FilterKey.UnreadFilter {
|
||||
return FilterKey.UnreadFilter;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,9 @@ import type { Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
export const enum FilterKey {
|
||||
FavouriteFilter,
|
||||
UnreadFilter,
|
||||
PeopleFilter,
|
||||
RoomsFilter,
|
||||
}
|
||||
|
||||
export interface Filter {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { EventType, KnownMembership, MatrixEvent, Room } from "matrix-js-sdk/src
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import type { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import type { RoomNotificationState } from "../../../../src/stores/notifications/RoomNotificationState";
|
||||
import { RoomListStoreV3Class } from "../../../../src/stores/room-list-v3/RoomListStoreV3";
|
||||
import { AsyncStoreWithClient } from "../../../../src/stores/AsyncStoreWithClient";
|
||||
import { RecencySorter } from "../../../../src/stores/room-list-v3/skip-list/sorters/RecencySorter";
|
||||
@@ -21,6 +22,8 @@ import SpaceStore from "../../../../src/stores/spaces/SpaceStore";
|
||||
import { MetaSpace, UPDATE_SELECTED_SPACE } from "../../../../src/stores/spaces";
|
||||
import { DefaultTagID } from "../../../../src/stores/room-list/models";
|
||||
import { FilterKey } from "../../../../src/stores/room-list-v3/skip-list/filters";
|
||||
import { RoomNotificationStateStore } from "../../../../src/stores/notifications/RoomNotificationStateStore";
|
||||
import DMRoomMap from "../../../../src/utils/DMRoomMap";
|
||||
|
||||
describe("RoomListStoreV3", () => {
|
||||
async function getRoomListStore() {
|
||||
@@ -37,6 +40,17 @@ describe("RoomListStoreV3", () => {
|
||||
jest.spyOn(SpaceStore.instance, "isRoomInSpace").mockImplementation((space) => space === MetaSpace.Home);
|
||||
jest.spyOn(SpaceStore.instance, "activeSpace", "get").mockImplementation(() => MetaSpace.Home);
|
||||
jest.spyOn(SpaceStore.instance, "storeReadyPromise", "get").mockImplementation(() => Promise.resolve());
|
||||
jest.spyOn(RoomNotificationStateStore.instance, "getRoomState").mockImplementation((room) => {
|
||||
const state = {
|
||||
isUnread: false,
|
||||
} as unknown as RoomNotificationState;
|
||||
return state;
|
||||
});
|
||||
jest.spyOn(DMRoomMap, "shared").mockImplementation((() => {
|
||||
return {
|
||||
getUserIdForRoomId: (id) => "",
|
||||
};
|
||||
}) as () => DMRoomMap);
|
||||
});
|
||||
|
||||
it("Provides an unsorted list of rooms", async () => {
|
||||
@@ -397,6 +411,89 @@ describe("RoomListStoreV3", () => {
|
||||
expect(result).toContain(rooms[i]);
|
||||
}
|
||||
});
|
||||
|
||||
it("supports filtering unread rooms", async () => {
|
||||
const { client, rooms } = getClientAndRooms();
|
||||
// Let's choose 5 rooms to put in space
|
||||
const { spaceRoom, roomIds } = createSpace(rooms, [6, 8, 13, 27, 75], client);
|
||||
|
||||
// Let's say 8, 27 are unread
|
||||
jest.spyOn(RoomNotificationStateStore.instance, "getRoomState").mockImplementation((room) => {
|
||||
const state = {
|
||||
isUnread: [rooms[8], rooms[27]].includes(room),
|
||||
} as unknown as RoomNotificationState;
|
||||
return state;
|
||||
});
|
||||
|
||||
setupMocks(spaceRoom, roomIds);
|
||||
const store = new RoomListStoreV3Class(dispatcher);
|
||||
await store.start();
|
||||
|
||||
// Should only give us rooms at index 8 and 27
|
||||
const result = store.getSortedRoomsInActiveSpace([FilterKey.UnreadFilter]);
|
||||
expect(result).toHaveLength(2);
|
||||
for (const i of [8, 27]) {
|
||||
expect(result).toContain(rooms[i]);
|
||||
}
|
||||
});
|
||||
|
||||
it("supports filtering by people and rooms", async () => {
|
||||
const { client, rooms } = getClientAndRooms();
|
||||
// Let's choose 5 rooms to put in space
|
||||
const { spaceRoom, roomIds } = createSpace(rooms, [6, 8, 13, 27, 75], client);
|
||||
|
||||
// Let's say 8, 27 are dms
|
||||
const ids = [8, 27].map((i) => rooms[i].roomId);
|
||||
jest.spyOn(DMRoomMap, "shared").mockImplementation((() => {
|
||||
return {
|
||||
getUserIdForRoomId: (id) => (ids.includes(id) ? "@myuser:matrix.org" : ""),
|
||||
};
|
||||
}) as () => DMRoomMap);
|
||||
|
||||
setupMocks(spaceRoom, roomIds);
|
||||
const store = new RoomListStoreV3Class(dispatcher);
|
||||
await store.start();
|
||||
|
||||
// Should only give us rooms at index 8 and 27
|
||||
const peopleRooms = store.getSortedRoomsInActiveSpace([FilterKey.PeopleFilter]);
|
||||
expect(peopleRooms).toHaveLength(2);
|
||||
for (const i of [8, 27]) {
|
||||
expect(peopleRooms).toContain(rooms[i]);
|
||||
}
|
||||
|
||||
// Rest are normal rooms
|
||||
const nonDms = store.getSortedRoomsInActiveSpace([FilterKey.RoomsFilter]);
|
||||
expect(nonDms).toHaveLength(3);
|
||||
for (const i of [6, 13, 75]) {
|
||||
expect(nonDms).toContain(rooms[i]);
|
||||
}
|
||||
});
|
||||
|
||||
it("supports multiple filters", async () => {
|
||||
const { client, rooms } = getClientAndRooms();
|
||||
// Let's choose 5 rooms to put in space
|
||||
const { spaceRoom, roomIds } = createSpace(rooms, [6, 8, 13, 27, 75], client);
|
||||
|
||||
// Let's say that 8 is a favourite room
|
||||
rooms[8].tags[DefaultTagID.Favourite] = {};
|
||||
|
||||
// Let's say 8, 27 are unread
|
||||
jest.spyOn(RoomNotificationStateStore.instance, "getRoomState").mockImplementation((room) => {
|
||||
const state = {
|
||||
isUnread: [rooms[8], rooms[27]].includes(room),
|
||||
} as unknown as RoomNotificationState;
|
||||
return state;
|
||||
});
|
||||
|
||||
setupMocks(spaceRoom, roomIds);
|
||||
const store = new RoomListStoreV3Class(dispatcher);
|
||||
await store.start();
|
||||
|
||||
// Should give us only room at 8 since that's the only room which matches both filters
|
||||
const result = store.getSortedRoomsInActiveSpace([FilterKey.UnreadFilter, FilterKey.FavouriteFilter]);
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result).toContain(rooms[8]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user