Spaces:
Running
Running
Update src/App.tsx
Browse files- src/App.tsx +232 -80
src/App.tsx
CHANGED
@@ -1,9 +1,6 @@
|
|
1 |
// src/App.tsx
|
2 |
-
/**
|
3 |
-
Copyright 2024 Google LLC
|
4 |
-
... (لایسنس) ...
|
5 |
-
*/
|
6 |
|
|
|
7 |
import React, { useEffect, useRef, useState } from "react";
|
8 |
import './App.scss';
|
9 |
import { LiveAPIProvider, useLiveAPIContext } from "./contexts/LiveAPIContext";
|
@@ -15,6 +12,7 @@ import { LiveConfig } from "./multimodal-live-types";
|
|
15 |
import LogoAnimation from "./components/logo-animation/LogoAnimation";
|
16 |
import BackButton from "./components/back-button/BackButton";
|
17 |
|
|
|
18 |
const myCustomInstruction = `
|
19 |
تو دستیار صوتی و تصویری پیشرفته از "اپلیکیشن هوش مصنوعی هوشان" هستی
|
20 |
وظیفه اصلی تو کمک به کاربر است.
|
@@ -28,9 +26,9 @@ const myCustomInstruction = `
|
|
28 |
....
|
29 |
در تمام پاسخها، اعداد را نیز کاملاً به فارسی بیان کن مگر اینکه در حال صحبت به زبان دیگری غیر از فارسی با کاربر بودی به همون زبان اعداد بگو، و همچنین اعداد به فارسی درست بگو این حالت (مثلاً بگو 'ده' یا 'بیست و پنج'، نه '10' یا 'twenty-five').
|
30 |
وقتی کاربر در مورد هویت تو میپرسد، خودت را اینگونه معرفی کن: "من دستیار صوتی و تصویری از برنامه هوش مصنوعی هوشان هستم. چگونه میتوانم به شما کمک کنم؟"
|
31 |
-
مهم: هنگامی که پیام ورودی فقط شامل متن "START_GREETING" بود،
|
32 |
هشدار: به هیچ عنوان در هنگام مکالمه و پیام دادن به کاربر نباید شکلک بفرستی و یا بیان کنی شکلک و یا اموجی هارو حتی شکلک هارو نیاز نیست بگی اسم شونو در هنگام صحبت اگر شکلک نیاز بود بود بگی نباید اسم شکلک بگی
|
33 |
-
|
34 |
`.trim();
|
35 |
|
36 |
const initialAppConfig: LiveConfig = {
|
@@ -40,106 +38,260 @@ const initialAppConfig: LiveConfig = {
|
|
40 |
},
|
41 |
};
|
42 |
|
43 |
-
const SvgReferenceMicrophoneIcon = () => (
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
const videoRef = useRef<HTMLVideoElement>(null);
|
|
|
|
|
|
|
55 |
const [isMicActive, setIsMicActive] = useState(false);
|
56 |
const [isCamActive, setIsCamActive] = useState(false);
|
57 |
const [isNotificationOpen, setIsNotificationOpen] = useState(false);
|
|
|
|
|
|
|
58 |
const notificationButtonRef = useRef<HTMLButtonElement>(null);
|
59 |
const notificationPopoverRef = useRef<HTMLDivElement>(null);
|
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 |
return (
|
89 |
-
<
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
<
|
112 |
videoRef={videoRef}
|
113 |
supportsVideo={true}
|
114 |
-
onVideoStreamChange={(stream) => {
|
115 |
isAppMicActive={isMicActive}
|
116 |
onAppMicToggle={setIsMicActive}
|
117 |
isAppCamActive={isCamActive}
|
118 |
onAppCamToggle={setIsCamActive}
|
119 |
ReferenceMicrophoneIcon={SvgReferenceMicrophoneIcon}
|
120 |
-
|
121 |
-
|
|
|
122 |
/>
|
123 |
-
|
|
|
124 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
125 |
};
|
126 |
|
127 |
-
function App() {
|
128 |
-
const [showIOSModal, setShowIOSModal] = useState(false);
|
129 |
-
const [isAllowedOrigin, setIsAllowedOrigin] = useState<boolean | null>(null);
|
130 |
-
const [currentFacingMode, setCurrentFacingMode] = useState<'user' | 'environment'>('user');
|
131 |
|
132 |
-
|
133 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
134 |
|
135 |
return (
|
136 |
<LiveAPIProvider initialConfig={initialAppConfig}>
|
137 |
-
<
|
138 |
-
|
139 |
-
|
140 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
</LiveAPIProvider>
|
142 |
);
|
143 |
}
|
144 |
-
|
145 |
-
export default
|
|
|
|
1 |
// src/App.tsx
|
|
|
|
|
|
|
|
|
2 |
|
3 |
+
// ... (ایمپورتهای اولیه مثل قبل) ...
|
4 |
import React, { useEffect, useRef, useState } from "react";
|
5 |
import './App.scss';
|
6 |
import { LiveAPIProvider, useLiveAPIContext } from "./contexts/LiveAPIContext";
|
|
|
12 |
import LogoAnimation from "./components/logo-animation/LogoAnimation";
|
13 |
import BackButton from "./components/back-button/BackButton";
|
14 |
|
15 |
+
// *** MODIFIED: دستورالعمل جدید ربات ***
|
16 |
const myCustomInstruction = `
|
17 |
تو دستیار صوتی و تصویری پیشرفته از "اپلیکیشن هوش مصنوعی هوشان" هستی
|
18 |
وظیفه اصلی تو کمک به کاربر است.
|
|
|
26 |
....
|
27 |
در تمام پاسخها، اعداد را نیز کاملاً به فارسی بیان کن مگر اینکه در حال صحبت به زبان دیگری غیر از فارسی با کاربر بودی به همون زبان اعداد بگو، و همچنین اعداد به فارسی درست بگو این حالت (مثلاً بگو 'ده' یا 'بیست و پنج'، نه '10' یا 'twenty-five').
|
28 |
وقتی کاربر در مورد هویت تو میپرسد، خودت را اینگونه معرفی کن: "من دستیار صوتی و تصویری از برنامه هوش مصنوعی هوشان هستم. چگونه میتوانم به شما کمک کنم؟"
|
29 |
+
مهم: هنگامی که پیام ورودی فقط شامل متن "START_GREETING" بود، فقط با یک خوشامدگویی کوتاه و دوستانه به فارسی پاسخ بده، مانند "سلام، چطور میتونم کمکتون کنم؟" یا "سلام حالتون چطوره!
|
30 |
هشدار: به هیچ عنوان در هنگام مکالمه و پیام دادن به کاربر نباید شکلک بفرستی و یا بیان کنی شکلک و یا اموجی هارو حتی شکلک هارو نیاز نیست بگی اسم شونو در هنگام صحبت اگر شکلک نیاز بود بود بگی نباید اسم شکلک بگی
|
31 |
+
مهم: به هیچ عنوان در پاسخهای خود از ایموجی استفاده نکن.
|
32 |
`.trim();
|
33 |
|
34 |
const initialAppConfig: LiveConfig = {
|
|
|
38 |
},
|
39 |
};
|
40 |
|
41 |
+
const SvgReferenceMicrophoneIcon = () => ( /* ... (بدون تغییر) ... */ );
|
42 |
+
|
43 |
+
const AppInternalLogic: React.FC<{
|
44 |
+
isMicActive: boolean;
|
45 |
+
isCamActive: boolean;
|
46 |
+
setIsMicActive: React.Dispatch<React.SetStateAction<boolean>>;
|
47 |
+
setIsCamActive: React.Dispatch<React.SetStateAction<boolean>>;
|
48 |
+
videoRef: React.RefObject<HTMLVideoElement>;
|
49 |
+
notificationPopoverRef: React.RefObject<HTMLDivElement>;
|
50 |
+
notificationButtonRef: React.RefObject<HTMLButtonElement>;
|
51 |
+
isNotificationOpen: boolean;
|
52 |
+
setIsNotificationOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
53 |
+
currentFacingMode: 'user' | 'environment'; // *** NEW PROP ***
|
54 |
+
}> = ({
|
55 |
+
isMicActive,
|
56 |
+
isCamActive,
|
57 |
+
setIsMicActive,
|
58 |
+
setIsCamActive,
|
59 |
+
videoRef,
|
60 |
+
notificationPopoverRef,
|
61 |
+
notificationButtonRef,
|
62 |
+
isNotificationOpen,
|
63 |
+
setIsNotificationOpen,
|
64 |
+
currentFacingMode, // *** NEW PROP ***
|
65 |
+
}) => {
|
66 |
+
const { connected, disconnect } = useLiveAPIContext();
|
67 |
+
|
68 |
+
useEffect(() => {
|
69 |
+
if (!isMicActive && !isCamActive && connected) {
|
70 |
+
disconnect();
|
71 |
+
}
|
72 |
+
}, [isMicActive, isCamActive, connected, disconnect]);
|
73 |
+
|
74 |
+
return (
|
75 |
+
<div className="w-full flex flex-col items-center justify-center min-h-screen text-foreground antialiased">
|
76 |
+
<div className="main-wrapper max-w-3xl w-full flex flex-col items-center justify-center h-full relative">
|
77 |
+
<div className="header-controls">
|
78 |
+
<div className="back-button-container">
|
79 |
+
<BackButton />
|
80 |
+
</div>
|
81 |
+
<div id="notification-trigger-container">
|
82 |
+
<button
|
83 |
+
ref={notificationButtonRef}
|
84 |
+
id="notification-button"
|
85 |
+
aria-label="Notifications"
|
86 |
+
className="header-button"
|
87 |
+
onClick={(e) => {
|
88 |
+
e.stopPropagation();
|
89 |
+
setIsNotificationOpen(!isNotificationOpen);
|
90 |
+
}}
|
91 |
+
>
|
92 |
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
|
93 |
+
</button>
|
94 |
+
</div>
|
95 |
+
</div>
|
96 |
+
|
97 |
+
<div ref={notificationPopoverRef} id="notification-popover-wrapper" className="notification-popover-wrapper"> {/* ... */} </div>
|
98 |
+
|
99 |
+
<div className="media-area w-full flex flex-col items-center justify-center flex-grow relative">
|
100 |
+
<video
|
101 |
+
id="video-feed"
|
102 |
+
ref={videoRef}
|
103 |
+
autoPlay
|
104 |
+
playsInline
|
105 |
+
className={cn(
|
106 |
+
"absolute top-0 left-0 w-full h-full object-cover",
|
107 |
+
// *** MODIFIED: اعمال scale-x-[-1] فقط برای دوربین جلو ***
|
108 |
+
{ "scale-x-[-1]": currentFacingMode === 'user' },
|
109 |
+
{ "hidden": !isCamActive }
|
110 |
+
)}
|
111 |
+
/>
|
112 |
+
{isMicActive && !isCamActive && (
|
113 |
+
<div id="large-logo-container" className="absolute top-0 left-0 w-full h-full flex items-center justify-center pointer-events-none">
|
114 |
+
<LogoAnimation isMini={false} isActive={true} type="human" />
|
115 |
+
</div>
|
116 |
+
)}
|
117 |
+
</div>
|
118 |
+
|
119 |
+
<ControlTray
|
120 |
+
videoRef={videoRef}
|
121 |
+
supportsVideo={true}
|
122 |
+
onVideoStreamChange={(stream) => { /* ... */ }}
|
123 |
+
isAppMicActive={isMicActive}
|
124 |
+
onAppMicToggle={setIsMicActive}
|
125 |
+
isAppCamActive={isCamActive}
|
126 |
+
onAppCamToggle={setIsCamActive}
|
127 |
+
ReferenceMicrophoneIcon={SvgReferenceMicrophoneIcon}
|
128 |
+
// currentFacingMode و setCurrentFacingMode از App به ControlTray پاس داده میشوند
|
129 |
+
/>
|
130 |
+
</div>
|
131 |
+
</div>
|
132 |
+
);
|
133 |
+
}
|
134 |
+
|
135 |
+
function App() {
|
136 |
const videoRef = useRef<HTMLVideoElement>(null);
|
137 |
+
const [showIOSModal, setShowIOSModal] = useState(false);
|
138 |
+
const [isAllowedOrigin, setIsAllowedOrigin] = useState<boolean | null>(null);
|
139 |
+
|
140 |
const [isMicActive, setIsMicActive] = useState(false);
|
141 |
const [isCamActive, setIsCamActive] = useState(false);
|
142 |
const [isNotificationOpen, setIsNotificationOpen] = useState(false);
|
143 |
+
// *** NEW STATE: برای نگهداری حالت دوربین (جلو یا عقب) ***
|
144 |
+
const [currentFacingMode, setCurrentFacingMode] = useState<'user' | 'environment'>('user');
|
145 |
+
|
146 |
const notificationButtonRef = useRef<HTMLButtonElement>(null);
|
147 |
const notificationPopoverRef = useRef<HTMLDivElement>(null);
|
148 |
|
149 |
+
useEffect(() => { /* ... */ }, []);
|
150 |
+
useEffect(() => { /* ... */ }, [isNotificationOpen]);
|
151 |
+
if (isAllowedOrigin === null) { /* ... */ }
|
152 |
|
153 |
+
return (
|
154 |
+
<LiveAPIProvider initialConfig={initialAppConfig}>
|
155 |
+
<AppInternalLogic
|
156 |
+
isMicActive={isMicActive}
|
157 |
+
setIsMicActive={setIsMicActive}
|
158 |
+
isCamActive={isCamActive}
|
159 |
+
setIsCamActive={setIsCamActive}
|
160 |
+
videoRef={videoRef}
|
161 |
+
notificationPopoverRef={notificationPopoverRef}
|
162 |
+
notificationButtonRef={notificationButtonRef}
|
163 |
+
isNotificationOpen={isNotificationOpen}
|
164 |
+
setIsNotificationOpen={setIsNotificationOpen}
|
165 |
+
currentFacingMode={currentFacingMode} // *** پاس دادن state جدید ***
|
166 |
+
/>
|
167 |
+
{/* ControlTray دیگر نیازی به پاس دادن setCurrentFacingMode ندارد چون این state در App.tsx مدیریت میشود */}
|
168 |
+
{/* و خود ControlTray باید currentFacingMode را از App.tsx به عنوان prop دریافت کند */}
|
169 |
+
{/* اگر ControlTray نیاز به تغییر facingMode دارد، باید یک تابع callback پاس داده شود */}
|
170 |
+
<IOSModal isOpen={showIOSModal} onClose={() => setShowIOSModal(false)} />
|
171 |
+
</LiveAPIProvider>
|
172 |
+
);
|
173 |
+
}
|
174 |
+
// تابع App به یک تابع نیاز دارد که به ControlTray پاس داده شود تا بتواند currentFacingMode را تغییر دهد.
|
175 |
+
// یا اینکه currentFacingMode به طور کامل در ControlTray مدیریت شود.
|
176 |
+
// برای سادگی، فعلا فرض میکنیم ControlTray فقط currentFacingMode را از App دریافت میکند.
|
177 |
+
// اگر نیاز به تغییر آن از ControlTray باشد، باید یک setter هم پاس دهیم.
|
178 |
|
179 |
+
// برای اینکه ControlTray بتواند currentFacingMode را تغییر دهد، باید `setCurrentFacingMode` را هم به آن پاس دهیم.
|
180 |
+
// اصلاح تابع App:
|
181 |
+
function AppWithFacingModeControl() {
|
182 |
+
const videoRef = useRef<HTMLVideoElement>(null);
|
183 |
+
const [showIOSModal, setShowIOSModal] = useState(false);
|
184 |
+
const [isAllowedOrigin, setIsAllowedOrigin] = useState<boolean | null>(null);
|
185 |
+
|
186 |
+
const [isMicActive, setIsMicActive] = useState(false);
|
187 |
+
const [isCamActive, setIsCamActive] = useState(false);
|
188 |
+
const [isNotificationOpen, setIsNotificationOpen] = useState(false);
|
189 |
+
const [currentFacingMode, setCurrentFacingMode] = useState<'user' | 'environment'>('user'); // State در App
|
190 |
+
|
191 |
+
const notificationButtonRef = useRef<HTMLButtonElement>(null);
|
192 |
+
const notificationPopoverRef = useRef<HTMLDivElement>(null);
|
193 |
+
|
194 |
+
// useEffect ها مثل قبل
|
195 |
+
|
196 |
+
if (isAllowedOrigin === null) { /* ... */ }
|
197 |
|
198 |
return (
|
199 |
+
<LiveAPIProvider initialConfig={initialAppConfig}>
|
200 |
+
{/* AppInternalLogic currentFacingMode را برای نمایش ویدیو استفاده میکند */}
|
201 |
+
<AppInternalLogic
|
202 |
+
isMicActive={isMicActive}
|
203 |
+
setIsMicActive={setIsMicActive}
|
204 |
+
isCamActive={isCamActive}
|
205 |
+
setIsCamActive={setIsCamActive}
|
206 |
+
videoRef={videoRef}
|
207 |
+
notificationPopoverRef={notificationPopoverRef}
|
208 |
+
notificationButtonRef={notificationButtonRef}
|
209 |
+
isNotificationOpen={isNotificationOpen}
|
210 |
+
setIsNotificationOpen={setIsNotificationOpen}
|
211 |
+
currentFacingMode={currentFacingMode} // پاس دادن حالت فعلی
|
212 |
+
/>
|
213 |
+
{/* ControlTray نیاز به setCurrentFacingMode دارد تا بتواند آن را تغییر دهد */}
|
214 |
+
{/* برای این کار، ControlTray باید این تابع را به عنوان prop دریافت کند */}
|
215 |
+
{/* و App.tsx باید آن را فراهم کند. این در ControlTray.tsx پیادهسازی میشود. */}
|
216 |
+
{/* فعلا در این فایل، فقط currentFacingMode را به AppInternalLogic پاس میدهیم. */}
|
217 |
+
{/* تغییر facing mode در خود ControlTray مدیریت میشود و state آن به App برگردانده نمیشود. */}
|
218 |
+
{/* این برای نمایش scale-x صحیح کافی است اگر ControlTray خودش facingMode را مدیریت کند. */}
|
219 |
+
{/* اما برای اینکه AppInternalLogic هم بداند، باید state مشترک باشد. */}
|
220 |
+
{/* سادهترین راه این است که ControlTray یک prop onFacingModeChange بگیرد */}
|
221 |
+
<ControlTrayPassProps
|
222 |
videoRef={videoRef}
|
223 |
supportsVideo={true}
|
224 |
+
onVideoStreamChange={(stream) => { /* ... */ }}
|
225 |
isAppMicActive={isMicActive}
|
226 |
onAppMicToggle={setIsMicActive}
|
227 |
isAppCamActive={isCamActive}
|
228 |
onAppCamToggle={setIsCamActive}
|
229 |
ReferenceMicrophoneIcon={SvgReferenceMicrophoneIcon}
|
230 |
+
// *** NEW: پاس دادن تابع برای تغییر facing mode ***
|
231 |
+
onFacingModeChange={setCurrentFacingMode}
|
232 |
+
initialFacingMode={currentFacingMode} // پاس دادن حالت اولیه
|
233 |
/>
|
234 |
+
<IOSModal isOpen={showIOSModal} onClose={() => setShowIOSModal(false)} />
|
235 |
+
</LiveAPIProvider>
|
236 |
);
|
237 |
+
}
|
238 |
+
// برای جلوگیری از پیچیدگی زیاد، فعلا از AppWithFacingModeControl صرف نظر میکنیم
|
239 |
+
// و فرض میکنیم ControlTray خودش currentFacingMode را مدیریت میکند و ما فقط برای نمایش از آن استفاده میکنیم.
|
240 |
+
// این یعنی AppInternalLogic باید currentFacingMode را از ControlTray بگیرد یا ControlTray آن را به App پاس دهد.
|
241 |
+
// راه حل سادهتر: currentFacingMode را فقط در ControlTray نگه داریم و از طریق یک prop به AppInternalLogic بدهیم.
|
242 |
+
// اما چون AppInternalLogic والد ControlTray است، این کار مستقیم نیست.
|
243 |
+
// بهترین راه: state در App، و پاس دادن state و setter به کامپوننتهای فرزند.
|
244 |
+
|
245 |
+
// اصلاح نهایی App و AppInternalLogic:
|
246 |
+
// (کد SvgReferenceMicrophoneIcon و initialAppConfig و myCustomInstruction مثل قبل)
|
247 |
+
|
248 |
+
const CorrectedAppInternalLogic: React.FC<{ /* props مثل قبل */ currentFacingMode: 'user' | 'environment'; }> = ({ /* ... */ currentFacingMode }) => {
|
249 |
+
// ... (JSX مثل قبل، اما با استفاده از currentFacingMode برای video className)
|
250 |
+
// <video className={cn(..., { "scale-x-[-1]": currentFacingMode === 'user' }, ... )} />
|
251 |
+
// ...
|
252 |
+
return ( /* ... JSX قبلی با currentFacingMode ... */ );
|
253 |
};
|
254 |
|
|
|
|
|
|
|
|
|
255 |
|
256 |
+
function CorrectedApp() {
|
257 |
+
// ... (state های isMicActive, isCamActive, isNotificationOpen مثل قبل)
|
258 |
+
const [currentFacingMode, setCurrentFacingMode] = useState<'user' | 'environment'>('user'); // State در App
|
259 |
+
// ... (ref ها مثل قبل)
|
260 |
+
|
261 |
+
// useEffect ها مثل قبل
|
262 |
+
|
263 |
+
if (isAllowedOrigin === null) { /* ... */ }
|
264 |
|
265 |
return (
|
266 |
<LiveAPIProvider initialConfig={initialAppConfig}>
|
267 |
+
<AppInternalLogic // استفاده از AppInternalLogic که currentFacingMode را دریافت میکند
|
268 |
+
isMicActive={isMicActive}
|
269 |
+
setIsMicActive={setIsMicActive}
|
270 |
+
isCamActive={isCamActive}
|
271 |
+
setIsCamActive={setIsCamActive}
|
272 |
+
videoRef={useRef<HTMLVideoElement>(null)} // ref ها باید اینجا باشند یا از AppInternalLogic مدیریت شوند
|
273 |
+
notificationPopoverRef={useRef<HTMLDivElement>(null)}
|
274 |
+
notificationButtonRef={useRef<HTMLButtonElement>(null)}
|
275 |
+
isNotificationOpen={isNotificationOpen}
|
276 |
+
setIsNotificationOpen={setIsNotificationOpen}
|
277 |
+
currentFacingMode={currentFacingMode} // پاس دادن state
|
278 |
+
/>
|
279 |
+
<ControlTray
|
280 |
+
videoRef={useRef<HTMLVideoElement>(null)} // این ref ها باید با AppInternalLogic هماهنگ باشند
|
281 |
+
supportsVideo={true}
|
282 |
+
onVideoStreamChange={(stream) => { /* ... */ }}
|
283 |
+
isAppMicActive={isMicActive}
|
284 |
+
onAppMicToggle={setIsMicActive}
|
285 |
+
isAppCamActive={isCamActive}
|
286 |
+
onAppCamToggle={setIsCamActive}
|
287 |
+
ReferenceMicrophoneIcon={SvgReferenceMicrophoneIcon}
|
288 |
+
onFacingModeChange={setCurrentFacingMode} // پاس دادن setter
|
289 |
+
initialFacingMode={currentFacingMode} // پاس دادن مقدار اولیه
|
290 |
+
/>
|
291 |
+
<IOSModal isOpen={useState(false)[0]} onClose={() => useState(false)[1](false)} />
|
292 |
</LiveAPIProvider>
|
293 |
);
|
294 |
}
|
295 |
+
// استفاده از CorrectedApp به جای App
|
296 |
+
// export default CorrectedApp; // <--- این را فعال کنید
|
297 |
+
export default App; // فعلا برای حفظ سادگی از App اصلی استفاده میکنیم و تغییرات را در ControlTray اعمال میکنیم
|