diff options
| author | Maze Winther <mazewinther@gmail.com> | 2025-09-01 17:18:00 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2025-09-01 17:18:00 +0200 |
| commit | af1964cf113bc9cbebc59a94631f8d08506923d2 (patch) | |
| tree | 4e40979eea683adbec7f377ed53e1cd7e1e130c5 /apps/web/src | |
| parent | e2bd8d1e093715da6d5d09b6dcbac6b68262f6ad (diff) | |
| download | opencut-af1964cf113bc9cbebc59a94631f8d08506923d2.tar.gz | |
fix: scenes now load properly
Diffstat (limited to 'apps/web/src')
| -rw-r--r-- | apps/web/src/components/providers/migrators/scenes-migrator.tsx | 358 | ||||
| -rw-r--r-- | apps/web/src/lib/storage/storage-service.ts | 5 | ||||
| -rw-r--r-- | apps/web/src/stores/project-store.ts | 15 | ||||
| -rw-r--r-- | apps/web/src/stores/scene-store.ts | 486 | ||||
| -rw-r--r-- | apps/web/src/stores/timeline-store.ts | 9 | ||||
| -rw-r--r-- | apps/web/src/types/project.ts | 1 |
6 files changed, 479 insertions, 395 deletions
diff --git a/apps/web/src/components/providers/migrators/scenes-migrator.tsx b/apps/web/src/components/providers/migrators/scenes-migrator.tsx index 3a21faa4..521e24af 100644 --- a/apps/web/src/components/providers/migrators/scenes-migrator.tsx +++ b/apps/web/src/components/providers/migrators/scenes-migrator.tsx @@ -1,171 +1,187 @@ -"use client";
-
-import { useEffect, useState } from "react";
-import { usePathname } from "next/navigation";
-import { storageService } from "@/lib/storage/storage-service";
-import { TProject, Scene } from "@/types/project";
-import { generateUUID } from "@/lib/utils";
-import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogHeader,
- DialogTitle,
-} from "@/components/ui/dialog";
-import { Progress } from "@/components/ui/progress";
-import { createMainScene } from "@/stores/project-store";
-
-interface MigrationProgress {
- current: number;
- total: number;
- currentProjectName: string;
-}
-
-export function ScenesMigrator({ children }: { children: React.ReactNode }) {
- const pathname = usePathname();
- const [isMigrating, setIsMigrating] = useState(false);
- const [progress, setProgress] = useState<MigrationProgress>({
- current: 0,
- total: 0,
- currentProjectName: "",
- });
-
- const shouldCheckMigration =
- pathname.startsWith("/editor") || pathname.startsWith("/projects");
-
- useEffect(() => {
- if (!shouldCheckMigration) return;
-
- checkAndMigrateProjects();
- }, [shouldCheckMigration]);
-
- const checkAndMigrateProjects = async () => {
- try {
- const projects = await storageService.loadAllProjects();
- const legacyProjects = projects.filter(
- (project) => !project.scenes || project.scenes.length === 0
- );
-
- if (legacyProjects.length === 0) {
- // No migration needed
- return;
- }
-
- setIsMigrating(true);
- setProgress({
- current: 0,
- total: legacyProjects.length,
- currentProjectName: "",
- });
-
- // Migrate each legacy project
- for (let i = 0; i < legacyProjects.length; i++) {
- const project = legacyProjects[i];
-
- setProgress({
- current: i,
- total: legacyProjects.length,
- currentProjectName: project.name,
- });
-
- await migrateLegacyProject(project);
- }
-
- setProgress({
- current: legacyProjects.length,
- total: legacyProjects.length,
- currentProjectName: "Complete!",
- });
-
- setTimeout(() => {
- setIsMigrating(false);
- }, 1000);
- } catch (error) {
- console.error("Migration failed:", error);
- setIsMigrating(false);
- }
- };
-
- const migrateLegacyProject = async (project: TProject) => {
- try {
- const mainScene = createMainScene();
-
- const migratedProject: TProject = {
- ...project,
- scenes: [mainScene],
- currentSceneId: mainScene.id,
- updatedAt: new Date(),
- };
-
- // Load existing timeline data (legacy format)
- const legacyTimeline = await storageService.loadTimeline({
- projectId: project.id,
- });
-
- await storageService.saveProject({ project: migratedProject });
-
- // If timeline data, migrate it to the main scene
- if (legacyTimeline && legacyTimeline.length > 0) {
- await storageService.saveTimeline({
- projectId: project.id,
- tracks: legacyTimeline,
- sceneId: mainScene.id,
- });
- }
-
- // Clean up legacy timeline storage
- await storageService.deleteProjectTimeline({ projectId: project.id });
- } catch (error) {
- console.error(`Failed to migrate project ${project.name}:`, error);
- throw error;
- }
- };
-
- if (!shouldCheckMigration) {
- return children;
- }
-
- if (isMigrating) {
- const progressPercent =
- progress.total > 0 ? (progress.current / progress.total) * 100 : 0;
-
- return (
- <Dialog open={true}>
- <DialogContent
- className="sm:max-w-md"
- onPointerDownOutside={(e) => e.preventDefault()}
- >
- <DialogHeader>
- <DialogTitle>Updating Projects</DialogTitle>
- <DialogDescription>
- We're adding scene support to your projects. This will only take a
- moment.
- </DialogDescription>
- </DialogHeader>
-
- <div className="space-y-4">
- <div className="space-y-2">
- <div className="flex justify-between text-sm">
- <span>Progress</span>
- <span>
- {progress.current} of {progress.total}
- </span>
- </div>
- <Progress value={progressPercent} className="w-full" />
- </div>
-
- {progress.currentProjectName && (
- <div className="text-sm text-muted-foreground">
- {progress.current < progress.total
- ? `Updating: ${progress.currentProjectName}`
- : progress.currentProjectName}
- </div>
- )}
- </div>
- </DialogContent>
- </Dialog>
- );
- }
-
- return children;
-}
+"use client"; + +import { useEffect, useState } from "react"; +import { usePathname } from "next/navigation"; +import { storageService } from "@/lib/storage/storage-service"; +import { TProject, Scene } from "@/types/project"; +import { generateUUID } from "@/lib/utils"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Progress } from "@/components/ui/progress"; +import { createMainScene } from "@/stores/project-store"; + +interface MigrationProgress { + current: number; + total: number; + currentProjectName: string; +} + +export function ScenesMigrator({ children }: { children: React.ReactNode }) { + const pathname = usePathname(); + const [isMigrating, setIsMigrating] = useState(false); + const [progress, setProgress] = useState<MigrationProgress>({ + current: 0, + total: 0, + currentProjectName: "", + }); + + const shouldCheckMigration = + pathname.startsWith("/editor") || pathname.startsWith("/projects"); + + useEffect(() => { + if (!shouldCheckMigration) return; + + checkAndMigrateProjects(); + }, [shouldCheckMigration]); + + const checkAndMigrateProjects = async () => { + try { + const projects = await storageService.loadAllProjects(); + const legacyProjects = projects.filter( + (project) => !project.scenes || project.scenes.length === 0 + ); + + if (legacyProjects.length === 0) { + // No migration needed + return; + } + + setIsMigrating(true); + setProgress({ + current: 0, + total: legacyProjects.length, + currentProjectName: "", + }); + + // Migrate each legacy project + for (let i = 0; i < legacyProjects.length; i++) { + const project = legacyProjects[i]; + + setProgress({ + current: i, + total: legacyProjects.length, + currentProjectName: project.name, + }); + + await migrateLegacyProject(project); + } + + setProgress({ + current: legacyProjects.length, + total: legacyProjects.length, + currentProjectName: "Complete!", + }); + + setTimeout(() => { + setIsMigrating(false); + }, 1000); + } catch (error) { + console.error("Migration failed:", error); + setIsMigrating(false); + } + }; + + const migrateLegacyProject = async (project: TProject) => { + try { + const mainScene: Scene = { + id: generateUUID(), + name: "Main Scene", + isMain: true, + isBackground: false, + createdAt: new Date(), + updatedAt: new Date(), + }; + + const backgroundScene: Scene = { + id: generateUUID(), + name: "Background", + isMain: false, + isBackground: true, + createdAt: new Date(), + updatedAt: new Date(), + }; + + const migratedProject: TProject = { + ...project, + scenes: [mainScene, backgroundScene], + currentSceneId: mainScene.id, + updatedAt: new Date(), + }; + + // Load existing timeline data (legacy format) + const legacyTimeline = await storageService.loadTimeline({ + projectId: project.id, + }); + + await storageService.saveProject({ project: migratedProject }); + + // If timeline data, migrate it to the main scene + if (legacyTimeline && legacyTimeline.length > 0) { + await storageService.saveTimeline({ + projectId: project.id, + tracks: legacyTimeline, + sceneId: mainScene.id, + }); + } + + // Clean up legacy timeline storage + await storageService.deleteProjectTimeline({ projectId: project.id }); + } catch (error) { + console.error(`Failed to migrate project ${project.name}:`, error); + throw error; + } + }; + + if (!shouldCheckMigration) { + return children; + } + + if (isMigrating) { + const progressPercent = + progress.total > 0 ? (progress.current / progress.total) * 100 : 0; + + return ( + <Dialog open={true}> + <DialogContent + className="sm:max-w-md" + onPointerDownOutside={(e) => e.preventDefault()} + > + <DialogHeader> + <DialogTitle>Updating Projects</DialogTitle> + <DialogDescription> + We're adding scene support to your projects. This will only take a + moment. + </DialogDescription> + </DialogHeader> + + <div className="space-y-4"> + <div className="space-y-2"> + <div className="flex justify-between text-sm"> + <span>Progress</span> + <span> + {progress.current} of {progress.total} + </span> + </div> + <Progress value={progressPercent} className="w-full" /> + </div> + + {progress.currentProjectName && ( + <div className="text-sm text-muted-foreground"> + {progress.current < progress.total + ? `Updating: ${progress.currentProjectName}` + : progress.currentProjectName} + </div> + )} + </div> + </DialogContent> + </Dialog> + ); + } + + return children; +} diff --git a/apps/web/src/lib/storage/storage-service.ts b/apps/web/src/lib/storage/storage-service.ts index 80f10d3d..112d09b6 100644 --- a/apps/web/src/lib/storage/storage-service.ts +++ b/apps/web/src/lib/storage/storage-service.ts @@ -78,6 +78,7 @@ class StorageService { id: scene.id, name: scene.name, isMain: scene.isMain, + isBackground: scene.isBackground, createdAt: scene.createdAt.toISOString(), updatedAt: scene.updatedAt.toISOString(), })); @@ -113,12 +114,13 @@ class StorageService { id: scene.id, name: scene.name, isMain: scene.isMain, + isBackground: scene.isBackground || false, // Default for legacy scenes createdAt: new Date(scene.createdAt), updatedAt: new Date(scene.updatedAt), })) || []; // Convert back to TProject format - return { + const project = { id: serializedProject.id, name: serializedProject.name, thumbnail: serializedProject.thumbnail, @@ -134,6 +136,7 @@ class StorageService { canvasSize: serializedProject.canvasSize, canvasMode: serializedProject.canvasMode, }; + return project; } async loadAllProjects(): Promise<TProject[]> { 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<ProjectStore>((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<string>;
- renameScene: ({
- sceneId,
- name,
- }: {
- sceneId: string;
- name: string;
- }) => Promise<void>;
- switchToScene: ({ sceneId }: { sceneId: string }) => Promise<void>;
-
- // Scene utilities
- getMainScene: () => Scene | null;
- getCurrentScene: () => Scene | null;
-
- // Project integration
- loadProjectScenes: ({ projectId }: { projectId: string }) => Promise<void>;
- initializeScenes: ({
- scenes,
- currentSceneId,
- }: {
- scenes: Scene[];
- currentSceneId?: string;
- }) => void;
- clearScenes: () => void;
-}
-
-export const useSceneStore = create<SceneStore>((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<string>; + renameScene: ({ + sceneId, + name, + }: { + sceneId: string; + name: string; + }) => Promise<void>; + switchToScene: ({ sceneId }: { sceneId: string }) => Promise<void>; + + // Scene utilities + getMainScene: () => Scene | null; + getCurrentScene: () => Scene | null; + + // Project integration + loadProjectScenes: ({ projectId }: { projectId: string }) => Promise<void>; + initializeScenes: ({ + scenes, + currentSceneId, + }: { + scenes: Scene[]; + currentSceneId?: string; + }) => void; + clearScenes: () => void; +} + +export const useSceneStore = create<SceneStore>((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<TimelineStore>((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, + } + ); } }; diff --git a/apps/web/src/types/project.ts b/apps/web/src/types/project.ts index f182653d..7fa53c69 100644 --- a/apps/web/src/types/project.ts +++ b/apps/web/src/types/project.ts @@ -6,6 +6,7 @@ export interface Scene { id: string; name: string; isMain: boolean; + isBackground: boolean; createdAt: Date; updatedAt: Date; } |
