Room list: drag and drop rooms into sections (#33366)

* chore: add dnd kit deps

* chore: patch dnd kit to fix ts error

* feat(sc): add drag-and-drop to room list item and wrapper

* feat(sc): make the room list header a droppable element

* feat(sc): add dnd to room list view

* feat(tags): can tag room as CHAT

* feat(vm): implement `changeRoomSection`

* feat(sc): disable dragging in flat list

* fix: disable keyboard navigation when dragging element

* test(sc): update snapshots

* test(sc): add dnd test

* test(e2e): add e2e tests for room drag and drop

* test(vm): add tests for changeRoomSection

* fix: remove focus visible when dropping with the mouse

* test(playwright): update existing screenshots

* chore(sc): move numbers out of main build

The Ew RecorderWorklet imports shared component bundle. However if the
bundle uses some deps using document/window which, the worklet will not
work.

The solution is to put the used functions into a separate bundle.

* doc(sc): add subpath import into README

* doc: typo barrel/bundle

* test: improve test expect

* refactor: add utils to section tag

* fix: incorrect check in tagRoom

* fix: add doc about dndkit tunning
This commit is contained in:
Florian Duros
2026-05-13 09:06:22 +00:00
committed by GitHub
parent 97da3be67a
commit 85aca65a81
50 changed files with 4845 additions and 3871 deletions
@@ -0,0 +1,21 @@
/*
* 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 { type Room } from "matrix-js-sdk/src/matrix";
import { type TagID } from "../../stores/room-list-v3/skip-list/tag";
import { getTagsForRoom } from "./getTagsForRoom";
import { isSectionTag } from "../../stores/room-list-v3/section";
/**
* Get the section tag for a given room.
* @param room The room to get the section tag for.
* @returns The section tag ID or null if none found.
*/
export function getSectionTagForRoom(room: Room): TagID | null {
return getTagsForRoom(room).find((t) => isSectionTag(t)) ?? null;
}
+11 -11
View File
@@ -9,11 +9,11 @@ Please see LICENSE files in the repository root for full details.
import { type Room } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import { DefaultTagID, type TagID } from "../../stores/room-list-v3/skip-list/tag";
import { type TagID } from "../../stores/room-list-v3/skip-list/tag";
import RoomListActions from "../../actions/RoomListActions";
import dis from "../../dispatcher/dispatcher";
import { getTagsForRoom } from "./getTagsForRoom";
import { isCustomSectionTag } from "../../stores/room-list-v3/section";
import { CHATS_TAG, isSectionTag } from "../../stores/room-list-v3/section";
import { getSectionTagForRoom } from "./getSectionTagForRoom";
/**
* Toggle tag for a given room.
@@ -23,19 +23,19 @@ import { isCustomSectionTag } from "../../stores/room-list-v3/section";
* @param tagId The tag to invert
*/
export function tagRoom(room: Room, tagId: TagID): void {
if (tagId !== DefaultTagID.Favourite && tagId !== DefaultTagID.LowPriority && !isCustomSectionTag(tagId)) {
logger.warn(`Unexpected tag ${tagId} applied to ${room.roomId}`);
const isChatTag = tagId === CHATS_TAG;
const tag = isChatTag ? null : tagId;
if (!isSectionTag(tagId)) {
logger.warn(`Unexpected tag ${tag} applied to ${room.roomId}`);
return;
}
// Find the section tag currently applied (Fav, LowPriority, or custom) — at most one exists
const currentSectionTag =
getTagsForRoom(room).find(
(t) => t === DefaultTagID.Favourite || t === DefaultTagID.LowPriority || isCustomSectionTag(t),
) ?? null;
const currentSectionTag = getSectionTagForRoom(room);
const isApplied = currentSectionTag === tagId;
const isApplied = currentSectionTag === tag;
const removeTag = currentSectionTag;
const addTag = isApplied ? null : tagId;
const addTag = isApplied ? null : tag;
dis.dispatch(RoomListActions.tagRoom(room.client, room, removeTag, addTag));
}