import { useCallback, useEffect, useState } from "react";
import { useApi } from "@/contexts/ApiContext";
import {
captureVideoFrame,
deleteCameraReference,
getCameraReference,
saveCameraReference,
} from "@/lib/cameraReferenceApi";
/**
* The saved reference frame for one robot camera, as an object URL ready to use
* as an `
`. Pass `robot = undefined` to disable it.
*/
export function useCameraReference(robot: string | undefined, camera: string) {
const { baseUrl, fetchWithHeaders } = useApi();
const [url, setUrl] = useState(null);
const [error, setError] = useState(null);
const show = useCallback((blob: Blob | null) => {
setUrl((prev) => {
if (prev) URL.revokeObjectURL(prev);
return blob ? URL.createObjectURL(blob) : null;
});
}, []);
useEffect(() => {
if (!robot) return;
const controller = new AbortController();
getCameraReference(baseUrl, fetchWithHeaders, robot, camera, controller.signal)
.then(show)
.catch((e) => {
if (!controller.signal.aborted) setError(String(e.message ?? e));
});
return () => {
controller.abort();
show(null);
};
}, [robot, camera, baseUrl, fetchWithHeaders, show]);
const save = useCallback(
async (video: HTMLVideoElement) => {
if (!robot) return;
setError(null);
try {
const png = await captureVideoFrame(video);
if (!png) throw new Error("No frame to capture yet");
await saveCameraReference(baseUrl, fetchWithHeaders, robot, camera, png);
show(png);
} catch (e) {
setError(e instanceof Error ? e.message : String(e));
}
},
[robot, camera, baseUrl, fetchWithHeaders, show],
);
const clear = useCallback(async () => {
if (!robot) return;
setError(null);
try {
await deleteCameraReference(baseUrl, fetchWithHeaders, robot, camera);
show(null);
} catch (e) {
setError(e instanceof Error ? e.message : String(e));
}
}, [robot, camera, baseUrl, fetchWithHeaders, show]);
return { url, error, save, clear };
}