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}`);