diff options
Diffstat (limited to 'apps/web/src/stores')
| -rw-r--r-- | apps/web/src/stores/media-store.ts | 62 | ||||
| -rw-r--r-- | apps/web/src/stores/project-store.ts | 186 |
2 files changed, 228 insertions, 20 deletions
diff --git a/apps/web/src/stores/media-store.ts b/apps/web/src/stores/media-store.ts index bdb4d52a..bf465811 100644 --- a/apps/web/src/stores/media-store.ts +++ b/apps/web/src/stores/media-store.ts @@ -1,4 +1,5 @@ import { create } from "zustand";
+import { storageService } from "@/lib/storage/storage-service";
export interface MediaItem {
id: string;
@@ -13,11 +14,13 @@ export interface MediaItem { interface MediaStore {
mediaItems: MediaItem[];
+ isLoading: boolean;
// Actions
- addMediaItem: (item: Omit<MediaItem, "id">) => void;
- removeMediaItem: (id: string) => void;
- clearAllMedia: () => void;
+ addMediaItem: (item: Omit<MediaItem, "id">) => Promise<void>;
+ removeMediaItem: (id: string) => Promise<void>;
+ loadAllMedia: () => Promise<void>;
+ clearAllMedia: () => Promise<void>;
}
// Helper function to determine file type
@@ -126,18 +129,32 @@ export const getMediaDuration = (file: File): Promise<number> => { export const useMediaStore = create<MediaStore>((set, get) => ({
mediaItems: [],
+ isLoading: false,
- addMediaItem: (item) => {
+ addMediaItem: async (item) => {
const newItem: MediaItem = {
...item,
id: crypto.randomUUID(),
};
+
+ // Add to local state immediately for UI responsiveness
set((state) => ({
mediaItems: [...state.mediaItems, newItem],
}));
+
+ // Save to persistent storage in background
+ try {
+ await storageService.saveMediaItem(newItem);
+ } catch (error) {
+ console.error("Failed to save media item:", error);
+ // Remove from local state if save failed
+ set((state) => ({
+ mediaItems: state.mediaItems.filter((item) => item.id !== newItem.id),
+ }));
+ }
},
- removeMediaItem: (id) => {
+ removeMediaItem: async (id) => {
const state = get();
const item = state.mediaItems.find((item) => item.id === id);
@@ -149,12 +166,34 @@ export const useMediaStore = create<MediaStore>((set, get) => ({ }
}
+ // Remove from local state immediately
set((state) => ({
mediaItems: state.mediaItems.filter((item) => item.id !== id),
}));
+
+ // Remove from persistent storage
+ try {
+ await storageService.deleteMediaItem(id);
+ } catch (error) {
+ console.error("Failed to delete media item:", error);
+ // Could re-add to local state here if needed
+ }
},
- clearAllMedia: () => {
+ loadAllMedia: async () => {
+ set({ isLoading: true });
+
+ try {
+ const mediaItems = await storageService.loadAllMediaItems();
+ set({ mediaItems });
+ } catch (error) {
+ console.error("Failed to load media items:", error);
+ } finally {
+ set({ isLoading: false });
+ }
+ },
+
+ clearAllMedia: async () => {
const state = get();
// Cleanup all object URLs
@@ -165,6 +204,17 @@ export const useMediaStore = create<MediaStore>((set, get) => ({ }
});
+ // Clear local state
set({ mediaItems: [] });
+
+ // Clear persistent storage
+ try {
+ const mediaIds = state.mediaItems.map((item) => item.id);
+ await Promise.all(
+ mediaIds.map((id) => storageService.deleteMediaItem(id))
+ );
+ } catch (error) {
+ console.error("Failed to clear media items from storage:", error);
+ }
},
}));
diff --git a/apps/web/src/stores/project-store.ts b/apps/web/src/stores/project-store.ts index 43113fc1..4fff83c2 100644 --- a/apps/web/src/stores/project-store.ts +++ b/apps/web/src/stores/project-store.ts @@ -1,19 +1,32 @@ import { TProject } from "@/types/project";
import { create } from "zustand";
+import { storageService } from "@/lib/storage/storage-service";
+import { toast } from "sonner";
interface ProjectStore {
activeProject: TProject | null;
+ savedProjects: TProject[];
+ isLoading: boolean;
+ isInitialized: boolean;
// Actions
- createNewProject: (name: string) => void;
+ createNewProject: (name: string) => Promise<string>;
+ loadProject: (id: string) => Promise<void>;
+ saveCurrentProject: () => Promise<void>;
+ loadAllProjects: () => Promise<void>;
+ deleteProject: (id: string) => Promise<void>;
closeProject: () => void;
- updateProjectName: (name: string) => void;
+ renameProject: (projectId: string, name: string) => Promise<void>;
+ duplicateProject: (projectId: string) => Promise<string>;
}
-export const useProjectStore = create<ProjectStore>((set) => ({
+export const useProjectStore = create<ProjectStore>((set, get) => ({
activeProject: null,
+ savedProjects: [],
+ isLoading: true,
+ isInitialized: false,
- createNewProject: (name: string) => {
+ createNewProject: async (name: string) => {
const newProject: TProject = {
id: crypto.randomUUID(),
name,
@@ -21,22 +34,167 @@ export const useProjectStore = create<ProjectStore>((set) => ({ createdAt: new Date(),
updatedAt: new Date(),
};
+
set({ activeProject: newProject });
+
+ try {
+ await storageService.saveProject(newProject);
+ // Reload all projects to update the list
+ await get().loadAllProjects();
+ return newProject.id;
+ } catch (error) {
+ toast.error("Failed to save new project");
+ throw error;
+ }
+ },
+
+ loadProject: async (id: string) => {
+ if (!get().isInitialized) {
+ set({ isLoading: true });
+ }
+
+ try {
+ const project = await storageService.loadProject(id);
+ if (project) {
+ set({ activeProject: project });
+ }
+ } catch (error) {
+ console.error("Failed to load project:", error);
+ } finally {
+ set({ isLoading: false });
+ }
+ },
+
+ saveCurrentProject: async () => {
+ const { activeProject } = get();
+ if (!activeProject) return;
+
+ try {
+ await storageService.saveProject(activeProject);
+ await get().loadAllProjects(); // Refresh the list
+ } catch (error) {
+ console.error("Failed to save project:", error);
+ }
+ },
+
+ loadAllProjects: async () => {
+ if (!get().isInitialized) {
+ set({ isLoading: true });
+ }
+
+ try {
+ const projects = await storageService.loadAllProjects();
+ set({ savedProjects: projects });
+ } catch (error) {
+ console.error("Failed to load projects:", error);
+ } finally {
+ set({ isLoading: false, isInitialized: true });
+ }
+ },
+
+ deleteProject: async (id: string) => {
+ try {
+ await storageService.deleteProject(id);
+ await get().loadAllProjects(); // Refresh the list
+
+ // If we deleted the active project, close it
+ const { activeProject } = get();
+ if (activeProject?.id === id) {
+ set({ activeProject: null });
+ }
+ } catch (error) {
+ console.error("Failed to delete project:", error);
+ }
},
closeProject: () => {
set({ activeProject: null });
},
- updateProjectName: (name: string) => {
- set((state) => ({
- activeProject: state.activeProject
- ? {
- ...state.activeProject,
- name,
- updatedAt: new Date(),
- }
- : null,
- }));
+ renameProject: async (id: string, name: string) => {
+ const { savedProjects } = get();
+
+ // Find the project to rename
+ const projectToRename = savedProjects.find((p) => p.id === id);
+ if (!projectToRename) {
+ toast.error("Project not found", {
+ description: "Please try again",
+ });
+ return;
+ }
+
+ const updatedProject = {
+ ...projectToRename,
+ name,
+ updatedAt: new Date(),
+ };
+
+ try {
+ // Save to storage
+ await storageService.saveProject(updatedProject);
+
+ await get().loadAllProjects();
+
+ // Update activeProject if it's the same project
+ const { activeProject } = get();
+ if (activeProject?.id === id) {
+ set({ activeProject: updatedProject });
+ }
+ } catch (error) {
+ console.error("Failed to rename project:", error);
+ toast.error("Failed to rename project", {
+ description:
+ error instanceof Error ? error.message : "Please try again",
+ });
+ }
+ },
+
+ duplicateProject: async (projectId: string) => {
+ try {
+ const project = await storageService.loadProject(projectId);
+ if (!project) {
+ toast.error("Project not found", {
+ description: "Please try again",
+ });
+ throw new Error("Project not found");
+ }
+
+ const { savedProjects } = get();
+
+ // Extract the base name (remove any existing numbering)
+ const numberMatch = project.name.match(/^\((\d+)\)\s+(.+)$/);
+ const baseName = numberMatch ? numberMatch[2] : project.name;
+ const existingNumbers: number[] = [];
+
+ // Check for pattern "(number) baseName" in existing projects
+ savedProjects.forEach((p) => {
+ const match = p.name.match(/^\((\d+)\)\s+(.+)$/);
+ if (match && match[2] === baseName) {
+ existingNumbers.push(parseInt(match[1], 10));
+ }
+ });
+
+ const nextNumber =
+ existingNumbers.length > 0 ? Math.max(...existingNumbers) + 1 : 1;
+
+ const newProject: TProject = {
+ id: crypto.randomUUID(),
+ name: `(${nextNumber}) ${baseName}`,
+ thumbnail: project.thumbnail,
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ };
+
+ await storageService.saveProject(newProject);
+ await get().loadAllProjects();
+ return newProject.id;
+ } catch (error) {
+ console.error("Failed to duplicate project:", error);
+ toast.error("Failed to duplicate project", {
+ description:
+ error instanceof Error ? error.message : "Please try again",
+ });
+ throw error;
+ }
},
}));
|
