AeThex-Connect/astro-site/src/react-app/components/Chat/VoiceVideoCall.jsx

129 lines
3.2 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* VoiceVideoCall Component
* Displays voice/video call interface with participant videos and controls
*/
import React, { useEffect, useState } from 'react';
import {
LiveKitRoom,
VideoConference,
GridLayout,
ParticipantTile,
useRemoteParticipant,
useLocalParticipant,
} from 'livekit-react';
import './VoiceVideoCall.css';
export default function VoiceVideoCall({
roomName,
userName,
token,
serverUrl,
onLeave,
}) {
const [callState, setCallState] = useState('connecting'); // connecting, connected, ended
if (!token || !serverUrl) {
return (
<div className="voice-video-error">
<p>Missing required configuration for video call</p>
</div>
);
}
return (
<div className="voice-video-call-container">
<LiveKitRoom
video={{ resolution: { width: 640, height: 480 } }}
audio={true}
token={token}
serverUrl={serverUrl}
onConnected={() => setCallState('connected')}
onDisconnected={() => {
setCallState('ended');
onLeave?.();
}}
options={{
adaptiveStream: true,
dynacast: true,
}}
>
{callState === 'connecting' && (
<div className="call-connecting">
<div className="spinner"></div>
<p>Connecting to {roomName}...</p>
</div>
)}
{callState === 'connected' && (
<>
<VideoConference />
<CallControls onLeave={onLeave} />
</>
)}
</LiveKitRoom>
</div>
);
}
/**
* CallControls Component
* Displays microphone, camera, screen share, and leave buttons
*/
function CallControls({ onLeave }) {
const { localParticipant } = useLocalParticipant();
const [isMuted, setIsMuted] = useState(false);
const [isCameraOff, setIsCameraOff] = useState(false);
const [isScreenSharing, setIsScreenSharing] = useState(false);
const handleToggleMicrophone = async () => {
await localParticipant?.setMicrophoneEnabled(isMuted);
setIsMuted(!isMuted);
};
const handleToggleCamera = async () => {
await localParticipant?.setCameraEnabled(isCameraOff);
setIsCameraOff(!isCameraOff);
};
const handleToggleScreenShare = async () => {
await localParticipant?.setScreenShareEnabled(!isScreenSharing);
setIsScreenSharing(!isScreenSharing);
};
return (
<div className="call-controls">
<button
className={`control-btn ${isMuted ? 'disabled' : ''}`}
onClick={handleToggleMicrophone}
title={isMuted ? 'Unmute' : 'Mute'}
>
{isMuted ? '🔇' : '🎤'}
</button>
<button
className={`control-btn ${isCameraOff ? 'disabled' : ''}`}
onClick={handleToggleCamera}
title={isCameraOff ? 'Turn on camera' : 'Turn off camera'}
>
{isCameraOff ? '📹' : '📷'}
</button>
<button
className={`control-btn ${isScreenSharing ? 'active' : ''}`}
onClick={handleToggleScreenShare}
title={isScreenSharing ? 'Stop sharing' : 'Share screen'}
>
🖥
</button>
<button
className="control-btn leave-btn"
onClick={onLeave}
title="Leave call"
>
</button>
</div>
);
}