import React from 'react'; import classNames from 'classnames'; import { CallDetailsType, HangUpType, SetLocalAudioType, SetLocalPreviewType, SetLocalVideoType, SetRendererCanvasType, } from '../state/ducks/calling'; import { Avatar } from './Avatar'; import { CallingButton, CallingButtonType } from './CallingButton'; import { CallState } from '../types/Calling'; import { LocalizerType } from '../types/Util'; export type PropsType = { callDetails?: CallDetailsType; callState?: CallState; hangUp: (_: HangUpType) => void; hasLocalAudio: boolean; hasLocalVideo: boolean; hasRemoteVideo: boolean; i18n: LocalizerType; setLocalAudio: (_: SetLocalAudioType) => void; setLocalVideo: (_: SetLocalVideoType) => void; setLocalPreview: (_: SetLocalPreviewType) => void; setRendererCanvas: (_: SetRendererCanvasType) => void; togglePip: () => void; toggleSettings: () => void; }; type StateType = { acceptedDuration: number | null; showControls: boolean; }; export class CallScreen extends React.Component { private interval: NodeJS.Timeout | null; private controlsFadeTimer: NodeJS.Timeout | null; private readonly localVideoRef: React.RefObject; private readonly remoteVideoRef: React.RefObject; constructor(props: PropsType) { super(props); this.state = { acceptedDuration: null, showControls: true, }; this.interval = null; this.controlsFadeTimer = null; this.localVideoRef = React.createRef(); this.remoteVideoRef = React.createRef(); } public componentDidMount(): void { const { setLocalPreview, setRendererCanvas } = this.props; // It's really jump with a value of 500ms. this.interval = setInterval(this.updateAcceptedTimer, 100); this.fadeControls(); document.addEventListener('keydown', this.handleKeyDown); setLocalPreview({ element: this.localVideoRef }); setRendererCanvas({ element: this.remoteVideoRef }); } public componentWillUnmount(): void { const { setLocalPreview, setRendererCanvas } = this.props; document.removeEventListener('keydown', this.handleKeyDown); if (this.interval) { clearInterval(this.interval); } if (this.controlsFadeTimer) { clearTimeout(this.controlsFadeTimer); } setLocalPreview({ element: undefined }); setRendererCanvas({ element: undefined }); } updateAcceptedTimer = (): void => { const { callDetails } = this.props; if (!callDetails) { return; } if (callDetails.acceptedTime) { this.setState({ acceptedDuration: Date.now() - callDetails.acceptedTime, }); } }; handleKeyDown = (event: KeyboardEvent): void => { const { callDetails } = this.props; if (!callDetails) { return; } let eventHandled = false; if (event.shiftKey && (event.key === 'V' || event.key === 'v')) { this.toggleVideo(); eventHandled = true; } else if (event.shiftKey && (event.key === 'M' || event.key === 'm')) { this.toggleAudio(); eventHandled = true; } if (eventHandled) { event.preventDefault(); event.stopPropagation(); this.showControls(); } }; showControls = (): void => { const { showControls } = this.state; if (!showControls) { this.setState({ showControls: true, }); } this.fadeControls(); }; fadeControls = (): void => { if (this.controlsFadeTimer) { clearTimeout(this.controlsFadeTimer); } this.controlsFadeTimer = setTimeout(() => { this.setState({ showControls: false, }); }, 5000); }; toggleAudio = (): void => { const { callDetails, hasLocalAudio, setLocalAudio } = this.props; if (!callDetails) { return; } setLocalAudio({ callId: callDetails.callId, enabled: !hasLocalAudio, }); }; toggleVideo = (): void => { const { callDetails, hasLocalVideo, setLocalVideo } = this.props; if (!callDetails) { return; } setLocalVideo({ callId: callDetails.callId, enabled: !hasLocalVideo }); }; public render(): JSX.Element | null { const { callDetails, callState, hangUp, hasLocalAudio, hasLocalVideo, hasRemoteVideo, i18n, togglePip, toggleSettings, } = this.props; const { showControls } = this.state; const isAudioOnly = !hasLocalVideo && !hasRemoteVideo; if (!callDetails || !callState) { return null; } const controlsFadeClass = classNames({ 'module-ongoing-call__controls--fadeIn': (showControls || isAudioOnly) && callState !== CallState.Accepted, 'module-ongoing-call__controls--fadeOut': !showControls && !isAudioOnly && callState === CallState.Accepted, }); const videoButtonType = hasLocalVideo ? CallingButtonType.VIDEO_ON : CallingButtonType.VIDEO_OFF; const audioButtonType = hasLocalAudio ? CallingButtonType.AUDIO_ON : CallingButtonType.AUDIO_OFF; return (
{callDetails.title}
{this.renderMessage(callState)}
{hasRemoteVideo ? this.renderRemoteVideo() : this.renderAvatar(callDetails)} {hasLocalVideo ? this.renderLocalVideo() : null}
{ hangUp({ callId: callDetails.callId }); }} tooltipDistance={24} />
); } private renderAvatar(callDetails: CallDetailsType) { const { i18n } = this.props; const { avatarPath, color, name, phoneNumber, profileName, title, } = callDetails; return (
); } private renderLocalVideo() { return (