This commit is contained in:
Maze Winther
2025-07-31 13:10:37 +02:00
parent f15508df62
commit 5f4f3cc6c4
2 changed files with 117 additions and 9 deletions
+91 -9
View File
@@ -32,39 +32,121 @@ export default function Editor() {
setPropertiesPanel, setPropertiesPanel,
} = usePanelStore(); } = usePanelStore();
const { activeProject, loadProject, createNewProject } = useProjectStore(); const {
activeProject,
loadProject,
createNewProject,
isInvalidProjectId,
markProjectIdAsInvalid,
} = useProjectStore();
const params = useParams(); const params = useParams();
const router = useRouter(); const router = useRouter();
const projectId = params.project_id as string; const projectId = params.project_id as string;
const handledProjectIds = useRef<Set<string>>(new Set()); const handledProjectIds = useRef<Set<string>>(new Set());
const isInitializingRef = useRef<boolean>(false);
usePlaybackControls(); usePlaybackControls();
useEffect(() => { useEffect(() => {
const initProject = async () => { let isCancelled = false;
if (!projectId) return;
const initProject = async () => {
if (!projectId) {
return;
}
// Prevent duplicate initialization
if (isInitializingRef.current) {
return;
}
// Check if project is already loaded
if (activeProject?.id === projectId) { if (activeProject?.id === projectId) {
return; return;
} }
// Check global invalid tracking first (most important for preventing duplicates)
if (isInvalidProjectId(projectId)) {
return;
}
// Check if we've already handled this project ID locally
if (handledProjectIds.current.has(projectId)) { if (handledProjectIds.current.has(projectId)) {
return; return;
} }
// Mark as initializing to prevent race conditions
isInitializingRef.current = true;
handledProjectIds.current.add(projectId);
try { try {
await loadProject(projectId); await loadProject(projectId);
} catch (error) {
handledProjectIds.current.add(projectId);
const newProjectId = await createNewProject("Untitled Project"); // Check if component was unmounted during async operation
router.replace(`/editor/${newProjectId}`); if (isCancelled) {
return; return;
}
// Project loaded successfully
isInitializingRef.current = false;
} catch (error) {
// Check if component was unmounted during async operation
if (isCancelled) {
return;
}
// More specific error handling - only create new project for actual "not found" errors
const isProjectNotFound =
error instanceof Error &&
(error.message.includes("not found") ||
error.message.includes("does not exist") ||
error.message.includes("Project not found"));
if (isProjectNotFound) {
// Mark this project ID as invalid globally BEFORE creating project
markProjectIdAsInvalid(projectId);
try {
const newProjectId = await createNewProject("Untitled Project");
// Check again if component was unmounted
if (isCancelled) {
return;
}
router.replace(`/editor/${newProjectId}`);
} catch (createError) {
console.error("Failed to create new project:", createError);
}
} else {
// For other errors (storage issues, corruption, etc.), don't create new project
console.error(
"Project loading failed with recoverable error:",
error
);
// Remove from handled set so user can retry
handledProjectIds.current.delete(projectId);
}
isInitializingRef.current = false;
} }
}; };
initProject(); initProject();
}, [projectId, activeProject?.id, loadProject, createNewProject, router]);
// Cleanup function to cancel async operations
return () => {
isCancelled = true;
isInitializingRef.current = false;
};
}, [
projectId,
loadProject,
createNewProject,
router,
isInvalidProjectId,
markProjectIdAsInvalid,
]);
return ( return (
<EditorProvider> <EditorProvider>
+26
View File
@@ -11,6 +11,7 @@ interface ProjectStore {
savedProjects: TProject[]; savedProjects: TProject[];
isLoading: boolean; isLoading: boolean;
isInitialized: boolean; isInitialized: boolean;
invalidProjectIds?: Set<string>;
// Actions // Actions
createNewProject: (name: string) => Promise<string>; createNewProject: (name: string) => Promise<string>;
@@ -32,6 +33,11 @@ interface ProjectStore {
searchQuery: string, searchQuery: string,
sortOption: string sortOption: string
) => TProject[]; ) => TProject[];
// Global invalid project ID tracking
isInvalidProjectId: (id: string) => boolean;
markProjectIdAsInvalid: (id: string) => void;
clearInvalidProjectIds: () => void;
} }
export const useProjectStore = create<ProjectStore>((set, get) => ({ export const useProjectStore = create<ProjectStore>((set, get) => ({
@@ -39,6 +45,7 @@ export const useProjectStore = create<ProjectStore>((set, get) => ({
savedProjects: [], savedProjects: [],
isLoading: true, isLoading: true,
isInitialized: false, isInitialized: false,
invalidProjectIds: new Set<string>(),
createNewProject: async (name: string) => { createNewProject: async (name: string) => {
const newProject: TProject = { const newProject: TProject = {
@@ -357,4 +364,23 @@ export const useProjectStore = create<ProjectStore>((set, get) => ({
return sortedProjects; 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() });
},
})); }));