fix: show loading spinner instead of hanging in open project dialog when user click open button

This commit is contained in:
Xiaohan-Tian
2026-05-04 21:44:25 -07:00
parent 213a4ee552
commit 6928416f78
2 changed files with 47 additions and 20 deletions
+19 -9
View File
@@ -29,6 +29,7 @@ import { KEY_SIGNATURE_MAP } from '../constants/coreConstants';
import { KGOfflineRenderer } from '../core/audio-interface/KGOfflineRenderer'; import { KGOfflineRenderer } from '../core/audio-interface/KGOfflineRenderer';
import KGDropdown from './common/KGDropdown'; import KGDropdown from './common/KGDropdown';
import FileImportModal from './common/FileImportModal'; import FileImportModal from './common/FileImportModal';
import LoadingOverlay from './common/LoadingOverlay';
import OpenProjectModal from './common/OpenProjectModal'; import OpenProjectModal from './common/OpenProjectModal';
import { clearChatHistoryAndUI } from '../util/chatUtil'; import { clearChatHistoryAndUI } from '../util/chatUtil';
import PianoIcon from './common/icons/PianoIcon'; import PianoIcon from './common/icons/PianoIcon';
@@ -77,6 +78,7 @@ const Toolbar: React.FC = () => {
// State for open project modal // State for open project modal
const [showOpenProject, setShowOpenProject] = React.useState(false); const [showOpenProject, setShowOpenProject] = React.useState(false);
const [isOpeningProject, setIsOpeningProject] = React.useState(false);
// Close zoom slider on click outside // Close zoom slider on click outside
React.useEffect(() => { React.useEffect(() => {
@@ -205,12 +207,7 @@ const Toolbar: React.FC = () => {
}; };
const handleOpenProjectSelect = async (projectNameToLoad: string) => { const handleOpenProjectSelect = async (projectNameToLoad: string) => {
const confirmed = await showConfirm( setIsOpeningProject(true);
'Open this project? Any unsaved changes in the current project will be lost.'
);
if (!confirmed) {
return false;
}
try { try {
const storage = KGProjectStorage.getInstance(); const storage = KGProjectStorage.getInstance();
@@ -218,18 +215,25 @@ const Toolbar: React.FC = () => {
if (!loadedProject) { if (!loadedProject) {
await showAlert(`Project "${projectNameToLoad}" not found.`); await showAlert(`Project "${projectNameToLoad}" not found.`);
return false; return;
} }
await loadProjectFromData(loadedProject, `Project "${projectNameToLoad}"`, projectNameToLoad); await loadProjectFromData(loadedProject, `Project "${projectNameToLoad}"`, projectNameToLoad);
return true;
} catch (error) { } catch (error) {
console.error("Error loading project:", error); console.error("Error loading project:", error);
await showAlert(`An error occurred while loading the 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 () => { const handleSaveProject = async () => {
if (DEBUG_MODE.TOOLBAR) { if (DEBUG_MODE.TOOLBAR) {
console.log("user clicked save button"); console.log("user clicked save button");
@@ -1207,9 +1211,15 @@ const Toolbar: React.FC = () => {
description="Drag and drop your project file here" description="Drag and drop your project file here"
/> />
<LoadingOverlay
visible={isOpeningProject}
message="Opening project..."
/>
{showOpenProject && ( {showOpenProject && (
<OpenProjectModal <OpenProjectModal
onClose={() => setShowOpenProject(false)} onClose={() => setShowOpenProject(false)}
onConfirmOpenProject={handleConfirmOpenProject}
onOpenProject={handleOpenProjectSelect} onOpenProject={handleOpenProjectSelect}
currentProjectName={savedProjectName} currentProjectName={savedProjectName}
onCreateNewProject={createNewProject} onCreateNewProject={createNewProject}
+28 -11
View File
@@ -7,7 +7,8 @@ import { showAlert, showConfirm, showPrompt } from '../../util/dialogUtil';
interface OpenProjectModalProps { interface OpenProjectModalProps {
onClose: () => void; onClose: () => void;
onOpenProject: (projectName: string) => Promise<boolean>; onConfirmOpenProject: (projectName: string) => Promise<boolean>;
onOpenProject: (projectName: string) => Promise<void>;
currentProjectName: string | null; currentProjectName: string | null;
onCreateNewProject: () => void; onCreateNewProject: () => void;
} }
@@ -44,12 +45,19 @@ const formatDate = (timestamp: number): string => {
}); });
}; };
const OpenProjectModal: React.FC<OpenProjectModalProps> = ({ onClose, onOpenProject, currentProjectName, onCreateNewProject }) => { const OpenProjectModal: React.FC<OpenProjectModalProps> = ({
onClose,
onConfirmOpenProject,
onOpenProject,
currentProjectName,
onCreateNewProject
}) => {
const [projects, setProjects] = useState<ProjectMeta[]>([]); const [projects, setProjects] = useState<ProjectMeta[]>([]);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [filter, setFilter] = useState(''); const [filter, setFilter] = useState('');
const [isClosing, setIsClosing] = useState(false); const [isClosing, setIsClosing] = useState(false);
const mouseDownOnOverlay = useRef(false); const mouseDownOnOverlay = useRef(false);
const pendingOpenProjectName = useRef<string | null>(null);
const [sortField, setSortField] = useState<SortField>('updatedAt'); const [sortField, setSortField] = useState<SortField>('updatedAt');
const [sortDirection, setSortDirection] = useState<SortDirection>('desc'); const [sortDirection, setSortDirection] = useState<SortDirection>('desc');
@@ -61,9 +69,16 @@ const OpenProjectModal: React.FC<OpenProjectModalProps> = ({ onClose, onOpenProj
const handleAnimationEnd = useCallback((e: React.AnimationEvent) => { const handleAnimationEnd = useCallback((e: React.AnimationEvent) => {
if (e.target !== e.currentTarget) return; if (e.target !== e.currentTarget) return;
if (!isClosing) return; if (!isClosing) return;
const projectNameToOpen = pendingOpenProjectName.current;
pendingOpenProjectName.current = null;
setIsClosing(false); setIsClosing(false);
onClose(); onClose();
}, [isClosing, onClose]);
if (projectNameToOpen) {
void onOpenProject(projectNameToOpen);
}
}, [isClosing, onClose, onOpenProject]);
const fetchProjects = useCallback(async (mode: ViewMode) => { const fetchProjects = useCallback(async (mode: ViewMode) => {
setIsLoading(true); setIsLoading(true);
@@ -121,10 +136,11 @@ const OpenProjectModal: React.FC<OpenProjectModalProps> = ({ onClose, onOpenProj
}, [projects, filter, sortField, sortDirection]); }, [projects, filter, sortField, sortDirection]);
const handleOpen = async (projectName: string) => { const handleOpen = async (projectName: string) => {
const opened = await onOpenProject(projectName); const confirmed = await onConfirmOpenProject(projectName);
if (opened) { if (!confirmed) return;
startClose();
} pendingOpenProjectName.current = projectName;
startClose();
}; };
const handleDuplicate = async (e: React.MouseEvent, projectName: string) => { const handleDuplicate = async (e: React.MouseEvent, projectName: string) => {
@@ -141,11 +157,12 @@ const OpenProjectModal: React.FC<OpenProjectModalProps> = ({ onClose, onOpenProj
try { try {
const storage = KGProjectStorage.getInstance(); const storage = KGProjectStorage.getInstance();
const finalName = await storage.resolveUniqueName(trimmed); const finalName = await storage.resolveUniqueName(trimmed);
const confirmed = await onConfirmOpenProject(finalName);
if (!confirmed) return;
await storage.duplicate(projectName, finalName); await storage.duplicate(projectName, finalName);
const opened = await onOpenProject(finalName); pendingOpenProjectName.current = finalName;
if (opened) { startClose();
startClose();
}
} catch (error) { } catch (error) {
console.error('Error duplicating project:', error); console.error('Error duplicating project:', error);
await showAlert(`Failed to duplicate project: ${error}`); await showAlert(`Failed to duplicate project: ${error}`);