refactor: improve playback controls by replacing inline functions with useCallback for clip manipulation and audio separation
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useCallback } from "react";
|
||||
import { usePlaybackStore } from "@/stores/playback-store";
|
||||
import { useTimelineStore } from "@/stores/timeline-store";
|
||||
import { toast } from "sonner";
|
||||
@ -15,8 +15,101 @@ export const usePlaybackControls = () => {
|
||||
separateAudio,
|
||||
} = useTimelineStore();
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyPress = (e: KeyboardEvent) => {
|
||||
const handleSplitSelectedClip = useCallback(() => {
|
||||
if (selectedClips.length !== 1) {
|
||||
toast.error("Select exactly one clip to split");
|
||||
return;
|
||||
}
|
||||
|
||||
const { trackId, clipId } = selectedClips[0];
|
||||
const track = tracks.find((t) => t.id === trackId);
|
||||
const clip = track?.clips.find((c) => c.id === clipId);
|
||||
|
||||
if (!clip) return;
|
||||
|
||||
const effectiveStart = clip.startTime;
|
||||
const effectiveEnd =
|
||||
clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd);
|
||||
|
||||
if (currentTime <= effectiveStart || currentTime >= effectiveEnd) {
|
||||
toast.error("Playhead must be within selected clip");
|
||||
return;
|
||||
}
|
||||
|
||||
splitClip(trackId, clipId, currentTime);
|
||||
toast.success("Clip split at playhead");
|
||||
}, [selectedClips, tracks, currentTime, splitClip]);
|
||||
|
||||
const handleSplitAndKeepLeftCallback = useCallback(() => {
|
||||
if (selectedClips.length !== 1) {
|
||||
toast.error("Select exactly one clip");
|
||||
return;
|
||||
}
|
||||
|
||||
const { trackId, clipId } = selectedClips[0];
|
||||
const track = tracks.find((t) => t.id === trackId);
|
||||
const clip = track?.clips.find((c) => c.id === clipId);
|
||||
|
||||
if (!clip) return;
|
||||
|
||||
const effectiveStart = clip.startTime;
|
||||
const effectiveEnd =
|
||||
clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd);
|
||||
|
||||
if (currentTime <= effectiveStart || currentTime >= effectiveEnd) {
|
||||
toast.error("Playhead must be within selected clip");
|
||||
return;
|
||||
}
|
||||
|
||||
splitAndKeepLeft(trackId, clipId, currentTime);
|
||||
toast.success("Split and kept left portion");
|
||||
}, [selectedClips, tracks, currentTime, splitAndKeepLeft]);
|
||||
|
||||
const handleSplitAndKeepRightCallback = useCallback(() => {
|
||||
if (selectedClips.length !== 1) {
|
||||
toast.error("Select exactly one clip");
|
||||
return;
|
||||
}
|
||||
|
||||
const { trackId, clipId } = selectedClips[0];
|
||||
const track = tracks.find((t) => t.id === trackId);
|
||||
const clip = track?.clips.find((c) => c.id === clipId);
|
||||
|
||||
if (!clip) return;
|
||||
|
||||
const effectiveStart = clip.startTime;
|
||||
const effectiveEnd =
|
||||
clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd);
|
||||
|
||||
if (currentTime <= effectiveStart || currentTime >= effectiveEnd) {
|
||||
toast.error("Playhead must be within selected clip");
|
||||
return;
|
||||
}
|
||||
|
||||
splitAndKeepRight(trackId, clipId, currentTime);
|
||||
toast.success("Split and kept right portion");
|
||||
}, [selectedClips, tracks, currentTime, splitAndKeepRight]);
|
||||
|
||||
const handleSeparateAudioCallback = useCallback(() => {
|
||||
if (selectedClips.length !== 1) {
|
||||
toast.error("Select exactly one video clip to separate audio");
|
||||
return;
|
||||
}
|
||||
|
||||
const { trackId, clipId } = selectedClips[0];
|
||||
const track = tracks.find((t) => t.id === trackId);
|
||||
|
||||
if (!track || track.type !== "video") {
|
||||
toast.error("Select a video clip to separate audio");
|
||||
return;
|
||||
}
|
||||
|
||||
separateAudio(trackId, clipId);
|
||||
toast.success("Audio separated to audio track");
|
||||
}, [selectedClips, tracks, separateAudio]);
|
||||
|
||||
const handleKeyPress = useCallback(
|
||||
(e: KeyboardEvent) => {
|
||||
if (
|
||||
e.target instanceof HTMLInputElement ||
|
||||
e.target instanceof HTMLTextAreaElement
|
||||
@ -44,131 +137,38 @@ export const usePlaybackControls = () => {
|
||||
case "q":
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
e.preventDefault();
|
||||
handleSplitAndKeepLeft();
|
||||
handleSplitAndKeepLeftCallback();
|
||||
}
|
||||
break;
|
||||
|
||||
case "w":
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
e.preventDefault();
|
||||
handleSplitAndKeepRight();
|
||||
handleSplitAndKeepRightCallback();
|
||||
}
|
||||
break;
|
||||
|
||||
case "d":
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
e.preventDefault();
|
||||
handleSeparateAudio();
|
||||
handleSeparateAudioCallback();
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const handleSplitSelectedClip = () => {
|
||||
if (selectedClips.length !== 1) {
|
||||
toast.error("Select exactly one clip to split");
|
||||
return;
|
||||
}
|
||||
|
||||
const { trackId, clipId } = selectedClips[0];
|
||||
const track = tracks.find((t) => t.id === trackId);
|
||||
const clip = track?.clips.find((c) => c.id === clipId);
|
||||
|
||||
if (!clip) return;
|
||||
|
||||
const effectiveStart = clip.startTime;
|
||||
const effectiveEnd =
|
||||
clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd);
|
||||
|
||||
if (currentTime <= effectiveStart || currentTime >= effectiveEnd) {
|
||||
toast.error("Playhead must be within selected clip");
|
||||
return;
|
||||
}
|
||||
|
||||
splitClip(trackId, clipId, currentTime);
|
||||
toast.success("Clip split at playhead");
|
||||
};
|
||||
|
||||
const handleSplitAndKeepLeft = () => {
|
||||
if (selectedClips.length !== 1) {
|
||||
toast.error("Select exactly one clip");
|
||||
return;
|
||||
}
|
||||
|
||||
const { trackId, clipId } = selectedClips[0];
|
||||
const track = tracks.find((t) => t.id === trackId);
|
||||
const clip = track?.clips.find((c) => c.id === clipId);
|
||||
|
||||
if (!clip) return;
|
||||
|
||||
const effectiveStart = clip.startTime;
|
||||
const effectiveEnd =
|
||||
clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd);
|
||||
|
||||
if (currentTime <= effectiveStart || currentTime >= effectiveEnd) {
|
||||
toast.error("Playhead must be within selected clip");
|
||||
return;
|
||||
}
|
||||
|
||||
splitAndKeepLeft(trackId, clipId, currentTime);
|
||||
toast.success("Split and kept left portion");
|
||||
};
|
||||
|
||||
const handleSplitAndKeepRight = () => {
|
||||
if (selectedClips.length !== 1) {
|
||||
toast.error("Select exactly one clip");
|
||||
return;
|
||||
}
|
||||
|
||||
const { trackId, clipId } = selectedClips[0];
|
||||
const track = tracks.find((t) => t.id === trackId);
|
||||
const clip = track?.clips.find((c) => c.id === clipId);
|
||||
|
||||
if (!clip) return;
|
||||
|
||||
const effectiveStart = clip.startTime;
|
||||
const effectiveEnd =
|
||||
clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd);
|
||||
|
||||
if (currentTime <= effectiveStart || currentTime >= effectiveEnd) {
|
||||
toast.error("Playhead must be within selected clip");
|
||||
return;
|
||||
}
|
||||
|
||||
splitAndKeepRight(trackId, clipId, currentTime);
|
||||
toast.success("Split and kept right portion");
|
||||
};
|
||||
|
||||
const handleSeparateAudio = () => {
|
||||
if (selectedClips.length !== 1) {
|
||||
toast.error("Select exactly one video clip to separate audio");
|
||||
return;
|
||||
}
|
||||
|
||||
const { trackId, clipId } = selectedClips[0];
|
||||
const track = tracks.find((t) => t.id === trackId);
|
||||
|
||||
if (!track || track.type !== "video") {
|
||||
toast.error("Select a video clip to separate audio");
|
||||
return;
|
||||
}
|
||||
|
||||
separateAudio(trackId, clipId);
|
||||
toast.success("Audio separated to audio track");
|
||||
};
|
||||
},
|
||||
[
|
||||
isPlaying,
|
||||
play,
|
||||
pause,
|
||||
handleSplitSelectedClip,
|
||||
handleSplitAndKeepLeftCallback,
|
||||
handleSplitAndKeepRightCallback,
|
||||
handleSeparateAudioCallback,
|
||||
]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("keydown", handleKeyPress);
|
||||
return () => document.removeEventListener("keydown", handleKeyPress);
|
||||
}, [
|
||||
isPlaying,
|
||||
currentTime,
|
||||
selectedClips,
|
||||
tracks,
|
||||
play,
|
||||
pause,
|
||||
splitClip,
|
||||
splitAndKeepLeft,
|
||||
splitAndKeepRight,
|
||||
separateAudio,
|
||||
]);
|
||||
}, [handleKeyPress]);
|
||||
};
|
||||
|
Reference in New Issue
Block a user