Spaces:
Running
Running
Update src/components/control-tray/ControlTray.tsx
Browse files
src/components/control-tray/ControlTray.tsx
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
|
3 |
import cn from "classnames";
|
4 |
import React, { memo, RefObject, useEffect, useState, useCallback, useRef } from "react";
|
5 |
-
import {
|
6 |
import { AudioRecorder } from "../../lib/audio-recorder";
|
7 |
import Logo from '../logo/Logo';
|
8 |
import { PauseIconWithSurroundPulse, microphoneIcon, cameraIcon, stopCamIcon } from '../icons';
|
@@ -11,19 +11,17 @@ const SvgSwitchCameraIcon = () => <svg xmlns="http://www.w3.org/2000/svg" viewBo
|
|
11 |
|
12 |
export type ControlTrayProps = {
|
13 |
videoRef: RefObject<HTMLVideoElement>;
|
14 |
-
|
15 |
-
onVideoStreamChange: (stream: MediaStream | null) => void;
|
16 |
isAppMicActive: boolean;
|
17 |
onAppMicToggle: (active: boolean) => void;
|
18 |
isAppCamActive: boolean;
|
19 |
onAppCamToggle: (active: boolean) => void;
|
20 |
currentFacingMode: 'user' | 'environment';
|
21 |
onFacingModeChange: (mode: 'user' | 'environment') => void;
|
22 |
-
onUserSpeakingChange: (isSpeaking: boolean) => void;
|
23 |
};
|
24 |
|
25 |
-
const ControlTray: React.FC<ControlTrayProps> = ({ videoRef,
|
26 |
-
const { client, connected, connect, volume } =
|
27 |
const audioRecorderRef = useRef<AudioRecorder | null>(null);
|
28 |
const [activeLocalVideoStream, setActiveLocalVideoStream] = useState<MediaStream | null>(null);
|
29 |
const [isSwitchingCamera, setIsSwitchingCamera] = useState(false);
|
@@ -58,7 +56,7 @@ const ControlTray: React.FC<ControlTrayProps> = ({ videoRef, onVideoStreamChange
|
|
58 |
audioRecorder.start();
|
59 |
}
|
60 |
} else {
|
61 |
-
if (audioRecorder
|
62 |
audioRecorder.stop();
|
63 |
}
|
64 |
}
|
@@ -80,12 +78,6 @@ const ControlTray: React.FC<ControlTrayProps> = ({ videoRef, onVideoStreamChange
|
|
80 |
}
|
81 |
}, [activeLocalVideoStream, videoRef]);
|
82 |
|
83 |
-
const stopWebcam = useCallback(() => {
|
84 |
-
if (activeLocalVideoStream) activeLocalVideoStream.getTracks().forEach(track => track.stop());
|
85 |
-
setActiveLocalVideoStream(null);
|
86 |
-
onVideoStreamChange(null);
|
87 |
-
}, [activeLocalVideoStream, onVideoStreamChange]);
|
88 |
-
|
89 |
const startWebcam = useCallback(async (facingModeToTry: 'user' | 'environment') => {
|
90 |
if (isSwitchingCamera) return;
|
91 |
setIsSwitchingCamera(true);
|
@@ -93,25 +85,24 @@ const ControlTray: React.FC<ControlTrayProps> = ({ videoRef, onVideoStreamChange
|
|
93 |
try {
|
94 |
const mediaStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: facingModeToTry }, audio: false });
|
95 |
setActiveLocalVideoStream(mediaStream);
|
96 |
-
onVideoStreamChange(mediaStream);
|
97 |
onFacingModeChange(facingModeToTry);
|
98 |
} catch (error) {
|
99 |
console.error(`❌ Start WC err ${facingModeToTry}:`, error);
|
100 |
setActiveLocalVideoStream(null);
|
101 |
-
onVideoStreamChange(null);
|
102 |
onAppCamToggle(false);
|
103 |
} finally {
|
104 |
setIsSwitchingCamera(false);
|
105 |
}
|
106 |
-
}, [isSwitchingCamera, activeLocalVideoStream,
|
107 |
-
|
108 |
useEffect(() => {
|
109 |
if (isAppCamActive && !activeLocalVideoStream && !isSwitchingCamera) {
|
110 |
startWebcam(currentFacingMode);
|
111 |
} else if (!isAppCamActive && activeLocalVideoStream) {
|
112 |
-
|
|
|
113 |
}
|
114 |
-
}, [isAppCamActive, activeLocalVideoStream, isSwitchingCamera, startWebcam,
|
115 |
|
116 |
useEffect(() => {
|
117 |
let timeoutId = -1;
|
@@ -178,22 +169,18 @@ const ControlTray: React.FC<ControlTrayProps> = ({ videoRef, onVideoStreamChange
|
|
178 |
const targetFacingMode = currentFacingMode === 'user' ? 'environment' : 'user';
|
179 |
activeLocalVideoStream.getTracks().forEach(track => track.stop());
|
180 |
setActiveLocalVideoStream(null);
|
181 |
-
onVideoStreamChange(null);
|
182 |
try {
|
183 |
const newStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: { exact: targetFacingMode } }, audio: false });
|
184 |
setActiveLocalVideoStream(newStream);
|
185 |
-
onVideoStreamChange(newStream);
|
186 |
onFacingModeChange(targetFacingMode);
|
187 |
} catch (error) {
|
188 |
console.error(`❌ Switch Cam err to ${targetFacingMode}:`, error);
|
189 |
try {
|
190 |
const restoredStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: {exact: currentFacingMode } }, audio: false });
|
191 |
setActiveLocalVideoStream(restoredStream);
|
192 |
-
onVideoStreamChange(restoredStream);
|
193 |
} catch (restoreError) {
|
194 |
console.error(`❌ Restore Cam err to ${currentFacingMode}:`, restoreError);
|
195 |
setActiveLocalVideoStream(null);
|
196 |
-
onVideoStreamChange(null);
|
197 |
onAppCamToggle(false);
|
198 |
}
|
199 |
} finally {
|
@@ -222,7 +209,7 @@ const ControlTray: React.FC<ControlTrayProps> = ({ videoRef, onVideoStreamChange
|
|
222 |
</div>
|
223 |
)}
|
224 |
|
225 |
-
<div id="cam-button-wrapper" className="control-button-wrapper
|
226 |
<div id="cam-button" className="control-button cam-button-color" onClick={handleCamToggle}>
|
227 |
{isAppCamActive ? stopCamIcon : cameraIcon}
|
228 |
</div>
|
|
|
2 |
|
3 |
import cn from "classnames";
|
4 |
import React, { memo, RefObject, useEffect, useState, useCallback, useRef } from "react";
|
5 |
+
import { useAppContext } from "../../contexts/AppContext"; // <-- تغییر اصلی اینجاست
|
6 |
import { AudioRecorder } from "../../lib/audio-recorder";
|
7 |
import Logo from '../logo/Logo';
|
8 |
import { PauseIconWithSurroundPulse, microphoneIcon, cameraIcon, stopCamIcon } from '../icons';
|
|
|
11 |
|
12 |
export type ControlTrayProps = {
|
13 |
videoRef: RefObject<HTMLVideoElement>;
|
14 |
+
onUserSpeakingChange: (isSpeaking: boolean) => void;
|
|
|
15 |
isAppMicActive: boolean;
|
16 |
onAppMicToggle: (active: boolean) => void;
|
17 |
isAppCamActive: boolean;
|
18 |
onAppCamToggle: (active: boolean) => void;
|
19 |
currentFacingMode: 'user' | 'environment';
|
20 |
onFacingModeChange: (mode: 'user' | 'environment') => void;
|
|
|
21 |
};
|
22 |
|
23 |
+
const ControlTray: React.FC<ControlTrayProps> = ({ videoRef, onUserSpeakingChange, isAppMicActive, onAppMicToggle, isAppCamActive, onAppCamToggle, currentFacingMode, onFacingModeChange }) => {
|
24 |
+
const { client, connected, connect, volume } = useAppContext(); // <-- تغییر اصلی اینجاست
|
25 |
const audioRecorderRef = useRef<AudioRecorder | null>(null);
|
26 |
const [activeLocalVideoStream, setActiveLocalVideoStream] = useState<MediaStream | null>(null);
|
27 |
const [isSwitchingCamera, setIsSwitchingCamera] = useState(false);
|
|
|
56 |
audioRecorder.start();
|
57 |
}
|
58 |
} else {
|
59 |
+
if (audioRecorder?.recording) {
|
60 |
audioRecorder.stop();
|
61 |
}
|
62 |
}
|
|
|
78 |
}
|
79 |
}, [activeLocalVideoStream, videoRef]);
|
80 |
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
const startWebcam = useCallback(async (facingModeToTry: 'user' | 'environment') => {
|
82 |
if (isSwitchingCamera) return;
|
83 |
setIsSwitchingCamera(true);
|
|
|
85 |
try {
|
86 |
const mediaStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: facingModeToTry }, audio: false });
|
87 |
setActiveLocalVideoStream(mediaStream);
|
|
|
88 |
onFacingModeChange(facingModeToTry);
|
89 |
} catch (error) {
|
90 |
console.error(`❌ Start WC err ${facingModeToTry}:`, error);
|
91 |
setActiveLocalVideoStream(null);
|
|
|
92 |
onAppCamToggle(false);
|
93 |
} finally {
|
94 |
setIsSwitchingCamera(false);
|
95 |
}
|
96 |
+
}, [isSwitchingCamera, activeLocalVideoStream, onFacingModeChange, onAppCamToggle]);
|
97 |
+
|
98 |
useEffect(() => {
|
99 |
if (isAppCamActive && !activeLocalVideoStream && !isSwitchingCamera) {
|
100 |
startWebcam(currentFacingMode);
|
101 |
} else if (!isAppCamActive && activeLocalVideoStream) {
|
102 |
+
activeLocalVideoStream.getTracks().forEach(track => track.stop());
|
103 |
+
setActiveLocalVideoStream(null);
|
104 |
}
|
105 |
+
}, [isAppCamActive, activeLocalVideoStream, isSwitchingCamera, startWebcam, currentFacingMode]);
|
106 |
|
107 |
useEffect(() => {
|
108 |
let timeoutId = -1;
|
|
|
169 |
const targetFacingMode = currentFacingMode === 'user' ? 'environment' : 'user';
|
170 |
activeLocalVideoStream.getTracks().forEach(track => track.stop());
|
171 |
setActiveLocalVideoStream(null);
|
|
|
172 |
try {
|
173 |
const newStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: { exact: targetFacingMode } }, audio: false });
|
174 |
setActiveLocalVideoStream(newStream);
|
|
|
175 |
onFacingModeChange(targetFacingMode);
|
176 |
} catch (error) {
|
177 |
console.error(`❌ Switch Cam err to ${targetFacingMode}:`, error);
|
178 |
try {
|
179 |
const restoredStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: {exact: currentFacingMode } }, audio: false });
|
180 |
setActiveLocalVideoStream(restoredStream);
|
|
|
181 |
} catch (restoreError) {
|
182 |
console.error(`❌ Restore Cam err to ${currentFacingMode}:`, restoreError);
|
183 |
setActiveLocalVideoStream(null);
|
|
|
184 |
onAppCamToggle(false);
|
185 |
}
|
186 |
} finally {
|
|
|
209 |
</div>
|
210 |
)}
|
211 |
|
212 |
+
<div id="cam-button-wrapper" className="control-button-wrapper">
|
213 |
<div id="cam-button" className="control-button cam-button-color" onClick={handleCamToggle}>
|
214 |
{isAppCamActive ? stopCamIcon : cameraIcon}
|
215 |
</div>
|