Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 90 additions & 28 deletions src/components/settings/SettingsSyncPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,13 @@ function ActivityLog() {
const { state, clearActivityLog } = useSettingsSync();
const [expanded, setExpanded] = createSignal(false);

// Reset expanded state when activity log is cleared
createEffect(() => {
if (state.activityLog.length === 0) {
setExpanded(false);
}
});

const visibleEntries = createMemo(() => {
return expanded() ? state.activityLog : state.activityLog.slice(0, 5);
});
Expand Down Expand Up @@ -719,6 +726,8 @@ function ImportExportSection() {
const [importing, setImporting] = createSignal(false);
const [exporting, setExporting] = createSignal(false);
const [importError, setImportError] = createSignal<string | null>(null);
const [confirmImport, setConfirmImport] = createSignal(false);
const [pendingImport, setPendingImport] = createSignal<string | null>(null);

let fileInputRef: HTMLInputElement | undefined;

Expand Down Expand Up @@ -749,20 +758,39 @@ function ImportExportSection() {
const file = input.files?.[0];
if (!file) return;

setImporting(true);
setImportError(null);

try {
const text = await file.text();
await importSettings(text);
setPendingImport(text);
setConfirmImport(true);
} catch (e) {
setImportError(e instanceof Error ? e.message : String(e));
} finally {
setImporting(false);
input.value = "";
}
};

const handleConfirmImport = async () => {
if (!pendingImport()) return;

setImporting(true);
try {
await importSettings(pendingImport()!);
setConfirmImport(false);
setPendingImport(null);
} catch (e) {
setImportError(e instanceof Error ? e.message : String(e));
} finally {
setImporting(false);
}
};

const handleCancelImport = () => {
setConfirmImport(false);
setPendingImport(null);
};

return (
<FormGroup title="Backup & Restore">
<div class="space-y-3">
Expand All @@ -776,31 +804,65 @@ function ImportExportSection() {
</InfoBox>
</Show>

<div class="flex items-center gap-2">
<Button
variant="secondary"
onClick={handleExport}
loading={exporting()}
icon={<DownloadIcon />}
>
Export Settings
</Button>
<Button
variant="secondary"
onClick={handleImportClick}
loading={importing()}
icon={<UploadIcon />}
>
Import Settings
</Button>
<input
ref={fileInputRef}
type="file"
accept=".json"
class="hidden"
onChange={handleFileSelect}
/>
</div>
<Show
when={confirmImport()}
fallback={
<div class="flex items-center gap-2">
<Button
variant="secondary"
onClick={handleExport}
loading={exporting()}
icon={<DownloadIcon />}
>
Export Settings
</Button>
<Button
variant="secondary"
onClick={handleImportClick}
loading={importing()}
icon={<UploadIcon />}
>
Import Settings
</Button>
<input
ref={fileInputRef}
type="file"
accept=".json"
class="hidden"
onChange={handleFileSelect}
/>
</div>
}
>
<div class="p-3 rounded-lg border border-yellow-500/30 bg-yellow-500/10">
<div class="flex items-start justify-between">
<div>
<span class="font-medium text-yellow-400">Confirm Import</span>
<p class="text-sm text-foreground-muted mt-1">
This will overwrite your current sync-managed settings with the imported backup.
This action cannot be undone.
</p>
</div>
</div>
<div class="mt-3 flex items-center gap-2">
<Button
variant="primary"
size="sm"
onClick={handleConfirmImport}
loading={importing()}
>
Yes, Import
</Button>
<Button
variant="ghost"
size="sm"
onClick={handleCancelImport}
>
Cancel
</Button>
</div>
</div>
</Show>
</div>
</FormGroup>
);
Expand Down