import { useState, useEffect, useRef, useCallback } from "react"; import { THEME, EXAMPLE_VIDEOS } from "../constants"; import type { VideoSourceType, VideoSource } from "../types"; type ExampleVideo = (typeof EXAMPLE_VIDEOS)[number]; interface SourceSelectorProps { onSourceSelected: (source: VideoSource) => void; onBack?: () => void; } interface SourceCardProps { icon: React.ReactNode; title: string; description: string; onClick: () => void; isActive?: boolean; isLoading?: boolean; badge?: string; } function SourceCard({ icon, title, description, onClick, isActive, isLoading, badge }: SourceCardProps) { return ( {badge && ( {badge} )} {isLoading ? ( ) : ( icon )} {title} {description} ); } function ExampleVideoCard({ video, onClick, isSelected }: { video: ExampleVideo; onClick: () => void; isSelected: boolean }) { return ( {isSelected && ( )} {video.name} {video.description} ); } export default function SourceSelector({ onSourceSelected, onBack }: SourceSelectorProps) { const [mounted, setMounted] = useState(false); const [activeSource, setActiveSource] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [selectedExample, setSelectedExample] = useState(null); const [showExamples, setShowExamples] = useState(false); const fileInputRef = useRef(null); useEffect(() => { setMounted(true); }, []); const handleWebcam = useCallback(async () => { setActiveSource("webcam"); setIsLoading(true); setError(null); try { const stream = await navigator.mediaDevices.getUserMedia({ video: { width: { ideal: 1920, max: 1920 }, height: { ideal: 1080, max: 1080 }, facingMode: "user", }, }); onSourceSelected({ type: "webcam", stream }); } catch (err) { setError(err instanceof Error ? err.message : "Failed to access webcam"); setActiveSource(null); } finally { setIsLoading(false); } }, [onSourceSelected]); const handleScreenShare = useCallback(async () => { setActiveSource("screen"); setIsLoading(true); setError(null); try { const stream = await navigator.mediaDevices.getDisplayMedia({ video: { width: { ideal: 1920 }, height: { ideal: 1080 }, }, }); // Handle stream end (user stops sharing) stream.getVideoTracks()[0].onended = () => { // This will be handled by the parent component }; onSourceSelected({ type: "screen", stream }); } catch (err) { if ((err as Error).name !== "AbortError") { setError(err instanceof Error ? err.message : "Failed to start screen sharing"); } setActiveSource(null); } finally { setIsLoading(false); } }, [onSourceSelected]); const handleFileUpload = useCallback(() => { fileInputRef.current?.click(); }, []); const handleFileChange = useCallback((e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { setActiveSource("upload"); const url = URL.createObjectURL(file); onSourceSelected({ type: "upload", url, name: file.name }); } }, [onSourceSelected]); const handleExampleSelect = useCallback((video: ExampleVideo) => { setSelectedExample(video.id); setActiveSource("example"); onSourceSelected({ type: "example", url: video.url, name: video.name }); }, [onSourceSelected]); const toggleExamples = useCallback(() => { setShowExamples(prev => !prev); }, []); return ( {/* Header Bar */} {/* Back Button */} {onBack && ( )} {/* Header */} Choose Video Source Select how you want to provide video for analysis {/* Error Message */} {error && ( {error} )} {/* Source Options Grid */} } title="Webcam" description="Use your camera for real-time video analysis" onClick={handleWebcam} isActive={activeSource === "webcam"} isLoading={isLoading && activeSource === "webcam"} badge="Live" /> } title="Screen Share" description="Capture your screen, window, or browser tab" onClick={handleScreenShare} isActive={activeSource === "screen"} isLoading={isLoading && activeSource === "screen"} /> } title="Upload Video" description="Select a video file from your device" onClick={handleFileUpload} isActive={activeSource === "upload"} /> } title="Examples" description="Try with pre-loaded sample videos" onClick={toggleExamples} isActive={showExamples || activeSource === "example"} /> {/* Example Videos Section */} {showExamples && ( Sample Videos {EXAMPLE_VIDEOS.map((video) => ( handleExampleSelect(video)} isSelected={selectedExample === video.id} /> ))} )} {/* Footer */} All processing happens locally in your browser ); }
{description}
{video.description}
Select how you want to provide video for analysis
All processing happens locally in your browser