From a1d2fbd96bb6713c3172198d71e94274af409aee Mon Sep 17 00:00:00 2001 From: naoNao89 <90588855+naoNao89@users.noreply.github.com> Date: Thu, 31 Jul 2025 05:29:42 +0700 Subject: [Editor] Prevent excessive project creation from invalid routes Race conditions in useEffect hook caused navigation to invalid project IDs to create multiple 'Untitled Project' entries. Users reported accumulating 885+ projects due to this bug. This change adds global invalid project ID tracking across component remounts and prevents race conditions through: - Global invalidProjectIds Set in project store with tracking functions - isInitializingRef to prevent duplicate initialization attempts - Proper useEffect cleanup with cancellation tokens - Specific error handling for 'not found' vs recoverable errors - Removed problematic activeProject?.id dependency Testing: - Add comprehensive test suite (15 tests) with Vitest + React Testing Library - Include integration tests demonstrating bug and validating fix - Update CI pipeline to run automated tests Fixes #182 --- apps/web/src/stores/project-store.ts | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'apps/web/src/stores/project-store.ts') diff --git a/apps/web/src/stores/project-store.ts b/apps/web/src/stores/project-store.ts index 7bb38039..82f449de 100644 --- a/apps/web/src/stores/project-store.ts +++ b/apps/web/src/stores/project-store.ts @@ -11,6 +11,7 @@ interface ProjectStore { savedProjects: TProject[]; isLoading: boolean; isInitialized: boolean; + invalidProjectIds?: Set; // Actions createNewProject: (name: string) => Promise; @@ -32,6 +33,11 @@ interface ProjectStore { searchQuery: string, sortOption: string ) => TProject[]; + + // Global invalid project ID tracking + isInvalidProjectId: (id: string) => boolean; + markProjectIdAsInvalid: (id: string) => void; + clearInvalidProjectIds: () => void; } export const useProjectStore = create((set, get) => ({ @@ -39,6 +45,7 @@ export const useProjectStore = create((set, get) => ({ savedProjects: [], isLoading: true, isInitialized: false, + invalidProjectIds: new Set(), createNewProject: async (name: string) => { const newProject: TProject = { @@ -52,14 +59,25 @@ export const useProjectStore = create((set, get) => ({ blurIntensity: 8, }; + // DEBUG: Log project creation + console.log("🆕 Creating new project:", { + id: newProject.id, + name: newProject.name, + timestamp: new Date().toISOString() + }); + set({ activeProject: newProject }); + console.log("📝 Set activeProject to new project"); try { await storageService.saveProject(newProject); + console.log("💾 Project saved to storage"); // Reload all projects to update the list await get().loadAllProjects(); + console.log("🔄 All projects reloaded"); return newProject.id; } catch (error) { + console.error("❌ Failed to save new project:", error); toast.error("Failed to save new project"); throw error; } @@ -357,4 +375,20 @@ export const useProjectStore = create((set, get) => ({ return sortedProjects; }, + + // Global invalid project ID tracking implementation + isInvalidProjectId: (id: string) => { + const invalidIds = get().invalidProjectIds || new Set(); + return invalidIds.has(id); + }, + + markProjectIdAsInvalid: (id: string) => { + set((state) => ({ + invalidProjectIds: new Set([...(state.invalidProjectIds || new Set()), id]) + })); + }, + + clearInvalidProjectIds: () => { + set({ invalidProjectIds: new Set() }); + }, })); -- cgit v1.3.1