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.
This commit is contained in:
1 parent
496a3372c4
commit
fb790091fd
1 file changed
+62
-15
@@ -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<SyncTargetConfig>(blankForm("webdav"));
|
||||
// When set, the form is editing an existing target rather than adding one.
|
||||
let editingId = $state<string | null>(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"}</span
|
||||
>
|
||||
<span class="host">{t.host ?? t.kind}</span>
|
||||
{#if !t.third_party}
|
||||
<button class="link" onclick={() => startEdit(t)}>Edit</button>
|
||||
{/if}
|
||||
<button class="link danger" onclick={() => settings.removeTarget(t.id)}
|
||||
>Remove</button
|
||||
>
|
||||
@@ -719,14 +759,16 @@
|
||||
</ul>
|
||||
{/if}
|
||||
|
||||
<h4>Add a target</h4>
|
||||
<div class="kinds">
|
||||
{#each ["webdav", "onedrive", "dropbox", "box"] as k (k)}
|
||||
<button class:active={form.kind === k} onclick={() => setKind(k as SyncKind)}
|
||||
>{k}</button
|
||||
>
|
||||
{/each}
|
||||
</div>
|
||||
<h4>{editingId ? "Edit target" : "Add a target"}</h4>
|
||||
{#if !editingId}
|
||||
<div class="kinds">
|
||||
{#each ["webdav", "onedrive", "dropbox", "box"] as k (k)}
|
||||
<button class:active={form.kind === k} onclick={() => setKind(k as SyncKind)}
|
||||
>{k}</button
|
||||
>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if form.kind === "webdav"}
|
||||
<p class="muted">
|
||||
@@ -753,7 +795,9 @@
|
||||
>App password<input
|
||||
type="password"
|
||||
bind:value={form.secret}
|
||||
placeholder="stored in OS credential store"
|
||||
placeholder={editingId
|
||||
? "leave blank to keep current password"
|
||||
: "stored in OS credential store"}
|
||||
/></label
|
||||
>
|
||||
</div>
|
||||
@@ -782,9 +826,12 @@
|
||||
</label>
|
||||
<div class="actions">
|
||||
<button onclick={testConnection}>Test connection</button>
|
||||
<button class="primary" onclick={addTarget} disabled={!form.name || !form.base_url}
|
||||
>Add target</button
|
||||
<button class="primary" onclick={saveTarget} disabled={!form.name || !form.base_url}
|
||||
>{editingId ? "Save changes" : "Add target"}</button
|
||||
>
|
||||
{#if editingId}
|
||||
<button class="link" onclick={cancelEdit}>Cancel</button>
|
||||
{/if}
|
||||
{#if testResult}
|
||||
<span class="test" class:ok={testResult.ok} class:fail={!testResult.ok}>
|
||||
{#if testResult.ok}<Check size={14} aria-hidden="true" />{:else}<X
|
||||
|
||||
Reference in new issue
Block a user