From f551efd5bfced4f9d993cc85d9b1bd3643fda379 Mon Sep 17 00:00:00 2001 From: Flavio Fois Date: Thu, 5 Feb 2026 22:19:39 +0100 Subject: [PATCH] feat: add export and import settings functionality with UI integration --- app.go | 59 +++++++++++++ frontend/messages/en.json | 13 ++- frontend/messages/it.json | 13 ++- .../src/routes/(app)/settings/+page.svelte | 85 ++++++++++++++++++- 4 files changed, 167 insertions(+), 3 deletions(-) diff --git a/app.go b/app.go index 98c13b9..45397d0 100644 --- a/app.go +++ b/app.go @@ -886,3 +886,62 @@ func (a *App) OpenFolderInExplorer(folderPath string) error { cmd := exec.Command("explorer", folderPath) return cmd.Start() } + +// ExportSettings opens a save dialog and exports settings JSON to the selected file +func (a *App) ExportSettings(settingsJSON string) (string, error) { + savePath, err := runtime.SaveFileDialog(a.ctx, runtime.SaveDialogOptions{ + DefaultFilename: "emly_settings.json", + Title: "Export Settings", + Filters: []runtime.FileFilter{ + { + DisplayName: "JSON Files (*.json)", + Pattern: "*.json", + }, + }, + }) + if err != nil { + return "", fmt.Errorf("failed to open save dialog: %w", err) + } + + if savePath == "" { + return "", nil // User cancelled + } + + // Ensure .json extension + if !strings.HasSuffix(strings.ToLower(savePath), ".json") { + savePath += ".json" + } + + if err := os.WriteFile(savePath, []byte(settingsJSON), 0644); err != nil { + return "", fmt.Errorf("failed to write settings file: %w", err) + } + + return savePath, nil +} + +// ImportSettings opens an open dialog and returns the contents of the selected JSON file +func (a *App) ImportSettings() (string, error) { + openPath, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{ + Title: "Import Settings", + Filters: []runtime.FileFilter{ + { + DisplayName: "JSON Files (*.json)", + Pattern: "*.json", + }, + }, + }) + if err != nil { + return "", fmt.Errorf("failed to open file dialog: %w", err) + } + + if openPath == "" { + return "", nil // User cancelled + } + + data, err := os.ReadFile(openPath) + if err != nil { + return "", fmt.Errorf("failed to read settings file: %w", err) + } + + return string(data), nil +} diff --git a/frontend/messages/en.json b/frontend/messages/en.json index bde7195..17d961d 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -109,5 +109,16 @@ "bugreport_open_folder": "Open Folder", "bugreport_close": "Close", "bugreport_error": "Failed to create bug report.", - "bugreport_copied": "Path copied to clipboard!" + "bugreport_copied": "Path copied to clipboard!", + "settings_export_import_title": "Export / Import Settings", + "settings_export_import_description": "Export your current settings to a file or import settings from a previously exported file.", + "settings_export_button": "Export Settings", + "settings_export_hint": "Save your current settings to a JSON file.", + "settings_import_button": "Import Settings", + "settings_import_hint": "Load settings from a previously exported JSON file.", + "settings_export_success": "Settings exported successfully!", + "settings_export_error": "Failed to export settings.", + "settings_import_success": "Settings imported successfully!", + "settings_import_error": "Failed to import settings.", + "settings_import_invalid": "Invalid settings file." } diff --git a/frontend/messages/it.json b/frontend/messages/it.json index dfbf8fe..8f5380a 100644 --- a/frontend/messages/it.json +++ b/frontend/messages/it.json @@ -109,5 +109,16 @@ "bugreport_open_folder": "Apri Cartella", "bugreport_close": "Chiudi", "bugreport_error": "Impossibile creare la segnalazione bug.", - "bugreport_copied": "Percorso copiato negli appunti!" + "bugreport_copied": "Percorso copiato negli appunti!", + "settings_export_import_title": "Esporta / Importa Impostazioni", + "settings_export_import_description": "Esporta le impostazioni correnti in un file o importa impostazioni da un file precedentemente esportato.", + "settings_export_button": "Esporta Impostazioni", + "settings_export_hint": "Salva le impostazioni correnti in un file JSON.", + "settings_import_button": "Importa Impostazioni", + "settings_import_hint": "Carica impostazioni da un file JSON precedentemente esportato.", + "settings_export_success": "Impostazioni esportate con successo!", + "settings_export_error": "Impossibile esportare le impostazioni.", + "settings_import_success": "Impostazioni importate con successo!", + "settings_import_error": "Impossibile importare le impostazioni.", + "settings_import_invalid": "File impostazioni non valido." } diff --git a/frontend/src/routes/(app)/settings/+page.svelte b/frontend/src/routes/(app)/settings/+page.svelte index 75148ab..0ba9c11 100644 --- a/frontend/src/routes/(app)/settings/+page.svelte +++ b/frontend/src/routes/(app)/settings/+page.svelte @@ -6,7 +6,7 @@ import { Label } from "$lib/components/ui/label"; import { Separator } from "$lib/components/ui/separator"; import { Switch } from "$lib/components/ui/switch"; - import { ChevronLeft, Flame } from "@lucide/svelte"; + import { ChevronLeft, Flame, Download, Upload } from "@lucide/svelte"; import type { EMLy_GUI_Settings } from "$lib/types"; import { toast } from "svelte-sonner"; import { It, Us } from "svelte-flags"; @@ -25,6 +25,7 @@ import { setLocale } from "$lib/paraglide/runtime"; import { mailState } from "$lib/stores/mail-state.svelte.js"; import { dev } from '$app/environment'; + import { ExportSettings, ImportSettings } from "$lib/wailsjs/go/main/App"; let { data } = $props(); let config = $derived(data.config); @@ -173,6 +174,42 @@ previousDangerZoneEnabled = $dangerZoneEnabled; })(); }); + + async function exportSettings() { + try { + const settingsJSON = JSON.stringify(form, null, 2); + const result = await ExportSettings(settingsJSON); + if (result) { + toast.success(m.settings_export_success()); + } + } catch (err) { + console.error("Failed to export settings:", err); + toast.error(m.settings_export_error()); + } + } + + async function importSettings() { + try { + const result = await ImportSettings(); + if (result) { + try { + const imported = JSON.parse(result) as EMLy_GUI_Settings; + // Validate that it looks like a valid settings object + if (typeof imported === 'object' && imported !== null) { + form = normalizeSettings(imported); + toast.success(m.settings_import_success()); + } else { + toast.error(m.settings_import_invalid()); + } + } catch { + toast.error(m.settings_import_invalid()); + } + } + } catch (err) { + console.error("Failed to import settings:", err); + toast.error(m.settings_import_error()); + } + }
@@ -244,6 +281,52 @@ + + + {m.settings_export_import_title()} + {m.settings_export_import_description()} + + +
+
+
{m.settings_export_button()}
+
+ {m.settings_export_hint()} +
+
+ +
+ +
+
+
{m.settings_import_button()}
+
+ {m.settings_import_hint()} +
+
+ +
+
+
+ {m.settings_preview_page_title()}