Initial commit
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useCallback, useRef } from "react";
|
||||
import { saveAnnotation, getAnnotationsForVersion } from "@/actions/annotations";
|
||||
import type { AnnotationShape, AnnotationDrawingData } from "@/types";
|
||||
import { useReviewStore } from "@/hooks/use-review-player";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
interface UseAnnotationsOptions {
|
||||
versionId: string;
|
||||
fps: number;
|
||||
}
|
||||
|
||||
export function useAnnotations({ versionId, fps }: UseAnnotationsOptions) {
|
||||
const [savedAnnotations, setSavedAnnotations] = useState<
|
||||
{ id: string; frameNumber: number; drawingData: AnnotationDrawingData }[]
|
||||
>([]);
|
||||
const [sessionShapes, setSessionShapes] = useState<AnnotationShape[]>([]);
|
||||
const [currentShape, setCurrentShape] = useState<AnnotationShape | null>(null);
|
||||
const [isDrawing, setIsDrawing] = useState(false);
|
||||
const { selectedTool, selectedColor, strokeWidth } = useReviewStore();
|
||||
const { toast } = useToast();
|
||||
|
||||
const startShape = useCallback(
|
||||
(frameNumber: number, point: { x: number; y: number }) => {
|
||||
const shape: AnnotationShape = {
|
||||
id: uuidv4(),
|
||||
tool: selectedTool,
|
||||
points: [point],
|
||||
color: selectedColor,
|
||||
strokeWidth,
|
||||
frameNumber,
|
||||
};
|
||||
setCurrentShape(shape);
|
||||
setIsDrawing(true);
|
||||
},
|
||||
[selectedTool, selectedColor, strokeWidth]
|
||||
);
|
||||
|
||||
const addPoint = useCallback((point: { x: number; y: number }) => {
|
||||
setCurrentShape((prev) =>
|
||||
prev ? { ...prev, points: [...prev.points, point] } : null
|
||||
);
|
||||
}, []);
|
||||
|
||||
const finishShape = useCallback(
|
||||
async (canvasWidth: number, canvasHeight: number) => {
|
||||
if (!currentShape) return;
|
||||
const finished = currentShape;
|
||||
setSessionShapes((prev) => [...prev, finished]);
|
||||
setCurrentShape(null);
|
||||
setIsDrawing(false);
|
||||
|
||||
const drawingData: AnnotationDrawingData = {
|
||||
shapes: [finished],
|
||||
canvasWidth,
|
||||
canvasHeight,
|
||||
version: "1.0",
|
||||
};
|
||||
|
||||
try {
|
||||
await saveAnnotation({
|
||||
versionId,
|
||||
frameNumber: finished.frameNumber,
|
||||
drawingData,
|
||||
color: selectedColor,
|
||||
});
|
||||
} catch {
|
||||
toast({ title: "Failed to save annotation", variant: "destructive" });
|
||||
}
|
||||
},
|
||||
[currentShape, versionId, selectedColor, toast]
|
||||
);
|
||||
|
||||
const clearSessionShapes = useCallback((frameNumber?: number) => {
|
||||
if (frameNumber !== undefined) {
|
||||
setSessionShapes((prev) => prev.filter((s) => s.frameNumber !== frameNumber));
|
||||
} else {
|
||||
setSessionShapes([]);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const loadAnnotations = useCallback(async () => {
|
||||
try {
|
||||
const data = await getAnnotationsForVersion(versionId);
|
||||
setSavedAnnotations(data as any);
|
||||
} catch {
|
||||
// non-fatal
|
||||
}
|
||||
}, [versionId]);
|
||||
|
||||
return {
|
||||
savedAnnotations,
|
||||
sessionShapes,
|
||||
currentShape,
|
||||
isDrawing,
|
||||
startShape,
|
||||
addPoint,
|
||||
finishShape,
|
||||
clearSessionShapes,
|
||||
loadAnnotations,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user