Files
Florian DurosandGitHub b68ecdb860 Remove legacy room list (#34040)
* refactor(room-list): migrate SpaceStore off the legacy room list store

SpaceStore.setActiveRoomInSpace iterated the legacy RoomListStore's
`orderedLists` in `TAG_ORDER`; switch it to the space-aware
RoomListStoreV3.getSortedRoomsInActiveSpace() accessor. This drops the
last non-UI dependency on the legacy store and on `TAG_ORDER` (exported
from LegacyRoomList, deleted next).

* feat(room-list)!: remove the legacy room list UI

Delete the old sublist-based room list and its components now that the
new RoomListPanel is the default. Removed: LegacyRoomList,
LegacyRoomListHeader, RoomSublist, ExtraTile, RoomTile (+ Subtitle/
CallSummary), RoomBreadcrumbs and RoomSearch, plus their styles and
tests.

LeftPanel collapses to the RoomListPanel-only path. The shared
`contextMenuBelow` helper is relocated into RoomResultContextMenus (its
only remaining consumer).

* feat(room-list)!: remove the legacy RoomListStore

The legacy sublist-based room list UI is gone, so the old
`stores/room-list` store (Algorithm, sorters, filters, layout store,
space
watcher) has no remaining consumers. Delete the directory and its tests.

MatrixChat.forgetRoom no longer calls the legacy `manualRoomUpdate`; the
new room list store removes the room on the `AfterForgetRoom` dispatch
that still fires. Drop the `mxRoomListStore`/`mxRoomListLayoutStore`
globals and the now-dead test imports.

* feat(room-list)!: remove the feature_new_room_list labs flag

The new room list is now the only room list, so remove the
feature_new_room_list labs flag and make its enabled behaviour
unconditional everywhere it was gated:

- LoggedInView: always use the resizable layout and
NEW_ROOM_LIST_MIN_WIDTH;
  drop the collapsible/minimized legacy path.
- SpaceStore: People and Favourites are dropped from metaSpaceOrder (per
the
  long-standing TODO on the removed accessor).
- MessagePreviewStore: stop appending thread replies to previews.
- Settings, SidebarUserSettingsTab, PreferencesUserSettingsTab,
  QuickSettingsButton, SpacePanel, LandmarkNavigation: drop the flag
reads and
  legacy branches.

Update the tests that toggled the flag; the People/Favourites meta space
tests covered behaviour that the flag (default on) already disabled.

* feat(room-list)!: remove the dead legacy left-panel resizer

LoggedInView still built the old `Resizer`/`CollapseDistributor` over an
`lp-resizer` ResizeHandle and persisted `mx_lhs_size`. That handle is no
longer rendered (the resizable layout is now driven by
LeftResizablePanelView + ResizerViewModel, which persists its own state
via RoomList.panelSize/RoomList.isPanelCollapsed), so the old resizer
was
inert dead code left over from the legacy room list.

Remove createResizer/loadResizer/loadResizerPreferences, the
_resizeContainer/resizeHandler refs, the ResizeHandle render, the
mx_lhs_size handling and NEW_ROOM_LIST_MIN_WIDTH, plus the unit tests
that
exercised the mocked resizer.

* feat(room-list)!: update i18n files

* refactor(room-list): remove the now-unused collapseLhs state

`collapseLhs` is write-only since the left panel no longer collapses: it
was last read by LoggedInView's `shouldUseMinimizedUI`, removed with the
feature_new_room_list flag. Drop it from MatrixChat's IState (and its
assignments), collapsing the hide/show_left_panel handlers to just the
`notifyLeftHandleResized()` call they still need, and from
LoggedInView's
IProps and the test props.

* fix(room-list): instantiate message previewers lazily

Removing the unused SettingsStore import from MessagePreviewStore (when
the
feature_new_room_list flag was dropped) changed module load order and
exposed a latent circular dependency: ReactionEventPreview imports
MessagePreviewStore, which eagerly did `new ReactionEventPreview()` at
module-eval — so importing ReactionEventPreview first (as its unit test
does) hit "ReactionEventPreview is not a constructor".

Construct the previewers lazily on first use (cached) instead of at
module
load, so nothing dereferences a mid-evaluation module. Fixes
ReactionEventPreview-test.

* test(room-list): remove `feature_new_room_list` labs flag in e2e tests

* chore: remove remaining `newRoomList` flag

* chore: cleanup theme files

* fix: restore the re-resizable TouchEvent polyfill

* chore: remove usage of breadcrumbs settings in BreadcrumbStore

* Revert "fix(room-list): instantiate message previewers lazily"

This reverts commit 4e6eedfff0449c68a96c0470a4eb425b5aec5512.

* chore: remove unused function in BreadCrumbStore

* test: remove unused fuction of BreadcrumStore in tests

* test: add tests for RoomResultContextMenu
2026-07-02 09:07:37 +00:00

130 lines
4.7 KiB
TypeScript

/*
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
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 { mocked } from "jest-mock";
import { type MatrixClient, Room } from "matrix-js-sdk/src/matrix";
import { createTestClient, flushPromises, setupAsyncStoreWithClient } from "../../test-utils";
import SettingsStore from "../../../src/settings/SettingsStore";
import { BreadcrumbsStore } from "../../../src/stores/BreadcrumbsStore";
import { Action } from "../../../src/dispatcher/actions";
import defaultDispatcher from "../../../src/dispatcher/dispatcher";
describe("BreadcrumbsStore", () => {
let store: BreadcrumbsStore;
const client: MatrixClient = createTestClient();
beforeEach(() => {
jest.resetAllMocks();
store = BreadcrumbsStore.instance;
setupAsyncStoreWithClient(store, client);
jest.spyOn(SettingsStore, "setValue").mockImplementation(() => Promise.resolve());
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
});
describe("If the feature_dynamic_room_predecessors is not enabled", () => {
beforeEach(() => {
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
});
it("Appends a room when you join", async () => {
// Sanity: no rooms initially
expect(store.rooms).toEqual([]);
// Given a room
const room = fakeRoom();
mocked(client.getRoom).mockReturnValue(room);
mocked(client.getRoomUpgradeHistory).mockReturnValue([]);
// When we hear that we have joined it
await dispatchJoinRoom(room.roomId);
// It is stored in the store's room list
expect(store.rooms.map((r) => r.roomId)).toEqual([room.roomId]);
});
it("Replaces the old room when a newer one joins", async () => {
// Given an old room and a new room
const oldRoom = fakeRoom();
const newRoom = fakeRoom();
mocked(client.getRoom).mockImplementation((roomId) => {
if (roomId === oldRoom.roomId) return oldRoom;
return newRoom;
});
// Where the new one is a predecessor of the old one
mocked(client.getRoomUpgradeHistory).mockReturnValue([oldRoom, newRoom]);
// When we hear that we joined the old room, then the new one
await dispatchJoinRoom(oldRoom.roomId);
await dispatchJoinRoom(newRoom.roomId);
// The store only has the new one
expect(store.rooms.map((r) => r.roomId)).toEqual([newRoom.roomId]);
});
it("Passes through the dynamic predecessor setting", async () => {
// Given a room
const room = fakeRoom();
mocked(client.getRoom).mockReturnValue(room);
mocked(client.getRoomUpgradeHistory).mockReturnValue([]);
// When we signal that we have joined
await dispatchJoinRoom(room.roomId);
// We pass the value of the dynamic predecessor setting through
expect(client.getRoomUpgradeHistory).toHaveBeenCalledWith(room.roomId, true, false);
});
});
describe("If the feature_dynamic_room_predecessors is enabled", () => {
beforeEach(() => {
// Turn on feature_dynamic_room_predecessors setting
jest.spyOn(SettingsStore, "getValue").mockImplementation(
(settingName) => settingName === "feature_dynamic_room_predecessors",
);
});
it("Passes through the dynamic predecessor setting", async () => {
// Given a room
const room = fakeRoom();
mocked(client.getRoom).mockReturnValue(room);
mocked(client.getRoomUpgradeHistory).mockReturnValue([]);
// When we signal that we have joined
await dispatchJoinRoom(room.roomId);
// We pass the value of the dynamic predecessor setting through
expect(client.getRoomUpgradeHistory).toHaveBeenCalledWith(room.roomId, true, true);
});
});
/**
* Send a JoinRoom event via the dispatcher, and wait for it to process.
*/
async function dispatchJoinRoom(roomId: string) {
defaultDispatcher.dispatch(
{
action: Action.JoinRoom,
roomId,
metricsTrigger: null,
},
true, // synchronous dispatch
);
// Wait for event dispatch to happen
await flushPromises();
}
let roomIdx = 0;
function fakeRoom(): Room {
roomIdx++;
return new Room(`room${roomIdx}`, client, "@user:example.com");
}
});