mirror of
https://github.com/OpenCut-app/OpenCut.git
synced 2026-07-13 21:52:53 +02:00
Merge pull request [#499](https://github.com/mazeincoding/AppCut/issues/499)
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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() });
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user