2026-02-05 21:05:14 +00:00
|
|
|
/*
|
|
|
|
|
* 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";
|
2026-02-10 10:49:08 +01:00
|
|
|
import { type Room } from "../RoomListItemView";
|
2026-02-05 21:05:14 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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 room IDs for virtualization */
|
|
|
|
|
roomIds: string[];
|
|
|
|
|
/** 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;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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) => any;
|
|
|
|
|
/** Called when the visible range changes (virtualization API) */
|
|
|
|
|
updateVisibleRooms: (startIndex: number, endIndex: number) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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}
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
};
|