diff --git a/src/components/Toolbar.tsx b/src/components/Toolbar.tsx
index ce3f7d7..bbebe48 100644
--- a/src/components/Toolbar.tsx
+++ b/src/components/Toolbar.tsx
@@ -29,6 +29,7 @@ import { KEY_SIGNATURE_MAP } from '../constants/coreConstants';
import { KGOfflineRenderer } from '../core/audio-interface/KGOfflineRenderer';
import KGDropdown from './common/KGDropdown';
import FileImportModal from './common/FileImportModal';
+import LoadingOverlay from './common/LoadingOverlay';
import OpenProjectModal from './common/OpenProjectModal';
import { clearChatHistoryAndUI } from '../util/chatUtil';
import PianoIcon from './common/icons/PianoIcon';
@@ -77,6 +78,7 @@ const Toolbar: React.FC = () => {
// State for open project modal
const [showOpenProject, setShowOpenProject] = React.useState(false);
+ const [isOpeningProject, setIsOpeningProject] = React.useState(false);
// Close zoom slider on click outside
React.useEffect(() => {
@@ -205,12 +207,7 @@ const Toolbar: React.FC = () => {
};
const handleOpenProjectSelect = async (projectNameToLoad: string) => {
- const confirmed = await showConfirm(
- 'Open this project? Any unsaved changes in the current project will be lost.'
- );
- if (!confirmed) {
- return false;
- }
+ setIsOpeningProject(true);
try {
const storage = KGProjectStorage.getInstance();
@@ -218,18 +215,25 @@ const Toolbar: React.FC = () => {
if (!loadedProject) {
await showAlert(`Project "${projectNameToLoad}" not found.`);
- return false;
+ return;
}
await loadProjectFromData(loadedProject, `Project "${projectNameToLoad}"`, projectNameToLoad);
- return true;
} catch (error) {
console.error("Error loading project:", error);
await showAlert(`An error occurred while loading the project: ${error}`);
- return false;
+ } finally {
+ setIsOpeningProject(false);
}
};
+ const handleConfirmOpenProject = async (_projectNameToLoad: string) => {
+ const confirmed = await showConfirm(
+ 'Open this project? Any unsaved changes in the current project will be lost.'
+ );
+ return confirmed;
+ };
+
const handleSaveProject = async () => {
if (DEBUG_MODE.TOOLBAR) {
console.log("user clicked save button");
@@ -1207,9 +1211,15 @@ const Toolbar: React.FC = () => {
description="Drag and drop your project file here"
/>
+
+
{showOpenProject && (
setShowOpenProject(false)}
+ onConfirmOpenProject={handleConfirmOpenProject}
onOpenProject={handleOpenProjectSelect}
currentProjectName={savedProjectName}
onCreateNewProject={createNewProject}
diff --git a/src/components/common/OpenProjectModal.tsx b/src/components/common/OpenProjectModal.tsx
index 8568ef6..a98b531 100644
--- a/src/components/common/OpenProjectModal.tsx
+++ b/src/components/common/OpenProjectModal.tsx
@@ -7,7 +7,8 @@ import { showAlert, showConfirm, showPrompt } from '../../util/dialogUtil';
interface OpenProjectModalProps {
onClose: () => void;
- onOpenProject: (projectName: string) => Promise;
+ onConfirmOpenProject: (projectName: string) => Promise;
+ onOpenProject: (projectName: string) => Promise;
currentProjectName: string | null;
onCreateNewProject: () => void;
}
@@ -44,12 +45,19 @@ const formatDate = (timestamp: number): string => {
});
};
-const OpenProjectModal: React.FC = ({ onClose, onOpenProject, currentProjectName, onCreateNewProject }) => {
+const OpenProjectModal: React.FC = ({
+ onClose,
+ onConfirmOpenProject,
+ onOpenProject,
+ currentProjectName,
+ onCreateNewProject
+}) => {
const [projects, setProjects] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [filter, setFilter] = useState('');
const [isClosing, setIsClosing] = useState(false);
const mouseDownOnOverlay = useRef(false);
+ const pendingOpenProjectName = useRef(null);
const [sortField, setSortField] = useState('updatedAt');
const [sortDirection, setSortDirection] = useState('desc');
@@ -61,9 +69,16 @@ const OpenProjectModal: React.FC = ({ onClose, onOpenProj
const handleAnimationEnd = useCallback((e: React.AnimationEvent) => {
if (e.target !== e.currentTarget) return;
if (!isClosing) return;
+
+ const projectNameToOpen = pendingOpenProjectName.current;
+ pendingOpenProjectName.current = null;
setIsClosing(false);
onClose();
- }, [isClosing, onClose]);
+
+ if (projectNameToOpen) {
+ void onOpenProject(projectNameToOpen);
+ }
+ }, [isClosing, onClose, onOpenProject]);
const fetchProjects = useCallback(async (mode: ViewMode) => {
setIsLoading(true);
@@ -121,10 +136,11 @@ const OpenProjectModal: React.FC = ({ onClose, onOpenProj
}, [projects, filter, sortField, sortDirection]);
const handleOpen = async (projectName: string) => {
- const opened = await onOpenProject(projectName);
- if (opened) {
- startClose();
- }
+ const confirmed = await onConfirmOpenProject(projectName);
+ if (!confirmed) return;
+
+ pendingOpenProjectName.current = projectName;
+ startClose();
};
const handleDuplicate = async (e: React.MouseEvent, projectName: string) => {
@@ -141,11 +157,12 @@ const OpenProjectModal: React.FC = ({ onClose, onOpenProj
try {
const storage = KGProjectStorage.getInstance();
const finalName = await storage.resolveUniqueName(trimmed);
+ const confirmed = await onConfirmOpenProject(finalName);
+ if (!confirmed) return;
+
await storage.duplicate(projectName, finalName);
- const opened = await onOpenProject(finalName);
- if (opened) {
- startClose();
- }
+ pendingOpenProjectName.current = finalName;
+ startClose();
} catch (error) {
console.error('Error duplicating project:', error);
await showAlert(`Failed to duplicate project: ${error}`);