File size: 7,264 Bytes
e8a19cb
3e36429
 
7f2a14a
3e36429
7f2a14a
 
e8a19cb
a77f50e
3e36429
cb75fe0
c2773be
7da7877
 
35ee466
 
 
 
 
cb7916c
3e36429
 
848947d
7f2a14a
c2773be
7da7877
35ee466
7da7877
35ee466
 
 
 
cb7916c
3e36429
 
d7461ed
3e36429
35ee466
 
3e36429
 
7da7877
d7461ed
 
3e36429
 
 
 
 
 
 
 
 
 
 
 
34dee1a
3e36429
 
 
34dee1a
3e36429
 
 
a9228b4
 
 
3e36429
 
 
a9228b4
 
 
 
3e36429
 
 
34dee1a
 
c2773be
3e36429
 
c2773be
 
 
3e36429
c2773be
3e36429
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a9228b4
c2773be
3e36429
 
 
 
 
c2773be
7da7877
c2773be
3e36429
6a9ed5b
3e36429
 
c2773be
44cae74
3e36429
e8a19cb
c2773be
 
44cae74
 
3e36429
c2773be
3e36429
c2773be
 
3e36429
 
c2773be
 
 
35ee466
 
7da7877
d7461ed
1476e59
688a2e9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// src/components/control-tray/ControlTray.tsx

// ... (ایمپورت‌های اولیه و SVGهای دیگر مثل قبل) ...
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';

// ... (SVG Icons: SvgPauseIcon, SvgCameraIcon, SvgStopCamIcon, SvgSwitchCameraIcon بدون تغییر) ...

export type ControlTrayProps = {
  videoRef: RefObject<HTMLVideoElement>;
  supportsVideo: boolean;
  onVideoStreamChange: (stream: MediaStream | null) => void;
  isAppMicActive: boolean;
  onAppMicToggle: (active: boolean) => void;
  isAppCamActive: boolean;
  onAppCamToggle: (active: boolean) => void;
  ReferenceMicrophoneIcon: () => JSX.Element;
  initialFacingMode: 'user' | 'environment'; // *** NEW PROP ***
  onFacingModeChange: (newMode: 'user' | 'environment') => void; // *** NEW PROP ***
};

const ControlTray: React.FC<ControlTrayProps> = ({
  videoRef,
  onVideoStreamChange,
  supportsVideo,
  isAppMicActive,
  onAppMicToggle,
  isAppCamActive,
  onAppCamToggle,
  ReferenceMicrophoneIcon,
  initialFacingMode,    // *** NEW PROP ***
  onFacingModeChange,   // *** NEW PROP ***
}) => {
  const { client, connected, connect } = useLiveAPIContext();
  const [audioRecorder] = useState(() => new AudioRecorder());
  const [activeLocalVideoStream, setActiveLocalVideoStream] = useState<MediaStream | null>(null);
  // *** currentFacingMode حالا از initialFacingMode مقدار اولیه می‌گیرد ***
  const [currentFacingMode, setCurrentFacingMode] = useState<'user' | 'environment'>(initialFacingMode);
  const [isSwitchingCamera, setIsSwitchingCamera] = useState(false);
  const renderCanvasRef = React.useRef<HTMLCanvasElement>(null);

  // وقتی initialFacingMode از بیرون تغییر می‌کند، state داخلی را هم آپدیت کن
  useEffect(() => {
    setCurrentFacingMode(initialFacingMode);
  }, [initialFacingMode]);

  // ... (useEffect های audioRecorder, sendVideoFrame, videoRef.srcObject بدون تغییر) ...
   useEffect(() => {
    if (isAppCamActive && !activeLocalVideoStream && !isSwitchingCamera) {
      // هنگام شروع وب‌کم، از currentFacingMode داخلی استفاده کن
      startWebcam(currentFacingMode);
    } else if (!isAppCamActive && activeLocalVideoStream) {
      stopWebcam();
    }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isAppCamActive, activeLocalVideoStream, isSwitchingCamera]); // currentFacingMode را از وابستگی‌ها حذف می‌کنیم تا startWebcam فقط یکبار با مقدار اولیه اجرا شود و بعدا توسط handleSwitchCamera مدیریت شود.


  const startWebcam = async (facingModeToTry: 'user' | 'environment' = currentFacingMode) => {
    if (isSwitchingCamera) return;
    setIsSwitchingCamera(true);
    try {
      const mediaStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: facingModeToTry }, audio: false });
      setActiveLocalVideoStream(mediaStream);
      onVideoStreamChange(mediaStream);
      setCurrentFacingMode(facingModeToTry); // آپدیت state داخلی
      onFacingModeChange(facingModeToTry);  // اطلاع به والد
    } catch (error) {
      console.error(`❌ Start WC err ${facingModeToTry}:`, error);
      setActiveLocalVideoStream(null);
      onVideoStreamChange(null);
      onAppCamToggle(false); // اگر وب‌کم شروع نشد، دکمه دوربین را خاموش کن
    } finally {
      setIsSwitchingCamera(false);
    }
  };

  // ... (stopWebcam, ensureConnectedAndReady, handleMicToggle, handleCamToggle بدون تغییر عمده) ...
  // فقط handleCamToggle ممکن است نیاز به استفاده از currentFacingMode داشته باشد اگر اولین بار دوربین را با جهت خاصی می‌خواهید روشن کنید

  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 داخلی
      onFacingModeChange(targetFacingMode);  // اطلاع به والد
    } catch (error) {
      console.error(`❌ Switch Cam err ${targetFacingMode}:`, error);
      // سعی در بازگرداندن به دوربین قبلی
      try {
        const restoredStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: currentFacingMode }, audio: false }); // استفاده از currentFacingMode قبلی
        setActiveLocalVideoStream(restoredStream);
        onVideoStreamChange(restoredStream);
        // onFacingModeChange(currentFacingMode); // نیازی نیست چون به حالت قبلی برگشتیم
      } catch (restoreError) {
        console.error('❌ Restore Cam err:', restoreError);
        setActiveLocalVideoStream(null);
        onVideoStreamChange(null);
        onAppCamToggle(false);
      }
    } finally {
      setIsSwitchingCamera(false);
    }
  };
  // ... (بقیه useEffect ها و توابع handle مثل قبل)
  const ensureConnectedAndReady = async (): Promise<boolean> => { /* ... */ return false; };
  const handleMicToggle = async () => { /* ... */ };
  const handleCamToggle = async () => { /* ... */ };


  return (
    <footer id="footer-controls" className="footer-controls-html-like">
      {/* ... (JSX فوتر مثل قبل) ... */}
      <canvas style={{ display: "none" }} ref={renderCanvasRef} />

      <div id="mic-button" className="control-button mic-button-color" onClick={handleMicToggle} >
        {isAppMicActive ? <SvgPauseIcon /> : <ReferenceMicrophoneIcon />}
      </div>

      {isAppCamActive && (
        <div id="small-logo-footer-container" className="small-logo-footer-html-like">
            <LogoAnimation isMini={true} isActive={true} type="human" forFooter={true} />
        </div>
      )}

      <div id="cam-button-wrapper" className="control-button-wrapper cam-wrapper-html-like">
        <div id="cam-button" className="control-button cam-button-color" onClick={handleCamToggle} >
          {isAppCamActive ? <SvgStopCamIcon /> : <SvgCameraIcon />}
        </div>
        <div id="switch-camera-button-container" className={cn("switch-camera-button-container", { visible: isAppCamActive && !isSwitchingCamera })} >
          <button id="switch-camera-button" aria-label="Switch Camera" className="switch-camera-button-content group" onClick={handleSwitchCamera} disabled={!isAppCamActive || isSwitchingCamera} >
            <SvgSwitchCameraIcon/>
          </button>
        </div>
      </div>
    </footer>
  );
};

export default memo(ControlTray);