t2-mapper/src/components/LoadDemoButton.tsx

71 lines
2.1 KiB
TypeScript
Raw Normal View History

2026-02-20 15:48:15 -08:00
import { useCallback, useRef } from "react";
2026-02-28 17:58:09 -08:00
import { MdOndemandVideo } from "react-icons/md";
import { useDemoActions, useDemoRecording } from "./DemoProvider";
import { createDemoStreamingRecording } from "../demo/streaming";
2026-03-01 09:40:17 -08:00
import styles from "./LoadDemoButton.module.css";
2026-02-20 15:48:15 -08:00
export function LoadDemoButton() {
2026-02-28 17:58:09 -08:00
const recording = useDemoRecording();
const { setRecording } = useDemoActions();
2026-02-20 15:48:15 -08:00
const inputRef = useRef<HTMLInputElement>(null);
2026-02-28 17:58:09 -08:00
const parseTokenRef = useRef(0);
2026-02-20 15:48:15 -08:00
const handleClick = useCallback(() => {
if (recording) {
// Unload the current recording.
2026-02-28 17:58:09 -08:00
parseTokenRef.current += 1;
2026-02-20 15:48:15 -08:00
setRecording(null);
return;
}
inputRef.current?.click();
}, [recording, setRecording]);
const handleFileChange = useCallback(
async (e: React.ChangeEvent<HTMLInputElement>) => {
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();
2026-02-28 17:58:09 -08:00
const parseToken = parseTokenRef.current + 1;
parseTokenRef.current = parseToken;
const recording = await createDemoStreamingRecording(buffer);
if (parseTokenRef.current !== parseToken) {
return;
}
// Metadata-first: mission/game-mode sync happens immediately.
setRecording(recording);
2026-02-20 15:48:15 -08:00
} catch (err) {
console.error("Failed to load demo:", err);
}
},
[setRecording],
);
return (
<>
<input
ref={inputRef}
type="file"
accept=".rec"
style={{ display: "none" }}
onChange={handleFileChange}
/>
<button
type="button"
2026-03-01 09:40:17 -08:00
className={styles.Root}
2026-02-20 15:48:15 -08:00
aria-label={recording ? "Unload demo" : "Load demo (.rec)"}
title={recording ? "Unload demo" : "Load demo (.rec)"}
onClick={handleClick}
data-active={recording ? "true" : undefined}
>
2026-03-01 09:40:17 -08:00
<MdOndemandVideo className={styles.DemoIcon} />
<span className={styles.ButtonLabel}>
2026-02-20 15:48:15 -08:00
{recording ? "Unload demo" : "Demo"}
</span>
</button>
</>
);
}