diff options
Diffstat (limited to 'apps/web/src/stores')
| -rw-r--r-- | apps/web/src/stores/keybindings-store.ts | 10 | ||||
| -rw-r--r-- | apps/web/src/stores/media-store.ts | 42 | ||||
| -rw-r--r-- | apps/web/src/stores/timeline-store.ts | 25 |
3 files changed, 60 insertions, 17 deletions
diff --git a/apps/web/src/stores/keybindings-store.ts b/apps/web/src/stores/keybindings-store.ts index ed1d6b8c..97cba23f 100644 --- a/apps/web/src/stores/keybindings-store.ts +++ b/apps/web/src/stores/keybindings-store.ts @@ -54,7 +54,7 @@ interface KeybindingsState { // Validation validateKeybinding: ( key: ShortcutKey, - action: ActionWithOptionalArgs, + action: ActionWithOptionalArgs ) => KeybindingConflict | null; getKeybindingsForAction: (action: ActionWithOptionalArgs) => ShortcutKey[]; @@ -129,7 +129,7 @@ export const useKeybindingsStore = create<KeybindingsState>()( validateKeybinding: ( key: ShortcutKey, - action: ActionWithOptionalArgs, + action: ActionWithOptionalArgs ) => { const { keybindings } = get(); const existingAction = keybindings[key]; @@ -151,7 +151,7 @@ export const useKeybindingsStore = create<KeybindingsState>()( getKeybindingsForAction: (action: ActionWithOptionalArgs) => { const { keybindings } = get(); return Object.keys(keybindings).filter( - (key) => keybindings[key as ShortcutKey] === action, + (key) => keybindings[key as ShortcutKey] === action ) as ShortcutKey[]; }, @@ -162,8 +162,8 @@ export const useKeybindingsStore = create<KeybindingsState>()( { name: "opencut-keybindings", version: 1, - }, - ), + } + ) ); // Utility functions diff --git a/apps/web/src/stores/media-store.ts b/apps/web/src/stores/media-store.ts index 029307d9..650c0540 100644 --- a/apps/web/src/stores/media-store.ts +++ b/apps/web/src/stores/media-store.ts @@ -183,24 +183,58 @@ export const useMediaStore = create<MediaStore>((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, + pushHistory, + } = timeline; + + // Find all elements that reference this media + const elementsToRemove: Array<{ trackId: string; elementId: string }> = []; + for (const track of tracks) { + for (const el of track.elements) { + if (el.type === "media" && el.mediaId === id) { + elementsToRemove.push({ trackId: track.id, elementId: el.id }); + } + } + } + + // If there are elements to remove, push history once before batch removal + if (elementsToRemove.length > 0) { + pushHistory(); + + // Remove all elements without pushing additional history entries + for (const { trackId, elementId } of elementsToRemove) { + if (rippleEditingEnabled) { + removeElementFromTrackWithRipple(trackId, elementId, false); + } else { + removeElementFromTrack(trackId, elementId, false); + } + } + } + + // 3) Remove from persistent storage try { await storageService.deleteMediaItem(projectId, id); } catch (error) { diff --git a/apps/web/src/stores/timeline-store.ts b/apps/web/src/stores/timeline-store.ts index 820025cc..6f9dfbd6 100644 --- a/apps/web/src/stores/timeline-store.ts +++ b/apps/web/src/stores/timeline-store.ts @@ -96,7 +96,11 @@ interface TimelineStore { removeTrack: (trackId: string) => void; removeTrackWithRipple: (trackId: string) => void; addElementToTrack: (trackId: string, element: CreateTimelineElement) => void; - removeElementFromTrack: (trackId: string, elementId: string) => void; + removeElementFromTrack: ( + trackId: string, + elementId: string, + pushHistory?: boolean + ) => void; moveElementToTrack: ( fromTrackId: string, toTrackId: string, @@ -156,7 +160,8 @@ interface TimelineStore { ) => void; removeElementFromTrackWithRipple: ( trackId: string, - elementId: string + elementId: string, + pushHistory?: boolean ) => void; // Computed values @@ -553,13 +558,13 @@ export const useTimelineStore = create<TimelineStore>((set, get) => { get().selectElement(trackId, newElement.id); }, - removeElementFromTrack: (trackId, elementId) => { + removeElementFromTrack: (trackId, elementId, pushHistory = true) => { const { rippleEditingEnabled } = get(); if (rippleEditingEnabled) { - get().removeElementFromTrackWithRipple(trackId, elementId); + get().removeElementFromTrackWithRipple(trackId, elementId, pushHistory); } else { - get().pushHistory(); + if (pushHistory) get().pushHistory(); updateTracksAndSave( get() ._tracks.map((track) => @@ -577,12 +582,16 @@ export const useTimelineStore = create<TimelineStore>((set, get) => { } }, - removeElementFromTrackWithRipple: (trackId, elementId) => { + removeElementFromTrackWithRipple: ( + trackId, + elementId, + pushHistory = true + ) => { const { _tracks, rippleEditingEnabled } = get(); if (!rippleEditingEnabled) { // If ripple editing is disabled, use regular removal - get().removeElementFromTrack(trackId, elementId); + get().removeElementFromTrack(trackId, elementId, pushHistory); return; } @@ -591,7 +600,7 @@ export const useTimelineStore = create<TimelineStore>((set, get) => { if (!element || !track) return; - get().pushHistory(); + if (pushHistory) get().pushHistory(); const elementStartTime = element.startTime; const elementDuration = |
