import { useCallback, useRef } from "react"; import { MdOndemandVideo } from "react-icons/md"; import { createLogger } from "../logger"; import { liveConnectionStore } from "../state/liveConnectionStore"; import { usePlaybackActions, useRecording } from "./RecordingProvider"; import styles from "./LoadDemoButton.module.css"; const log = createLogger("LoadDemoButton"); export function LoadDemoButton({ isActive = false, choosingMap = false, onCancelChoosingMap, }: { isActive?: boolean; choosingMap?: boolean; onCancelChoosingMap?: () => void; }) { const recording = useRecording(); const isDemoLoaded = recording?.source === "demo"; const { setRecording } = usePlaybackActions(); const inputRef = useRef(null); const parseTokenRef = useRef(0); const handleClick = useCallback(() => { if (choosingMap && isDemoLoaded) { onCancelChoosingMap?.(); return; } if (isDemoLoaded) { // Unload the recording/parser but leave entities frozen in the store. parseTokenRef.current += 1; setRecording(null); return; } inputRef.current?.click(); }, [isDemoLoaded, choosingMap, onCancelChoosingMap, setRecording]); const handleFileChange = useCallback( async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; // Reset the input so the same file can be re-selected. e.target.value = ""; try { const buffer = await file.arrayBuffer(); const parseToken = parseTokenRef.current + 1; parseTokenRef.current = parseToken; const { createDemoStreamingRecording } = await import("../stream/demoStreaming"); const recording = await createDemoStreamingRecording(buffer); if (parseTokenRef.current !== parseToken) { return; } // Disconnect from any live server before loading the demo. const liveState = liveConnectionStore.getState(); liveState.disconnectServer(); // Metadata-first: mission/game-mode sync happens immediately. setRecording(recording); } catch (err) { log.error("Failed to load demo: %o", err); } }, [setRecording], ); return ( <> ); }