feat: add export and import settings functionality with UI integration
This commit is contained in:
59
app.go
59
app.go
@@ -886,3 +886,62 @@ func (a *App) OpenFolderInExplorer(folderPath string) error {
|
|||||||
cmd := exec.Command("explorer", folderPath)
|
cmd := exec.Command("explorer", folderPath)
|
||||||
return cmd.Start()
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -109,5 +109,16 @@
|
|||||||
"bugreport_open_folder": "Open Folder",
|
"bugreport_open_folder": "Open Folder",
|
||||||
"bugreport_close": "Close",
|
"bugreport_close": "Close",
|
||||||
"bugreport_error": "Failed to create bug report.",
|
"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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,5 +109,16 @@
|
|||||||
"bugreport_open_folder": "Apri Cartella",
|
"bugreport_open_folder": "Apri Cartella",
|
||||||
"bugreport_close": "Chiudi",
|
"bugreport_close": "Chiudi",
|
||||||
"bugreport_error": "Impossibile creare la segnalazione bug.",
|
"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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import { Label } from "$lib/components/ui/label";
|
import { Label } from "$lib/components/ui/label";
|
||||||
import { Separator } from "$lib/components/ui/separator";
|
import { Separator } from "$lib/components/ui/separator";
|
||||||
import { Switch } from "$lib/components/ui/switch";
|
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 type { EMLy_GUI_Settings } from "$lib/types";
|
||||||
import { toast } from "svelte-sonner";
|
import { toast } from "svelte-sonner";
|
||||||
import { It, Us } from "svelte-flags";
|
import { It, Us } from "svelte-flags";
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
import { setLocale } from "$lib/paraglide/runtime";
|
import { setLocale } from "$lib/paraglide/runtime";
|
||||||
import { mailState } from "$lib/stores/mail-state.svelte.js";
|
import { mailState } from "$lib/stores/mail-state.svelte.js";
|
||||||
import { dev } from '$app/environment';
|
import { dev } from '$app/environment';
|
||||||
|
import { ExportSettings, ImportSettings } from "$lib/wailsjs/go/main/App";
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
let config = $derived(data.config);
|
let config = $derived(data.config);
|
||||||
@@ -173,6 +174,42 @@
|
|||||||
previousDangerZoneEnabled = $dangerZoneEnabled;
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="min-h-[calc(100vh-1rem)] from-background to-muted/30">
|
<div class="min-h-[calc(100vh-1rem)] from-background to-muted/30">
|
||||||
@@ -244,6 +281,52 @@
|
|||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card.Root>
|
</Card.Root>
|
||||||
|
|
||||||
|
<Card.Root>
|
||||||
|
<Card.Header class="space-y-1">
|
||||||
|
<Card.Title>{m.settings_export_import_title()}</Card.Title>
|
||||||
|
<Card.Description>{m.settings_export_import_description()}</Card.Description>
|
||||||
|
</Card.Header>
|
||||||
|
<Card.Content class="space-y-4">
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-between gap-4 rounded-lg border bg-card p-4"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div class="font-medium">{m.settings_export_button()}</div>
|
||||||
|
<div class="text-sm text-muted-foreground">
|
||||||
|
{m.settings_export_hint()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
class="cursor-pointer hover:cursor-pointer"
|
||||||
|
onclick={exportSettings}
|
||||||
|
>
|
||||||
|
<Download class="size-4 mr-2" />
|
||||||
|
{m.settings_export_button()}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Separator />
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-between gap-4 rounded-lg border bg-card p-4"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div class="font-medium">{m.settings_import_button()}</div>
|
||||||
|
<div class="text-sm text-muted-foreground">
|
||||||
|
{m.settings_import_hint()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
class="cursor-pointer hover:cursor-pointer"
|
||||||
|
onclick={importSettings}
|
||||||
|
>
|
||||||
|
<Upload class="size-4 mr-2" />
|
||||||
|
{m.settings_import_button()}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Card.Content>
|
||||||
|
</Card.Root>
|
||||||
|
|
||||||
<Card.Root>
|
<Card.Root>
|
||||||
<Card.Header class="space-y-1">
|
<Card.Header class="space-y-1">
|
||||||
<Card.Title>{m.settings_preview_page_title()}</Card.Title>
|
<Card.Title>{m.settings_preview_page_title()}</Card.Title>
|
||||||
|
|||||||
Reference in New Issue
Block a user