// src/components/control-tray/ControlTray.tsx
/**
Copyright 2024 Google LLC
... (لایسنس) ...
*/
import cn from "classnames";
import React, { memo, ReactNode, RefObject, useEffect, useState } from "react";
import { useLiveAPIContext } from "../../contexts/LiveAPIContext";
import { AudioRecorder } from "../../lib/audio-recorder";
import LogoAnimation from '../logo-animation/LogoAnimation';
const SvgPauseIcon = () => ;
const SvgCameraIcon = () => ;
const SvgStopCamIcon = () => ;
const SvgSwitchCameraIcon = () => ;
export type ControlTrayProps = {
videoRef: RefObject;
supportsVideo: boolean;
onVideoStreamChange: (stream: MediaStream | null) => void;
isAppMicActive: boolean;
onAppMicToggle: (active: boolean) => void;
isAppCamActive: boolean;
onAppCamToggle: (active: boolean) => void;
ReferenceMicrophoneIcon: () => JSX.Element;
currentFacingMode: 'user' | 'environment';
setCurrentFacingMode: React.Dispatch>;
};
const ControlTray: React.FC = ({
videoRef,
onVideoStreamChange,
supportsVideo,
isAppMicActive,
onAppMicToggle,
isAppCamActive,
onAppCamToggle,
ReferenceMicrophoneIcon,
currentFacingMode,
setCurrentFacingMode,
}) => {
const { client, connected, connect } = useLiveAPIContext();
const [audioRecorder] = useState(() => new AudioRecorder());
const [activeLocalVideoStream, setActiveLocalVideoStream] = useState(null);
const [isSwitchingCamera, setIsSwitchingCamera] = useState(false);
const renderCanvasRef = React.useRef(null);
useEffect(() => {
if (isAppCamActive && !activeLocalVideoStream && !isSwitchingCamera) {
startWebcam(currentFacingMode); // استفاده از currentFacingMode از پراپ
} else if (!isAppCamActive && activeLocalVideoStream) {
stopWebcam();
}
}, [isAppCamActive, activeLocalVideoStream, isSwitchingCamera, currentFacingMode]);
useEffect(() => { /* ... audioRecorder ... */ }, [connected, client, isAppMicActive, audioRecorder]);
useEffect(() => { /* ... sendVideoFrame ... */ }, [connected, activeLocalVideoStream, client, videoRef, renderCanvasRef]);
useEffect(() => { /* ... videoRef.srcObject ... */ }, [activeLocalVideoStream, videoRef]);
const ensureConnectedAndReady = async (): Promise => { if (!connected) { try { await connect(); return true; } catch (err) { console.error('❌ CT Connect err:', err); return false; } } return true; };
const handleMicToggle = async () => { if (isSwitchingCamera) return; const newMicState = !isAppMicActive; if (newMicState && !(await ensureConnectedAndReady())) { onAppMicToggle(false); return; } onAppMicToggle(newMicState); };
const startWebcam = async (facingModeToTry: 'user' | 'environment') => {
if (isSwitchingCamera) return;
setIsSwitchingCamera(true);
try {
const mediaStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: facingModeToTry }, audio: false });
setActiveLocalVideoStream(mediaStream);
onVideoStreamChange(mediaStream);
// setCurrentFacingMode در اینجا نباید مستقیما ست شود، چون از پراپ می آید و در handleSwitchCamera و handleCamToggle مدیریت می شود
} catch (error) {
console.error(`❌ Start WC err ${facingModeToTry}:`, error);
setActiveLocalVideoStream(null); onVideoStreamChange(null); onAppCamToggle(false);
} finally { setIsSwitchingCamera(false); }
};
const stopWebcam = () => { if (activeLocalVideoStream) activeLocalVideoStream.getTracks().forEach(track => track.stop()); setActiveLocalVideoStream(null); onVideoStreamChange(null); };
const handleCamToggle = async () => {
if (isSwitchingCamera) return;
const newCamState = !isAppCamActive;
if (newCamState) {
if (!(await ensureConnectedAndReady())) { onAppCamToggle(false); return; }
if (!isAppMicActive) { onAppMicToggle(true); }
onAppCamToggle(true); // این باعث اجرای useEffect برای startWebcam با currentFacingMode فعلی می شود
} else {
onAppCamToggle(false);
}
};
const handleSwitchCamera = async () => {
if (!isAppCamActive || !activeLocalVideoStream || isSwitchingCamera) return;
setIsSwitchingCamera(true);
const targetFacingMode = currentFacingMode === 'user' ? 'environment' : 'user';
activeLocalVideoStream.getTracks().forEach(track => track.stop());
try {
const newStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: { exact: targetFacingMode } }, audio: false });
setActiveLocalVideoStream(newStream);
onVideoStreamChange(newStream);
setCurrentFacingMode(targetFacingMode); // آپدیت state در App.tsx
} catch (error) {
console.error(`❌ Switch Cam err ${targetFacingMode}:`, error);
try {
const restoredStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: currentFacingMode }, audio: false }); // بازگشت به دوربین قبلی
setActiveLocalVideoStream(restoredStream);
onVideoStreamChange(restoredStream);
// setCurrentFacingMode تغییر نمی کند
} catch (restoreError) {
console.error('❌ Restore Cam err:', restoreError);
setActiveLocalVideoStream(null); onVideoStreamChange(null); onAppCamToggle(false);
}
} finally { setIsSwitchingCamera(false); }
};
return (
);
};
export default memo(ControlTray);