From cf6ef8f1c4a9a70898cafe10e06ba12192524644 Mon Sep 17 00:00:00 2001 From: Notoriousbrain Date: Tue, 22 Jul 2025 16:44:16 +0530 Subject: [PATCH 1/5] fix: removed the media from timeline after its deleted --- apps/web/src/stores/media-store.ts | 40 ++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/apps/web/src/stores/media-store.ts b/apps/web/src/stores/media-store.ts index c4ee5f3e..6eb06e35 100644 --- a/apps/web/src/stores/media-store.ts +++ b/apps/web/src/stores/media-store.ts @@ -183,24 +183,46 @@ export const useMediaStore = create((set, get) => ({ } }, - removeMediaItem: async (projectId, id: string) => { + removeMediaItem: async (projectId: string, id: string) => { const state = get(); const item = state.mediaItems.find((media) => media.id === id); // Cleanup object URLs to prevent memory leaks - if (item && item.url) { + if (item?.url) { URL.revokeObjectURL(item.url); if (item.thumbnailUrl) { URL.revokeObjectURL(item.thumbnailUrl); } } - // Remove from local state immediately + // 1) Remove from local state immediately set((state) => ({ mediaItems: state.mediaItems.filter((media) => media.id !== id), })); - // Remove from persistent storage + // 2) Cascade into the timeline: remove any elements using this media ID + const timeline = useTimelineStore.getState(); + const { + tracks, + removeElementFromTrack, + removeElementFromTrackWithRipple, + rippleEditingEnabled, + } = timeline; + + // Iterate over a snapshot of tracks and their elements + tracks.forEach((track) => { + track.elements.forEach((el) => { + if (el.type === "media" && el.mediaId === id) { + if (rippleEditingEnabled) { + removeElementFromTrackWithRipple(track.id, el.id); + } else { + removeElementFromTrack(track.id, el.id); + } + } + }); + }); + + // 3) Remove from persistent storage try { await storageService.deleteMediaItem(projectId, id); } catch (error) { @@ -219,15 +241,19 @@ export const useMediaStore = create((set, get) => ({ mediaItems.map(async (item) => { if (item.type === "video" && item.file) { try { - const { thumbnailUrl, width, height } = await generateVideoThumbnail(item.file); + const { thumbnailUrl, width, height } = + await generateVideoThumbnail(item.file); return { ...item, thumbnailUrl, width: width || item.width, - height: height || item.height + height: height || item.height, }; } catch (error) { - console.error(`Failed to regenerate thumbnail for video ${item.id}:`, error); + console.error( + `Failed to regenerate thumbnail for video ${item.id}:`, + error + ); return item; } } From ec875268426aced70048883dbea90cd1d0f691df Mon Sep 17 00:00:00 2001 From: Notoriousbrain Date: Tue, 22 Jul 2025 17:24:40 +0530 Subject: [PATCH 2/5] changed the code to make sure it doesnt skip any media --- apps/web/src/stores/media-store.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/web/src/stores/media-store.ts b/apps/web/src/stores/media-store.ts index 6eb06e35..8a58b7c6 100644 --- a/apps/web/src/stores/media-store.ts +++ b/apps/web/src/stores/media-store.ts @@ -211,13 +211,14 @@ export const useMediaStore = create((set, get) => ({ // Iterate over a snapshot of tracks and their elements tracks.forEach((track) => { - track.elements.forEach((el) => { - if (el.type === "media" && el.mediaId === id) { - if (rippleEditingEnabled) { - removeElementFromTrackWithRipple(track.id, el.id); - } else { - removeElementFromTrack(track.id, el.id); - } + const elementsToRemove = track.elements.filter( + (el) => el.type === "media" && el.mediaId === id + ); + elementsToRemove.forEach((el) => { + if (rippleEditingEnabled) { + removeElementFromTrackWithRipple(track.id, el.id); + } else { + removeElementFromTrack(track.id, el.id); } }); }); From 4fb6018cf4a2a23b2ca6f8581e9b6c0d0639a89c Mon Sep 17 00:00:00 2001 From: Notoriousbrain Date: Tue, 22 Jul 2025 17:54:56 +0530 Subject: [PATCH 3/5] changed it to const for loop in removeMediaItem --- apps/web/src/stores/media-store.ts | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/apps/web/src/stores/media-store.ts b/apps/web/src/stores/media-store.ts index 8a58b7c6..45f6ee04 100644 --- a/apps/web/src/stores/media-store.ts +++ b/apps/web/src/stores/media-store.ts @@ -210,18 +210,17 @@ export const useMediaStore = create((set, get) => ({ } = timeline; // Iterate over a snapshot of tracks and their elements - tracks.forEach((track) => { - const elementsToRemove = track.elements.filter( - (el) => el.type === "media" && el.mediaId === id - ); - elementsToRemove.forEach((el) => { - if (rippleEditingEnabled) { - removeElementFromTrackWithRipple(track.id, el.id); - } else { - removeElementFromTrack(track.id, el.id); + for (const track of tracks) { + for (const el of track.elements) { + if (el.type === "media" && el.mediaId === id) { + if (rippleEditingEnabled) { + removeElementFromTrackWithRipple(track.id, el.id); + } else { + removeElementFromTrack(track.id, el.id); + } } - }); - }); + } + } // 3) Remove from persistent storage try { From 8be8f0a5a573d6c97cb378c08a5ffcee6e1485ca Mon Sep 17 00:00:00 2001 From: Notoriousbrain Date: Thu, 24 Jul 2025 03:20:00 +0530 Subject: [PATCH 4/5] added the dialog before deleting --- .../editor/media-panel/views/media.tsx | 51 ++++++++++++++++--- 1 file changed, 43 insertions(+), 8 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 631fe39a..edda64dd 100644 --- a/apps/web/src/components/editor/media-panel/views/media.tsx +++ b/apps/web/src/components/editor/media-panel/views/media.tsx @@ -25,6 +25,14 @@ import { import { DraggableMediaItem } from "@/components/ui/draggable-item"; import { useProjectStore } from "@/stores/project-store"; import { useTimelineStore } from "@/stores/timeline-store"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; export function MediaView() { const { mediaItems, addMediaItem, removeMediaItem } = useMediaStore(); @@ -34,6 +42,8 @@ export function MediaView() { const [progress, setProgress] = useState(0); const [searchQuery, setSearchQuery] = useState(""); const [mediaFilter, setMediaFilter] = useState("all"); + const [dialogOpen, setDialogOpen] = useState(false); + const [itemToRemove, setItemToRemove] = useState(null); const processFiles = async (files: FileList | File[]) => { if (!files || files.length === 0) return; @@ -76,17 +86,21 @@ export function MediaView() { e.target.value = ""; // Reset input }; - const handleRemove = async (e: React.MouseEvent, id: string) => { - // Remove a media item from the store + const handleRemoveClick = (e: React.MouseEvent, item: MediaItem) => { e.stopPropagation(); + setItemToRemove(item); + setDialogOpen(true); + }; - if (!activeProject) { - toast.error("No active project"); + // ← NEW: actually run removeMediaItem only when they confirm + const handleConfirmRemove = async () => { + if (!activeProject || !itemToRemove) { + setDialogOpen(false); return; } - - // Media store now handles cascade deletion automatically - await removeMediaItem(activeProject.id, id); + await removeMediaItem(activeProject.id, itemToRemove.id); + setDialogOpen(false); + setItemToRemove(null); }; const formatDuration = (duration: number) => { @@ -282,7 +296,7 @@ export function MediaView() { Export clips handleRemove(e, item.id)} + onClick={(e) => handleRemoveClick(e, item)} > Delete @@ -293,6 +307,27 @@ export function MediaView() { )} + {itemToRemove && ( + + + + Delete “{itemToRemove.name}”? + + This will permanently remove the media and any clips on your + timeline. Are you sure you want to continue? + + + + + + + + + )} ); } From 9e711c1e09f5eb80fa587fc7f875407c188ca309 Mon Sep 17 00:00:00 2001 From: Notoriousbrain Date: Thu, 24 Jul 2025 03:28:35 +0530 Subject: [PATCH 5/5] added the error handling --- .../components/editor/media-panel/views/media.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 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 edda64dd..17231fbe 100644 --- a/apps/web/src/components/editor/media-panel/views/media.tsx +++ b/apps/web/src/components/editor/media-panel/views/media.tsx @@ -98,9 +98,15 @@ export function MediaView() { setDialogOpen(false); return; } - await removeMediaItem(activeProject.id, itemToRemove.id); - setDialogOpen(false); - setItemToRemove(null); + try { + await removeMediaItem(activeProject.id, itemToRemove.id); + } catch (error) { + console.error("Failed to remove media item:", error); + toast.error("Failed to delete media item"); + } finally { + setDialogOpen(false); + setItemToRemove(null); + } }; const formatDuration = (duration: number) => {