import { useState, useEffect, useRef } from 'react'; import { useParams } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; import { RefreshCw, AlertTriangle, Camera, Maximize, Minimize } from 'lucide-react'; import { api } from '../api/client'; export function CameraPage() { const { printerId } = useParams<{ printerId: string }>(); const id = parseInt(printerId || '0', 10); const [streamMode, setStreamMode] = useState<'stream' | 'snapshot'>('stream'); const [streamError, setStreamError] = useState(false); const [streamLoading, setStreamLoading] = useState(true); const [imageKey, setImageKey] = useState(Date.now()); const [transitioning, setTransitioning] = useState(false); const [isFullscreen, setIsFullscreen] = useState(false); const imgRef = useRef(null); const containerRef = useRef(null); // Fetch printer info for the title const { data: printer } = useQuery({ queryKey: ['printer', id], queryFn: () => api.getPrinter(id), enabled: id > 0, }); // Update document title useEffect(() => { if (printer) { document.title = `${printer.name} - Camera`; } return () => { document.title = 'Bambuddy'; }; }, [printer]); // Cleanup on unmount useEffect(() => { return () => { if (imgRef.current) { imgRef.current.src = ''; } }; }, []); // Auto-hide loading after timeout useEffect(() => { if (streamLoading && !transitioning) { const timeout = streamMode === 'stream' ? 3000 : 20000; const timer = setTimeout(() => { setStreamLoading(false); }, timeout); return () => clearTimeout(timer); } }, [streamMode, streamLoading, imageKey, transitioning]); // Fullscreen change listener useEffect(() => { const handleFullscreenChange = () => { setIsFullscreen(!!document.fullscreenElement); }; document.addEventListener('fullscreenchange', handleFullscreenChange); return () => document.removeEventListener('fullscreenchange', handleFullscreenChange); }, []); const handleStreamError = () => { setStreamError(true); setStreamLoading(false); }; const handleStreamLoad = () => { setStreamLoading(false); setStreamError(false); }; const switchToMode = (newMode: 'stream' | 'snapshot') => { if (streamMode === newMode || transitioning) return; setTransitioning(true); setStreamLoading(true); setStreamError(false); if (imgRef.current) { imgRef.current.src = ''; } setTimeout(() => { setStreamMode(newMode); setImageKey(Date.now()); setTransitioning(false); }, 100); }; const refresh = () => { if (transitioning) return; setTransitioning(true); setStreamLoading(true); setStreamError(false); if (imgRef.current) { imgRef.current.src = ''; } setTimeout(() => { setImageKey(Date.now()); setTransitioning(false); }, 100); }; const toggleFullscreen = () => { if (!containerRef.current) return; if (document.fullscreenElement) { document.exitFullscreen(); } else { containerRef.current.requestFullscreen(); } }; const currentUrl = transitioning ? '' : streamMode === 'stream' ? `/api/v1/printers/${id}/camera/stream?fps=10&t=${imageKey}` : `/api/v1/printers/${id}/camera/snapshot?t=${imageKey}`; const isDisabled = streamLoading || transitioning; if (!id) { return (

Invalid printer ID

); } return (
{/* Header */}

{printer?.name || `Printer ${id}`}

{/* Mode toggle */}
{/* Video area */}
{(streamLoading || transitioning) && (

{streamMode === 'stream' ? 'Connecting to camera...' : 'Capturing snapshot...'}

)} {streamError && (

Camera unavailable

Make sure the printer is powered on and connected.

)} Camera stream
); }