-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Room list: edit or remove custom sections #33283
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 12 commits
849e976
3da55bb
4ccf2b7
a279708
dee195b
3eb11d4
fe2d7ae
cb37acb
67c6487
68878b6
0caaac2
3b40921
b0f6ce4
3e174f4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| /* | ||
| * Copyright 2026 Element Creations 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. | ||
| */ | ||
|
|
||
| .mx_RemoveSectionDialog { | ||
| color: var(--cpd-color-text-primary); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,11 @@ | |
| import { _t } from "../../../languageHandler"; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Padding around the title is EW default. Compound and EW have different dialog designs and until we decide to update the dialog styles this kind of differences will exist imo |
||
|
|
||
| interface CreateSectionDialogProps { | ||
| /** | ||
| * The name of the section being edited if defined. Otherwise, create a new section. | ||
| */ | ||
| sectionToEdit?: string; | ||
|
|
||
| /** | ||
| * Callback called when the dialog is closed. | ||
| * @param shouldCreateSection Whether a section should be created or not. This will be false if the user cancels the dialog. | ||
|
|
@@ -25,15 +30,16 @@ | |
| /** | ||
| * Dialog shown to the user to create a new section in the room list. | ||
| */ | ||
| export function CreateSectionDialog({ onFinished }: CreateSectionDialogProps): JSX.Element { | ||
| const [value, setValue] = useState(""); | ||
| export function CreateSectionDialog({ onFinished, sectionToEdit }: CreateSectionDialogProps): JSX.Element { | ||
|
Check warning on line 33 in apps/web/src/components/views/dialogs/CreateSectionDialog.tsx
|
||
| const isEdition = Boolean(sectionToEdit); | ||
| const [value, setValue] = useState(sectionToEdit ?? ""); | ||
| const isInvalid = Boolean(value.trim().length === 0); | ||
|
|
||
| return ( | ||
| <BaseDialog | ||
| className="mx_CreateSectionDialog" | ||
| onFinished={() => onFinished(false, value)} | ||
| title={_t("create_section_dialog|title")} | ||
| title={isEdition ? _t("create_section_dialog|title_edition") : _t("create_section_dialog|title")} | ||
| hasCancel={true} | ||
| > | ||
| <Flex gap="var(--cpd-space-6x)" direction="column" className="mx_CreateSectionDialog_content"> | ||
|
|
@@ -43,18 +49,24 @@ | |
| <Form.Root | ||
| className="mx_CreateSectionDialog_form" | ||
| onSubmit={(e) => { | ||
| onFinished(true, value); | ||
| e.preventDefault(); | ||
| if (!isInvalid) onFinished(true, value); | ||
| }} | ||
| > | ||
| <Form.Field name="sectionName"> | ||
| <Form.Label> {_t("create_section_dialog|label")}</Form.Label> | ||
| <Form.TextControl onChange={(evt) => setValue(evt.target.value)} required={true} /> | ||
| <Form.TextControl | ||
| value={value} | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fwiw, in my testing I could insert whitetext before or after my section name and it would not be rendered in the list but accepted by this dialog
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| onChange={(evt) => setValue(evt.target.value)} | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also noting it's possible to have duplicate section names
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep that is totally possible |
||
| required={true} | ||
| /> | ||
| </Form.Field> | ||
| </Form.Root> | ||
| </Flex> | ||
| <DialogButtons | ||
| primaryButton={_t("create_section_dialog|create_section")} | ||
| primaryButton={ | ||
| isEdition ? _t("create_section_dialog|edit_section") : _t("create_section_dialog|create_section") | ||
| } | ||
| primaryDisabled={isInvalid} | ||
| hasCancel={true} | ||
| onCancel={() => onFinished(false, "")} | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| /* | ||
| * Copyright 2026 Element Creations 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 React from "react"; | ||
| import { type JSX } from "react"; | ||
| import { Text } from "@vector-im/compound-web"; | ||
|
|
||
| import { _t } from "../../../languageHandler"; | ||
| import BaseDialog from "./BaseDialog"; | ||
| import DialogButtons from "../elements/DialogButtons"; | ||
|
|
||
| interface RemoveSectionDialogProps { | ||
| onFinished: (shouldRemoveSection: boolean) => void; | ||
| } | ||
|
|
||
| /** | ||
| * Dialog shown to the user to remove section in the room list. | ||
| */ | ||
| export function RemoveSectionDialog({ onFinished }: RemoveSectionDialogProps): JSX.Element { | ||
|
Check warning on line 23 in apps/web/src/components/views/dialogs/RemoveSectionDialog.tsx
|
||
| return ( | ||
| <BaseDialog | ||
| className="mx_RemoveSectionDialog" | ||
| onFinished={() => onFinished(false)} | ||
| title={_t("remove_section_dialog|title")} | ||
| hasCancel={true} | ||
| > | ||
| <Text as="span">{_t("remove_section_dialog|confirmation")}</Text> | ||
| <Text as="span">{_t("remove_section_dialog|description")}</Text> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ooi, is there a reason for the two spans rather than combining into one?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To have the same formatting than in figma. You prefer to put a |
||
| <DialogButtons | ||
| primaryButton={_t("remove_section_dialog|remove_section")} | ||
| hasCancel={true} | ||
| onCancel={() => onFinished(false)} | ||
| onPrimaryButtonClick={() => onFinished(true)} | ||
| /> | ||
| </BaseDialog> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,10 +5,13 @@ | |
| * Please see LICENSE files in the repository root for full details. | ||
| */ | ||
|
|
||
| import { logger } from "matrix-js-sdk/src/logger"; | ||
|
|
||
| import { SettingLevel } from "../../settings/SettingLevel"; | ||
| import SettingsStore from "../../settings/SettingsStore"; | ||
| import Modal from "../../Modal"; | ||
| import { CreateSectionDialog } from "../../components/views/dialogs/CreateSectionDialog"; | ||
| import { RemoveSectionDialog } from "../../components/views/dialogs/RemoveSectionDialog"; | ||
|
|
||
| type Tag = string; | ||
|
|
||
|
|
@@ -69,3 +72,51 @@ export async function createSection(): Promise<string | undefined> { | |
| await SettingsStore.setValue("RoomList.OrderedCustomSections", null, SettingLevel.ACCOUNT, orderedSections); | ||
| return tag; | ||
| } | ||
|
|
||
| /** | ||
| * Edits an existing custom section by showing a dialog to the user to enter the new section name. If the user confirms, it updates the section data in the settings. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Holy line length batman (it would be helpful if this was kept to around 120 chars per line so it was easier to read)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no currently formating rules enforcing it |
||
| * @param tag - The tag of the section to edit. | ||
| */ | ||
| export async function editSection(tag: string): Promise<void> { | ||
| const sectionData = SettingsStore.getValue("RoomList.CustomSectionData") || {}; | ||
| const section = sectionData[tag]; | ||
| if (!section) { | ||
| logger.info("Unknown section tag, cannot edit section", tag); | ||
| return; | ||
| } | ||
|
|
||
| const modal = Modal.createDialog(CreateSectionDialog, { sectionToEdit: section.name }); | ||
|
|
||
| const [shouldEditSection, newName] = await modal.finished; | ||
| const isSameName = newName === section.name; | ||
| if (!shouldEditSection || !newName || isSameName) return; | ||
|
|
||
| // Save the new name | ||
| sectionData[tag].name = newName; | ||
| await SettingsStore.setValue("RoomList.CustomSectionData", null, SettingLevel.ACCOUNT, sectionData); | ||
| } | ||
|
|
||
| /** | ||
| * Deletes a custom section by showing a confirmation dialog to the user. If the user confirms, it removes the section data from the settings and updates the ordered list of sections. | ||
| * @param tag - The tag of the section to delete. | ||
| */ | ||
| export async function deleteSection(tag: string): Promise<void> { | ||
| const sectionData = SettingsStore.getValue("RoomList.CustomSectionData"); | ||
| if (!sectionData[tag]) { | ||
| logger.info("Unknown section tag, cannot delete section", tag); | ||
| return; | ||
| } | ||
|
|
||
| const modal = Modal.createDialog(RemoveSectionDialog); | ||
| const [shouldRemoveSection] = await modal.finished; | ||
| if (!shouldRemoveSection) return; | ||
|
|
||
| // Remove the section from the ordered list of sections | ||
| const orderedSections = SettingsStore.getValue("RoomList.OrderedCustomSections"); | ||
| const newOrderedSections = orderedSections.filter((sectionTag) => sectionTag !== tag); | ||
| await SettingsStore.setValue("RoomList.OrderedCustomSections", null, SettingLevel.ACCOUNT, newOrderedSections); | ||
|
|
||
| // Remove the section data | ||
| delete sectionData[tag]; | ||
| await SettingsStore.setValue("RoomList.CustomSectionData", null, SettingLevel.ACCOUNT, sectionData); | ||
| } | ||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't have one for CreateSectionDialog. Presumably this is an archaic bug we're fixing, can you leave a comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does https://github.com/element-hq/element-web/blob/develop/apps/web/res/css/views/dialogs/_CreateSectionDialog.pcss#L9
Not an archaic bug, EW dialog uses another color by default