import { useEffect, useReducer, useRef } from "react"; import { useEditor } from "@/editor/use-editor"; import { useShiftKey } from "@/hooks/use-shift-key"; import { usePreviewViewport } from "@/preview/components/preview-viewport"; import type { SnapLine } from "@/preview/preview-snap"; import { registerCanceller } from "@/editor/cancel-interaction"; import { PreviewInteractionController, type PreviewInteractionDeps, type PreviewInteractionDepsRef, } from "@/preview/controllers/preview-interaction-controller"; export type OnSnapLinesChange = (lines: SnapLine[]) => void; export function usePreviewInteraction({ onSnapLinesChange, isMaskMode = false, }: { onSnapLinesChange?: OnSnapLinesChange; isMaskMode?: boolean; }) { const editor = useEditor(); const isShiftHeldRef = useShiftKey(); const viewport = usePreviewViewport(); const deps: PreviewInteractionDeps = { viewport: { screenToCanvas: viewport.screenToCanvas, screenPixelsToLogicalThreshold: viewport.screenPixelsToLogicalThreshold, }, input: { isShiftHeld: () => isShiftHeldRef.current, }, scene: { getTracks: () => editor.scenes.getActiveScene().tracks, getCurrentTime: () => editor.playback.getCurrentTime(), getMediaAssets: () => editor.media.getAssets(), getCanvasSize: () => editor.project.getActive().settings.canvasSize, }, selection: { getSelected: () => editor.selection.getSelectedElements(), setSelected: (elements) => editor.selection.setSelectedElements({ elements: [...elements] }), clearSelection: () => editor.selection.clearSelection(), }, timeline: { getElementsWithTracks: ({ elements }) => editor.timeline.getElementsWithTracks({ elements: [...elements] }), previewElements: (updates) => editor.timeline.previewElements({ updates: [...updates] }), commitPreview: () => editor.timeline.commitPreview(), discardPreview: () => editor.timeline.discardPreview(), }, playback: { getIsPlaying: () => editor.playback.getIsPlaying(), subscribe: (listener) => editor.playback.subscribe(listener), }, preview: { isMaskMode: () => isMaskMode, onSnapLinesChange, }, }; const depsRef = useRef(deps); depsRef.current = deps; const controllerRef = useRef(null); if (!controllerRef.current) { controllerRef.current = new PreviewInteractionController({ depsRef: depsRef as PreviewInteractionDepsRef, }); } const controller = controllerRef.current; const [, rerender] = useReducer((n: number) => n + 1, 0); useEffect( () => controller.subscribe({ listener: rerender }), [controller], ); useEffect(() => { if (!controller.isDragging) return; return registerCanceller({ fn: () => controller.cancel() }); }, [controller.isDragging, controller]); useEffect(() => () => controller.destroy(), [controller]); return { onPointerDown: controller.onPointerDown, onPointerMove: controller.onPointerMove, onPointerUp: controller.onPointerUp, onDoubleClick: controller.onDoubleClick, editingText: controller.editingText, commitTextEdit: controller.commitTextEdit, }; }