/* Copyright 2024 New Vector Ltd. SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ import { type TrackReferenceOrPlaceholder } from "@livekit/components-core"; import { animated } from "@react-spring/web"; import { type RoomMember } from "matrix-js-sdk"; import { type FC, type ComponentProps, type ReactNode } from "react"; import { useTranslation } from "react-i18next"; import classNames from "classnames"; import { VideoTrack } from "@livekit/components-react"; import { Text, Tooltip } from "@vector-im/compound-web"; import { ErrorSolidIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; import styles from "./MediaView.module.css"; import { Avatar } from "../Avatar"; import { type EncryptionStatus } from "../state/MediaViewModel"; import { RaisedHandIndicator } from "../reactions/RaisedHandIndicator"; import { showHandRaisedTimer, useSetting } from "../settings/settings"; import { type ReactionOption } from "../reactions"; import { ReactionIndicator } from "../reactions/ReactionIndicator"; import { RTCConnectionStats } from "../RTCConnectionStats"; interface Props extends ComponentProps { className?: string; style?: ComponentProps["style"]; targetWidth: number; targetHeight: number; video: TrackReferenceOrPlaceholder | undefined; videoFit: "cover" | "contain"; mirror: boolean; member: RoomMember; videoEnabled: boolean; unencryptedWarning: boolean; encryptionStatus: EncryptionStatus; nameTagLeadingIcon?: ReactNode; displayName: string; focusable: boolean; primaryButton?: ReactNode; raisedHandTime?: Date; currentReaction?: ReactionOption; raisedHandOnClick?: () => void; localParticipant: boolean; audioStreamStats?: RTCInboundRtpStreamStats | RTCOutboundRtpStreamStats; videoStreamStats?: RTCInboundRtpStreamStats | RTCOutboundRtpStreamStats; } export const MediaView: FC = ({ ref, className, style, targetWidth, targetHeight, video, videoFit, mirror, member, videoEnabled, unencryptedWarning, nameTagLeadingIcon, displayName, focusable, primaryButton, encryptionStatus, raisedHandTime, currentReaction, raisedHandOnClick, localParticipant, audioStreamStats, videoStreamStats, ...props }) => { const { t } = useTranslation(); const [handRaiseTimerVisible] = useSetting(showHandRaisedTimer); const avatarSize = Math.round(Math.min(targetWidth, targetHeight) / 2); return (
{video?.publication !== undefined && ( )}
{currentReaction && ( )}
{!video && !localParticipant && (
{t("video_tile.waiting_for_media")}
)} {(audioStreamStats || videoStreamStats) && ( )} {/* TODO: Bring this back once encryption status is less broken */} {/*encryptionStatus !== EncryptionStatus.Okay && (
{encryptionStatus === EncryptionStatus.Connecting && t("e2ee_encryption_status.connecting")} {encryptionStatus === EncryptionStatus.KeyMissing && t("e2ee_encryption_status.key_missing")} {encryptionStatus === EncryptionStatus.KeyInvalid && t("e2ee_encryption_status.key_invalid")} {encryptionStatus === EncryptionStatus.PasswordInvalid && t("e2ee_encryption_status.password_invalid")}
)*/}
{nameTagLeadingIcon} {displayName} {unencryptedWarning && ( )}
{primaryButton}
); }; MediaView.displayName = "MediaView";