Spaces:
Running
Running
Update src/components/control-tray/ControlTray.tsx
Browse files
src/components/control-tray/ControlTray.tsx
CHANGED
@@ -36,8 +36,8 @@ const ControlTray: React.FC<ControlTrayProps> = ({
|
|
36 |
isAppCamActive,
|
37 |
onAppCamToggle,
|
38 |
ReferenceMicrophoneIcon,
|
39 |
-
currentFacingMode,
|
40 |
-
setCurrentFacingMode,
|
41 |
}) => {
|
42 |
const { client, connected, connect } = useLiveAPIContext();
|
43 |
const [audioRecorder] = useState(() => new AudioRecorder());
|
@@ -45,14 +45,48 @@ const ControlTray: React.FC<ControlTrayProps> = ({
|
|
45 |
const [isSwitchingCamera, setIsSwitchingCamera] = useState(false);
|
46 |
const renderCanvasRef = React.useRef<HTMLCanvasElement>(null);
|
47 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
useEffect(() => {
|
49 |
-
if (isAppCamActive
|
50 |
-
|
51 |
-
|
52 |
-
|
|
|
|
|
|
|
|
|
53 |
}
|
54 |
}, [isAppCamActive, activeLocalVideoStream, isSwitchingCamera, currentFacingMode]);
|
55 |
|
|
|
56 |
useEffect(() => { /* ... audioRecorder ... */ }, [connected, client, isAppMicActive, audioRecorder]);
|
57 |
useEffect(() => { /* ... sendVideoFrame ... */ }, [connected, activeLocalVideoStream, client, videoRef, renderCanvasRef]);
|
58 |
useEffect(() => { /* ... videoRef.srcObject ... */ }, [activeLocalVideoStream, videoRef]);
|
@@ -60,56 +94,42 @@ const ControlTray: React.FC<ControlTrayProps> = ({
|
|
60 |
const ensureConnectedAndReady = async (): Promise<boolean> => { if (!connected) { try { await connect(); return true; } catch (err) { console.error('❌ CT Connect err:', err); return false; } } return true; };
|
61 |
const handleMicToggle = async () => { if (isSwitchingCamera) return; const newMicState = !isAppMicActive; if (newMicState && !(await ensureConnectedAndReady())) { onAppMicToggle(false); return; } onAppMicToggle(newMicState); };
|
62 |
|
63 |
-
const startWebcam = async (facingModeToTry: 'user' | 'environment') => {
|
64 |
-
if (isSwitchingCamera) return;
|
65 |
-
setIsSwitchingCamera(true);
|
66 |
-
try {
|
67 |
-
const mediaStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: facingModeToTry }, audio: false });
|
68 |
-
setActiveLocalVideoStream(mediaStream);
|
69 |
-
onVideoStreamChange(mediaStream);
|
70 |
-
// setCurrentFacingMode در اینجا نباید مستقیما ست شود، چون از پراپ می آید و در handleSwitchCamera و handleCamToggle مدیریت می شود
|
71 |
-
} catch (error) {
|
72 |
-
console.error(`❌ Start WC err ${facingModeToTry}:`, error);
|
73 |
-
setActiveLocalVideoStream(null); onVideoStreamChange(null); onAppCamToggle(false);
|
74 |
-
} finally { setIsSwitchingCamera(false); }
|
75 |
-
};
|
76 |
-
|
77 |
-
const stopWebcam = () => { if (activeLocalVideoStream) activeLocalVideoStream.getTracks().forEach(track => track.stop()); setActiveLocalVideoStream(null); onVideoStreamChange(null); };
|
78 |
-
|
79 |
const handleCamToggle = async () => {
|
80 |
if (isSwitchingCamera) return;
|
81 |
const newCamState = !isAppCamActive;
|
82 |
-
|
83 |
-
|
84 |
-
if (!
|
85 |
-
|
86 |
-
|
87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
88 |
}
|
89 |
};
|
90 |
|
91 |
const handleSwitchCamera = async () => {
|
92 |
if (!isAppCamActive || !activeLocalVideoStream || isSwitchingCamera) return;
|
93 |
-
|
94 |
const targetFacingMode = currentFacingMode === 'user' ? 'environment' : 'user';
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
} catch (restoreError) {
|
109 |
-
console.error('❌ Restore Cam err:', restoreError);
|
110 |
-
setActiveLocalVideoStream(null); onVideoStreamChange(null); onAppCamToggle(false);
|
111 |
-
}
|
112 |
-
} finally { setIsSwitchingCamera(false); }
|
113 |
};
|
114 |
|
115 |
return (
|
|
|
36 |
isAppCamActive,
|
37 |
onAppCamToggle,
|
38 |
ReferenceMicrophoneIcon,
|
39 |
+
currentFacingMode, // دریافت از AppCore
|
40 |
+
setCurrentFacingMode, // دریافت از AppCore
|
41 |
}) => {
|
42 |
const { client, connected, connect } = useLiveAPIContext();
|
43 |
const [audioRecorder] = useState(() => new AudioRecorder());
|
|
|
45 |
const [isSwitchingCamera, setIsSwitchingCamera] = useState(false);
|
46 |
const renderCanvasRef = React.useRef<HTMLCanvasElement>(null);
|
47 |
|
48 |
+
const _startWebcam = async (facingModeToTry: 'user' | 'environment') => {
|
49 |
+
if (isSwitchingCamera) return null; // برگرداندن null اگر در حال تعویض دوربین هستیم
|
50 |
+
setIsSwitchingCamera(true);
|
51 |
+
try {
|
52 |
+
const mediaStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: facingModeToTry }, audio: false });
|
53 |
+
setActiveLocalVideoStream(mediaStream);
|
54 |
+
onVideoStreamChange(mediaStream);
|
55 |
+
// setCurrentFacingMode در اینجا ست نمیشود، چون توسط handleSwitchCamera یا handleCamToggle مدیریت میشود
|
56 |
+
return mediaStream; // برگرداندن استریم برای استفاده در توابع دیگر
|
57 |
+
} catch (error) {
|
58 |
+
console.error(`❌ Start WC err ${facingModeToTry}:`, error);
|
59 |
+
setActiveLocalVideoStream(null);
|
60 |
+
onVideoStreamChange(null);
|
61 |
+
onAppCamToggle(false); // خاموش کردن دوربین در App.tsx اگر خطا رخ داد
|
62 |
+
return null;
|
63 |
+
} finally {
|
64 |
+
setIsSwitchingCamera(false);
|
65 |
+
}
|
66 |
+
};
|
67 |
+
|
68 |
+
const _stopWebcam = () => {
|
69 |
+
if (activeLocalVideoStream) {
|
70 |
+
activeLocalVideoStream.getTracks().forEach(track => track.stop());
|
71 |
+
}
|
72 |
+
setActiveLocalVideoStream(null);
|
73 |
+
onVideoStreamChange(null);
|
74 |
+
};
|
75 |
+
|
76 |
+
// useEffect برای مدیریت روشن/خاموش شدن دوربین بر اساس isAppCamActive
|
77 |
useEffect(() => {
|
78 |
+
if (isAppCamActive) {
|
79 |
+
if (!activeLocalVideoStream && !isSwitchingCamera) {
|
80 |
+
_startWebcam(currentFacingMode); // استفاده از currentFacingMode از state والد
|
81 |
+
}
|
82 |
+
} else {
|
83 |
+
if (activeLocalVideoStream) {
|
84 |
+
_stopWebcam();
|
85 |
+
}
|
86 |
}
|
87 |
}, [isAppCamActive, activeLocalVideoStream, isSwitchingCamera, currentFacingMode]);
|
88 |
|
89 |
+
|
90 |
useEffect(() => { /* ... audioRecorder ... */ }, [connected, client, isAppMicActive, audioRecorder]);
|
91 |
useEffect(() => { /* ... sendVideoFrame ... */ }, [connected, activeLocalVideoStream, client, videoRef, renderCanvasRef]);
|
92 |
useEffect(() => { /* ... videoRef.srcObject ... */ }, [activeLocalVideoStream, videoRef]);
|
|
|
94 |
const ensureConnectedAndReady = async (): Promise<boolean> => { if (!connected) { try { await connect(); return true; } catch (err) { console.error('❌ CT Connect err:', err); return false; } } return true; };
|
95 |
const handleMicToggle = async () => { if (isSwitchingCamera) return; const newMicState = !isAppMicActive; if (newMicState && !(await ensureConnectedAndReady())) { onAppMicToggle(false); return; } onAppMicToggle(newMicState); };
|
96 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
97 |
const handleCamToggle = async () => {
|
98 |
if (isSwitchingCamera) return;
|
99 |
const newCamState = !isAppCamActive;
|
100 |
+
|
101 |
+
if (newCamState) { // روشن کردن دوربین
|
102 |
+
if (!(await ensureConnectedAndReady())) {
|
103 |
+
onAppCamToggle(false); // آپدیت state در App.tsx
|
104 |
+
return;
|
105 |
+
}
|
106 |
+
if (!isAppMicActive) {
|
107 |
+
onAppMicToggle(true); // فعال کردن میکروفون اگر خاموش است
|
108 |
+
}
|
109 |
+
onAppCamToggle(true); // این باعث میشود useEffect بالا _startWebcam را فراخوانی کند
|
110 |
+
// با currentFacingMode فعلی که از App.tsx میآید.
|
111 |
+
} else { // خاموش کردن دوربین
|
112 |
+
onAppCamToggle(false); // این باعث میشود useEffect بالا _stopWebcam را فراخوانی کند
|
113 |
}
|
114 |
};
|
115 |
|
116 |
const handleSwitchCamera = async () => {
|
117 |
if (!isAppCamActive || !activeLocalVideoStream || isSwitchingCamera) return;
|
118 |
+
|
119 |
const targetFacingMode = currentFacingMode === 'user' ? 'environment' : 'user';
|
120 |
+
_stopWebcam(); // ابتدا استریم فعلی را متوقف کن
|
121 |
+
|
122 |
+
// کمی تاخیر برای اطمینان از آزاد شدن دوربین قبل از درخواست جدید
|
123 |
+
await new Promise(resolve => setTimeout(resolve, 100));
|
124 |
+
|
125 |
+
const newStream = await _startWebcam(targetFacingMode); // تلاش برای شروع با دوربین جدید
|
126 |
+
if (newStream) {
|
127 |
+
setCurrentFacingMode(targetFacingMode); // آپدیت state در App.tsx فقط در صورت موفقیت
|
128 |
+
} else {
|
129 |
+
// اگر شروع با دوربین جدید ناموفق بود، سعی کن به دوربین قبلی برگردی
|
130 |
+
// (currentFacingMode هنوز مقدار قبلی را دارد)
|
131 |
+
await _startWebcam(currentFacingMode);
|
132 |
+
}
|
|
|
|
|
|
|
|
|
|
|
133 |
};
|
134 |
|
135 |
return (
|