Files
ThreadNet-Web/src/components/views/rooms/RoomListPanel/RoomListItemView.tsx
T
Florian DurosandGitHub 07d5a72f26 New room list: video room and video call decoration (#29693)
* feat: add video call and EC call to room list item vm

* feat: add video call notification decoration to notification decoration component

* feat: add video call support to room list item view

* feat: add new RoomAvatarView component

* feat: deprecate `DecoratedRoomAvatar`

* feat: use `RoomAvatarView` in room list item

* feat: allow custom class for `RoomAvatar`

* test: update notification decoration

* test: update room list item view

* test: update room list snapshot

* test: add tests for room avatar vm

* test: add tests for room avatar view

* test(e2e): update snapshots

* fix: video room creation rights

* test: e2e add test for public and video room
2025-04-14 09:27:43 +00:00

101 lines
4.3 KiB
TypeScript

/*
* Copyright 2025 New Vector 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, useState } from "react";
import { type Room } from "matrix-js-sdk/src/matrix";
import classNames from "classnames";
import { useRoomListItemViewModel } from "../../../viewmodels/roomlist/RoomListItemViewModel";
import { Flex } from "../../../utils/Flex";
import { RoomListItemMenuView } from "./RoomListItemMenuView";
import { NotificationDecoration } from "../NotificationDecoration";
import { RoomAvatarView } from "../../avatars/RoomAvatarView";
interface RoomListItemViewPropsProps extends React.HTMLAttributes<HTMLButtonElement> {
/**
* The room to display
*/
room: Room;
/**
* Whether the room is selected
*/
isSelected: boolean;
}
/**
* An item in the room list
*/
export function RoomListItemView({ room, isSelected, ...props }: RoomListItemViewPropsProps): JSX.Element {
const vm = useRoomListItemViewModel(room);
const [isHover, setIsHover] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false);
// The compound menu in RoomListItemMenuView needs to be rendered when the hover menu is shown
// Using display: none; and then display:flex when hovered in CSS causes the menu to be misaligned
const showHoverDecoration = (isMenuOpen || isHover) && vm.showHoverMenu;
const isNotificationDecorationVisible =
!showHoverDecoration &&
(vm.notificationState.hasAnyNotificationOrActivity || vm.notificationState.muted || vm.hasParticipantInCall);
return (
<button
className={classNames("mx_RoomListItemView", {
mx_RoomListItemView_empty: !isNotificationDecorationVisible && !showHoverDecoration,
mx_RoomListItemView_notification_decoration: isNotificationDecorationVisible,
mx_RoomListItemView_menu_open: showHoverDecoration,
mx_RoomListItemView_selected: isSelected,
mx_RoomListItemView_bold: vm.isBold,
})}
type="button"
aria-selected={isSelected}
aria-label={vm.a11yLabel}
onClick={() => vm.openRoom()}
onMouseOver={() => setIsHover(true)}
onMouseOut={() => setIsHover(false)}
onFocus={() => setIsHover(true)}
onBlur={() => setIsHover(false)}
{...props}
>
{/* We need this extra div between the button and the content in order to add a padding which is not messing with the virtualized list */}
<Flex className="mx_RoomListItemView_container" gap="var(--cpd-space-3x)" align="center">
<RoomAvatarView room={room} />
<Flex
className="mx_RoomListItemView_content"
gap="var(--cpd-space-3x)"
align="center"
justify="space-between"
>
{/* We truncate the room name when too long. Title here is to show the full name on hover */}
<span className="mx_RoomListItemView_roomName" title={room.name}>
{room.name}
</span>
{showHoverDecoration ? (
<RoomListItemMenuView
room={room}
setMenuOpen={(isOpen) => {
if (isOpen) setIsMenuOpen(isOpen);
// To avoid icon blinking when closing the menu, we delay the state update
else setTimeout(() => setIsMenuOpen(isOpen), 0);
}}
/>
) : (
<>
{/* aria-hidden because we summarise the unread count/notification status in a11yLabel variable */}
<NotificationDecoration
notificationState={vm.notificationState}
aria-hidden={true}
hasVideoCall={vm.hasParticipantInCall}
/>
</>
)}
</Flex>
</Flex>
</button>
);
}