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

View File

@ -32,39 +32,121 @@ export default function Editor() {
setPropertiesPanel,
} = usePanelStore();
const { activeProject, loadProject, createNewProject } = useProjectStore();
const {
activeProject,
loadProject,
createNewProject,
isInvalidProjectId,
markProjectIdAsInvalid,
} = useProjectStore();
const params = useParams();
const router = useRouter();
const projectId = params.project_id as string;
const handledProjectIds = useRef<Set<string>>(new Set());
const isInitializingRef = useRef<boolean>(false);
usePlaybackControls();
useEffect(() => {
const initProject = async () => {
if (!projectId) return;
let isCancelled = false;
const initProject = async () => {
if (!projectId) {
return;
}
// Prevent duplicate initialization
if (isInitializingRef.current) {
return;
}
// Check if project is already loaded
if (activeProject?.id === projectId) {
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)) {
return;
}
// Mark as initializing to prevent race conditions
isInitializingRef.current = true;
handledProjectIds.current.add(projectId);
try {
await loadProject(projectId);
} catch (error) {
handledProjectIds.current.add(projectId);
const newProjectId = await createNewProject("Untitled Project");
router.replace(`/editor/${newProjectId}`);
return;
// Check if component was unmounted during async operation
if (isCancelled) {
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();
}, [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 (
<EditorProvider>

View File

@ -11,6 +11,7 @@ interface ProjectStore {
savedProjects: TProject[];
isLoading: boolean;
isInitialized: boolean;
invalidProjectIds?: Set<string>;
// Actions
createNewProject: (name: string) => Promise<string>;
@ -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<ProjectStore>((set, get) => ({
@ -39,6 +45,7 @@ export const useProjectStore = create<ProjectStore>((set, get) => ({
savedProjects: [],
isLoading: true,
isInitialized: false,
invalidProjectIds: new Set<string>(),
createNewProject: async (name: string) => {
const newProject: TProject = {
@ -357,4 +364,23 @@ export const useProjectStore = create<ProjectStore>((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() });
},
}));