fix: missing media files when renaming + saving the project
This commit is contained in:
+18
-12
@@ -30,6 +30,7 @@ import PianoIcon from './common/icons/PianoIcon';
|
|||||||
const Toolbar: React.FC = () => {
|
const Toolbar: React.FC = () => {
|
||||||
const {
|
const {
|
||||||
projectName, setProjectName,
|
projectName, setProjectName,
|
||||||
|
savedProjectName, setSavedProjectName,
|
||||||
bpm, timeSignature, keySignature, setStatus,
|
bpm, timeSignature, keySignature, setStatus,
|
||||||
isPlaying, startPlaying, stopPlaying, setPlayheadPosition,
|
isPlaying, startPlaying, stopPlaying, setPlayheadPosition,
|
||||||
currentTime, setBpm, setTimeSignature, setKeySignature,
|
currentTime, setBpm, setTimeSignature, setKeySignature,
|
||||||
@@ -73,25 +74,25 @@ const Toolbar: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Common project loading logic extracted for reuse
|
// Common project loading logic extracted for reuse
|
||||||
const loadProjectFromData = async (project: KGProject, sourceDescription: string) => {
|
const loadProjectFromData = async (project: KGProject, sourceDescription: string, savedName?: string) => {
|
||||||
try {
|
try {
|
||||||
// Clean up UI state first
|
// Clean up UI state first
|
||||||
cleanupProjectState();
|
cleanupProjectState();
|
||||||
|
|
||||||
// Automatically clear chat history when loading a project
|
// Automatically clear chat history when loading a project
|
||||||
clearChatHistoryAndUI();
|
clearChatHistoryAndUI();
|
||||||
|
|
||||||
// Load the project using the store's loadProject method
|
// Load the project using the store's loadProject method
|
||||||
const { loadProject: storeLoadProject } = useProjectStore.getState();
|
const { loadProject: storeLoadProject } = useProjectStore.getState();
|
||||||
await storeLoadProject(project);
|
await storeLoadProject(project, savedName);
|
||||||
|
|
||||||
// Update status to indicate project loaded
|
// Update status to indicate project loaded
|
||||||
setStatus(`${sourceDescription} loaded successfully`);
|
setStatus(`${sourceDescription} loaded successfully`);
|
||||||
|
|
||||||
if (DEBUG_MODE.TOOLBAR) {
|
if (DEBUG_MODE.TOOLBAR) {
|
||||||
console.log(`project loaded successfully from ${sourceDescription}`);
|
console.log(`project loaded successfully from ${sourceDescription}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error loading project from ${sourceDescription}:`, error);
|
console.error(`Error loading project from ${sourceDescription}:`, error);
|
||||||
setStatus(`Failed to load project: ${error}`);
|
setStatus(`Failed to load project: ${error}`);
|
||||||
@@ -159,8 +160,8 @@ const Toolbar: React.FC = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use common loading logic
|
// Use common loading logic — pass the OPFS folder name so savedProjectName is set correctly
|
||||||
await loadProjectFromData(loadedProject, `Project "${projectNameToLoad}"`);
|
await loadProjectFromData(loadedProject, `Project "${projectNameToLoad}"`, projectNameToLoad.trim());
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error loading project:", error);
|
console.error("Error loading project:", error);
|
||||||
@@ -174,7 +175,12 @@ const Toolbar: React.FC = () => {
|
|||||||
console.log("user clicked save button");
|
console.log("user clicked save button");
|
||||||
}
|
}
|
||||||
|
|
||||||
await saveProject(projectName, setStatus);
|
await saveProject(projectName, savedProjectName, setStatus, (finalName) => {
|
||||||
|
setSavedProjectName(finalName);
|
||||||
|
if (finalName !== projectName) {
|
||||||
|
setProjectName(finalName);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleExportProject = (exportType: string) => {
|
const handleExportProject = (exportType: string) => {
|
||||||
@@ -317,7 +323,7 @@ const Toolbar: React.FC = () => {
|
|||||||
throw new Error('Failed to load imported project');
|
throw new Error('Failed to load imported project');
|
||||||
}
|
}
|
||||||
|
|
||||||
await loadProjectFromData(loaded, `KGStudio file "${file.name}"`);
|
await loadProjectFromData(loaded, `KGStudio file "${file.name}"`, projectName);
|
||||||
|
|
||||||
if (DEBUG_MODE.TOOLBAR) {
|
if (DEBUG_MODE.TOOLBAR) {
|
||||||
console.log("KGStudio file imported successfully:", projectName);
|
console.log("KGStudio file imported successfully:", projectName);
|
||||||
@@ -357,7 +363,7 @@ const Toolbar: React.FC = () => {
|
|||||||
throw new Error('Failed to load imported project from storage');
|
throw new Error('Failed to load imported project from storage');
|
||||||
}
|
}
|
||||||
|
|
||||||
await loadProjectFromData(loaded, `JSON file "${file.name}"`);
|
await loadProjectFromData(loaded, `JSON file "${file.name}"`, importedName);
|
||||||
|
|
||||||
if (DEBUG_MODE.TOOLBAR) {
|
if (DEBUG_MODE.TOOLBAR) {
|
||||||
console.log("KGStudio JSON project imported and saved to OPFS:", importedName);
|
console.log("KGStudio JSON project imported and saved to OPFS:", importedName);
|
||||||
|
|||||||
@@ -85,6 +85,10 @@
|
|||||||
box-shadow: 0 0 0 1px #6aab7a;
|
box-shadow: 0 0 0 1px #6aab7a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.track-region.audio-region.selected {
|
||||||
|
border-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
.track-region.audio-region .region-header {
|
.track-region.audio-region .region-header {
|
||||||
background-color: #4a8b5a;
|
background-color: #4a8b5a;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,14 @@ import { OPFS_CONSTANTS } from '../../constants/coreConstants';
|
|||||||
export class KGAudioFileStorage {
|
export class KGAudioFileStorage {
|
||||||
/**
|
/**
|
||||||
* Store an audio file in the project's media/ directory.
|
* Store an audio file in the project's media/ directory.
|
||||||
|
* Creates the project and media directories if they don't exist yet.
|
||||||
*/
|
*/
|
||||||
public static async storeAudioFile(
|
public static async storeAudioFile(
|
||||||
projectName: string,
|
projectName: string,
|
||||||
fileId: string,
|
fileId: string,
|
||||||
file: File
|
file: File
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const mediaDir = await KGAudioFileStorage.getMediaDir(projectName);
|
const mediaDir = await KGAudioFileStorage.getOrCreateMediaDir(projectName);
|
||||||
const fileHandle = await mediaDir.getFileHandle(fileId, { create: true });
|
const fileHandle = await mediaDir.getFileHandle(fileId, { create: true });
|
||||||
const writable = await fileHandle.createWritable();
|
const writable = await fileHandle.createWritable();
|
||||||
await writable.write(await file.arrayBuffer());
|
await writable.write(await file.arrayBuffer());
|
||||||
@@ -61,7 +62,8 @@ export class KGAudioFileStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the media directory handle for a project.
|
* Get the media directory handle for an existing project (read/delete path).
|
||||||
|
* Does NOT create directories — fails if project folder doesn't exist.
|
||||||
*/
|
*/
|
||||||
private static async getMediaDir(projectName: string): Promise<FileSystemDirectoryHandle> {
|
private static async getMediaDir(projectName: string): Promise<FileSystemDirectoryHandle> {
|
||||||
const root = await navigator.storage.getDirectory();
|
const root = await navigator.storage.getDirectory();
|
||||||
@@ -69,4 +71,15 @@ export class KGAudioFileStorage {
|
|||||||
const projectDir = await projectsDir.getDirectoryHandle(projectName);
|
const projectDir = await projectsDir.getDirectoryHandle(projectName);
|
||||||
return projectDir.getDirectoryHandle(OPFS_CONSTANTS.MEDIA_DIR, { create: true });
|
return projectDir.getDirectoryHandle(OPFS_CONSTANTS.MEDIA_DIR, { create: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or create the media directory handle for a project (write path).
|
||||||
|
* Creates project and media directories if they don't exist.
|
||||||
|
*/
|
||||||
|
private static async getOrCreateMediaDir(projectName: string): Promise<FileSystemDirectoryHandle> {
|
||||||
|
const root = await navigator.storage.getDirectory();
|
||||||
|
const projectsDir = await root.getDirectoryHandle(OPFS_CONSTANTS.ROOT_DIR, { create: true });
|
||||||
|
const projectDir = await projectsDir.getDirectoryHandle(projectName, { create: true });
|
||||||
|
return projectDir.getDirectoryHandle(OPFS_CONSTANTS.MEDIA_DIR, { create: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,10 +206,31 @@ export class KGProjectStorage {
|
|||||||
project.setName(newName);
|
project.setName(newName);
|
||||||
await this.save(newName, project, false);
|
await this.save(newName, project, false);
|
||||||
|
|
||||||
|
// Copy media files from old to new location
|
||||||
|
await this.copyMediaFiles(oldName, newName);
|
||||||
|
|
||||||
// Delete old location
|
// Delete old location
|
||||||
await this.delete(oldName);
|
await this.delete(oldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save a project under a new name, migrating media files from the old folder.
|
||||||
|
* Used when the user renames the project and saves. Handles the case where the
|
||||||
|
* old folder doesn't exist yet (new project never saved).
|
||||||
|
*/
|
||||||
|
public async saveWithRename(oldName: string, newName: string, data: KGProject): Promise<void> {
|
||||||
|
this.ensureInitialized();
|
||||||
|
|
||||||
|
// Save project JSON to the new folder
|
||||||
|
await this.save(newName, data, false);
|
||||||
|
|
||||||
|
// Migrate media files only if the old folder exists
|
||||||
|
if (await this.exists(oldName)) {
|
||||||
|
await this.copyMediaFiles(oldName, newName);
|
||||||
|
await this.delete(oldName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export a project folder as a zip Blob (.kgstudio bundle).
|
* Export a project folder as a zip Blob (.kgstudio bundle).
|
||||||
* Includes project.json, meta.json, and all files in media/.
|
* Includes project.json, meta.json, and all files in media/.
|
||||||
@@ -374,6 +395,42 @@ export class KGProjectStorage {
|
|||||||
return candidate;
|
return candidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Media migration ---
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy all files from projects/<fromName>/media/ to projects/<toName>/media/.
|
||||||
|
* If the source media directory doesn't exist, returns without error.
|
||||||
|
*/
|
||||||
|
private async copyMediaFiles(fromName: string, toName: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const fromDir = await this.projectsDirHandle!.getDirectoryHandle(fromName);
|
||||||
|
let fromMedia: FileSystemDirectoryHandle;
|
||||||
|
try {
|
||||||
|
fromMedia = await fromDir.getDirectoryHandle(OPFS_CONSTANTS.MEDIA_DIR);
|
||||||
|
} catch {
|
||||||
|
// No media directory in source — nothing to copy
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const toDir = await this.projectsDirHandle!.getDirectoryHandle(toName);
|
||||||
|
const toMedia = await toDir.getDirectoryHandle(OPFS_CONSTANTS.MEDIA_DIR, { create: true });
|
||||||
|
|
||||||
|
for await (const entry of fromMedia.values()) {
|
||||||
|
if (entry.kind === 'file') {
|
||||||
|
const fileHandle = entry as FileSystemFileHandle;
|
||||||
|
const file = await fileHandle.getFile();
|
||||||
|
const newHandle = await toMedia.getFileHandle(entry.name, { create: true });
|
||||||
|
const writable = await newHandle.createWritable();
|
||||||
|
await writable.write(await file.arrayBuffer());
|
||||||
|
await writable.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error copying media files from "${fromName}" to "${toName}":`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- File I/O helpers ---
|
// --- File I/O helpers ---
|
||||||
|
|
||||||
private async writeFile(
|
private async writeFile(
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { selectAllNotesInActiveRegion } from '../util/selectionUtil';
|
|||||||
* Handles keyboard shortcuts defined in the configuration
|
* Handles keyboard shortcuts defined in the configuration
|
||||||
*/
|
*/
|
||||||
export const useGlobalKeyboardHandler = () => {
|
export const useGlobalKeyboardHandler = () => {
|
||||||
const { undo, redo, setStatus, isPlaying, startPlaying, stopPlaying, toggleLoop, projectName } = useProjectStore();
|
const { undo, redo, setStatus, isPlaying, startPlaying, stopPlaying, toggleLoop, projectName, savedProjectName, setSavedProjectName, setProjectName } = useProjectStore();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
@@ -39,7 +39,10 @@ export const useGlobalKeyboardHandler = () => {
|
|||||||
|
|
||||||
// Early return to handle save immediately
|
// Early return to handle save immediately
|
||||||
try {
|
try {
|
||||||
saveProject(projectName, setStatus);
|
saveProject(projectName, savedProjectName, setStatus, (finalName) => {
|
||||||
|
setSavedProjectName(finalName);
|
||||||
|
if (finalName !== projectName) setProjectName(finalName);
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Save failed:', error);
|
console.error('Save failed:', error);
|
||||||
setStatus('Save failed');
|
setStatus('Save failed');
|
||||||
@@ -154,7 +157,10 @@ export const useGlobalKeyboardHandler = () => {
|
|||||||
if (saveShortcut && matchesKeyboardShortcut(event, saveShortcut)) {
|
if (saveShortcut && matchesKeyboardShortcut(event, saveShortcut)) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
try {
|
try {
|
||||||
saveProject(projectName, setStatus);
|
saveProject(projectName, savedProjectName, setStatus, (finalName) => {
|
||||||
|
setSavedProjectName(finalName);
|
||||||
|
if (finalName !== projectName) setProjectName(finalName);
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Save failed:', error);
|
console.error('Save failed:', error);
|
||||||
setStatus('Save failed');
|
setStatus('Save failed');
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ function updateMaxBarsCSS(maxBars: number): void {
|
|||||||
interface ProjectState {
|
interface ProjectState {
|
||||||
// State
|
// State
|
||||||
projectName: string;
|
projectName: string;
|
||||||
|
savedProjectName: string; // OPFS folder name where the project is currently saved
|
||||||
tracks: KGTrack[];
|
tracks: KGTrack[];
|
||||||
currentStatus: string;
|
currentStatus: string;
|
||||||
maxBars: number;
|
maxBars: number;
|
||||||
@@ -82,6 +83,7 @@ interface ProjectState {
|
|||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
setProjectName: (name: string) => void;
|
setProjectName: (name: string) => void;
|
||||||
|
setSavedProjectName: (name: string) => void;
|
||||||
addTrack: () => Promise<void>;
|
addTrack: () => Promise<void>;
|
||||||
addAudioTrack: () => Promise<void>;
|
addAudioTrack: () => Promise<void>;
|
||||||
importAudioToTrack: (trackId: string, file: File) => Promise<void>;
|
importAudioToTrack: (trackId: string, file: File) => Promise<void>;
|
||||||
@@ -95,7 +97,7 @@ interface ProjectState {
|
|||||||
setStatus: (status: string) => void;
|
setStatus: (status: string) => void;
|
||||||
removeStatus: () => void;
|
removeStatus: () => void;
|
||||||
refreshStatus: () => void;
|
refreshStatus: () => void;
|
||||||
loadProject: (project: KGProject | null) => Promise<void>;
|
loadProject: (project: KGProject | null, savedName?: string) => Promise<void>;
|
||||||
setPlayheadPosition: (position: number) => void;
|
setPlayheadPosition: (position: number) => void;
|
||||||
startPlaying: () => Promise<void>;
|
startPlaying: () => Promise<void>;
|
||||||
stopPlaying: () => Promise<void>;
|
stopPlaying: () => Promise<void>;
|
||||||
@@ -234,6 +236,7 @@ export const useProjectStore = create<ProjectState>((set, get) => {
|
|||||||
return {
|
return {
|
||||||
// Initial state
|
// Initial state
|
||||||
projectName: currentProject.getName(),
|
projectName: currentProject.getName(),
|
||||||
|
savedProjectName: currentProject.getName(),
|
||||||
tracks: currentProject.getTracks() as KGTrack[],
|
tracks: currentProject.getTracks() as KGTrack[],
|
||||||
currentStatus: KGCore.instance().getStatus() || 'Unknown',
|
currentStatus: KGCore.instance().getStatus() || 'Unknown',
|
||||||
maxBars: currentProject.getMaxBars(),
|
maxBars: currentProject.getMaxBars(),
|
||||||
@@ -281,10 +284,10 @@ export const useProjectStore = create<ProjectState>((set, get) => {
|
|||||||
// Create and execute the change project property command
|
// Create and execute the change project property command
|
||||||
const command = new ChangeProjectPropertyCommand({ name });
|
const command = new ChangeProjectPropertyCommand({ name });
|
||||||
KGCore.instance().executeCommand(command);
|
KGCore.instance().executeCommand(command);
|
||||||
|
|
||||||
// Update the store state
|
// Update the store state
|
||||||
set({ projectName: name });
|
set({ projectName: name });
|
||||||
|
|
||||||
console.log(`Set project name to "${name}"`);
|
console.log(`Set project name to "${name}"`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error setting project name:', error);
|
console.error('Error setting project name:', error);
|
||||||
@@ -292,6 +295,10 @@ export const useProjectStore = create<ProjectState>((set, get) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setSavedProjectName: (name: string) => {
|
||||||
|
set({ savedProjectName: name });
|
||||||
|
},
|
||||||
|
|
||||||
addTrack: async () => {
|
addTrack: async () => {
|
||||||
try {
|
try {
|
||||||
// Create and execute the add track command
|
// Create and execute the add track command
|
||||||
@@ -579,7 +586,7 @@ export const useProjectStore = create<ProjectState>((set, get) => {
|
|||||||
set({ currentStatus: KGCore.instance().getStatus() || 'Unknown' });
|
set({ currentStatus: KGCore.instance().getStatus() || 'Unknown' });
|
||||||
},
|
},
|
||||||
|
|
||||||
loadProject: async (project: KGProject | null = null) => {
|
loadProject: async (project: KGProject | null = null, savedName?: string) => {
|
||||||
try {
|
try {
|
||||||
const { setPlayheadPosition } = get();
|
const { setPlayheadPosition } = get();
|
||||||
|
|
||||||
@@ -675,6 +682,7 @@ export const useProjectStore = create<ProjectState>((set, get) => {
|
|||||||
// Force a new array reference for tracks to trigger React/Zustand re-render
|
// Force a new array reference for tracks to trigger React/Zustand re-render
|
||||||
set({
|
set({
|
||||||
projectName: projectToLoad.getName(),
|
projectName: projectToLoad.getName(),
|
||||||
|
savedProjectName: savedName ?? projectToLoad.getName(),
|
||||||
tracks: [...tracks],
|
tracks: [...tracks],
|
||||||
maxBars,
|
maxBars,
|
||||||
timeSignature,
|
timeSignature,
|
||||||
|
|||||||
+52
-31
@@ -2,58 +2,79 @@ import { KGProjectStorage, DuplicateEntryError } from '../core/io/KGProjectStora
|
|||||||
import { KGCore } from '../core/KGCore';
|
import { KGCore } from '../core/KGCore';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save project utility function
|
* Save project utility function.
|
||||||
* Handles saving the current project with proper error handling and user confirmation
|
* Detects renames (savedProjectName !== projectName) and migrates the OPFS folder,
|
||||||
* @param projectName - The name of the project to save
|
* including media files. Duplicate name conflicts during rename auto-resolve with
|
||||||
* @param setStatus - Function to update the status message
|
* the {name} (1), {name} (2), ... pattern.
|
||||||
* @returns Promise<boolean> - Returns true if save was successful, false otherwise
|
*
|
||||||
|
* @param projectName Current in-memory project name
|
||||||
|
* @param savedProjectName OPFS folder name the project was last saved under
|
||||||
|
* @param setStatus Function to update the status message
|
||||||
|
* @param onSaveSuccess Called with the final saved name on success
|
||||||
*/
|
*/
|
||||||
export const saveProject = async (
|
export const saveProject = async (
|
||||||
projectName: string,
|
projectName: string,
|
||||||
setStatus: (status: string) => void
|
savedProjectName: string,
|
||||||
|
setStatus: (status: string) => void,
|
||||||
|
onSaveSuccess: (finalName: string) => void,
|
||||||
): Promise<boolean> => {
|
): Promise<boolean> => {
|
||||||
const storage = KGProjectStorage.getInstance();
|
const storage = KGProjectStorage.getInstance();
|
||||||
|
const isRename = savedProjectName !== projectName;
|
||||||
|
|
||||||
|
if (isRename) {
|
||||||
|
// Determine the target name, resolving conflicts automatically
|
||||||
|
let targetName = projectName;
|
||||||
|
if (await storage.exists(projectName)) {
|
||||||
|
targetName = await storage.resolveUniqueName(projectName);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await storage.saveWithRename(
|
||||||
|
savedProjectName,
|
||||||
|
targetName,
|
||||||
|
KGCore.instance().getCurrentProject(),
|
||||||
|
);
|
||||||
|
|
||||||
|
const statusMsg =
|
||||||
|
targetName !== projectName
|
||||||
|
? `Project renamed to "${targetName}" and saved`
|
||||||
|
: `Project "${targetName}" has been saved`;
|
||||||
|
setStatus(statusMsg);
|
||||||
|
onSaveSuccess(targetName);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving renamed project:', error);
|
||||||
|
window.alert(`An error occurred while saving: ${error}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same name — existing overwrite logic
|
||||||
try {
|
try {
|
||||||
await storage.save(
|
await storage.save(projectName, KGCore.instance().getCurrentProject(), false);
|
||||||
projectName,
|
|
||||||
KGCore.instance().getCurrentProject(),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
setStatus(`Project "${projectName}" has been saved`);
|
setStatus(`Project "${projectName}" has been saved`);
|
||||||
console.log("project saved successfully");
|
onSaveSuccess(projectName);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error saving project:", error);
|
|
||||||
|
|
||||||
if (error instanceof DuplicateEntryError) {
|
if (error instanceof DuplicateEntryError) {
|
||||||
const confirmed = window.confirm(`Project "${projectName}" already exists. Do you want to overwrite it?`);
|
const confirmed = window.confirm(
|
||||||
|
`Project "${projectName}" already exists. Do you want to overwrite it?`,
|
||||||
|
);
|
||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
try {
|
try {
|
||||||
await storage.save(
|
await storage.save(projectName, KGCore.instance().getCurrentProject(), true);
|
||||||
projectName,
|
|
||||||
KGCore.instance().getCurrentProject(),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
setStatus(`Project "${projectName}" has been saved`);
|
setStatus(`Project "${projectName}" has been saved`);
|
||||||
console.log("project saved successfully after overwrite");
|
onSaveSuccess(projectName);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (overwriteError) {
|
} catch (overwriteError) {
|
||||||
console.error("Error overwriting project:", overwriteError);
|
console.error('Error overwriting project:', overwriteError);
|
||||||
window.alert(`An error occurred while overwriting the project: ${overwriteError}`);
|
window.alert(`An error occurred while overwriting the project: ${overwriteError}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// User cancelled the overwrite
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
console.error("Error saving project:", error);
|
console.error('Error saving project:', error);
|
||||||
window.alert(`An unknown error ${error} occurred. Please try again.`);
|
window.alert(`An unknown error ${error} occurred. Please try again.`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user