summaryrefslogtreecommitdiff
path: root/apps/web/src/hooks
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/hooks')
-rw-r--r--apps/web/src/hooks/use-drag-clip.ts89
-rw-r--r--apps/web/src/hooks/use-playback-controls.ts91
2 files changed, 95 insertions, 85 deletions
diff --git a/apps/web/src/hooks/use-drag-clip.ts b/apps/web/src/hooks/use-drag-clip.ts
index eb4d7435..1d38343b 100644
--- a/apps/web/src/hooks/use-drag-clip.ts
+++ b/apps/web/src/hooks/use-drag-clip.ts
@@ -3,23 +3,24 @@ import { useTimelineStore } from "@/stores/timeline-store";
interface DragState {
isDragging: boolean;
- clipId: string | null;
+ elementId: string | null;
trackId: string | null;
startMouseX: number;
- startClipTime: number;
+ startElementTime: number;
clickOffsetTime: number;
currentTime: number;
}
export function useDragClip(zoomLevel: number) {
- const { tracks, updateClipStartTime, moveClipToTrack } = useTimelineStore();
+ const { tracks, updateElementStartTime, moveElementToTrack } =
+ useTimelineStore();
const [dragState, setDragState] = useState<DragState>({
isDragging: false,
- clipId: null,
+ elementId: null,
trackId: null,
startMouseX: 0,
- startClipTime: 0,
+ startElementTime: 0,
clickOffsetTime: 0,
currentTime: 0,
});
@@ -33,9 +34,9 @@ export function useDragClip(zoomLevel: number) {
const startDrag = useCallback(
(
e: React.MouseEvent,
- clipId: string,
+ elementId: string,
trackId: string,
- clipStartTime: number,
+ elementStartTime: number,
clickOffsetTime: number
) => {
e.preventDefault();
@@ -43,12 +44,12 @@ export function useDragClip(zoomLevel: number) {
setDragState({
isDragging: true,
- clipId,
+ elementId,
trackId,
startMouseX: e.clientX,
- startClipTime: clipStartTime,
+ startElementTime: elementStartTime,
clickOffsetTime,
- currentTime: clipStartTime,
+ currentTime: elementStartTime,
});
},
[]
@@ -76,7 +77,7 @@ export function useDragClip(zoomLevel: number) {
const endDrag = useCallback(
(targetTrackId?: string) => {
- if (!dragState.isDragging || !dragState.clipId || !dragState.trackId)
+ if (!dragState.isDragging || !dragState.elementId || !dragState.trackId)
return;
const finalTrackId = targetTrackId || dragState.trackId;
@@ -85,71 +86,81 @@ export function useDragClip(zoomLevel: number) {
// Check for overlaps
const sourceTrack = tracks.find((t) => t.id === dragState.trackId);
const targetTrack = tracks.find((t) => t.id === finalTrackId);
- const movingClip = sourceTrack?.clips.find(
- (c) => c.id === dragState.clipId
+ const movingElement = sourceTrack?.elements.find(
+ (e) => e.id === dragState.elementId
);
- if (!movingClip || !targetTrack) {
+ if (!movingElement || !targetTrack) {
setDragState((prev) => ({ ...prev, isDragging: false }));
return;
}
- const movingClipDuration =
- movingClip.duration - movingClip.trimStart - movingClip.trimEnd;
- const movingClipEnd = finalTime + movingClipDuration;
+ const movingElementDuration =
+ movingElement.duration -
+ movingElement.trimStart -
+ movingElement.trimEnd;
+ const movingElementEnd = finalTime + movingElementDuration;
- const hasOverlap = targetTrack.clips.some((existingClip) => {
- // Skip the clip being moved if it's on the same track
+ const hasOverlap = targetTrack.elements.some((existingElement) => {
+ // Skip the element being moved if it's on the same track
if (
dragState.trackId === finalTrackId &&
- existingClip.id === dragState.clipId
+ existingElement.id === dragState.elementId
) {
return false;
}
- const existingStart = existingClip.startTime;
+ const existingStart = existingElement.startTime;
const existingEnd =
- existingClip.startTime +
- (existingClip.duration -
- existingClip.trimStart -
- existingClip.trimEnd);
+ existingElement.startTime +
+ (existingElement.duration -
+ existingElement.trimStart -
+ existingElement.trimEnd);
- return finalTime < existingEnd && movingClipEnd > existingStart;
+ return finalTime < existingEnd && movingElementEnd > existingStart;
});
if (!hasOverlap) {
if (dragState.trackId === finalTrackId) {
// Moving within same track
- updateClipStartTime(finalTrackId, dragState.clipId!, finalTime);
+ updateElementStartTime(finalTrackId, dragState.elementId!, finalTime);
} else {
// Moving to different track
- moveClipToTrack(dragState.trackId!, finalTrackId, dragState.clipId!);
+ moveElementToTrack(
+ dragState.trackId!,
+ finalTrackId,
+ dragState.elementId!
+ );
requestAnimationFrame(() => {
- updateClipStartTime(finalTrackId, dragState.clipId!, finalTime);
+ updateElementStartTime(
+ finalTrackId,
+ dragState.elementId!,
+ finalTime
+ );
});
}
}
setDragState({
isDragging: false,
- clipId: null,
+ elementId: null,
trackId: null,
startMouseX: 0,
- startClipTime: 0,
+ startElementTime: 0,
clickOffsetTime: 0,
currentTime: 0,
});
},
- [dragState, tracks, updateClipStartTime, moveClipToTrack]
+ [dragState, tracks, updateElementStartTime, moveElementToTrack]
);
const cancelDrag = useCallback(() => {
setDragState({
isDragging: false,
- clipId: null,
+ elementId: null,
trackId: null,
startMouseX: 0,
- startClipTime: 0,
+ startElementTime: 0,
clickOffsetTime: 0,
currentTime: 0,
});
@@ -176,12 +187,12 @@ export function useDragClip(zoomLevel: number) {
};
}, [dragState.isDragging, updateDrag, endDrag, cancelDrag]);
- const getDraggedClipPosition = useCallback(
- (clipId: string) => {
+ const getDraggedElementPosition = useCallback(
+ (elementId: string) => {
// Use ref to get current state, not stale closure
const currentDragState = dragStateRef.current;
const isMatch =
- currentDragState.isDragging && currentDragState.clipId === clipId;
+ currentDragState.isDragging && currentDragState.elementId === elementId;
if (isMatch) {
return currentDragState.currentTime;
@@ -209,7 +220,7 @@ export function useDragClip(zoomLevel: number) {
return {
// State
isDragging: dragState.isDragging,
- draggedClipId: dragState.clipId,
+ draggedElementId: dragState.elementId,
currentDragTime: dragState.currentTime,
clickOffsetTime: dragState.clickOffsetTime,
@@ -217,7 +228,7 @@ export function useDragClip(zoomLevel: number) {
startDrag,
endDrag,
cancelDrag,
- getDraggedClipPosition,
+ getDraggedElementPosition,
isValidDropTarget,
// Refs
diff --git a/apps/web/src/hooks/use-playback-controls.ts b/apps/web/src/hooks/use-playback-controls.ts
index 2c689470..1191d4b4 100644
--- a/apps/web/src/hooks/use-playback-controls.ts
+++ b/apps/web/src/hooks/use-playback-controls.ts
@@ -7,106 +7,105 @@ export const usePlaybackControls = () => {
const { isPlaying, currentTime, play, pause, seek } = usePlaybackStore();
const {
- selectedClips,
+ selectedElements,
tracks,
- splitClip,
+ splitElement,
splitAndKeepLeft,
splitAndKeepRight,
separateAudio,
} = useTimelineStore();
- const handleSplitSelectedClip = useCallback(() => {
- if (selectedClips.length !== 1) {
- toast.error("Select exactly one clip to split");
+ const handleSplitSelectedElement = useCallback(() => {
+ if (selectedElements.length !== 1) {
+ toast.error("Select exactly one element to split");
return;
}
- const { trackId, clipId } = selectedClips[0];
+ const { trackId, elementId } = selectedElements[0];
const track = tracks.find((t) => t.id === trackId);
- const clip = track?.clips.find((c) => c.id === clipId);
+ const element = track?.elements.find((e) => e.id === elementId);
- if (!clip) return;
+ if (!element) return;
- const effectiveStart = clip.startTime;
+ const effectiveStart = element.startTime;
const effectiveEnd =
- clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd);
+ element.startTime +
+ (element.duration - element.trimStart - element.trimEnd);
if (currentTime <= effectiveStart || currentTime >= effectiveEnd) {
- toast.error("Playhead must be within selected clip");
+ toast.error("Playhead must be within selected element");
return;
}
- splitClip(trackId, clipId, currentTime);
- toast.success("Clip split at playhead");
- }, [selectedClips, tracks, currentTime, splitClip]);
+ splitElement(trackId, elementId, currentTime);
+ }, [selectedElements, tracks, currentTime, splitElement]);
const handleSplitAndKeepLeftCallback = useCallback(() => {
- if (selectedClips.length !== 1) {
- toast.error("Select exactly one clip");
+ if (selectedElements.length !== 1) {
+ toast.error("Select exactly one element");
return;
}
- const { trackId, clipId } = selectedClips[0];
+ const { trackId, elementId } = selectedElements[0];
const track = tracks.find((t) => t.id === trackId);
- const clip = track?.clips.find((c) => c.id === clipId);
+ const element = track?.elements.find((e) => e.id === elementId);
- if (!clip) return;
+ if (!element) return;
- const effectiveStart = clip.startTime;
+ const effectiveStart = element.startTime;
const effectiveEnd =
- clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd);
+ element.startTime +
+ (element.duration - element.trimStart - element.trimEnd);
if (currentTime <= effectiveStart || currentTime >= effectiveEnd) {
- toast.error("Playhead must be within selected clip");
+ toast.error("Playhead must be within selected element");
return;
}
- splitAndKeepLeft(trackId, clipId, currentTime);
- toast.success("Split and kept left portion");
- }, [selectedClips, tracks, currentTime, splitAndKeepLeft]);
+ splitAndKeepLeft(trackId, elementId, currentTime);
+ }, [selectedElements, tracks, currentTime, splitAndKeepLeft]);
const handleSplitAndKeepRightCallback = useCallback(() => {
- if (selectedClips.length !== 1) {
- toast.error("Select exactly one clip");
+ if (selectedElements.length !== 1) {
+ toast.error("Select exactly one element");
return;
}
- const { trackId, clipId } = selectedClips[0];
+ const { trackId, elementId } = selectedElements[0];
const track = tracks.find((t) => t.id === trackId);
- const clip = track?.clips.find((c) => c.id === clipId);
+ const element = track?.elements.find((e) => e.id === elementId);
- if (!clip) return;
+ if (!element) return;
- const effectiveStart = clip.startTime;
+ const effectiveStart = element.startTime;
const effectiveEnd =
- clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd);
+ element.startTime +
+ (element.duration - element.trimStart - element.trimEnd);
if (currentTime <= effectiveStart || currentTime >= effectiveEnd) {
- toast.error("Playhead must be within selected clip");
+ toast.error("Playhead must be within selected element");
return;
}
- splitAndKeepRight(trackId, clipId, currentTime);
- toast.success("Split and kept right portion");
- }, [selectedClips, tracks, currentTime, splitAndKeepRight]);
+ splitAndKeepRight(trackId, elementId, currentTime);
+ }, [selectedElements, tracks, currentTime, splitAndKeepRight]);
const handleSeparateAudioCallback = useCallback(() => {
- if (selectedClips.length !== 1) {
- toast.error("Select exactly one video clip to separate audio");
+ if (selectedElements.length !== 1) {
+ toast.error("Select exactly one media element to separate audio");
return;
}
- const { trackId, clipId } = selectedClips[0];
+ const { trackId, elementId } = selectedElements[0];
const track = tracks.find((t) => t.id === trackId);
- if (!track || track.type !== "video") {
- toast.error("Select a video clip to separate audio");
+ if (!track || track.type !== "media") {
+ toast.error("Select a media element to separate audio");
return;
}
- separateAudio(trackId, clipId);
- toast.success("Audio separated to audio track");
- }, [selectedClips, tracks, separateAudio]);
+ separateAudio(trackId, elementId);
+ }, [selectedElements, tracks, separateAudio]);
const handleKeyPress = useCallback(
(e: KeyboardEvent) => {
@@ -130,7 +129,7 @@ export const usePlaybackControls = () => {
case "s":
if (e.ctrlKey || e.metaKey) {
e.preventDefault();
- handleSplitSelectedClip();
+ handleSplitSelectedElement();
}
break;
@@ -160,7 +159,7 @@ export const usePlaybackControls = () => {
isPlaying,
play,
pause,
- handleSplitSelectedClip,
+ handleSplitSelectedElement,
handleSplitAndKeepLeftCallback,
handleSplitAndKeepRightCallback,
handleSeparateAudioCallback,