From fb790091fdfb6df4389622759fb0ce9da2e5b8ce Mon Sep 17 00:00:00 2001 From: iamdoubz <> Date: Mon, 6 Jul 2026 10:41:02 -0500 Subject: [PATCH] feat(settings): edit a saved sync target (fix URL/username/options) Each webdav target gets an Edit button that loads it into the form; save routes to update_sync_target. Password left blank keeps the stored one; kind is locked during edit. Fixes not being able to correct a mistyped Server URL. --- src/lib/views/Settings.svelte | 77 ++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 15 deletions(-) diff --git a/src/lib/views/Settings.svelte b/src/lib/views/Settings.svelte index 73d29f4..a3727b2 100644 --- a/src/lib/views/Settings.svelte +++ b/src/lib/views/Settings.svelte @@ -8,7 +8,7 @@ import ConsentNotice from "../components/ConsentNotice.svelte"; import { open } from "@tauri-apps/plugin-dialog"; import { api, errorMessage, events } from "../api"; - import type { BackendId, SyncKind, SyncTargetConfig } from "../api"; + import type { BackendId, SyncKind, SyncTargetConfig, SyncTargetInfo } from "../api"; import { trapFocus } from "../actions/trapFocus"; import { X, @@ -343,6 +343,8 @@ // ---- Add-target form ---- const PROVIDERS = ["nextcloud", "owncloud", "cloudreve", "seafile", "synology", "generic"]; let form = $state(blankForm("webdav")); + // When set, the form is editing an existing target rather than adding one. + let editingId = $state(null); function blankForm(kind: SyncKind): SyncTargetConfig { return { @@ -371,11 +373,46 @@ const r = await settings.test(form); testResult = { ok: r.ok, message: r.message }; } - async function addTarget() { - await settings.addTarget(form); - form = blankForm(form.kind); + // Load an existing target into the form for editing. The secret is never + // returned to the UI (FR-SYNC-6), so it stays blank — left blank on save it + // keeps the stored password; typed, it rotates. No `id` on the form itself, so + // "Test connection" checks the typed values (needs the password re-entered). + function startEdit(t: SyncTargetInfo) { + editingId = t.id; + form = { + name: t.name, + kind: t.kind, + provider_hint: t.provider_hint ?? undefined, + base_url: t.base_url ?? "", + remote_base_path: t.remote_base_path, + username: t.username ?? "", + secret: "", + enabled: t.enabled, + upload_transcript: t.upload_transcript, + upload_notes: t.upload_notes, + upload_summary: t.upload_summary, + upload_recording: t.upload_recording, + trigger_on_finalize: t.trigger_on_finalize, + allow_plaintext_lan: t.allow_plaintext_lan, + encrypt_before_upload: t.encrypt_before_upload, + }; testResult = null; } + function cancelEdit() { + editingId = null; + form = blankForm("webdav"); + testResult = null; + } + async function saveTarget() { + if (editingId) { + await settings.updateTarget({ ...form, id: editingId }); + cancelEdit(); + } else { + await settings.addTarget(form); + form = blankForm(form.kind); + testResult = null; + } + } function linkOAuth() { // begin_oauth_link → loopback PKCE flow; the target is created in the // background on success (sync://linked), which the store handles. @@ -711,6 +748,9 @@ >{t.third_party ? "third-party" : "your server"} {t.host ?? t.kind} + {#if !t.third_party} + + {/if} @@ -719,14 +759,16 @@ {/if} -

Add a target

-
- {#each ["webdav", "onedrive", "dropbox", "box"] as k (k)} - - {/each} -
+

{editingId ? "Edit target" : "Add a target"}

+ {#if !editingId} +
+ {#each ["webdav", "onedrive", "dropbox", "box"] as k (k)} + + {/each} +
+ {/if} {#if form.kind === "webdav"}

@@ -753,7 +795,9 @@ >App password @@ -782,9 +826,12 @@

- {editingId ? "Save changes" : "Add target"} + {#if editingId} + + {/if} {#if testResult} {#if testResult.ok}