abcVideo 플레이어 소스 (client / server / shared / pythonsource / docs / .claude). .gitignore 적용으로 node_modules·storage·samplevideo·미디어 등 대용량 일괄 제외. 103 files, ~964K. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
62 lines
2.0 KiB
TypeScript
62 lines
2.0 KiB
TypeScript
import { useEffect, useCallback } from 'react';
|
|
import { useAnnotationStore } from '../store/annotationStore';
|
|
import type { Annotation, CreateAnnotationInput, UpdateAnnotationInput } from '@abcvideo/shared';
|
|
|
|
export function useAnnotations(videoId: string | null) {
|
|
const { annotations, setAnnotations, addAnnotation, updateAnnotation, removeAnnotation } =
|
|
useAnnotationStore();
|
|
|
|
useEffect(() => {
|
|
if (!videoId) { setAnnotations([]); return; }
|
|
fetch(`/api/annotations/${videoId}`)
|
|
.then((r) => r.json())
|
|
.then((data: Annotation[]) => setAnnotations(data))
|
|
.catch(console.error);
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [videoId]);
|
|
|
|
const create = useCallback(
|
|
async (input: Omit<CreateAnnotationInput, 'videoId'>) => {
|
|
if (!videoId) return;
|
|
const res = await fetch(`/api/annotations/${videoId}`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ ...input, videoId }),
|
|
});
|
|
const annotation: Annotation = await res.json();
|
|
addAnnotation(annotation);
|
|
return annotation;
|
|
},
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
[videoId]
|
|
);
|
|
|
|
const update = useCallback(
|
|
async (id: string, input: UpdateAnnotationInput) => {
|
|
if (!videoId) return;
|
|
const res = await fetch(`/api/annotations/${videoId}/${id}`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(input),
|
|
});
|
|
const annotation: Annotation = await res.json();
|
|
updateAnnotation(id, annotation);
|
|
return annotation;
|
|
},
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
[videoId]
|
|
);
|
|
|
|
const remove = useCallback(
|
|
async (id: string) => {
|
|
if (!videoId) return;
|
|
await fetch(`/api/annotations/${videoId}/${id}`, { method: 'DELETE' });
|
|
removeAnnotation(id);
|
|
},
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
[videoId]
|
|
);
|
|
|
|
return { annotations, create, update, remove };
|
|
}
|