summaryrefslogtreecommitdiff
path: root/apps/web/src/timeline/hooks/use-timeline-seek.ts
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/timeline/hooks/use-timeline-seek.ts')
-rw-r--r--apps/web/src/timeline/hooks/use-timeline-seek.ts195
1 files changed, 31 insertions, 164 deletions
diff --git a/apps/web/src/timeline/hooks/use-timeline-seek.ts b/apps/web/src/timeline/hooks/use-timeline-seek.ts
index 889177c9..56f68ed6 100644
--- a/apps/web/src/timeline/hooks/use-timeline-seek.ts
+++ b/apps/web/src/timeline/hooks/use-timeline-seek.ts
@@ -1,9 +1,9 @@
-import { useCallback, useRef } from "react";
-import type { MutableRefObject, RefObject } from "react";
-import { BASE_TIMELINE_PIXELS_PER_SECOND } from "@/timeline/scale";
-import { snappedSeekTime } from "opencut-wasm";
-import { TICKS_PER_SECOND } from "@/wasm";
+import { useEffect, useRef, type RefObject } from "react";
import { useEditor } from "@/editor/use-editor";
+import {
+ SeekController,
+ type SeekConfig,
+} from "@/timeline/controllers/seek-controller";
interface UseTimelineSeekProps {
playheadRef: RefObject<HTMLDivElement | null>;
@@ -17,44 +17,6 @@ interface UseTimelineSeekProps {
seek: (time: number) => void;
}
-function resetMouseTracking({
- mouseTrackingRef,
-}: {
- mouseTrackingRef: MutableRefObject<{
- isMouseDown: boolean;
- downX: number;
- downY: number;
- downTime: number;
- }>;
-}) {
- mouseTrackingRef.current = {
- isMouseDown: false,
- downX: 0,
- downY: 0,
- downTime: 0,
- };
-}
-
-function setMouseTracking({
- mouseTrackingRef,
- event,
-}: {
- mouseTrackingRef: MutableRefObject<{
- isMouseDown: boolean;
- downX: number;
- downY: number;
- downTime: number;
- }>;
- event: React.MouseEvent;
-}) {
- mouseTrackingRef.current = {
- isMouseDown: true,
- downX: event.clientX,
- downY: event.clientY,
- downTime: event.timeStamp,
- };
-}
-
export function useTimelineSeek({
playheadRef,
trackLabelsRef,
@@ -67,131 +29,36 @@ export function useTimelineSeek({
seek,
}: UseTimelineSeekProps) {
const editor = useEditor();
- const activeProject = editor.project.getActive();
-
- const mouseTrackingRef = useRef({
- isMouseDown: false,
- downX: 0,
- downY: 0,
- downTime: 0,
- });
-
- const handleTracksMouseDown = useCallback((event: React.MouseEvent) => {
- if (event.button !== 0) return;
- setMouseTracking({ mouseTrackingRef, event });
- }, []);
-
- const handleRulerMouseDown = useCallback((event: React.MouseEvent) => {
- if (event.button !== 0) return;
- setMouseTracking({ mouseTrackingRef, event });
- }, []);
-
- const shouldProcessTimelineClick = useCallback(
- ({ event }: { event: React.MouseEvent }) => {
- const target = event.target as HTMLElement;
- const { isMouseDown, downX, downY, downTime } = mouseTrackingRef.current;
- const deltaX = Math.abs(event.clientX - downX);
- const deltaY = Math.abs(event.clientY - downY);
- const deltaTime = event.timeStamp - downTime;
- const isPlayhead = !!playheadRef.current?.contains(target);
- const isTrackLabels = !!trackLabelsRef.current?.contains(target);
- const shouldBlockForDrag = deltaX > 5 || deltaY > 5 || deltaTime > 500;
-
- if (!isMouseDown) return false;
- if (shouldBlockForDrag) return false;
- if (isSelecting) return false;
- if (isPlayhead) return false;
- if (isTrackLabels) {
- clearSelectedElements();
- return false;
- }
-
- return true;
- },
- [isSelecting, clearSelectedElements, playheadRef, trackLabelsRef],
- );
-
- const handleTimelineSeek = useCallback(
- ({
- event,
- source,
- }: {
- event: React.MouseEvent;
- source: "ruler" | "tracks";
- }) => {
- const scrollContainer =
- source === "ruler" ? rulerScrollRef.current : tracksScrollRef.current;
-
- if (!scrollContainer) return;
-
- const rect = scrollContainer.getBoundingClientRect();
- const mouseX = event.clientX - rect.left;
- const scrollLeft = scrollContainer.scrollLeft;
-
- const rawTimeSeconds = Math.max(
- 0,
- Math.min(
- duration / TICKS_PER_SECOND,
- (mouseX + scrollLeft) / (BASE_TIMELINE_PIXELS_PER_SECOND * zoomLevel),
- ),
- );
- const rawTime = Math.round(rawTimeSeconds * TICKS_PER_SECOND);
-
- const rate = activeProject?.settings.fps;
- const time = rate
- ? (snappedSeekTime({ time: rawTime, duration, rate }) ?? rawTime)
- : rawTime;
- seek(time);
- editor.project.setTimelineViewState({
- viewState: {
- zoomLevel,
- scrollLeft: scrollContainer.scrollLeft,
- playheadTime: time,
- },
- });
- },
- [
- duration,
- zoomLevel,
- rulerScrollRef,
- tracksScrollRef,
- seek,
- editor,
- activeProject?.settings.fps.numerator,
- activeProject?.settings.fps.denominator,
- ],
- );
-
- const handleTracksClick = useCallback(
- (event: React.MouseEvent) => {
- const shouldProcess = shouldProcessTimelineClick({ event });
- resetMouseTracking({ mouseTrackingRef });
+ const config: SeekConfig = {
+ zoomLevel,
+ duration,
+ isSelecting,
+ getPlayheadEl: () => playheadRef.current,
+ getTrackLabelsEl: () => trackLabelsRef.current,
+ getRulerScrollEl: () => rulerScrollRef.current,
+ getTracksScrollEl: () => tracksScrollRef.current,
+ getActiveProjectFps: () => editor.project.getActive()?.settings.fps ?? null,
+ clearSelectedElements,
+ seek,
+ setTimelineViewState: (viewState) =>
+ editor.project.setTimelineViewState({ viewState }),
+ };
- if (shouldProcess) {
- clearSelectedElements();
- handleTimelineSeek({ event, source: "tracks" });
- }
- },
- [shouldProcessTimelineClick, handleTimelineSeek, clearSelectedElements],
- );
+ const configRef = useRef(config);
+ configRef.current = config;
- const handleRulerClick = useCallback(
- (event: React.MouseEvent) => {
- const shouldProcess = shouldProcessTimelineClick({ event });
- resetMouseTracking({ mouseTrackingRef });
+ const controllerRef = useRef<SeekController | null>(null);
+ if (!controllerRef.current) {
+ controllerRef.current = new SeekController({ configRef });
+ }
+ const controller = controllerRef.current;
- if (shouldProcess) {
- clearSelectedElements();
- handleTimelineSeek({ event, source: "ruler" });
- }
- },
- [shouldProcessTimelineClick, handleTimelineSeek, clearSelectedElements],
- );
+ useEffect(() => () => controller.destroy(), [controller]);
return {
- handleTracksMouseDown,
- handleTracksClick,
- handleRulerMouseDown,
- handleRulerClick,
+ handleTracksMouseDown: controller.onTracksMouseDown,
+ handleTracksClick: controller.onTracksClick,
+ handleRulerMouseDown: controller.onRulerMouseDown,
+ handleRulerClick: controller.onRulerClick,
};
}