summaryrefslogtreecommitdiff
path: root/apps/web/src/stores
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/stores')
-rw-r--r--apps/web/src/stores/keybindings-store.ts10
-rw-r--r--apps/web/src/stores/media-store.ts42
-rw-r--r--apps/web/src/stores/timeline-store.ts25
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 =