From 2c90ddd5d5f16c868f5e04943b64e0a3a78147f1 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Wed, 6 Aug 2025 12:30:48 +0200 Subject: [PATCH] feat: allow external file drops to individual timeline tracks --- .../editor/timeline/timeline-track.tsx | 47 +++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/apps/web/src/components/editor/timeline/timeline-track.tsx b/apps/web/src/components/editor/timeline/timeline-track.tsx index 1982d03e..75910c46 100644 --- a/apps/web/src/components/editor/timeline/timeline-track.tsx +++ b/apps/web/src/components/editor/timeline/timeline-track.tsx @@ -4,11 +4,10 @@ import { useRef, useState, useEffect } from "react"; import { useTimelineStore } from "@/stores/timeline-store"; import { useMediaStore } from "@/stores/media-store"; import { toast } from "sonner"; +import { processMediaFiles } from "@/lib/media-processing"; import { TimelineElement } from "./timeline-element"; import { TimelineTrack, - sortTracksByOrder, - ensureMainTrack, getMainTrack, canElementGoOnTrack, } from "@/types/timeline"; @@ -689,8 +688,9 @@ export function TimelineTrackContent({ const hasMediaItem = e.dataTransfer.types.includes( "application/x-media-item" ); + const hasFiles = e.dataTransfer.files?.length > 0; - if (!hasTimelineElement && !hasMediaItem) return; + if (!hasTimelineElement && !hasMediaItem && !hasFiles) return; const trackContainer = e.currentTarget.querySelector( ".track-elements-container" @@ -1050,6 +1050,47 @@ export function TimelineTrackContent({ trimEnd: 0, }); } + } else if (hasFiles) { + // External file drops + const { activeProject } = useProjectStore.getState(); + const { addMediaItem } = useMediaStore.getState(); + const { addElementToTrack } = useTimelineStore.getState(); + + if (!activeProject) { + toast.error("No active project"); + return; + } + + // Process and add files to timeline at the drop position + processMediaFiles(e.dataTransfer.files) + .then(async (processedItems) => { + for (const processedItem of processedItems) { + await addMediaItem(activeProject.id, processedItem); + const currentMediaItems = useMediaStore.getState().mediaItems; + const addedItem = currentMediaItems.find( + (item) => + item.name === processedItem.name && + item.url === processedItem.url + ); + + if (addedItem) { + // Add to current track at the drop position + addElementToTrack(track.id, { + type: "media", + mediaId: addedItem.id, + name: addedItem.name, + duration: addedItem.duration || 5, + startTime: snappedTime, + trimStart: 0, + trimEnd: 0, + }); + } + } + }) + .catch((error) => { + console.error("Error processing external files:", error); + toast.error("Failed to process dropped files"); + }); } } catch (error) { console.error("Error handling drop:", error);