Room list: edit or remove custom sections (#33283)

* feat(sc): add section menu to section header

* feat(rls): add edit and remove sections

* feat(dialog): add editing mode to CreateSectionDialog

* feat(dialog): add remove section dialog

* feat(vm): wire up vm and stores

* test: update existing snapshots

* test(e2e): add playwright tests to edit and remove a section

* chore: fix remove section i18n key

* fix: able to send empty sections

* chore: update create section editing docs

* chore: remove useless fallback

* chore: add logs when section is unknown

* feat: use different wording when removing an empty section

* fix: only animate the chevron icon in the section header

* fix: change dialog subtitle weight to medium
This commit is contained in:
Florian Duros
2026-04-28 10:16:34 +00:00
committed by GitHub
parent 1dd5748d6f
commit c363d2eb82
22 changed files with 1090 additions and 160 deletions
@@ -15,6 +15,9 @@ import {
import { RoomNotificationStateStore } from "../../stores/notifications/RoomNotificationStateStore";
import { NotificationStateEvents } from "../../stores/notifications/NotificationState";
import { type RoomNotificationState } from "../../stores/notifications/RoomNotificationState";
import SettingsStore from "../../settings/SettingsStore";
import { DefaultTagID } from "../../stores/room-list-v3/skip-list/tag";
import RoomListStoreV3, { CHATS_TAG } from "../../stores/room-list-v3/RoomListStoreV3";
interface RoomListSectionHeaderViewModelProps {
tag: string;
@@ -42,7 +45,19 @@ export class RoomListSectionHeaderViewModel
private readonly expandedBySpace = new Map<string, boolean>();
public constructor(props: RoomListSectionHeaderViewModelProps) {
super(props, { id: props.tag, title: props.title, isExpanded: true, isUnread: false });
const isDefaultSection =
props.tag === DefaultTagID.Favourite || props.tag === DefaultTagID.LowPriority || props.tag === CHATS_TAG;
super(props, {
id: props.tag,
title: props.title,
isExpanded: true,
isUnread: false,
displaySectionMenu: !isDefaultSection,
});
const sectionWatherRef = SettingsStore.watchSetting("RoomList.CustomSectionData", null, () =>
this.onCustomSectionDataChange(),
);
this.disposables.track(() => SettingsStore.unwatchSetting(sectionWatherRef));
}
public onClick = (): void => {
@@ -120,4 +135,25 @@ export class RoomListSectionHeaderViewModel
this.roomNotificationStates.clear();
super.dispose();
}
/**
* Handle changes to custom section data.
*/
private onCustomSectionDataChange(): void {
const customSectionData = SettingsStore.getValue("RoomList.CustomSectionData") || {};
const sectionData = customSectionData[this.props.tag];
if (sectionData) {
this.snapshot.merge({ title: sectionData.name });
}
}
public editSection = async (): Promise<void> => {
await RoomListStoreV3.instance.editSection(this.props.tag);
};
public removeSection = async (): Promise<void> => {
// There is one notification state per room in the section
const isEmpty = this.roomNotificationStates.size === 0;
await RoomListStoreV3.instance.removeSection(this.props.tag, isEmpty);
};
}