Spaces:
Running
Running
Update src/App.tsx
Browse files- src/App.tsx +47 -44
src/App.tsx
CHANGED
@@ -104,10 +104,16 @@ const initialAppConfig: LiveConfig = {
|
|
104 |
},
|
105 |
};
|
106 |
|
107 |
-
|
108 |
-
|
|
|
|
|
|
|
|
|
|
|
109 |
);
|
110 |
|
|
|
111 |
const AppInternalLogic: React.FC<{
|
112 |
isMicActive: boolean;
|
113 |
isCamActive: boolean;
|
@@ -134,39 +140,37 @@ const AppInternalLogic: React.FC<{
|
|
134 |
const { connected, disconnect } = useLiveAPIContext();
|
135 |
|
136 |
useEffect(() => {
|
|
|
137 |
if (!isMicActive && !isCamActive && connected) {
|
138 |
disconnect();
|
139 |
}
|
140 |
}, [isMicActive, isCamActive, connected, disconnect]);
|
141 |
|
142 |
return (
|
143 |
-
<div className="
|
144 |
-
<div className="
|
145 |
<div className="header-controls">
|
146 |
-
<div id="notification-trigger-container">
|
147 |
<button
|
148 |
ref={notificationButtonRef}
|
149 |
id="notification-button"
|
150 |
aria-label="Notifications"
|
151 |
-
className="header-button"
|
152 |
onClick={(e) => {
|
153 |
e.stopPropagation();
|
154 |
setIsNotificationOpen(!isNotificationOpen);
|
155 |
}}
|
156 |
>
|
157 |
-
{/* Notification Icon SVG */}
|
158 |
<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>
|
159 |
</button>
|
160 |
</div>
|
161 |
-
<div className="back-button-container">
|
162 |
-
|
163 |
-
<button // Changed from div to button for better accessibility and event handling
|
164 |
id="back-button"
|
165 |
aria-label="Go back"
|
166 |
-
className="header-button"
|
167 |
onClick={() => alert('Back clicked (implement navigation)')}
|
168 |
>
|
169 |
-
{/* Back Icon SVG - Ensure its size is consistent with notification icon or adjust padding */}
|
170 |
<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>
|
171 |
</button>
|
172 |
</div>
|
@@ -186,29 +190,21 @@ const AppInternalLogic: React.FC<{
|
|
186 |
</div>
|
187 |
</div>
|
188 |
|
189 |
-
|
|
|
190 |
<video
|
191 |
id="video-feed"
|
192 |
ref={videoRef}
|
193 |
autoPlay
|
194 |
playsInline
|
195 |
-
className={cn(
|
196 |
-
"absolute top-0 left-0 w-full h-full object-cover scale-x-[-1]",
|
197 |
-
{ "hidden": !isCamActive }
|
198 |
-
)}
|
199 |
/>
|
200 |
-
|
201 |
-
|
202 |
-
className=
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
"hidden": !isMicActive || isCamActive,
|
207 |
-
}
|
208 |
-
)}
|
209 |
-
>
|
210 |
-
{isMicActive && !isCamActive && createLogoFunction(false, true, 'human')}
|
211 |
-
</div>
|
212 |
</div>
|
213 |
|
214 |
<ControlTray
|
@@ -226,20 +222,21 @@ const AppInternalLogic: React.FC<{
|
|
226 |
);
|
227 |
}
|
228 |
|
|
|
229 |
const logoColorConfig = {
|
230 |
blue: {
|
231 |
ping: "bg-blue-200 dark:bg-blue-700",
|
232 |
-
outer: "bg-blue-200 dark:bg-blue-700",
|
233 |
mid: "bg-blue-300 dark:bg-blue-600",
|
234 |
-
inner: "bg-blue-400 dark:bg-blue-500",
|
235 |
},
|
236 |
-
green: {
|
237 |
ping: "bg-green-200 dark:bg-green-700",
|
238 |
outer: "bg-green-200 dark:bg-green-700",
|
239 |
mid: "bg-green-300 dark:bg-green-600",
|
240 |
inner: "bg-green-400 dark:bg-green-500",
|
241 |
},
|
242 |
-
gray: {
|
243 |
ping: "bg-gray-200 dark:bg-gray-700",
|
244 |
outer: "bg-gray-200 dark:bg-gray-700",
|
245 |
mid: "bg-gray-300 dark:bg-gray-600",
|
@@ -247,6 +244,7 @@ const logoColorConfig = {
|
|
247 |
}
|
248 |
};
|
249 |
|
|
|
250 |
function App() {
|
251 |
const videoRef = useRef<HTMLVideoElement>(null);
|
252 |
const [showIOSModal, setShowIOSModal] = useState(false);
|
@@ -293,28 +291,33 @@ function App() {
|
|
293 |
|
294 |
const createLogoFunction = (isMini: boolean, isActive: boolean, type: 'human' | 'ai' = 'human') => {
|
295 |
if (!isActive) return null;
|
296 |
-
|
297 |
-
|
|
|
298 |
const insetBase = isMini
|
299 |
-
? { ping: 10, outer: 0, mid: 5, inner: 12, icon: 22 }
|
300 |
-
: { ping: 40, outer: 0, mid: 20, inner: 50, icon: 65 };
|
301 |
|
|
|
302 |
const effectiveType = (type === 'human') ? 'blue' : (type === 'ai' ? 'green' : 'gray');
|
303 |
const currentColors = logoColorConfig[effectiveType as keyof typeof logoColorConfig] || logoColorConfig.gray;
|
304 |
|
305 |
return (
|
306 |
-
<div className="
|
307 |
-
|
308 |
-
<div className={`
|
309 |
-
<div className={`
|
310 |
-
<div className={`
|
311 |
-
<div className=
|
312 |
-
|
|
|
|
|
313 |
</div>
|
314 |
</div>
|
315 |
);
|
316 |
};
|
317 |
|
|
|
318 |
return (
|
319 |
<LiveAPIProvider initialConfig={initialAppConfig}>
|
320 |
<AppInternalLogic
|
|
|
104 |
},
|
105 |
};
|
106 |
|
107 |
+
// آیکون انسان از HTML شما (برای لوگوی بزرگ و کوچک)
|
108 |
+
// stroke="currentColor" باعث میشود رنگ خطوط از CSS (var(--foreground)) ارثبری کند
|
109 |
+
// ** مهم: آیکون انسان شما از HTML دارای stroke="#FCFCFC" است، آن را به "currentColor" تغییر میدهیم
|
110 |
+
// تا با تم برنامه هماهنگ شود یا اگر میخواهید سفید بماند، نیازی به تغییر نیست.
|
111 |
+
// برای سادگی فعلا همان کد HTML را استفاده میکنیم.
|
112 |
+
const SvgHumanIconFromHTML = () => (
|
113 |
+
<svg width="70" height="70" viewBox="0 0 88 89" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M75.1481 81.6361H12.9259C9.66667 81.6361 7 78.9721 7 75.7161V58.5112C7 57.5862 7 57.1052 7.44444 56.2172C8.85185 52.9612 13 50.2232 19.4815 47.8922C24.1111 56.6982 33.3704 62.6921 44 62.6921C54.6296 62.6921 63.9259 56.6982 68.5185 47.8922C75 50.1862 79.1852 52.9982 80.5556 56.2172C81 56.6612 81 57.6232 81 58.5112V75.7161C81 78.9721 78.3333 81.6361 75.0741 81.6361H75.1481Z" stroke="#FCFCFC" strokeWidth="6.42146" strokeLinecap="round" strokeLinejoin="round"/><path d="M44.0371 50.1862C33.8519 50.1862 25.5186 41.8612 25.5186 31.6863V26.1363C25.5186 15.9613 33.8519 7.63635 44.0371 7.63635C54.2223 7.63635 62.5556 15.9613 62.5556 26.1363V31.6863C62.5556 41.8612 54.2223 50.1862 44.0371 50.1862Z" stroke="#FCFCFC" strokeWidth="6.42146" strokeLinecap="round" strokeLinejoin="round"/></svg>
|
114 |
);
|
115 |
|
116 |
+
|
117 |
const AppInternalLogic: React.FC<{
|
118 |
isMicActive: boolean;
|
119 |
isCamActive: boolean;
|
|
|
140 |
const { connected, disconnect } = useLiveAPIContext();
|
141 |
|
142 |
useEffect(() => {
|
143 |
+
// Disconnect if neither mic nor cam is active, but connection exists
|
144 |
if (!isMicActive && !isCamActive && connected) {
|
145 |
disconnect();
|
146 |
}
|
147 |
}, [isMicActive, isCamActive, connected, disconnect]);
|
148 |
|
149 |
return (
|
150 |
+
<div className="app-container"> {/* Renamed for clarity */}
|
151 |
+
<div className="app-content-wrapper"> {/* Renamed for clarity */}
|
152 |
<div className="header-controls">
|
153 |
+
<div id="notification-trigger-container">
|
154 |
<button
|
155 |
ref={notificationButtonRef}
|
156 |
id="notification-button"
|
157 |
aria-label="Notifications"
|
158 |
+
className="header-button"
|
159 |
onClick={(e) => {
|
160 |
e.stopPropagation();
|
161 |
setIsNotificationOpen(!isNotificationOpen);
|
162 |
}}
|
163 |
>
|
|
|
164 |
<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>
|
165 |
</button>
|
166 |
</div>
|
167 |
+
<div className="back-button-container">
|
168 |
+
<button
|
|
|
169 |
id="back-button"
|
170 |
aria-label="Go back"
|
171 |
+
className="header-button"
|
172 |
onClick={() => alert('Back clicked (implement navigation)')}
|
173 |
>
|
|
|
174 |
<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>
|
175 |
</button>
|
176 |
</div>
|
|
|
190 |
</div>
|
191 |
</div>
|
192 |
|
193 |
+
{/* Media Area: Video and Large Logo */}
|
194 |
+
<div className="media-area"> {/* For video and large logo */}
|
195 |
<video
|
196 |
id="video-feed"
|
197 |
ref={videoRef}
|
198 |
autoPlay
|
199 |
playsInline
|
200 |
+
className={cn("video-feed", { "hidden": !isCamActive })}
|
|
|
|
|
|
|
201 |
/>
|
202 |
+
{/* Large Logo for Mic-Only mode, centered */}
|
203 |
+
{isMicActive && !isCamActive && (
|
204 |
+
<div id="large-logo-container" className="large-logo-container">
|
205 |
+
{createLogoFunction(false, true, 'human')}
|
206 |
+
</div>
|
207 |
+
)}
|
|
|
|
|
|
|
|
|
|
|
|
|
208 |
</div>
|
209 |
|
210 |
<ControlTray
|
|
|
222 |
);
|
223 |
}
|
224 |
|
225 |
+
// For Tailwind JIT compatibility for dynamic classes
|
226 |
const logoColorConfig = {
|
227 |
blue: {
|
228 |
ping: "bg-blue-200 dark:bg-blue-700",
|
229 |
+
outer: "bg-blue-200 dark:bg-blue-700", // Same as ping or different if desired
|
230 |
mid: "bg-blue-300 dark:bg-blue-600",
|
231 |
+
inner: "bg-blue-400 dark:bg-blue-500", // Corrected inner class
|
232 |
},
|
233 |
+
green: { // Example for AI, if you add it
|
234 |
ping: "bg-green-200 dark:bg-green-700",
|
235 |
outer: "bg-green-200 dark:bg-green-700",
|
236 |
mid: "bg-green-300 dark:bg-green-600",
|
237 |
inner: "bg-green-400 dark:bg-green-500",
|
238 |
},
|
239 |
+
gray: { // Fallback
|
240 |
ping: "bg-gray-200 dark:bg-gray-700",
|
241 |
outer: "bg-gray-200 dark:bg-gray-700",
|
242 |
mid: "bg-gray-300 dark:bg-gray-600",
|
|
|
244 |
}
|
245 |
};
|
246 |
|
247 |
+
|
248 |
function App() {
|
249 |
const videoRef = useRef<HTMLVideoElement>(null);
|
250 |
const [showIOSModal, setShowIOSModal] = useState(false);
|
|
|
291 |
|
292 |
const createLogoFunction = (isMini: boolean, isActive: boolean, type: 'human' | 'ai' = 'human') => {
|
293 |
if (!isActive) return null;
|
294 |
+
|
295 |
+
const size = isMini ? 80 : 200; // As per HTML
|
296 |
+
// const iconSize = isMini ? 35 : 70; // SVG has fixed size, this is for container div
|
297 |
const insetBase = isMini
|
298 |
+
? { ping: 10, outer: 0, mid: 5, inner: 12, icon: 22 } // For small logo
|
299 |
+
: { ping: 40, outer: 0, mid: 20, inner: 50, icon: 65 }; // For large logo
|
300 |
|
301 |
+
// Determine color scheme based on type (human or AI)
|
302 |
const effectiveType = (type === 'human') ? 'blue' : (type === 'ai' ? 'green' : 'gray');
|
303 |
const currentColors = logoColorConfig[effectiveType as keyof typeof logoColorConfig] || logoColorConfig.gray;
|
304 |
|
305 |
return (
|
306 |
+
<div className="logo-animation-wrapper" style={{ width: `${size}px`, height: `${size}px` }}>
|
307 |
+
{/* These class names (e.g., bg-blue-200) should exist in your Tailwind config or be full utility classes */}
|
308 |
+
<div className={`logo-ping ${currentColors.ping}`} style={{ inset: `${insetBase.ping}px` }}></div>
|
309 |
+
<div className={`logo-outer ${currentColors.outer}`} style={{ inset: `${insetBase.outer}px` }}></div>
|
310 |
+
<div className={`logo-mid ${currentColors.mid}`} style={{ inset: `${insetBase.mid}px` }}></div>
|
311 |
+
<div className={`logo-inner ${currentColors.inner}`} style={{ inset: `${insetBase.inner}px` }}></div>
|
312 |
+
<div className="logo-icon-container" style={{ inset: `${insetBase.icon}px` }}>
|
313 |
+
{type === 'human' && <SvgHumanIconFromHTML />}
|
314 |
+
{/* {type === 'ai' && <AiIconSVG />} You would define AiIconSVG similarly */}
|
315 |
</div>
|
316 |
</div>
|
317 |
);
|
318 |
};
|
319 |
|
320 |
+
|
321 |
return (
|
322 |
<LiveAPIProvider initialConfig={initialAppConfig}>
|
323 |
<AppInternalLogic
|