2022-05-04 17:09:48 +01:00
|
|
|
/*
|
2023-01-03 16:55:26 +00:00
|
|
|
Copyright 2022 New Vector Ltd
|
2022-05-04 17:09:48 +01:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2022-05-31 10:43:05 -04:00
|
|
|
import React, { forwardRef } from "react";
|
2022-04-07 14:22:36 -07:00
|
|
|
import { animated } from "@react-spring/web";
|
|
|
|
import classNames from "classnames";
|
2022-10-10 09:19:10 -04:00
|
|
|
import { useTranslation } from "react-i18next";
|
2023-06-02 14:49:11 +02:00
|
|
|
import { LocalParticipant, RemoteParticipant, Track } from "livekit-client";
|
|
|
|
import { useMediaTrack } from "@livekit/components-react";
|
2022-08-12 19:27:34 +02:00
|
|
|
|
2022-04-07 14:22:36 -07:00
|
|
|
import styles from "./VideoTile.module.css";
|
|
|
|
import { ReactComponent as MicMutedIcon } from "../icons/MicMuted.svg";
|
|
|
|
import { ReactComponent as VideoMutedIcon } from "../icons/VideoMuted.svg";
|
|
|
|
|
2022-08-12 19:27:34 +02:00
|
|
|
interface Props {
|
|
|
|
name: string;
|
|
|
|
avatar?: JSX.Element;
|
2022-09-16 10:21:41 -04:00
|
|
|
maximised?: boolean;
|
2022-08-12 19:27:34 +02:00
|
|
|
className?: string;
|
2023-06-02 14:49:11 +02:00
|
|
|
sfuParticipant: LocalParticipant | RemoteParticipant;
|
2022-08-12 19:27:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export const VideoTile = forwardRef<HTMLDivElement, Props>(
|
2023-06-02 14:49:11 +02:00
|
|
|
({ name, avatar, maximised, className, sfuParticipant, ...rest }, ref) => {
|
2022-10-10 09:19:10 -04:00
|
|
|
const { t } = useTranslation();
|
|
|
|
|
2023-06-02 14:49:11 +02:00
|
|
|
const videoEl = React.useRef<HTMLVideoElement>(null);
|
|
|
|
const { isMuted: cameraMuted } = useMediaTrack(
|
|
|
|
Track.Source.Camera,
|
|
|
|
sfuParticipant,
|
|
|
|
{
|
|
|
|
element: videoEl,
|
2023-01-07 10:09:20 +01:00
|
|
|
}
|
2023-06-02 14:49:11 +02:00
|
|
|
);
|
2022-09-22 17:35:23 -04:00
|
|
|
|
2023-06-02 14:49:11 +02:00
|
|
|
const audioEl = React.useRef<HTMLAudioElement>(null);
|
|
|
|
const { isMuted: microphoneMuted } = useMediaTrack(
|
|
|
|
Track.Source.Microphone,
|
|
|
|
sfuParticipant,
|
|
|
|
{
|
|
|
|
element: audioEl,
|
2022-09-22 17:35:23 -04:00
|
|
|
}
|
2023-06-02 14:49:11 +02:00
|
|
|
);
|
2022-10-21 17:24:56 +01:00
|
|
|
|
2022-05-31 10:43:05 -04:00
|
|
|
return (
|
|
|
|
<animated.div
|
|
|
|
className={classNames(styles.videoTile, className, {
|
2023-06-02 14:49:11 +02:00
|
|
|
[styles.isLocal]: sfuParticipant.isLocal,
|
|
|
|
[styles.speaking]: sfuParticipant.isSpeaking,
|
|
|
|
[styles.muted]: microphoneMuted,
|
|
|
|
[styles.screenshare]: false,
|
2022-09-14 19:05:05 -04:00
|
|
|
[styles.maximised]: maximised,
|
2022-05-31 10:43:05 -04:00
|
|
|
})}
|
|
|
|
ref={ref}
|
|
|
|
{...rest}
|
|
|
|
>
|
2023-06-02 14:49:11 +02:00
|
|
|
{cameraMuted && (
|
2022-05-31 10:43:05 -04:00
|
|
|
<>
|
|
|
|
<div className={styles.videoMutedOverlay} />
|
|
|
|
{avatar}
|
|
|
|
</>
|
|
|
|
)}
|
2022-09-14 19:05:05 -04:00
|
|
|
{!maximised &&
|
2023-06-02 14:49:11 +02:00
|
|
|
(sfuParticipant.isScreenShareEnabled ? (
|
2022-09-14 19:05:05 -04:00
|
|
|
<div className={styles.presenterLabel}>
|
2022-10-10 09:19:10 -04:00
|
|
|
<span>{t("{{name}} is presenting", { name })}</span>
|
2022-09-14 19:05:05 -04:00
|
|
|
</div>
|
|
|
|
) : (
|
|
|
|
<div className={classNames(styles.infoBubble, styles.memberName)}>
|
2023-01-09 11:10:59 -05:00
|
|
|
{
|
|
|
|
/* If the user is speaking, it's safe to say they're unmuted.
|
|
|
|
Mute state is currently sent over to-device messages, which
|
|
|
|
aren't quite real-time, so this is an important kludge to make
|
|
|
|
sure no one appears muted when they've clearly begun talking. */
|
2023-06-02 14:49:11 +02:00
|
|
|
microphoneMuted &&
|
|
|
|
!cameraMuted &&
|
|
|
|
!sfuParticipant.isSpeaking && <MicMutedIcon />
|
2023-01-09 11:10:59 -05:00
|
|
|
}
|
2023-06-02 14:49:11 +02:00
|
|
|
{cameraMuted && <VideoMutedIcon />}
|
2022-09-14 19:05:05 -04:00
|
|
|
</div>
|
|
|
|
))}
|
2023-06-02 14:49:11 +02:00
|
|
|
<video ref={videoEl} />
|
|
|
|
<audio ref={audioEl} />
|
2022-05-31 10:43:05 -04:00
|
|
|
</animated.div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
);
|