From 906f95399d3a0119af0e57c2cb31fafbcdc31391 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Sat, 26 Jul 2025 13:25:10 +0200 Subject: [PATCH] refactor: better error handling --- .../editor/timeline/timeline-element.tsx | 12 +- apps/web/src/stores/timeline-store.ts | 103 +++++++++++++----- 2 files changed, 79 insertions(+), 36 deletions(-) diff --git a/apps/web/src/components/editor/timeline/timeline-element.tsx b/apps/web/src/components/editor/timeline/timeline-element.tsx index c66e9fcb..f581bbf0 100644 --- a/apps/web/src/components/editor/timeline/timeline-element.tsx +++ b/apps/web/src/components/editor/timeline/timeline-element.tsx @@ -157,16 +157,16 @@ export function TimelineElement({ if (!file) return; try { - const success = await replaceElementMedia(track.id, element.id, file); - if (success) { + const result = await replaceElementMedia(track.id, element.id, file); + if (result.success) { toast.success("Clip replaced successfully"); } else { - toast.error("Failed to replace clip"); + toast.error(result.error || "Failed to replace clip"); } } catch (error) { - toast.error("Failed to replace clip"); - console.log( - JSON.stringify({ error: "Failed to replace clip", details: error }) + console.error("Unexpected error replacing clip:", error); + toast.error( + `Unexpected error: ${error instanceof Error ? error.message : "Unknown error"}` ); } }; diff --git a/apps/web/src/stores/timeline-store.ts b/apps/web/src/stores/timeline-store.ts index 9e3f56d6..f3497be9 100644 --- a/apps/web/src/stores/timeline-store.ts +++ b/apps/web/src/stores/timeline-store.ts @@ -150,7 +150,7 @@ interface TimelineStore { trackId: string, elementId: string, newFile: File - ) => Promise; + ) => Promise<{ success: boolean; error?: string }>; // Ripple editing functions updateElementStartTimeWithRipple: ( @@ -1077,18 +1077,33 @@ export const useTimelineStore = create((set, get) => { }, // Replace media for an element - replaceElementMedia: async (trackId, elementId, newFile) => { + replaceElementMedia: async ( + trackId: string, + elementId: string, + newFile: File + ): Promise<{ success: boolean; error?: string }> => { const { _tracks } = get(); const track = _tracks.find((t) => t.id === trackId); const element = track?.elements.find((c) => c.id === elementId); - if (!element || element.type !== "media") return false; + if (!element) { + return { success: false, error: "Timeline element not found" }; + } + + if (element.type !== "media") { + return { + success: false, + error: "Replace is only available for media clips", + }; + } try { const mediaStore = useMediaStore.getState(); const projectStore = useProjectStore.getState(); - if (!projectStore.activeProject) return false; + if (!projectStore.activeProject) { + return { success: false, error: "No active project found" }; + } // Import required media processing functions const { @@ -1099,7 +1114,13 @@ export const useTimelineStore = create((set, get) => { } = await import("./media-store"); const fileType = getFileType(newFile); - if (!fileType) return false; + if (!fileType) { + return { + success: false, + error: + "Unsupported file type. Please select a video, audio, or image file.", + }; + } // Process the new media file const mediaData: any = { @@ -1109,32 +1130,56 @@ export const useTimelineStore = create((set, get) => { url: URL.createObjectURL(newFile), }; - // Get media-specific metadata - if (fileType === "image") { - const { width, height } = await getImageDimensions(newFile); - mediaData.width = width; - mediaData.height = height; - } else if (fileType === "video") { - const [duration, { thumbnailUrl, width, height }] = await Promise.all( - [getMediaDuration(newFile), generateVideoThumbnail(newFile)] - ); - mediaData.duration = duration; - mediaData.thumbnailUrl = thumbnailUrl; - mediaData.width = width; - mediaData.height = height; - } else if (fileType === "audio") { - mediaData.duration = await getMediaDuration(newFile); + try { + // Get media-specific metadata + if (fileType === "image") { + const { width, height } = await getImageDimensions(newFile); + mediaData.width = width; + mediaData.height = height; + } else if (fileType === "video") { + const [duration, { thumbnailUrl, width, height }] = + await Promise.all([ + getMediaDuration(newFile), + generateVideoThumbnail(newFile), + ]); + mediaData.duration = duration; + mediaData.thumbnailUrl = thumbnailUrl; + mediaData.width = width; + mediaData.height = height; + } else if (fileType === "audio") { + mediaData.duration = await getMediaDuration(newFile); + } + } catch (error) { + return { + success: false, + error: `Failed to process ${fileType} file: ${error instanceof Error ? error.message : "Unknown error"}`, + }; } // Add new media item to store - await mediaStore.addMediaItem(projectStore.activeProject.id, mediaData); + try { + await mediaStore.addMediaItem( + projectStore.activeProject.id, + mediaData + ); + } catch (error) { + return { + success: false, + error: `Failed to add media to project: ${error instanceof Error ? error.message : "Unknown error"}`, + }; + } // Find the newly created media item const newMediaItem = mediaStore.mediaItems.find( (item) => item.file === newFile ); - if (!newMediaItem) return false; + if (!newMediaItem) { + return { + success: false, + error: "Failed to create media item in project. Please try again.", + }; + } get().pushHistory(); @@ -1160,15 +1205,13 @@ export const useTimelineStore = create((set, get) => { ) ); - return true; + return { success: true }; } catch (error) { - console.log( - JSON.stringify({ - error: "Failed to replace element media", - details: error, - }) - ); - return false; + console.error("Failed to replace element media:", error); + return { + success: false, + error: `Unexpected error: ${error instanceof Error ? error.message : "Unknown error"}`, + }; } },