import { useState, useEffect, useRef, useCallback, useMemo } from "react"; import LoadingScreen from "./components/LoadingScreen"; import CaptioningView from "./components/CaptioningView"; import WelcomeScreen from "./components/WelcomeScreen"; import SourceSelector from "./components/SourceSelector"; import type { AppState, VideoSource } from "./types"; export default function App() { const [appState, setAppState] = useState("source-selection"); const [videoSource, setVideoSource] = useState(null); const [isVideoReady, setIsVideoReady] = useState(false); const videoRef = useRef(null); const handleSourceSelected = useCallback((source: VideoSource) => { setVideoSource(source); setAppState("welcome"); }, []); const handleBackToSourceSelection = useCallback(() => { // Clean up current source if (videoSource?.stream) { videoSource.stream.getTracks().forEach(track => track.stop()); } if (videoSource?.url && videoSource.type === "upload") { URL.revokeObjectURL(videoSource.url); } setVideoSource(null); setIsVideoReady(false); setAppState("source-selection"); }, [videoSource]); const handleStart = useCallback(() => { setAppState("loading"); }, []); const handleLoadingComplete = useCallback(() => { setAppState("captioning"); }, []); const playVideo = useCallback(async (video: HTMLVideoElement) => { try { await video.play(); } catch (error) { console.error("Failed to play video:", error); } }, []); const setupVideo = useCallback( (video: HTMLVideoElement, source: VideoSource) => { // Reset video element video.srcObject = null; video.src = ""; video.load(); if (source.stream) { // Stream-based sources (webcam, screen share) video.srcObject = source.stream; } else if (source.url) { // URL-based sources (upload, examples) video.src = source.url; video.loop = true; // Loop for uploaded/example videos } const handleCanPlay = () => { setIsVideoReady(true); playVideo(video); }; video.addEventListener("canplay", handleCanPlay, { once: true }); // Handle stream ended (e.g., user stops screen sharing) if (source.stream) { const track = source.stream.getVideoTracks()[0]; if (track) { track.onended = () => { handleBackToSourceSelection(); }; } } return () => { video.removeEventListener("canplay", handleCanPlay); }; }, [playVideo, handleBackToSourceSelection], ); useEffect(() => { if (videoSource && videoRef.current) { const video = videoRef.current; const cleanup = setupVideo(video, videoSource); return cleanup; } }, [videoSource, setupVideo]); const videoBlurState = useMemo(() => { switch (appState) { case "source-selection": return "blur(20px) brightness(0.2) saturate(0.5)"; case "welcome": return "blur(12px) brightness(0.3) saturate(0.7)"; case "loading": return "blur(8px) brightness(0.4) saturate(0.8)"; case "captioning": return "none"; default: return "blur(20px) brightness(0.2) saturate(0.5)"; } }, [appState]); const getSourceLabel = () => { if (!videoSource) return null; switch (videoSource.type) { case "webcam": return "Webcam"; case "screen": return "Screen Share"; case "upload": return videoSource.name || "Uploaded Video"; case "example": return videoSource.name || "Example Video"; } }; return (
{videoSource && (