From 48a21c2c596b8f3a9db4697099ce9b218e84ec7e Mon Sep 17 00:00:00 2001 From: Joe Julian Date: Mon, 6 Apr 2026 16:40:43 -0700 Subject: [PATCH] Shift remote open copy to local-first flow --- main.go | 23 +++++++++++++++++++++++ main_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ ui_forms.go | 2 +- 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 65f256e..e7de6c1 100644 --- a/main.go +++ b/main.go @@ -2602,13 +2602,36 @@ func (u *ui) remoteOpenRetryAvailable() bool { return u.lifecycleMode == "remote" && strings.HasPrefix(strings.TrimSpace(u.state.ErrorMessage), "open remote vault failed:") } +func (u *ui) selectedRemoteUsesLocalCache() bool { + return u.hasSelectedRemoteTarget() && + strings.TrimSpace(u.vaultPath.Text()) != "" && + strings.TrimSpace(u.selectedVaultRemoteProfileID) != "" && + strings.TrimSpace(u.selectedVaultRemoteCredentialEntryID) != "" +} + +func (u *ui) remoteLifecycleMessage() string { + if u.selectedRemoteUsesLocalCache() { + return "Open the local cache for this remote vault, then unlock and sync it with the vault-stored remote settings." + } + return "Connect to a remote vault, then unlock it with the KeePass master key." +} + func (u *ui) remoteOpenButtonLabel() string { switch { case u.lifecycleBusy(): + if u.selectedRemoteUsesLocalCache() { + return "Opening Cached Vault..." + } return "Opening Remote Vault..." case u.remoteOpenRetryAvailable(): + if u.selectedRemoteUsesLocalCache() { + return "Retry Cached Vault" + } return "Retry Remote Vault" default: + if u.selectedRemoteUsesLocalCache() { + return "Open Cached Vault" + } return "Open Remote Vault" } } diff --git a/main_test.go b/main_test.go index 37a5adf..7b86ebb 100644 --- a/main_test.go +++ b/main_test.go @@ -5301,6 +5301,47 @@ func TestUIRemoteOpenButtonLabelOffersRetryAfterFailure(t *testing.T) { } } +func TestUIRemoteLifecycleMessageUsesLocalCacheLanguageForBoundRemote(t *testing.T) { + t.Parallel() + + u := newUIWithSession("desktop", &session.Manager{}) + u.lifecycleMode = "remote" + u.applyRecentRemoteRecord(recentRemoteRecord{ + BaseURL: "https://dav.example.invalid", + Path: "vaults/home.kdbx", + LocalVaultPath: "/vaults/cache/home.kdbx", + RemoteProfileID: "remote-profile-1", + CredentialEntryID: "remote-creds-1", + }) + + if got := u.remoteLifecycleMessage(); got != "Open the local cache for this remote vault, then unlock and sync it with the vault-stored remote settings." { + t.Fatalf("remoteLifecycleMessage() = %q, want local-cache guidance", got) + } +} + +func TestUIRemoteOpenButtonLabelUsesLocalCacheLanguageForBoundRemote(t *testing.T) { + t.Parallel() + + u := newUIWithSession("desktop", &session.Manager{}) + u.lifecycleMode = "remote" + u.applyRecentRemoteRecord(recentRemoteRecord{ + BaseURL: "https://dav.example.invalid", + Path: "vaults/home.kdbx", + LocalVaultPath: "/vaults/cache/home.kdbx", + RemoteProfileID: "remote-profile-1", + CredentialEntryID: "remote-creds-1", + }) + + if got := u.remoteOpenButtonLabel(); got != "Open Cached Vault" { + t.Fatalf("remoteOpenButtonLabel() = %q, want %q", got, "Open Cached Vault") + } + + u.state.ErrorMessage = "open remote vault failed: dial tcp timeout" + if got := u.remoteOpenButtonLabel(); got != "Retry Cached Vault" { + t.Fatalf("remoteOpenButtonLabel() after error = %q, want %q", got, "Retry Cached Vault") + } +} + func TestUIOpenRemoteVaultRestoresLastOpenedGroupForThatConnection(t *testing.T) { t.Parallel() diff --git a/ui_forms.go b/ui_forms.go index c22633e..5aae825 100644 --- a/ui_forms.go +++ b/ui_forms.go @@ -33,7 +33,7 @@ func (u *ui) lifecycleControls(gtx layout.Context) layout.Dimensions { layout.Rigid(func(gtx layout.Context) layout.Dimensions { message := "Choose a recent vault or enter a .kdbx path, then unlock it." if u.lifecycleMode == "remote" { - message = "Connect to a remote vault, then unlock it with the KeePass master key." + message = u.remoteLifecycleMessage() } lbl := material.Label(u.theme, unit.Sp(14), message) lbl.Color = accentColor