diff options
| author | melad <meladnaieem@yahoo.com> | 2025-07-31 13:50:22 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-31 12:50:22 +0200 |
| commit | f255ccb818358b90edd0c1bc1f3a690dc1e729ac (patch) | |
| tree | 8c2a0503d591d24edaa6cd0eeea5f152397f6bd3 /apps/web/src/stores | |
| parent | b229d94a8da7b258f1b49867e8cee225d5feb735 (diff) | |
| download | opencut-f255ccb818358b90edd0c1bc1f3a690dc1e729ac.tar.gz | |
feat: add hide or show for media elements, mute or unmute for audio elements (#500)
* feat: create external tools section (#493)
* feat: create external tools section
* fix: better wording
* Feature, added hide or show for media elements, mute or unmute for audio elements both effective on timeline and preview panel, with overlay icon for indicating so and state storing,
* Minor UI vertical separator between magnifier and other buttons
* fixed the AbortError issue in the AudioWaveform component. The error was occurring because of a race condition during cleanup when the component unmounts.
* feat: implement bookmarking functionality in the timeline and project storage
- Added bookmark management methods in the project store for toggling and checking bookmarks.
- Updated the timeline component to display bookmark markers and integrate bookmark actions in the context menu.
- Enhanced the storage service to handle bookmarks in project serialization and deserialization.
- Updated project types to include bookmarks as an array of numbers.
---------
Co-authored-by: Dominik K. <dominik@koch-bautechnik.de>
Co-authored-by: Maze Winther <mazewinther@gmail.com>
Diffstat (limited to 'apps/web/src/stores')
| -rw-r--r-- | apps/web/src/stores/project-store.ts | 97 | ||||
| -rw-r--r-- | apps/web/src/stores/timeline-store.ts | 19 |
2 files changed, 116 insertions, 0 deletions
diff --git a/apps/web/src/stores/project-store.ts b/apps/web/src/stores/project-store.ts index 7bb38039..69aca3ec 100644 --- a/apps/web/src/stores/project-store.ts +++ b/apps/web/src/stores/project-store.ts @@ -27,6 +27,11 @@ interface ProjectStore { options?: { backgroundColor?: string; blurIntensity?: number } ) => Promise<void>; updateProjectFps: (fps: number) => Promise<void>; + + // Bookmark methods + toggleBookmark: (time: number) => Promise<void>; + isBookmarked: (time: number) => boolean; + removeBookmark: (time: number) => Promise<void>; getFilteredAndSortedProjects: ( searchQuery: string, @@ -39,6 +44,97 @@ export const useProjectStore = create<ProjectStore>((set, get) => ({ savedProjects: [], isLoading: true, isInitialized: false, + + // Implementation of bookmark methods + toggleBookmark: async (time: number) => { + const { activeProject } = get(); + if (!activeProject) return; + + // Round time to the nearest frame + const fps = activeProject.fps || 30; + const frameTime = Math.round(time * fps) / fps; + + const bookmarks = activeProject.bookmarks || []; + let updatedBookmarks: number[]; + + // Check if already bookmarked + const bookmarkIndex = bookmarks.findIndex( + bookmark => Math.abs(bookmark - frameTime) < 0.001 + ); + + if (bookmarkIndex !== -1) { + // Remove bookmark + updatedBookmarks = bookmarks.filter((_, i) => i !== bookmarkIndex); + } else { + // Add bookmark + updatedBookmarks = [...bookmarks, frameTime].sort((a, b) => a - b); + } + + const updatedProject = { + ...activeProject, + bookmarks: updatedBookmarks, + updatedAt: new Date(), + }; + + try { + await storageService.saveProject(updatedProject); + set({ activeProject: updatedProject }); + await get().loadAllProjects(); // Refresh the list + } catch (error) { + console.error("Failed to update project bookmarks:", error); + toast.error("Failed to update bookmarks", { + description: "Please try again", + }); + } + }, + + isBookmarked: (time: number) => { + const { activeProject } = get(); + if (!activeProject || !activeProject.bookmarks) return false; + + // Round time to the nearest frame + const fps = activeProject.fps || 30; + const frameTime = Math.round(time * fps) / fps; + + return activeProject.bookmarks.some( + bookmark => Math.abs(bookmark - frameTime) < 0.001 + ); + }, + + removeBookmark: async (time: number) => { + const { activeProject } = get(); + if (!activeProject || !activeProject.bookmarks) return; + + // Round time to the nearest frame + const fps = activeProject.fps || 30; + const frameTime = Math.round(time * fps) / fps; + + const updatedBookmarks = activeProject.bookmarks.filter( + bookmark => Math.abs(bookmark - frameTime) >= 0.001 + ); + + if (updatedBookmarks.length === activeProject.bookmarks.length) { + // No bookmark found to remove + return; + } + + const updatedProject = { + ...activeProject, + bookmarks: updatedBookmarks, + updatedAt: new Date(), + }; + + try { + await storageService.saveProject(updatedProject); + set({ activeProject: updatedProject }); + await get().loadAllProjects(); // Refresh the list + } catch (error) { + console.error("Failed to update project bookmarks:", error); + toast.error("Failed to remove bookmark", { + description: "Please try again", + }); + } + }, createNewProject: async (name: string) => { const newProject: TProject = { @@ -50,6 +146,7 @@ export const useProjectStore = create<ProjectStore>((set, get) => ({ backgroundColor: "#000000", backgroundType: "color", blurIntensity: 8, + bookmarks: [], }; set({ activeProject: newProject }); diff --git a/apps/web/src/stores/timeline-store.ts b/apps/web/src/stores/timeline-store.ts index f3497be9..0f441176 100644 --- a/apps/web/src/stores/timeline-store.ts +++ b/apps/web/src/stores/timeline-store.ts @@ -126,6 +126,7 @@ interface TimelineStore { pushHistory?: boolean ) => void; toggleTrackMute: (trackId: string) => void; + toggleElementHidden: (trackId: string, elementId: string) => void; // Split operations for elements splitElement: ( @@ -868,6 +869,24 @@ export const useTimelineStore = create<TimelineStore>((set, get) => { ); }, + toggleElementHidden: (trackId, elementId) => { + get().pushHistory(); + updateTracksAndSave( + get()._tracks.map((track) => + track.id === trackId + ? { + ...track, + elements: track.elements.map((element) => + element.id === elementId + ? { ...element, hidden: !element.hidden } + : element + ), + } + : track + ) + ); + }, + updateTextElement: (trackId, elementId, updates) => { get().pushHistory(); updateTracksAndSave( |
