mirror of
https://github.com/vector-im/element-web.git
synced 2026-05-04 19:56:45 +02:00
feat: add custom section to room list store v3
This commit is contained in:
parent
b39210aad5
commit
97035fda32
@ -40,6 +40,7 @@ import { DefaultTagID } from "./skip-list/tag";
|
||||
import { ExcludeTagsFilter } from "./skip-list/filters/ExcludeTagsFilter";
|
||||
import { TagFilter } from "./skip-list/filters/TagFilter";
|
||||
import { filterBoolean } from "../../utils/arrays";
|
||||
import { createSection } from "./section";
|
||||
|
||||
/**
|
||||
* These are the filters passed to the room skip list.
|
||||
@ -59,6 +60,8 @@ export enum RoomListStoreV3Event {
|
||||
ListsUpdate = "lists_update",
|
||||
// The event which is called when the room list is loaded.
|
||||
ListsLoaded = "lists_loaded",
|
||||
/** Fired when a new section is created in the room list. */
|
||||
SectionCreated = "section_created",
|
||||
}
|
||||
|
||||
// The result object for returning rooms from the store
|
||||
@ -89,6 +92,8 @@ export const CHATS_TAG = "chats";
|
||||
|
||||
export const LISTS_UPDATE_EVENT = RoomListStoreV3Event.ListsUpdate;
|
||||
export const LISTS_LOADED_EVENT = RoomListStoreV3Event.ListsLoaded;
|
||||
export const SECTION_CREATED_EVENT = RoomListStoreV3Event.SectionCreated;
|
||||
|
||||
/**
|
||||
* This store allows for fast retrieval of the room list in a sorted and filtered manner.
|
||||
* This is the third such implementation hence the "V3".
|
||||
@ -108,7 +113,7 @@ export class RoomListStoreV3Class extends AsyncStoreWithClient<EmptyObject> {
|
||||
/**
|
||||
* Defines the display order of sections.
|
||||
*/
|
||||
private readonly sortedTags: string[] = [DefaultTagID.Favourite, CHATS_TAG, DefaultTagID.LowPriority];
|
||||
private sortedTags: string[] = [DefaultTagID.Favourite, CHATS_TAG, DefaultTagID.LowPriority];
|
||||
|
||||
private readonly msc3946ProcessDynamicPredecessor: boolean;
|
||||
|
||||
@ -125,6 +130,7 @@ export class RoomListStoreV3Class extends AsyncStoreWithClient<EmptyObject> {
|
||||
this.onActiveSpaceChanged();
|
||||
});
|
||||
SpaceStore.instance.on(UPDATE_HOME_BEHAVIOUR, () => this.onActiveSpaceChanged());
|
||||
SettingsStore.watchSetting("RoomList.OrderedCustomSections", null, () => this.onOrderedCustomSectionsChange());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -196,6 +202,8 @@ export class RoomListStoreV3Class extends AsyncStoreWithClient<EmptyObject> {
|
||||
|
||||
protected async onReady(): Promise<any> {
|
||||
if (this.roomSkipList?.initialized || !this.matrixClient) return;
|
||||
this.loadCustomSections();
|
||||
|
||||
const sorter = this.getPreferredSorter(this.matrixClient.getSafeUserId());
|
||||
|
||||
this.roomSkipList = new RoomSkipList(sorter, this.getSkipListFilters());
|
||||
@ -463,6 +471,37 @@ export class RoomListStoreV3Class extends AsyncStoreWithClient<EmptyObject> {
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle changes to the order of custom sections.
|
||||
* Reloads the custom sections, updates the skip list filters to reflect the new order and emits an update.
|
||||
* Emit {@link LISTS_UPDATE_EVENT}.
|
||||
*/
|
||||
private onOrderedCustomSectionsChange(): void {
|
||||
this.loadCustomSections();
|
||||
if (!this.roomSkipList) return;
|
||||
this.roomSkipList.useNewFilters(this.getSkipListFilters());
|
||||
this.scheduleEmit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new section.
|
||||
* Emits {@link SECTION_CREATED_EVENT} and {@link LISTS_UPDATE_EVENT} if the section was successfully created.
|
||||
*/
|
||||
public async createSection(): Promise<void> {
|
||||
const sectionIsCreated = await createSection();
|
||||
if (!sectionIsCreated) return;
|
||||
this.emit(SECTION_CREATED_EVENT);
|
||||
this.scheduleEmit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the custom sections from the settings store and update the sorted tags.
|
||||
*/
|
||||
private loadCustomSections(): void {
|
||||
const orderedCustomSections = SettingsStore.getValue("RoomList.OrderedCustomSections");
|
||||
this.sortedTags = [DefaultTagID.Favourite, ...orderedCustomSections, CHATS_TAG, DefaultTagID.LowPriority];
|
||||
}
|
||||
}
|
||||
|
||||
export default class RoomListStoreV3 {
|
||||
|
||||
@ -14,9 +14,11 @@ import type { RoomNotificationState } from "../../../../src/stores/notifications
|
||||
import {
|
||||
CHATS_TAG,
|
||||
LISTS_UPDATE_EVENT,
|
||||
SECTION_CREATED_EVENT,
|
||||
RoomListStoreV3Class,
|
||||
type Section,
|
||||
} from "../../../../src/stores/room-list-v3/RoomListStoreV3";
|
||||
import * as sectionModule from "../../../../src/stores/room-list-v3/section";
|
||||
import { AsyncStoreWithClient } from "../../../../src/stores/AsyncStoreWithClient";
|
||||
import { RecencySorter } from "../../../../src/stores/room-list-v3/skip-list/sorters/RecencySorter";
|
||||
import { mkEvent, mkMessage, mkSpace, mkStubRoom, stubClient, upsertRoomStateEvents } from "../../../test-utils";
|
||||
@ -830,6 +832,7 @@ describe("RoomListStoreV3", () => {
|
||||
function enableSections(): void {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting: string) => {
|
||||
if (setting === "feature_room_list_sections") return true;
|
||||
if (setting === "RoomList.OrderedCustomSections") return [];
|
||||
return false;
|
||||
});
|
||||
}
|
||||
@ -1007,6 +1010,84 @@ describe("RoomListStoreV3", () => {
|
||||
const favSection = findSection(sections, DefaultTagID.Favourite)!;
|
||||
expect(favSection.rooms).toContain(rooms[3]);
|
||||
});
|
||||
|
||||
describe("createSection", () => {
|
||||
it("emits SECTION_CREATED_EVENT and LISTS_UPDATE_EVENT when section is created", async () => {
|
||||
enableSections();
|
||||
getClientAndRooms();
|
||||
jest.spyOn(sectionModule, "createSection").mockResolvedValue(true);
|
||||
|
||||
const store = new RoomListStoreV3Class(dispatcher);
|
||||
await store.start();
|
||||
|
||||
const sectionCreatedListener = jest.fn();
|
||||
const listsUpdateListener = jest.fn();
|
||||
store.on(SECTION_CREATED_EVENT, sectionCreatedListener);
|
||||
store.on(LISTS_UPDATE_EVENT, listsUpdateListener);
|
||||
|
||||
await store.createSection();
|
||||
|
||||
expect(sectionCreatedListener).toHaveBeenCalled();
|
||||
expect(listsUpdateListener).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not emit when section creation is cancelled", async () => {
|
||||
enableSections();
|
||||
getClientAndRooms();
|
||||
jest.spyOn(sectionModule, "createSection").mockResolvedValue(false);
|
||||
|
||||
const store = new RoomListStoreV3Class(dispatcher);
|
||||
await store.start();
|
||||
|
||||
const sectionCreatedListener = jest.fn();
|
||||
store.on(SECTION_CREATED_EVENT, sectionCreatedListener);
|
||||
|
||||
await store.createSection();
|
||||
|
||||
expect(sectionCreatedListener).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("updates sections when RoomList.OrderedCustomSections setting changes", async () => {
|
||||
enableSections();
|
||||
const { rooms } = getClientAndRooms();
|
||||
|
||||
let settingsWatcher: (settingName: string) => void = () => {};
|
||||
jest.spyOn(SettingsStore, "watchSetting").mockImplementation((settingName, _roomId, callback) => {
|
||||
if (settingName === "RoomList.OrderedCustomSections") settingsWatcher = callback as () => void;
|
||||
return "watcher-id";
|
||||
});
|
||||
|
||||
const customTag = "element.io.section.custom";
|
||||
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting: string) => {
|
||||
if (setting === "feature_room_list_sections") return true;
|
||||
if (setting === "RoomList.OrderedCustomSections") return [];
|
||||
return false;
|
||||
});
|
||||
|
||||
const store = new RoomListStoreV3Class(dispatcher);
|
||||
await store.start();
|
||||
|
||||
// Initial state: 3 sections (Favourite, Chats, LowPriority)
|
||||
expect(store.getSortedRoomsInActiveSpace().sections).toHaveLength(3);
|
||||
|
||||
// Mark a room with the custom tag and update the settings
|
||||
rooms[0].tags = { [customTag]: { order: 0 } };
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting: string) => {
|
||||
if (setting === "feature_room_list_sections") return true;
|
||||
if (setting === "RoomList.OrderedCustomSections") return [customTag];
|
||||
return false;
|
||||
});
|
||||
|
||||
// Trigger the settings watcher
|
||||
settingsWatcher("RoomList.OrderedCustomSections");
|
||||
|
||||
// Now there should be 4 sections (Favourite, custom, Chats, LowPriority)
|
||||
expect(store.getSortedRoomsInActiveSpace().sections).toHaveLength(4);
|
||||
const customSection = findSection(store.getSortedRoomsInActiveSpace().sections, customTag)!;
|
||||
expect(customSection.rooms).toContain(rooms[0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Muted rooms", () => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user