Ezmary commited on
Commit
9910b51
·
verified ·
1 Parent(s): ff1e22f

Update src/App.tsx

Browse files
Files changed (1) hide show
  1. src/App.tsx +160 -148
src/App.tsx CHANGED
@@ -1,4 +1,16 @@
1
- // ... (ایمپورت‌ها، myCustomInstruction, initialAppConfig, SVG Icons, AppInternalLogic، و بخش اول تابع App بدون تغییر) ...
 
 
 
 
 
 
 
 
 
 
 
 
2
  import React, { useEffect, useRef, useState } from "react";
3
  import './App.scss';
4
  import { LiveAPIProvider, useLiveAPIContext } from "./contexts/LiveAPIContext";
@@ -107,109 +119,119 @@ const SvgReferenceMicrophoneIcon = () => (
107
  </svg>
108
  );
109
 
110
- const AppInternalLogic: React.FC<{ /* ... props ... */ }> = ({ /* ... props ... */ }) => {
111
- // ... (بدون تغییر)
112
- isMicActive,
113
- isCamActive,
114
- setIsMicActive,
115
- setIsCamActive,
116
- createLogoFunction,
117
- videoRef,
118
- notificationPopoverRef,
119
- notificationButtonRef,
120
- isNotificationOpen,
121
- setIsNotificationOpen
122
- }) => {
123
- const { connected, disconnect } = useLiveAPIContext();
124
-
125
- useEffect(() => {
126
- if (!isMicActive && !isCamActive && connected) {
127
- disconnect();
128
- }
129
- }, [isMicActive, isCamActive, connected, disconnect]);
130
-
131
- return (
132
- <div className="w-full flex flex-col items-center justify-center min-h-screen text-foreground antialiased">
133
- <div className="main-wrapper max-w-3xl w-full flex flex-col items-center justify-center h-full relative">
134
- <div className="header-controls">
135
- <div id="notification-trigger-container">
136
- <button
137
- ref={notificationButtonRef}
138
- id="notification-button"
139
- aria-label="Notifications"
140
- className="header-button"
141
- onClick={(e) => {
142
- e.stopPropagation();
143
- setIsNotificationOpen(!isNotificationOpen);
144
- }}
145
- >
146
- <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>
147
- </button>
148
- </div>
149
- <div className="back-button-container">
150
- <button
151
- id="back-button"
152
- aria-label="Go back"
153
- className="header-button"
154
- onClick={() => alert('Back clicked (implement navigation)')}
155
- >
156
- <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"><path d="m15 18-6-6 6-6"></path></svg>
157
- </button>
158
- </div>
159
  </div>
160
-
161
- <div ref={notificationPopoverRef} id="notification-popover-wrapper" className="notification-popover-wrapper">
162
- <div
163
- id="notification-popover"
164
- className={cn("popover-content", {
165
- "open animate-popover-open-top-center": isNotificationOpen,
166
- "animate-popover-close-top-center": !isNotificationOpen && document.getElementById('notification-popover')?.classList.contains('open'),
167
- })}
168
  >
169
- <div className="notification-popover-text-content">
170
- مدل‌های هوش مصنوعی می‌توانند اشتباه کنند، صحت اطلاعات مهم را بررسی کنید و از وارد کردن اطلاعات حساس بپرهیزید.
171
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
172
  </div>
173
  </div>
174
-
175
- <div className="media-area w-full flex flex-col items-center justify-center flex-grow relative">
176
- <video
177
- id="video-feed"
178
- ref={videoRef}
179
- autoPlay
180
- playsInline
181
- className={cn(
182
- "absolute top-0 left-0 w-full h-full object-cover scale-x-[-1]",
183
- { "hidden": !isCamActive }
184
- )}
185
- />
186
- {isMicActive && !isCamActive && (
187
- <div
188
- id="large-logo-container"
189
- className="absolute top-0 left-0 w-full h-full flex items-center justify-center pointer-events-none"
190
- >
191
- {createLogoFunction(false, true, 'human', false)}
192
- </div>
193
  )}
194
- </div>
195
-
196
- <ControlTray
197
- videoRef={videoRef}
198
- supportsVideo={true}
199
- onVideoStreamChange={(stream) => { /* ... */ }}
200
- isAppMicActive={isMicActive}
201
- onAppMicToggle={setIsMicActive}
202
- isAppCamActive={isCamActive}
203
- onAppCamToggle={setIsCamActive}
204
- createLogoFunction={createLogoFunction}
205
- ReferenceMicrophoneIcon={SvgReferenceMicrophoneIcon}
206
  />
 
 
 
 
 
 
 
 
207
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
208
  </div>
209
- );
 
210
  };
211
 
212
- const logoColorConfig = { /* ... (بدون تغییر) ... */
213
  blue: {
214
  ping: "bg-blue-200 dark:bg-blue-700",
215
  outer: "bg-blue-200 dark:bg-blue-700",
@@ -230,51 +252,50 @@ const logoColorConfig = { /* ... (بدون تغییر) ... */
230
  }
231
  };
232
 
233
- function App() { /* ... (useEffect ها و state ها بدون تغییر) ... */
234
- const videoRef = useRef<HTMLVideoElement>(null);
235
- const [showIOSModal, setShowIOSModal] = useState(false);
236
- const [isAllowedOrigin, setIsAllowedOrigin] = useState<boolean | null>(null);
237
-
238
- const [isMicActive, setIsMicActive] = useState(false);
239
- const [isCamActive, setIsCamActive] = useState(false);
240
- const [isNotificationOpen, setIsNotificationOpen] = useState(false);
241
-
242
- const notificationPopoverRef = useRef<HTMLDivElement>(null);
243
- const notificationButtonRef = useRef<HTMLButtonElement>(null);
244
-
245
- useEffect(() => {
246
- if (isIOS()) {
247
- setShowIOSModal(true);
248
- }
249
- const timer = setTimeout(() => {
250
- setIsAllowedOrigin(true);
251
- }, 100);
252
- return () => clearTimeout(timer);
253
- }, []);
254
-
255
- useEffect(() => {
256
- const handleClickOutside = (event: MouseEvent) => {
257
- if (
258
- isNotificationOpen &&
259
- notificationPopoverRef.current &&
260
- !notificationPopoverRef.current.contains(event.target as Node) &&
261
- notificationButtonRef.current &&
262
- !notificationButtonRef.current.contains(event.target as Node)
263
- ) {
264
- setIsNotificationOpen(false);
265
- }
266
- };
267
- document.addEventListener("mousedown", handleClickOutside);
268
- return () => {
269
- document.removeEventListener("mousedown", handleClickOutside);
270
- };
271
- }, [isNotificationOpen]);
272
-
273
- if (isAllowedOrigin === null) {
274
- return <div style={{ padding: '20px', textAlign: 'center' }}>در حال بررسی دسترسی...</div>;
275
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
 
277
- // *** MODIFIED: createLogoFunction با دقت بیشتر در inset ها و کلاس‌ها ***
278
  const createLogoFunction = (isMini: boolean, isActive: boolean, type: 'human' | 'ai' = 'human', forFooter: boolean = false) => {
279
  if (!isActive) return null;
280
 
@@ -285,13 +306,9 @@ function App() { /* ... (useEffect ها و state ها بدون تغییر) ... *
285
  const iconDisplaySize = isMini ? 35 : 70;
286
  const iconInset = (size - iconDisplaySize) / 2;
287
 
288
- // مقادیر inset برای حلقه‌ها از HTML مرجع شما:
289
- // بزرگ: ping: 40, outer: 0, mid: 20, inner: 50, icon: 65
290
- // کوچک: ping: 10, outer: 0, mid: 5, inner: 12, icon: 22
291
- // ما iconInset را خودمان محاسبه کردیم، بقیه را از HTML می‌گیریم.
292
  const insetValues = {
293
  ping: isMini ? 10 : 40,
294
- outer: 0, // در HTML شما برای outer از inset:0 استفاده شده
295
  mid: isMini ? 5 : 20,
296
  inner: isMini ? 12 : 50,
297
  icon: iconInset
@@ -301,15 +318,10 @@ function App() { /* ... (useEffect ها و state ها بدون تغییر) ... *
301
 
302
  return (
303
  <div className={cn("logo-animation-wrapper", {"for-footer": forFooter})} style={{ width: `${size}px`, height: `${size}px` }}>
304
- {/* حلقه پینگ */}
305
  <div className={`absolute rounded-full opacity-50 animate-ping ${currentColors.ping}`} style={{ inset: `${insetValues.ping}px` }}></div>
306
- {/* حلقه بیرونی */}
307
  <div className={`absolute rounded-full opacity-50 ${currentColors.outer}`} style={{ inset: `${insetValues.outer}px` }}></div>
308
- {/* حلقه میانی */}
309
  <div className={`absolute rounded-full opacity-50 ${currentColors.mid}`} style={{ inset: `${insetValues.mid}px` }}></div>
310
- {/* حلقه داخلی */}
311
  <div className={`absolute rounded-full opacity-50 ${currentColors.inner}`} style={{ inset: `${insetValues.inner}px` }}></div>
312
- {/* کانتینر آیکون */}
313
  <div className="z-10 absolute flex items-center justify-center" style={{ inset: `${insetValues.icon}px`, width: `${iconDisplaySize}px`, height: `${iconDisplaySize}px` }}>
314
  {IconComponent && <IconComponent />}
315
  </div>
@@ -317,7 +329,7 @@ function App() { /* ... (useEffect ها و state ها بدون تغییر) ... *
317
  );
318
  };
319
 
320
- return ( /* ... (LiveAPIProvider و AppInternalLogic مثل قبل) ... */
321
  <LiveAPIProvider initialConfig={initialAppConfig}>
322
  <AppInternalLogic
323
  isMicActive={isMicActive}
 
1
+ /**
2
+ Copyright 2024 Google LLC
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0
7
+ Unless required by applicable law or agreed to in writing, software
8
+ distributed under the License is distributed on an "AS IS" BASIS,
9
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ See the License for the specific language governing permissions and
11
+ limitations under the License.
12
+ */
13
+
14
  import React, { useEffect, useRef, useState } from "react";
15
  import './App.scss';
16
  import { LiveAPIProvider, useLiveAPIContext } from "./contexts/LiveAPIContext";
 
119
  </svg>
120
  );
121
 
122
+ const AppInternalLogic: React.FC<{
123
+ isMicActive: boolean;
124
+ isCamActive: boolean;
125
+ setIsMicActive: React.Dispatch<React.SetStateAction<boolean>>;
126
+ setIsCamActive: React.Dispatch<React.SetStateAction<boolean>>;
127
+ createLogoFunction: (isMini: boolean, isActive: boolean, type?: 'human' | 'ai', forFooter?: boolean) => React.ReactNode;
128
+ videoRef: React.RefObject<HTMLVideoElement>;
129
+ notificationPopoverRef: React.RefObject<HTMLDivElement>;
130
+ notificationButtonRef: React.RefObject<HTMLButtonElement>;
131
+ isNotificationOpen: boolean;
132
+ setIsNotificationOpen: React.Dispatch<React.SetStateAction<boolean>>;
133
+ }> = ({
134
+ isMicActive,
135
+ isCamActive,
136
+ setIsMicActive,
137
+ setIsCamActive,
138
+ createLogoFunction,
139
+ videoRef,
140
+ notificationPopoverRef,
141
+ notificationButtonRef,
142
+ isNotificationOpen,
143
+ setIsNotificationOpen // اطمینان از وجود کاما در صورت نیاز در کپی پیست‌های قبلی
144
+ }) => {
145
+ const { connected, disconnect } = useLiveAPIContext();
146
+
147
+ useEffect(() => {
148
+ if (!isMicActive && !isCamActive && connected) {
149
+ disconnect();
150
+ }
151
+ }, [isMicActive, isCamActive, connected, disconnect]);
152
+
153
+ return (
154
+ <div className="w-full flex flex-col items-center justify-center min-h-screen text-foreground antialiased">
155
+ <div className="main-wrapper max-w-3xl w-full flex flex-col items-center justify-center h-full relative">
156
+ <div className="header-controls">
157
+ <div id="notification-trigger-container">
158
+ <button
159
+ ref={notificationButtonRef}
160
+ id="notification-button"
161
+ aria-label="Notifications"
162
+ className="header-button"
163
+ onClick={(e) => {
164
+ e.stopPropagation();
165
+ setIsNotificationOpen(!isNotificationOpen);
166
+ }}
167
+ >
168
+ <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>
169
+ </button>
 
170
  </div>
171
+ <div className="back-button-container">
172
+ <button
173
+ id="back-button"
174
+ aria-label="Go back"
175
+ className="header-button"
176
+ onClick={() => alert('Back clicked (implement navigation)')}
 
 
177
  >
178
+ <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"><path d="m15 18-6-6 6-6"></path></svg>
179
+ </button>
180
+ </div>
181
+ </div>
182
+
183
+ <div ref={notificationPopoverRef} id="notification-popover-wrapper" className="notification-popover-wrapper">
184
+ <div
185
+ id="notification-popover"
186
+ className={cn("popover-content", {
187
+ "open animate-popover-open-top-center": isNotificationOpen,
188
+ "animate-popover-close-top-center": !isNotificationOpen && document.getElementById('notification-popover')?.classList.contains('open'),
189
+ })}
190
+ >
191
+ <div className="notification-popover-text-content">
192
+ مدل‌های هوش مصنوعی می‌توانند اشتباه کنند، صحت اطلاعات مهم را بررسی کنید و از وارد کردن اطلاعات حساس بپرهیزید.
193
  </div>
194
  </div>
195
+ </div>
196
+
197
+ <div className="media-area w-full flex flex-col items-center justify-center flex-grow relative">
198
+ <video
199
+ id="video-feed"
200
+ ref={videoRef}
201
+ autoPlay
202
+ playsInline
203
+ className={cn(
204
+ "absolute top-0 left-0 w-full h-full object-cover scale-x-[-1]",
205
+ { "hidden": !isCamActive }
 
 
 
 
 
 
 
 
206
  )}
 
 
 
 
 
 
 
 
 
 
 
 
207
  />
208
+ {isMicActive && !isCamActive && (
209
+ <div
210
+ id="large-logo-container"
211
+ className="absolute top-0 left-0 w-full h-full flex items-center justify-center pointer-events-none"
212
+ >
213
+ {createLogoFunction(false, true, 'human', false)}
214
+ </div>
215
+ )}
216
  </div>
217
+
218
+ <ControlTray
219
+ videoRef={videoRef}
220
+ supportsVideo={true}
221
+ onVideoStreamChange={(stream) => { /* ... */ }}
222
+ isAppMicActive={isMicActive}
223
+ onAppMicToggle={setIsMicActive}
224
+ isAppCamActive={isCamActive}
225
+ onAppCamToggle={setIsCamActive}
226
+ createLogoFunction={createLogoFunction}
227
+ ReferenceMicrophoneIcon={SvgReferenceMicrophoneIcon}
228
+ />
229
  </div>
230
+ </div>
231
+ );
232
  };
233
 
234
+ const logoColorConfig = {
235
  blue: {
236
  ping: "bg-blue-200 dark:bg-blue-700",
237
  outer: "bg-blue-200 dark:bg-blue-700",
 
252
  }
253
  };
254
 
255
+ function App() {
256
+ const videoRef = useRef<HTMLVideoElement>(null);
257
+ const [showIOSModal, setShowIOSModal] = useState(false);
258
+ const [isAllowedOrigin, setIsAllowedOrigin] = useState<boolean | null>(null);
259
+
260
+ const [isMicActive, setIsMicActive] = useState(false);
261
+ const [isCamActive, setIsCamActive] = useState(false);
262
+ const [isNotificationOpen, setIsNotificationOpen] = useState(false);
263
+
264
+ const notificationPopoverRef = useRef<HTMLDivElement>(null);
265
+ const notificationButtonRef = useRef<HTMLButtonElement>(null);
266
+
267
+ useEffect(() => {
268
+ if (isIOS()) {
269
+ setShowIOSModal(true);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
  }
271
+ const timer = setTimeout(() => {
272
+ setIsAllowedOrigin(true);
273
+ }, 100);
274
+ return () => clearTimeout(timer);
275
+ }, []);
276
+
277
+ useEffect(() => {
278
+ const handleClickOutside = (event: MouseEvent) => {
279
+ if (
280
+ isNotificationOpen &&
281
+ notificationPopoverRef.current &&
282
+ !notificationPopoverRef.current.contains(event.target as Node) &&
283
+ notificationButtonRef.current &&
284
+ !notificationButtonRef.current.contains(event.target as Node)
285
+ ) {
286
+ setIsNotificationOpen(false);
287
+ }
288
+ };
289
+ document.addEventListener("mousedown", handleClickOutside);
290
+ return () => {
291
+ document.removeEventListener("mousedown", handleClickOutside);
292
+ };
293
+ }, [isNotificationOpen]);
294
+
295
+ if (isAllowedOrigin === null) {
296
+ return <div style={{ padding: '20px', textAlign: 'center' }}>در حال بررسی دسترسی...</div>;
297
+ }
298
 
 
299
  const createLogoFunction = (isMini: boolean, isActive: boolean, type: 'human' | 'ai' = 'human', forFooter: boolean = false) => {
300
  if (!isActive) return null;
301
 
 
306
  const iconDisplaySize = isMini ? 35 : 70;
307
  const iconInset = (size - iconDisplaySize) / 2;
308
 
 
 
 
 
309
  const insetValues = {
310
  ping: isMini ? 10 : 40,
311
+ outer: 0,
312
  mid: isMini ? 5 : 20,
313
  inner: isMini ? 12 : 50,
314
  icon: iconInset
 
318
 
319
  return (
320
  <div className={cn("logo-animation-wrapper", {"for-footer": forFooter})} style={{ width: `${size}px`, height: `${size}px` }}>
 
321
  <div className={`absolute rounded-full opacity-50 animate-ping ${currentColors.ping}`} style={{ inset: `${insetValues.ping}px` }}></div>
 
322
  <div className={`absolute rounded-full opacity-50 ${currentColors.outer}`} style={{ inset: `${insetValues.outer}px` }}></div>
 
323
  <div className={`absolute rounded-full opacity-50 ${currentColors.mid}`} style={{ inset: `${insetValues.mid}px` }}></div>
 
324
  <div className={`absolute rounded-full opacity-50 ${currentColors.inner}`} style={{ inset: `${insetValues.inner}px` }}></div>
 
325
  <div className="z-10 absolute flex items-center justify-center" style={{ inset: `${insetValues.icon}px`, width: `${iconDisplaySize}px`, height: `${iconDisplaySize}px` }}>
326
  {IconComponent && <IconComponent />}
327
  </div>
 
329
  );
330
  };
331
 
332
+ return (
333
  <LiveAPIProvider initialConfig={initialAppConfig}>
334
  <AppInternalLogic
335
  isMicActive={isMicActive}