Add remote sync settings shortcut

This commit is contained in:
Joe Julian
2026-04-06 20:47:57 -07:00
parent 554d4f760d
commit 26748d4b84
3 changed files with 164 additions and 2 deletions
+30 -1
View File
@@ -2358,6 +2358,9 @@ func (u *ui) prefillAdvancedSyncRemoteFromSavedBinding() {
func (u *ui) syncDialogTitle() string { func (u *ui) syncDialogTitle() string {
if u.syncDialogPurpose == syncDialogPurposeRemoteSetup { if u.syncDialogPurpose == syncDialogPurposeRemoteSetup {
if _, ok := u.selectedVaultRemoteBinding(); ok {
return "Remote Sync Settings"
}
return "Set Up Remote Sync" return "Set Up Remote Sync"
} }
return "Advanced Sync" return "Advanced Sync"
@@ -2365,6 +2368,9 @@ func (u *ui) syncDialogTitle() string {
func (u *ui) syncDialogDescription() string { func (u *ui) syncDialogDescription() string {
if u.syncDialogPurpose == syncDialogPurposeRemoteSetup { if u.syncDialogPurpose == syncDialogPurposeRemoteSetup {
if _, ok := u.selectedVaultRemoteBinding(); ok {
return "Review or change this vault's saved WebDAV target, credentials, and sync mode."
}
return "Send this local vault to a WebDAV target, then use that target for future sync." return "Send this local vault to a WebDAV target, then use that target for future sync."
} }
return "Pick direction, choose the other vault, and then run the merge. Saved source and direction defaults now live in Settings." return "Pick direction, choose the other vault, and then run the merge. Saved source and direction defaults now live in Settings."
@@ -2372,6 +2378,9 @@ func (u *ui) syncDialogDescription() string {
func (u *ui) syncDialogConfirmButtonLabel() string { func (u *ui) syncDialogConfirmButtonLabel() string {
if u.syncDialogPurpose == syncDialogPurposeRemoteSetup { if u.syncDialogPurpose == syncDialogPurposeRemoteSetup {
if _, ok := u.selectedVaultRemoteBinding(); ok {
return "Save Remote Sync Settings"
}
return "Set Up Remote Sync" return "Set Up Remote Sync"
} }
return "Synchronize" return "Synchronize"
@@ -2625,6 +2634,18 @@ func (u *ui) directRemoteSyncShortcutLabel() string {
return "Use Remote Sync" return "Use Remote Sync"
} }
func (u *ui) shouldShowRemoteSyncSettingsShortcut() bool {
if !u.hasOpenVault() || u.isVaultLocked() || u.state.Section != appstate.SectionEntries {
return false
}
_, ok := u.selectedVaultRemoteBinding()
return ok
}
func (u *ui) remoteSyncSettingsShortcutLabel() string {
return "Remote Sync Settings"
}
func (u *ui) shouldShowRemoteSyncSetupShortcut() bool { func (u *ui) shouldShowRemoteSyncSetupShortcut() bool {
if !u.hasOpenVault() || u.isVaultLocked() || u.state.Section != appstate.SectionEntries { if !u.hasOpenVault() || u.isVaultLocked() || u.state.Section != appstate.SectionEntries {
return false return false
@@ -6836,7 +6857,7 @@ func (u *ui) pathBar(gtx layout.Context) layout.Dimensions {
return children return children
}()...) }()...)
} }
if !u.shouldShowDirectRemoteSyncShortcut() && !u.shouldShowRemoteSyncSetupShortcut() { if !u.shouldShowDirectRemoteSyncShortcut() && !u.shouldShowRemoteSyncSetupShortcut() && !u.shouldShowRemoteSyncSettingsShortcut() {
return crumbBar(gtx) return crumbBar(gtx)
} }
children := []layout.FlexChild{ children := []layout.FlexChild{
@@ -6856,6 +6877,14 @@ func (u *ui) pathBar(gtx layout.Context) layout.Dimensions {
return tonedButton(gtx, u.theme, &u.useSavedAdvancedSyncRemote, u.remoteSyncSetupShortcutLabel()) return tonedButton(gtx, u.theme, &u.useSavedAdvancedSyncRemote, u.remoteSyncSetupShortcutLabel())
})) }))
} }
if u.shouldShowRemoteSyncSettingsShortcut() {
if u.shouldShowDirectRemoteSyncShortcut() || u.shouldShowRemoteSyncSetupShortcut() {
children = append(children, layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout))
}
children = append(children, layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return tonedButton(gtx, u.theme, &u.useSavedAdvancedSyncRemote, u.remoteSyncSettingsShortcutLabel())
}))
}
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, children...) return layout.Flex{Axis: layout.Vertical}.Layout(gtx, children...)
} }
+129
View File
@@ -5869,6 +5869,41 @@ func TestUIRemoteSyncSetupShortcutLabelUsesClearLanguage(t *testing.T) {
} }
} }
func TestUIShouldShowRemoteSyncSettingsShortcutForSavedBinding(t *testing.T) {
t.Parallel()
u := newUIWithModel("desktop", vault.Model{
Entries: []vault.Entry{{
ID: "remote-creds-1",
Title: "WebDAV Sign-In",
Username: "tjulian",
Path: []string{"Crew", "Internet"},
}},
RemoteProfiles: []vault.RemoteProfile{{
ID: "family-webdav",
Name: "Family Vault",
Backend: vault.RemoteBackendWebDAV,
BaseURL: "https://dav.example.invalid/remote.php/dav",
Path: "files/family/keepass.kdbx",
}},
})
u.state.Section = appstate.SectionEntries
if !u.shouldShowRemoteSyncSettingsShortcut() {
t.Fatal("shouldShowRemoteSyncSettingsShortcut() = false, want true when a saved binding exists")
}
}
func TestUIRemoteSyncSettingsShortcutLabelUsesClearLanguage(t *testing.T) {
t.Parallel()
u := newUIWithSession("desktop", &session.Manager{})
if got := u.remoteSyncSettingsShortcutLabel(); got != "Remote Sync Settings" {
t.Fatalf("remoteSyncSettingsShortcutLabel() = %q, want Remote Sync Settings", got)
}
}
func TestUIOpenRemoteSyncSetupDialogPrefillsCurrentVaultSetupFlow(t *testing.T) { func TestUIOpenRemoteSyncSetupDialogPrefillsCurrentVaultSetupFlow(t *testing.T) {
t.Parallel() t.Parallel()
@@ -5937,6 +5972,54 @@ func TestUISelectedLocalVaultRemoteSyncSummaryMentionsSetup(t *testing.T) {
} }
} }
func TestUISelectedLocalVaultRemoteSyncSummaryMentionsAutomaticSync(t *testing.T) {
t.Parallel()
dir := t.TempDir()
u := newUIWithSession("desktop", &session.Manager{}, statePaths{
DefaultSaveAsPath: filepath.Join(dir, "vault.kdbx"),
RecentVaultsPath: filepath.Join(dir, "recent-vaults.json"),
RecentRemotesPath: filepath.Join(dir, "recent-remotes.json"),
UIPreferencesPath: filepath.Join(dir, "ui-prefs.json"),
})
u.recentRemotes = []recentRemoteRecord{{
BaseURL: "https://dav.example.invalid/remote.php/dav",
Path: "files/family/keepass.kdbx",
LocalVaultPath: "/vaults/family.kdbx",
RemoteProfileID: "family-webdav",
CredentialEntryID: "remote-creds-1",
SyncMode: string(appstate.SyncModeAutomaticOnOpenSave),
}}
if got := u.selectedLocalVaultRemoteSyncSummary("/vaults/family.kdbx"); got != "Saved remote sync target: keepass.kdbx · dav.example.invalid · Syncs automatically on open and save." {
t.Fatalf("selectedLocalVaultRemoteSyncSummary() = %q, want automatic sync guidance", got)
}
}
func TestUISelectedLocalVaultRemoteSyncSummaryMentionsManualSync(t *testing.T) {
t.Parallel()
dir := t.TempDir()
u := newUIWithSession("desktop", &session.Manager{}, statePaths{
DefaultSaveAsPath: filepath.Join(dir, "vault.kdbx"),
RecentVaultsPath: filepath.Join(dir, "recent-vaults.json"),
RecentRemotesPath: filepath.Join(dir, "recent-remotes.json"),
UIPreferencesPath: filepath.Join(dir, "ui-prefs.json"),
})
u.recentRemotes = []recentRemoteRecord{{
BaseURL: "https://dav.example.invalid/remote.php/dav",
Path: "files/family/keepass.kdbx",
LocalVaultPath: "/vaults/family.kdbx",
RemoteProfileID: "family-webdav",
CredentialEntryID: "remote-creds-1",
SyncMode: string(appstate.SyncModeManual),
}}
if got := u.selectedLocalVaultRemoteSyncSummary("/vaults/family.kdbx"); got != "Saved remote sync target: keepass.kdbx · dav.example.invalid · Sync manually when you choose Use Remote Sync." {
t.Fatalf("selectedLocalVaultRemoteSyncSummary() = %q, want manual sync guidance", got)
}
}
func TestUISyncDialogUsesRemoteSetupCopy(t *testing.T) { func TestUISyncDialogUsesRemoteSetupCopy(t *testing.T) {
t.Parallel() t.Parallel()
@@ -5967,6 +6050,52 @@ func TestUISyncDialogUsesRemoteSetupCopy(t *testing.T) {
} }
} }
func TestUISyncDialogUsesRemoteSettingsCopyForExistingBinding(t *testing.T) {
t.Parallel()
u := newUIWithModel("desktop", vault.Model{
Entries: []vault.Entry{{
ID: "remote-creds-1",
Title: "WebDAV Sign-In",
Username: "tjulian",
Password: "token-1",
Path: []string{"Crew", "Internet"},
}},
RemoteProfiles: []vault.RemoteProfile{{
ID: "family-webdav",
Name: "Family Vault",
Backend: vault.RemoteBackendWebDAV,
BaseURL: "https://dav.example.invalid/remote.php/dav",
Path: "files/family/keepass.kdbx",
}},
})
u.state.Section = appstate.SectionEntries
u.vaultPath.SetText("/vaults/family.kdbx")
u.recentRemotes = []recentRemoteRecord{{
BaseURL: "https://dav.example.invalid/remote.php/dav",
Path: "files/family/keepass.kdbx",
LocalVaultPath: "/vaults/family.kdbx",
RemoteProfileID: "family-webdav",
CredentialEntryID: "remote-creds-1",
SyncMode: string(appstate.SyncModeManual),
}}
u.openRemoteSyncSetupDialog()
if got := u.syncDialogTitle(); got != "Remote Sync Settings" {
t.Fatalf("syncDialogTitle() = %q, want Remote Sync Settings", got)
}
if got := u.syncDialogDescription(); got != "Review or change this vault's saved WebDAV target, credentials, and sync mode." {
t.Fatalf("syncDialogDescription() = %q, want settings guidance", got)
}
if got := u.syncDialogConfirmButtonLabel(); got != "Save Remote Sync Settings" {
t.Fatalf("syncDialogConfirmButtonLabel() = %q, want Save Remote Sync Settings", got)
}
if u.syncSetupAutomatic.Value {
t.Fatal("syncSetupAutomatic.Value = true, want false for an existing manual binding")
}
}
func TestUISyncDialogUsesAdvancedCopy(t *testing.T) { func TestUISyncDialogUsesAdvancedCopy(t *testing.T) {
t.Parallel() t.Parallel()
+5 -1
View File
@@ -332,7 +332,11 @@ func (u *ui) selectedLocalVaultCard(gtx layout.Context, path string) layout.Dime
func (u *ui) selectedLocalVaultRemoteSyncSummary(path string) string { func (u *ui) selectedLocalVaultRemoteSyncSummary(path string) string {
if record, ok := u.boundRecentRemoteForLocalVault(path); ok { if record, ok := u.boundRecentRemoteForLocalVault(path); ok {
return "Saved remote sync target: " + friendlyRecentRemoteLabel(record) summary := "Saved remote sync target: " + friendlyRecentRemoteLabel(record)
if normalizeUISyncMode(appstate.SyncMode(record.SyncMode)) == appstate.SyncModeAutomaticOnOpenSave {
return summary + " · Syncs automatically on open and save."
}
return summary + " · Sync manually when you choose Use Remote Sync."
} }
return "Open this vault to set up a WebDAV sync target for it." return "Open this vault to set up a WebDAV sync target for it."
} }