diff options
| author | Maze Winther <mazewinther@gmail.com> | 2025-07-07 19:06:36 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2025-07-07 19:06:36 +0200 |
| commit | bd0c7f22069d1a2e4b2a2d962becaf6b9e942414 (patch) | |
| tree | 595eb9b49f655f7c30b008065c26612077474a82 /apps/web/src/components/editor | |
| parent | 11c0b89bd162fe0cb0633687fe5ad07977956ace (diff) | |
| download | opencut-bd0c7f22069d1a2e4b2a2d962becaf6b9e942414.tar.gz | |
refactor: store media relative to project, add storage for timeline data, and other things
Diffstat (limited to 'apps/web/src/components/editor')
| -rw-r--r-- | apps/web/src/components/editor/media-panel/views/media.tsx | 16 | ||||
| -rw-r--r-- | apps/web/src/components/editor/timeline-track.tsx | 61 | ||||
| -rw-r--r-- | apps/web/src/components/editor/timeline.tsx | 9 |
3 files changed, 63 insertions, 23 deletions
diff --git a/apps/web/src/components/editor/media-panel/views/media.tsx b/apps/web/src/components/editor/media-panel/views/media.tsx index f975496e..f7da684d 100644 --- a/apps/web/src/components/editor/media-panel/views/media.tsx +++ b/apps/web/src/components/editor/media-panel/views/media.tsx @@ -24,9 +24,11 @@ import { SelectValue,
} from "@/components/ui/select";
import { DraggableMediaItem } from "@/components/ui/draggable-item";
+import { useProjectStore } from "@/stores/project-store";
export function MediaView() {
const { mediaItems, addMediaItem, removeMediaItem } = useMediaStore();
+ const { activeProject } = useProjectStore();
const fileInputRef = useRef<HTMLInputElement>(null);
const [isProcessing, setIsProcessing] = useState(false);
const [progress, setProgress] = useState(0);
@@ -35,6 +37,11 @@ export function MediaView() { const processFiles = async (files: FileList | File[]) => {
if (!files || files.length === 0) return;
+ if (!activeProject) {
+ toast.error("No active project");
+ return;
+ }
+
setIsProcessing(true);
setProgress(0);
try {
@@ -44,7 +51,7 @@ export function MediaView() { );
// Add each processed media item to the store
for (const item of processedItems) {
- await addMediaItem(item);
+ await addMediaItem(activeProject.id, item);
}
} catch (error) {
// Show error toast if processing fails
@@ -73,6 +80,11 @@ export function MediaView() { // Remove a media item from the store
e.stopPropagation();
+ if (!activeProject) {
+ toast.error("No active project");
+ return;
+ }
+
// Remove elements automatically when delete media
const { tracks, removeTrack } = useTimelineStore.getState();
tracks.forEach((track) => {
@@ -92,7 +104,7 @@ export function MediaView() { removeTrack(track.id);
}
});
- await removeMediaItem(id);
+ await removeMediaItem(activeProject.id, id);
};
const formatDuration = (duration: number) => {
diff --git a/apps/web/src/components/editor/timeline-track.tsx b/apps/web/src/components/editor/timeline-track.tsx index eb58e1ee..2751fa5a 100644 --- a/apps/web/src/components/editor/timeline-track.tsx +++ b/apps/web/src/components/editor/timeline-track.tsx @@ -13,11 +13,12 @@ import { ContextMenuSeparator, ContextMenuTrigger, } from "../ui/context-menu"; -import { +import { TimelineTrack, sortTracksByOrder, ensureMainTrack, - getMainTrack + getMainTrack, + canElementGoOnTrack, } from "@/types/timeline"; import { usePlaybackStore } from "@/stores/playback-store"; import type { @@ -572,7 +573,9 @@ export function TimelineTrackContent({ // dropPosition === "on" but track is not text type // Insert above main track if main track exists, otherwise at top if (mainTrack) { - const mainTrackIndex = tracks.findIndex(t => t.id === mainTrack.id); + const mainTrackIndex = tracks.findIndex( + (t) => t.id === mainTrack.id + ); insertIndex = mainTrackIndex; } else { insertIndex = 0; // Top of timeline @@ -648,9 +651,11 @@ export function TimelineTrackContent({ const isVideoOrImage = dragData.type === "video" || dragData.type === "image"; const isAudio = dragData.type === "audio"; - const isCompatible = - (track.type === "media" && isVideoOrImage) || - (track.type === "audio" && isAudio); + const isCompatible = isVideoOrImage + ? canElementGoOnTrack("media", track.type) + : isAudio + ? canElementGoOnTrack("media", track.type) + : false; let targetTrack = tracks.find((t) => t.id === targetTrackId); @@ -662,7 +667,7 @@ export function TimelineTrackContent({ if (isVideoOrImage) { // For video/image, check if we need a main track or additional media track const mainTrack = getMainTrack(tracks); - + if (!mainTrack) { // No main track exists, create it const updatedTracks = ensureMainTrack(tracks); @@ -672,22 +677,32 @@ export function TimelineTrackContent({ targetTrack = newMainTrack; } else { // Main track was created but somehow has elements, create new media track - const mainTrackIndex = updatedTracks.findIndex(t => t.id === newMainTrack?.id); + const mainTrackIndex = updatedTracks.findIndex( + (t) => t.id === newMainTrack?.id + ); targetTrackId = insertTrackAt("media", mainTrackIndex); - const updatedTracksAfterInsert = useTimelineStore.getState().tracks; - const newTargetTrack = updatedTracksAfterInsert.find(t => t.id === targetTrackId); + const updatedTracksAfterInsert = + useTimelineStore.getState().tracks; + const newTargetTrack = updatedTracksAfterInsert.find( + (t) => t.id === targetTrackId + ); if (!newTargetTrack) return; targetTrack = newTargetTrack; } - } else if (mainTrack.elements.length === 0 && dropPosition === "on") { + } else if ( + mainTrack.elements.length === 0 && + dropPosition === "on" + ) { // Main track exists and is empty, use it targetTrackId = mainTrack.id; targetTrack = mainTrack; } else { // Create new media track above main track - const mainTrackIndex = tracks.findIndex(t => t.id === mainTrack.id); + const mainTrackIndex = tracks.findIndex( + (t) => t.id === mainTrack.id + ); let insertIndex: number; - + if (dropPosition === "above") { insertIndex = currentTrackIndex; } else if (dropPosition === "below") { @@ -696,10 +711,12 @@ export function TimelineTrackContent({ // Insert above main track insertIndex = mainTrackIndex; } - + targetTrackId = insertTrackAt("media", insertIndex); const updatedTracks = useTimelineStore.getState().tracks; - const newTargetTrack = updatedTracks.find(t => t.id === targetTrackId); + const newTargetTrack = updatedTracks.find( + (t) => t.id === targetTrackId + ); if (!newTargetTrack) return; targetTrack = newTargetTrack; } @@ -707,7 +724,7 @@ export function TimelineTrackContent({ // Audio tracks go at the bottom const mainTrack = getMainTrack(tracks); let insertIndex: number; - + if (dropPosition === "above") { insertIndex = currentTrackIndex; } else if (dropPosition === "below") { @@ -715,16 +732,20 @@ export function TimelineTrackContent({ } else { // Insert after main track (bottom area) if (mainTrack) { - const mainTrackIndex = tracks.findIndex(t => t.id === mainTrack.id); + const mainTrackIndex = tracks.findIndex( + (t) => t.id === mainTrack.id + ); insertIndex = mainTrackIndex + 1; } else { insertIndex = tracks.length; // Bottom of timeline } } - + targetTrackId = insertTrackAt("audio", insertIndex); const updatedTracks = useTimelineStore.getState().tracks; - const newTargetTrack = updatedTracks.find(t => t.id === targetTrackId); + const newTargetTrack = updatedTracks.find( + (t) => t.id === targetTrackId + ); if (!newTargetTrack) return; targetTrack = newTargetTrack; } @@ -805,7 +826,7 @@ export function TimelineTrackContent({ ? wouldOverlap ? "Cannot drop - would overlap" : "Drop element here" - : "Drop media here"} + : ""} </div> ) : ( <> diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx index 283f6b04..f98ab5ec 100644 --- a/apps/web/src/components/editor/timeline.tsx +++ b/apps/web/src/components/editor/timeline.tsx @@ -31,6 +31,7 @@ import { import { useTimelineStore } from "@/stores/timeline-store"; import { useMediaStore } from "@/stores/media-store"; import { usePlaybackStore } from "@/stores/playback-store"; +import { useProjectStore } from "@/stores/project-store"; import { processMediaFiles } from "@/lib/media-processing"; import { toast } from "sonner"; import { useState, useRef, useEffect, useCallback } from "react"; @@ -65,6 +66,7 @@ export function Timeline() { redo, } = useTimelineStore(); const { mediaItems, addMediaItem } = useMediaStore(); + const { activeProject } = useProjectStore(); const { currentTime, duration, @@ -377,6 +379,11 @@ export function Timeline() { } } else if (e.dataTransfer.files?.length > 0) { // Handle file drops by creating new tracks + if (!activeProject) { + toast.error("No active project"); + return; + } + setIsProcessing(true); setProgress(0); try { @@ -385,7 +392,7 @@ export function Timeline() { (p) => setProgress(p) ); for (const processedItem of processedItems) { - await addMediaItem(processedItem); + await addMediaItem(activeProject.id, processedItem); const currentMediaItems = useMediaStore.getState().mediaItems; const addedItem = currentMediaItems.find( (item) => |
