Room list: fix expanded/collapse state of sections (#33074)

* fix: section being empty in flat list mode

When switching space (or removing a section later), if the Chat section
is collpased and the room list is in flat list mode in the other space,
the room list is empty.

The fix forces the section to be in expanded state if in flat list mode

* fix: store section expanded state by space
This commit is contained in:
Florian Duros
2026-04-08 13:44:52 +00:00
committed by GitHub
parent ce498ef983
commit 121c2d18e9
4 changed files with 106 additions and 8 deletions
@@ -19,6 +19,10 @@ import { type RoomNotificationState } from "../../stores/notifications/RoomNotif
interface RoomListSectionHeaderViewModelProps {
tag: string;
title: string;
/**
* The ID of the current space.
*/
spaceId: string;
onToggleExpanded: (isExpanded: boolean) => void;
}
@@ -31,12 +35,19 @@ export class RoomListSectionHeaderViewModel
*/
private roomNotificationStates = new Set<RoomNotificationState>();
/**
* Tracks the expanded/collapsed state per space.
* Key is spaceId. Defaults to expanded if not set.
*/
private readonly expandedBySpace = new Map<string, boolean>();
public constructor(props: RoomListSectionHeaderViewModelProps) {
super(props, { id: props.tag, title: props.title, isExpanded: true, isUnread: false });
}
public onClick = (): void => {
const isExpanded = !this.snapshot.current.isExpanded;
this.expandedBySpace.set(this.props.spaceId, isExpanded);
this.snapshot.merge({ isExpanded });
this.props.onToggleExpanded(isExpanded);
};
@@ -48,6 +59,25 @@ export class RoomListSectionHeaderViewModel
return this.snapshot.current.isExpanded;
}
/**
* Set whether the section is expanded for the current space.
* This will not trigger the onToggleExpanded callback.
*/
public set isExpanded(value: boolean) {
this.expandedBySpace.set(this.props.spaceId, value);
this.snapshot.merge({ isExpanded: value });
}
/**
* Switch to a different space, restoring the expanded state for that space.
* Defaults to expanded if no state has been saved for the space.
*/
public setSpace(spaceId: string): void {
this.props.spaceId = spaceId;
const isExpanded = this.expandedBySpace.get(this.props.spaceId) ?? true;
this.snapshot.merge({ isExpanded });
}
/**
* Update the rooms tracked by this section header for unread state computation.
* Only subscribes to new rooms and unsubscribes from rooms no longer in the section.
@@ -258,6 +258,7 @@ export class RoomListViewModel
const viewModel = new RoomListSectionHeaderViewModel({
tag,
title,
spaceId: this.roomsResult.spaceId,
onToggleExpanded: () => this.updateRoomListData(),
});
this.roomSectionHeaderViewModels.set(tag, viewModel);
@@ -367,6 +368,11 @@ export class RoomListViewModel
this.updateRoomsMap(this.roomsResult);
// Restore the expanded/collapsed state for the new space
for (const viewModel of this.roomSectionHeaderViewModels.values()) {
viewModel.setSpace(newSpaceId);
}
// Space changed - get the last selected room for the new space to prevent flicker
const lastSelectedRoom = SpaceStore.instance.getLastSelectedRoomIdForSpace(newSpaceId);
@@ -501,6 +507,12 @@ export class RoomListViewModel
this.roomsResult,
(tag) => this.roomSectionHeaderViewModels.get(tag)?.isExpanded ?? true,
);
// If it's a flat list, we need to make sure the single section is expanded and has all rooms, otherwise the room list will be empty
if (isFlatList) {
const chatSections = this.roomSectionHeaderViewModels.get(CHATS_TAG);
if (chatSections) chatSections.isExpanded = true;
chatSections?.setRooms(this.roomsResult.sections.flatMap((section) => section.rooms));
}
this.sections = sections;
// Calculate the active room index from the computed sections (which exclude collapsed sections' rooms)