Files
ThreadNet-Web/packages/shared-components/src/room-list/RoomListView/RoomListView.tsx
T
Florian DurosandGitHub ee5d2609df Room list: add sections to shared components (#32735)
* feat: add section header

* refactor: remove index and role related to list box from RoomListItemView

* feat: add wrapper to RoomListItemView to handle different accessiblity pattern

* feat: add section support to VirtualizedRoomListView

* feat: add sections support to RoomListView

* test: add screenshot for sections header

* test: add/update screenshots for sections

* feat: force flat list on view model

This is an intermediary step before implementing sections in the vm. We
force the flat list but we use the underneath the view supporting
sections.

* test: update RoomListViewModel test

* test: fix breaking test

* chore: rename `getSectionViewModel` to `getSectionHeaderViewModel`

* chore: add missing `RoomListItemAccessibilityWrapper` export

* chore: merge `react` imports

* chore: simplify and add comment to `getItemKey` and `getHeaderKey`

* chore add comments to `getItemComponent` variants

* chore: fix typo in example doc
2026-03-16 16:40:12 +00:00

114 lines
4.0 KiB
TypeScript

/*
* 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, { type JSX, type ReactNode } from "react";
import { useViewModel, type ViewModel } from "../../viewmodel";
import { RoomListPrimaryFilters, type FilterId } from "../RoomListPrimaryFilters";
import { RoomListLoadingSkeleton } from "./RoomListLoadingSkeleton";
import { RoomListEmptyStateView } from "./RoomListEmptyStateView";
import { VirtualizedRoomListView, type RoomListViewState } from "../VirtualizedRoomListView";
import { type Room, type RoomItemViewModel } from "../RoomListItemView";
import { type RoomListSectionHeaderViewModel } from "../RoomListSectionHeaderView";
export type RoomListSection = {
/** Unique identifier for the section */
id: string;
/** Array of room IDs that belong to this section */
roomIds: string[];
};
/**
* Snapshot for the room list view
*/
export type RoomListSnapshot = {
/** Whether the rooms are currently loading */
isLoadingRooms: boolean;
/** Whether the room list is empty */
isRoomListEmpty: boolean;
/** Array of filter IDs */
filterIds: FilterId[];
/** Currently active filter ID (if any) */
activeFilterId?: FilterId;
/** Room list state */
roomListState: RoomListViewState;
/** Array of sections in the room list */
sections: RoomListSection[];
/** Optional description for the empty state */
emptyStateDescription?: string;
/** Optional action element for the empty state */
emptyStateAction?: ReactNode;
/** Whether the user can create rooms */
canCreateRoom?: boolean;
/** Whether the room list is displayed as a flat list */
isFlatList: boolean;
};
/**
* Actions interface for room list operations
*/
export interface RoomListViewActions {
/** Called when a filter is toggled */
onToggleFilter: (filterId: FilterId) => void;
/** Called to create a new chat room */
createChatRoom: () => void;
/** Called to create a new room */
createRoom: () => void;
/** Get view model for a specific room (virtualization API) */
getRoomItemViewModel: (roomId: string) => RoomItemViewModel;
/** Called when the visible range changes (virtualization API) */
updateVisibleRooms: (startIndex: number, endIndex: number) => void;
/** Get view model for a specific section header (virtualization API) */
getSectionHeaderViewModel: (sectionId: string) => RoomListSectionHeaderViewModel;
}
/**
* The view model type for the room list view
*/
export type RoomListViewModel = ViewModel<RoomListSnapshot, RoomListViewActions>;
/**
* Props for RoomListView component
*/
export interface RoomListViewProps {
/** The view model containing all data and callbacks */
vm: RoomListViewModel;
/** Render function for room avatar */
renderAvatar: (room: Room) => ReactNode;
/** Optional callback for keyboard events on the room list */
onKeyDown?: (e: React.KeyboardEvent<HTMLDivElement>) => void;
}
/**
* Room list view component that manages filters, loading states, empty states, and the room list.
*/
export const RoomListView: React.FC<RoomListViewProps> = ({ vm, renderAvatar, onKeyDown }): JSX.Element => {
const snapshot = useViewModel(vm);
let listBody: ReactNode;
if (snapshot.isLoadingRooms) {
listBody = <RoomListLoadingSkeleton />;
} else if (snapshot.isRoomListEmpty) {
listBody = <RoomListEmptyStateView vm={vm} />;
} else {
listBody = <VirtualizedRoomListView vm={vm} renderAvatar={renderAvatar} onKeyDown={onKeyDown} />;
}
return (
<>
<div>
<RoomListPrimaryFilters
filterIds={snapshot.filterIds}
activeFilterId={snapshot.activeFilterId}
onToggleFilter={vm.onToggleFilter}
/>
</div>
{listBody}
</>
);
};