From af1964cf113bc9cbebc59a94631f8d08506923d2 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Mon, 1 Sep 2025 17:18:00 +0200 Subject: fix: scenes now load properly --- apps/web/src/stores/project-store.ts | 15 +- apps/web/src/stores/scene-store.ts | 486 +++++++++++++++++++--------------- apps/web/src/stores/timeline-store.ts | 9 + 3 files changed, 287 insertions(+), 223 deletions(-) (limited to 'apps/web/src/stores') diff --git a/apps/web/src/stores/project-store.ts b/apps/web/src/stores/project-store.ts index 70c11b28..0c67be9f 100644 --- a/apps/web/src/stores/project-store.ts +++ b/apps/web/src/stores/project-store.ts @@ -4,7 +4,7 @@ import { storageService } from "@/lib/storage/storage-service"; import { toast } from "sonner"; import { useMediaStore } from "./media-store"; import { useTimelineStore } from "./timeline-store"; -import { useSceneStore } from "./scene-store"; +import { createBackgroundScene, useSceneStore } from "./scene-store"; import { generateUUID } from "@/lib/utils"; import { CanvasSize, CanvasMode } from "@/types/editor"; @@ -16,6 +16,7 @@ export function createMainScene(): Scene { id: generateUUID(), name: "Main scene", isMain: true, + isBackground: false, createdAt: new Date(), updatedAt: new Date(), }; @@ -23,13 +24,15 @@ export function createMainScene(): Scene { const createDefaultProject = (name: string): TProject => { const mainScene = createMainScene(); + const backgroundScene = createBackgroundScene(); + return { id: generateUUID(), name, thumbnail: "", createdAt: new Date(), updatedAt: new Date(), - scenes: [mainScene], + scenes: [mainScene, backgroundScene], currentSceneId: mainScene.id, backgroundColor: "#000000", backgroundType: "color", @@ -226,15 +229,19 @@ export const useProjectStore = create((set, get) => ({ if (project) { set({ activeProject: project }); + let currentScene = null; if (project.scenes && project.scenes.length > 0) { sceneStore.initializeScenes({ scenes: project.scenes, currentSceneId: project.currentSceneId, }); + // Get current scene directly from project data (don't rely on store state) + currentScene = + project.scenes.find((s) => s.id === project.currentSceneId) || + project.scenes.find((s) => s.isMain) || + project.scenes[0]; } - const currentScene = sceneStore.currentScene; - await Promise.all([ mediaStore.loadProjectMedia(id), timelineStore.loadProjectTimeline({ diff --git a/apps/web/src/stores/scene-store.ts b/apps/web/src/stores/scene-store.ts index e16185e9..1b789427 100644 --- a/apps/web/src/stores/scene-store.ts +++ b/apps/web/src/stores/scene-store.ts @@ -1,219 +1,267 @@ -import { create } from "zustand"; -import { Scene } from "@/types/project"; -import { useProjectStore } from "./project-store"; -import { useTimelineStore } from "./timeline-store"; -import { storageService } from "@/lib/storage/storage-service"; -import { generateUUID } from "@/lib/utils"; - -export function getMainScene({ scenes }: { scenes: Scene[] }): Scene | null { - return scenes.find((scene) => scene.isMain) || null; -} - -interface SceneStore { - // Current scene state - currentScene: Scene | null; - scenes: Scene[]; - - // Scene management - createScene: ({ - name, - isMain, - }: { - name: string; - isMain: boolean; - }) => Promise; - renameScene: ({ - sceneId, - name, - }: { - sceneId: string; - name: string; - }) => Promise; - switchToScene: ({ sceneId }: { sceneId: string }) => Promise; - - // Scene utilities - getMainScene: () => Scene | null; - getCurrentScene: () => Scene | null; - - // Project integration - loadProjectScenes: ({ projectId }: { projectId: string }) => Promise; - initializeScenes: ({ - scenes, - currentSceneId, - }: { - scenes: Scene[]; - currentSceneId?: string; - }) => void; - clearScenes: () => void; -} - -export const useSceneStore = create((set, get) => ({ - currentScene: null, - scenes: [], - - createScene: async ({ name, isMain = false }) => { - const { scenes } = get(); - - const newScene = { - id: generateUUID(), - name, - isMain, - createdAt: new Date(), - updatedAt: new Date(), - }; - const updatedScenes = [...scenes, newScene]; - - const projectStore = useProjectStore.getState(); - const { activeProject } = projectStore; - - if (!activeProject) { - throw new Error("No active project"); - } - - const updatedProject = { - ...activeProject, - scenes: updatedScenes, - updatedAt: new Date(), - }; - - try { - await storageService.saveProject({ project: updatedProject }); - useProjectStore.setState({ activeProject: updatedProject }); - set({ scenes: updatedScenes }); - return newScene.id; - } catch (error) { - console.error("Failed to create scene:", error); - throw error; - } - }, - - renameScene: async ({ sceneId, name }: { sceneId: string; name: string }) => { - const { scenes } = get(); - const updatedScenes = scenes.map((scene) => - scene.id === sceneId ? { ...scene, name, updatedAt: new Date() } : scene - ); - - // Update project - const projectStore = useProjectStore.getState(); - const { activeProject } = projectStore; - - if (!activeProject) { - throw new Error("No active project"); - } - - const updatedProject = { - ...activeProject, - scenes: updatedScenes, - updatedAt: new Date(), - }; - - try { - await storageService.saveProject({ project: updatedProject }); - useProjectStore.setState({ activeProject: updatedProject }); - set({ - scenes: updatedScenes, - currentScene: updatedScenes.find((s) => s.id === sceneId) || null, - }); - } catch (error) { - console.error("Failed to rename scene:", error); - throw error; - } - }, - - switchToScene: async ({ sceneId }: { sceneId: string }) => { - const { scenes } = get(); - const targetScene = scenes.find((s) => s.id === sceneId); - - if (!targetScene) { - throw new Error("Scene not found"); - } - - const timelineStore = useTimelineStore.getState(); - const projectStore = useProjectStore.getState(); - const { activeProject } = projectStore; - const { currentScene } = get(); - - if (activeProject && currentScene) { - await timelineStore.saveProjectTimeline({ - projectId: activeProject.id, - sceneId: currentScene.id, - }); - } - - if (activeProject) { - await timelineStore.loadProjectTimeline({ - projectId: activeProject.id, - sceneId, - }); - - const updatedProject = { - ...activeProject, - currentSceneId: sceneId, - updatedAt: new Date(), - }; - - await storageService.saveProject({ project: updatedProject }); - useProjectStore.setState({ activeProject: updatedProject }); - } - - set({ currentScene: targetScene }); - }, - - getMainScene: () => { - const { scenes } = get(); - return scenes.find((scene) => scene.isMain) || null; - }, - - getCurrentScene: () => { - return get().currentScene; - }, - - loadProjectScenes: async ({ projectId }: { projectId: string }) => { - try { - const project = await storageService.loadProject({ id: projectId }); - if (project?.scenes) { - const ensuredScenes = project.scenes.map((scene) => ({ - ...scene, - isMain: scene.isMain || false, - })); - const currentScene = - ensuredScenes.find((s) => s.id === project.currentSceneId) || - ensuredScenes[0]; - - set({ - scenes: ensuredScenes, - currentScene, - }); - } - } catch (error) { - console.error("Failed to load project scenes:", error); - set({ scenes: [], currentScene: null }); - } - }, - - initializeScenes: ({ - scenes, - currentSceneId, - }: { - scenes: Scene[]; - currentSceneId?: string; - }) => { - const currentScene = currentSceneId - ? scenes.find((s) => s.id === currentSceneId) - : null; - - const fallbackScene = getMainScene({ scenes }); - - set({ - scenes, - currentScene: currentScene || fallbackScene, - }); - }, - - clearScenes: () => { - set({ - scenes: [], - currentScene: null, - }); - }, -})); +import { create } from "zustand"; +import { Scene } from "@/types/project"; +import { useProjectStore } from "./project-store"; +import { useTimelineStore } from "./timeline-store"; +import { storageService } from "@/lib/storage/storage-service"; +import { generateUUID } from "@/lib/utils"; + +export function getMainScene({ scenes }: { scenes: Scene[] }): Scene | null { + return scenes.find((scene) => scene.isMain) || null; +} + +export function getBackgroundScene({ + scenes, +}: { + scenes: Scene[]; +}): Scene | null { + return scenes.find((scene) => scene.isBackground) || null; +} + +export function createBackgroundScene(): Scene { + return { + id: generateUUID(), + name: "Background", + isMain: false, + isBackground: true, + createdAt: new Date(), + updatedAt: new Date(), + }; +} + +function ensureBackgroundScene({ scenes }: { scenes: Scene[] }): Scene[] { + const hasBackground = scenes.some((scene) => scene.isBackground); + if (!hasBackground) { + return [...scenes, createBackgroundScene()]; + } + return scenes; +} + +interface SceneStore { + // Current scene state + currentScene: Scene | null; + scenes: Scene[]; + + // Scene management + createScene: ({ + name, + isMain, + }: { + name: string; + isMain: boolean; + }) => Promise; + renameScene: ({ + sceneId, + name, + }: { + sceneId: string; + name: string; + }) => Promise; + switchToScene: ({ sceneId }: { sceneId: string }) => Promise; + + // Scene utilities + getMainScene: () => Scene | null; + getCurrentScene: () => Scene | null; + + // Project integration + loadProjectScenes: ({ projectId }: { projectId: string }) => Promise; + initializeScenes: ({ + scenes, + currentSceneId, + }: { + scenes: Scene[]; + currentSceneId?: string; + }) => void; + clearScenes: () => void; +} + +export const useSceneStore = create((set, get) => ({ + currentScene: null, + scenes: [], + + createScene: async ({ name, isMain = false }) => { + const { scenes } = get(); + + const newScene = { + id: generateUUID(), + name, + isMain, + isBackground: false, + createdAt: new Date(), + updatedAt: new Date(), + }; + const updatedScenes = [...scenes, newScene]; + + const projectStore = useProjectStore.getState(); + const { activeProject } = projectStore; + + if (!activeProject) { + throw new Error("No active project"); + } + + const updatedProject = { + ...activeProject, + scenes: updatedScenes, + updatedAt: new Date(), + }; + + try { + await storageService.saveProject({ project: updatedProject }); + useProjectStore.setState({ activeProject: updatedProject }); + set({ scenes: updatedScenes }); + return newScene.id; + } catch (error) { + console.error("Failed to create scene:", error); + throw error; + } + }, + + renameScene: async ({ sceneId, name }: { sceneId: string; name: string }) => { + const { scenes } = get(); + const updatedScenes = scenes.map((scene) => + scene.id === sceneId ? { ...scene, name, updatedAt: new Date() } : scene + ); + + // Update project + const projectStore = useProjectStore.getState(); + const { activeProject } = projectStore; + + if (!activeProject) { + throw new Error("No active project"); + } + + const updatedProject = { + ...activeProject, + scenes: updatedScenes, + updatedAt: new Date(), + }; + + try { + await storageService.saveProject({ project: updatedProject }); + useProjectStore.setState({ activeProject: updatedProject }); + set({ + scenes: updatedScenes, + currentScene: updatedScenes.find((s) => s.id === sceneId) || null, + }); + } catch (error) { + console.error("Failed to rename scene:", error); + throw error; + } + }, + + switchToScene: async ({ sceneId }: { sceneId: string }) => { + const { scenes } = get(); + const targetScene = scenes.find((s) => s.id === sceneId); + + if (!targetScene) { + throw new Error("Scene not found"); + } + + const timelineStore = useTimelineStore.getState(); + const projectStore = useProjectStore.getState(); + const { activeProject } = projectStore; + const { currentScene } = get(); + + if (activeProject && currentScene) { + await timelineStore.saveProjectTimeline({ + projectId: activeProject.id, + sceneId: currentScene.id, + }); + } + + if (activeProject) { + await timelineStore.loadProjectTimeline({ + projectId: activeProject.id, + sceneId, + }); + + const updatedProject = { + ...activeProject, + currentSceneId: sceneId, + updatedAt: new Date(), + }; + + await storageService.saveProject({ project: updatedProject }); + useProjectStore.setState({ activeProject: updatedProject }); + } + + set({ currentScene: targetScene }); + }, + + getMainScene: () => { + const { scenes } = get(); + return scenes.find((scene) => scene.isMain) || null; + }, + + getCurrentScene: () => { + return get().currentScene; + }, + + loadProjectScenes: async ({ projectId }: { projectId: string }) => { + try { + const project = await storageService.loadProject({ id: projectId }); + if (project?.scenes) { + const ensuredScenes = project.scenes.map((scene) => ({ + ...scene, + isMain: scene.isMain || false, + })); + const currentScene = + ensuredScenes.find((s) => s.id === project.currentSceneId) || + ensuredScenes[0]; + + set({ + scenes: ensuredScenes, + currentScene, + }); + } + } catch (error) { + console.error("Failed to load project scenes:", error); + set({ scenes: [], currentScene: null }); + } + }, + + initializeScenes: ({ + scenes, + currentSceneId, + }: { + scenes: Scene[]; + currentSceneId?: string; + }) => { + const ensuredScenes = ensureBackgroundScene({ scenes }); + const currentScene = currentSceneId + ? ensuredScenes.find((s) => s.id === currentSceneId) + : null; + + const fallbackScene = getMainScene({ scenes: ensuredScenes }); + + set({ + scenes: ensuredScenes, + currentScene: currentScene || fallbackScene, + }); + + if (ensuredScenes.length > scenes.length) { + const projectStore = useProjectStore.getState(); + const { activeProject } = projectStore; + + if (activeProject) { + const updatedProject = { + ...activeProject, + scenes: ensuredScenes, + updatedAt: new Date(), + }; + + storageService.saveProject({ project: updatedProject }).then(() => { + useProjectStore.setState({ activeProject: updatedProject }); + }).catch(error => { + console.error("Failed to save project with background scene:", error); + }); + } + } + }, + + clearScenes: () => { + set({ + scenes: [], + currentScene: null, + }); + }, +})); diff --git a/apps/web/src/stores/timeline-store.ts b/apps/web/src/stores/timeline-store.ts index 8caa71a7..24b6eca3 100644 --- a/apps/web/src/stores/timeline-store.ts +++ b/apps/web/src/stores/timeline-store.ts @@ -260,6 +260,15 @@ export const useTimelineStore = create((set, get) => { } catch (error) { console.error("Failed to auto-save timeline:", error); } + } else { + console.warn( + "Auto-save skipped - missing activeProject or currentScene:", + { + hasProject: !!activeProject, + hasScene: !!currentScene, + sceneName: currentScene?.name, + } + ); } }; -- cgit v1.3.1