Room List Store: Implement secondary filters (#29458)

* Implement the secondary filters

* Use the new filters in the store

* Write tests
This commit is contained in:
R Midhun Suresh
2025-03-10 17:07:37 +05:30
committed by GitHub
parent 53065f9437
commit 47976447b5
6 changed files with 146 additions and 1 deletions

View File

@@ -28,11 +28,22 @@ 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";
import { InvitesFilter } from "./skip-list/filters/InvitesFilter";
import { MentionsFilter } from "./skip-list/filters/MentionsFilter";
import { LowPriorityFilter } from "./skip-list/filters/LowPriorityFilter";
/**
* These are the filters passed to the room skip list.
*/
const FILTERS = [new FavouriteFilter(), new UnreadFilter(), new PeopleFilter(), new RoomsFilter()];
const FILTERS = [
new FavouriteFilter(),
new UnreadFilter(),
new PeopleFilter(),
new RoomsFilter(),
new InvitesFilter(),
new MentionsFilter(),
new LowPriorityFilter(),
];
/**
* This store allows for fast retrieval of the room list in a sorted and filtered manner.

View 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, KnownMembership } from "matrix-js-sdk/src/matrix";
import type { Filter } from ".";
import { FilterKey } from ".";
export class InvitesFilter implements Filter {
public matches(room: Room): boolean {
return room.getMyMembership() === KnownMembership.Invite;
}
public get key(): FilterKey.InvitesFilter {
return FilterKey.InvitesFilter;
}
}

View 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 { DefaultTagID } from "../../../room-list/models";
export class LowPriorityFilter implements Filter {
public matches(room: Room): boolean {
return !!room.tags[DefaultTagID.LowPriority];
}
public get key(): FilterKey.LowPriorityFilter {
return FilterKey.LowPriorityFilter;
}
}

View 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 MentionsFilter implements Filter {
public matches(room: Room): boolean {
return RoomNotificationStateStore.instance.getRoomState(room).hasMentions;
}
public get key(): FilterKey.MentionsFilter {
return FilterKey.MentionsFilter;
}
}

View File

@@ -11,6 +11,9 @@ export const enum FilterKey {
UnreadFilter,
PeopleFilter,
RoomsFilter,
LowPriorityFilter,
MentionsFilter,
InvitesFilter,
}
export interface Filter {

View File

@@ -469,6 +469,77 @@ describe("RoomListStoreV3", () => {
}
});
it("supports filtering invited rooms", async () => {
const { client, rooms } = getClientAndRooms();
// Let's add 5 rooms that we are invited to
const invitedRooms = getMockedRooms(client, 5);
for (const room of invitedRooms) {
room.getMyMembership = jest.fn().mockReturnValue(KnownMembership.Invite);
}
rooms.push(...invitedRooms);
// Let's choose 5 rooms to put in space
const { spaceRoom, roomIds } = createSpace(rooms, [6, 8, 100, 101, 102, 103, 104], client);
setupMocks(spaceRoom, roomIds);
const store = new RoomListStoreV3Class(dispatcher);
await store.start();
const result = store.getSortedRoomsInActiveSpace([FilterKey.InvitesFilter]);
expect(result).toHaveLength(5);
for (const room of invitedRooms) {
expect(result).toContain(room);
}
});
it("supports filtering by mentions", 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 have mentions
jest.spyOn(RoomNotificationStateStore.instance, "getRoomState").mockImplementation((room) => {
const state = {
hasMentions: [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.MentionsFilter]);
expect(result).toHaveLength(2);
for (const i of [8, 27]) {
expect(result).toContain(rooms[i]);
}
});
it("supports filtering low priority 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 that 8, 27 an 75 are low priority rooms
[8, 27, 75].forEach((i) => {
rooms[i].tags[DefaultTagID.LowPriority] = {};
});
setupMocks(spaceRoom, roomIds);
const store = new RoomListStoreV3Class(dispatcher);
await store.start();
// Sorted, filtered rooms should be 8, 27 and 75
const result = store.getSortedRoomsInActiveSpace([FilterKey.LowPriorityFilter]);
expect(result).toHaveLength(3);
for (const i of [8, 27, 75]) {
expect(result).toContain(rooms[i]);
}
});
it("supports multiple filters", async () => {
const { client, rooms } = getClientAndRooms();
// Let's choose 5 rooms to put in space