Skip to content

Commit f4cd250

Browse files
committed
🔧 Refactor import/export code slightly
1 parent f75e004 commit f4cd250

File tree

4 files changed

+64
-83
lines changed

4 files changed

+64
-83
lines changed

docs/changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ This changelog follows the rules of [Keep a Changelog](http://keepachangelog.com
2323

2424
### :tada: Added
2525

26+
- Added the possibility to import and export individual menus via JSON files. Thanks to [@Yar2000T](https://github.com/Yar2000T) for this contribution!
2627
- Some wlroots-specific options to handle the case where no pointer position is available. There are some timeouts which can now be configured in the settings dialog. Thanks to [@make-42](https://github.com/make-42) for this contribution!
2728

2829
### :wrench: Changed

src/main/app.ts

Lines changed: 45 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -823,60 +823,61 @@ export class KandoApp {
823823
);
824824
});
825825

826-
// Export a single menu to a JSON file. If no filePath is provided, show a save dialog.
827-
ipcMain.handle(
828-
'settings-window.export-menu',
829-
async (event, menuIndex: number, filePath?: string) => {
830-
const settings = this.menuSettings.get();
831-
832-
if (menuIndex < 0 || menuIndex >= settings.menus.length) {
833-
console.error('Failed to export menu: Invalid menu index.');
834-
return false;
835-
}
826+
// Export a single menu to a JSON file. If no filePath is provided, show a save
827+
// dialog.
828+
ipcMain.handle('settings-window.export-menu', async (event, menuIndex: number) => {
829+
const settings = this.menuSettings.get();
836830

837-
// We only export the root menu item (so exported files stay compact and
838-
// don't include local UI flags like centered/anchored/hoverMode). This
839-
// also makes future extensions easier.
840-
const menu = settings.menus[menuIndex];
841-
const menuData: ExportedMenuV1 = {
842-
version: settings.version,
843-
menu: menu.root as MenuItem,
844-
};
831+
if (menuIndex < 0 || menuIndex >= settings.menus.length) {
832+
console.error('Failed to export menu: Invalid menu index.');
833+
return false;
834+
}
845835

846-
try {
847-
let targetPath = filePath;
848-
if (!targetPath) {
849-
const result = await dialog.showSaveDialog(this.settingsWindow, {
850-
defaultPath: `${menu.root.name}.json`,
851-
filters: [{ name: 'JSON', extensions: ['json'] }],
852-
});
853-
854-
if (result.canceled || !result.filePath) {
855-
return false;
856-
}
836+
// We only export the root menu item (so exported files stay compact and don't
837+
// include local UI flags like centered/anchored/hoverMode). This also makes future
838+
// extensions easier.
839+
const menu = settings.menus[menuIndex];
840+
const menuData: ExportedMenuV1 = {
841+
version: settings.version,
842+
menu: menu.root as MenuItem,
843+
};
857844

858-
targetPath = result.filePath;
859-
}
845+
try {
846+
const result = await dialog.showSaveDialog(this.settingsWindow, {
847+
defaultPath: `${menu.root.name}.json`,
848+
filters: [{ name: 'JSON', extensions: ['json'] }],
849+
});
860850

861-
fs.writeFileSync(targetPath, JSON.stringify(menuData, null, 2), 'utf-8');
862-
return true;
863-
} catch (error) {
864-
console.error('Failed to export menu:', error);
865-
await dialog.showMessageBox(this.settingsWindow, {
866-
type: 'error',
867-
title: i18next.t('settings.export-menu-error-title', 'Failed to export menu'),
868-
message: 'Failed to export menu.',
869-
detail: error instanceof Error ? error.message : String(error),
870-
});
851+
if (result.canceled || !result.filePath) {
871852
return false;
872853
}
854+
855+
fs.writeFileSync(result.filePath, JSON.stringify(menuData, null, 2), 'utf-8');
856+
return true;
857+
} catch (error) {
858+
console.error('Failed to export menu:', error);
859+
await dialog.showMessageBox(this.settingsWindow, {
860+
type: 'error',
861+
title: i18next.t('settings.export-menu-error-title', 'Failed to export menu'),
862+
message: 'Failed to export menu.',
863+
detail: error instanceof Error ? error.message : String(error),
864+
});
865+
return false;
873866
}
874-
);
867+
});
875868

876869
// Import a menu from a JSON file
877-
ipcMain.handle('settings-window.import-menu', async (event, filePath: string) => {
870+
ipcMain.handle('settings-window.import-menu', async () => {
871+
const result = await dialog.showOpenDialog(this.settingsWindow, {
872+
filters: [{ name: 'JSON', extensions: ['json'] }],
873+
});
874+
875+
if (result.canceled || !result.filePaths[0]) {
876+
return false;
877+
}
878+
878879
try {
879-
const content = fsExtra.readJsonSync(filePath, 'utf-8');
880+
const content = fsExtra.readJsonSync(result.filePaths[0], 'utf-8');
880881

881882
// Validate the exported file format first (version + root menu item)
882883
const exported = EXPORTED_MENU_SCHEMA_V1.parse(content, { reportInput: true });

src/settings-renderer/components/menu-list/MenuList.tsx

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -76,31 +76,6 @@ export default function MenuList() {
7676
const moveMenu = useMenuSettings((state) => state.moveMenu);
7777
const moveMenuItem = useMenuSettings((state) => state.moveMenuItem);
7878

79-
// Handle menu export
80-
const handleExportMenu = async () => {
81-
if (selectedMenu < 0 || selectedMenu >= menus.length) {
82-
return;
83-
}
84-
85-
// Let the main process show the save dialog and perform the export.
86-
await window.settingsAPI.exportMenu(selectedMenu);
87-
};
88-
89-
// Handle menu import
90-
const handleImportMenu = async () => {
91-
const result = await window.settingsAPI.openFilePicker({
92-
filters: [{ name: 'JSON', extensions: ['json'] }],
93-
});
94-
95-
if (result) {
96-
const success = await window.settingsAPI.importMenu(result);
97-
if (!success) {
98-
// Import failed; the main process already showed a descriptive error dialog.
99-
console.error('Import failed for file:', result);
100-
}
101-
}
102-
};
103-
10479
// This is set by the search bar in the collection details.
10580
const [filterTerm, setFilterTerm] = React.useState('');
10681

@@ -357,15 +332,25 @@ export default function MenuList() {
357332
size="large"
358333
tooltip={i18next.t('settings.export-menu')}
359334
variant="floating"
360-
onClick={handleExportMenu}
335+
onClick={async () => {
336+
if (selectedMenu < 0 || selectedMenu >= menus.length) {
337+
return;
338+
}
339+
340+
// Let the main process show the save dialog and perform the export.
341+
await window.settingsAPI.exportMenu(selectedMenu);
342+
}}
361343
/>
362344
<Button
363345
isGrouped
364346
icon={<TbDownload />}
365347
size="large"
366348
tooltip={i18next.t('settings.import-menu')}
367349
variant="floating"
368-
onClick={handleImportMenu}
350+
onClick={async () => {
351+
// Let the main process show the open dialog and perform the import.
352+
await window.settingsAPI.importMenu();
353+
}}
369354
/>
370355
<Button
371356
isGrouped

src/settings-renderer/settings-window-api.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -187,20 +187,14 @@ export const SETTINGS_WINDOW_API = {
187187
ipcRenderer.send('settings-window.restore-menu-settings');
188188
},
189189

190-
/**
191-
* This will export a single menu to a JSON file. The native save dialog will be shown
192-
* if no file path is given.
193-
*/
194-
exportMenu: (menuIndex: number, filePath?: string): Promise<boolean> => {
195-
return ipcRenderer.invoke('settings-window.export-menu', menuIndex, filePath);
190+
/** This will trigger the export of the given menu to a JSON file. */
191+
exportMenu: (menuIndex: number): Promise<boolean> => {
192+
return ipcRenderer.invoke('settings-window.export-menu', menuIndex);
196193
},
197194

198-
/**
199-
* This will import a menu from a JSON file. The handler will show an error dialog on
200-
* failure.
201-
*/
202-
importMenu: (filePath: string): Promise<boolean> => {
203-
return ipcRenderer.invoke('settings-window.import-menu', filePath);
195+
/** This will trigger the import of a menu from a JSON file. */
196+
importMenu: (): Promise<boolean> => {
197+
return ipcRenderer.invoke('settings-window.import-menu');
204198
},
205199
};
206200

0 commit comments

Comments
 (0)