Local-first remote sync and cross-platform UI parity #2

Merged
joejulian merged 53 commits from feature/local-first-remote-sync into main 2026-04-11 06:15:47 +00:00
6 changed files with 219 additions and 215 deletions
Showing only changes of commit cdf0c0c2c7 - Show all commits
+95
View File
@@ -0,0 +1,95 @@
package actions
import "git.julianfamily.org/keepassgo/internal/appstate"
type SyncMenuModel struct {
HasOpenVault bool
HasSelectedBinding bool
ShowSelectors bool
ShowShare bool
ShowSaveCurrentBinding bool
SavedBindingSummary SyncMenuBindingSummary
RemoteBaseURL string
RemotePath string
RemoteUsername string
RemotePassword string
SelectedVaultSyncMode appstate.SyncMode
}
type SyncMenuBindingSummary struct {
ProfileLabel string
CredentialLabel string
SyncLabel string
OK bool
}
func (m SyncMenuModel) SavedBindingHeading() string {
if !m.ShowSelectors {
return "Use this vault's saved remote sync target"
}
return "Use a saved remote profile from this vault"
}
func (m SyncMenuModel) OpenSelectedButtonLabel() string {
if !m.ShowSelectors {
return "Use Remote Sync"
}
return "Open Saved Remote"
}
func (m SyncMenuModel) ShowDirectRemoteSyncShortcut() bool {
return m.HasOpenVault && m.HasSelectedBinding
}
func (m SyncMenuModel) DirectRemoteSyncShortcutLabel() string {
return "Use Remote Sync"
}
func (m SyncMenuModel) ShowRemoteSyncSettingsShortcut() bool {
return m.HasOpenVault && m.HasSelectedBinding
}
func (m SyncMenuModel) RemoteSyncSettingsShortcutLabel() string {
return "Remote Sync Settings"
}
func (m SyncMenuModel) ShowRemoveRemoteSyncShortcut() bool {
return m.ShowRemoteSyncSettingsShortcut()
}
func (m SyncMenuModel) RemoveRemoteSyncShortcutLabel() string {
return "Stop Using Remote Sync"
}
func (m SyncMenuModel) ShowRemoteSyncSetupShortcut() bool {
return m.HasOpenVault && !m.HasSelectedBinding
}
func (m SyncMenuModel) RemoteSyncSetupShortcutLabel() string {
return "Set Up Remote Sync"
}
func (m SyncMenuModel) ActionLabels() []string {
labels := []string{"Open Advanced Sync"}
if m.ShowRemoteSyncSetupShortcut() {
labels = append(labels, m.RemoteSyncSetupShortcutLabel())
}
if m.ShowDirectRemoteSyncShortcut() {
labels = append(labels, m.DirectRemoteSyncShortcutLabel())
}
if m.ShowRemoteSyncSettingsShortcut() {
labels = append(labels, m.RemoteSyncSettingsShortcutLabel())
}
if m.ShowRemoveRemoteSyncShortcut() {
labels = append(labels, m.RemoveRemoteSyncShortcutLabel())
}
return labels
}
func (m SyncMenuModel) SaveCurrentRemoteBindingHeading() string {
return "Bind this local vault to the current remote target"
}
func (m SyncMenuModel) SaveCurrentRemoteBindingButtonLabel() string {
return "Save Remote In Vault"
}
+14 -14
View File
@@ -2740,60 +2740,60 @@ func (u *ui) shouldShowSavedRemoteBindingSelectors() bool {
func (u *ui) savedRemoteBindingSummary() (profileLabel, credentialLabel, syncLabel string, ok bool) {
summary := u.computeSavedRemoteBindingSummary()
return summary.profileLabel, summary.credentialLabel, summary.syncLabel, summary.ok
return summary.ProfileLabel, summary.CredentialLabel, summary.SyncLabel, summary.OK
}
func (u *ui) savedRemoteBindingHeading() string {
return u.buildSyncMenuModel().savedBindingHeading()
return u.buildSyncMenuModel().SavedBindingHeading()
}
func (u *ui) openSelectedVaultRemoteButtonLabel() string {
return u.buildSyncMenuModel().openSelectedButtonLabel()
return u.buildSyncMenuModel().OpenSelectedButtonLabel()
}
func (u *ui) shouldShowDirectRemoteSyncShortcut() bool {
if !u.hasOpenVault() || u.isVaultLocked() || u.state.Section != appstate.SectionEntries {
return false
}
return u.buildSyncMenuModel().showDirectRemoteSyncShortcut()
return u.buildSyncMenuModel().ShowDirectRemoteSyncShortcut()
}
func (u *ui) directRemoteSyncShortcutLabel() string {
return u.buildSyncMenuModel().directRemoteSyncShortcutLabel()
return u.buildSyncMenuModel().DirectRemoteSyncShortcutLabel()
}
func (u *ui) shouldShowRemoteSyncSettingsShortcut() bool {
if !u.hasOpenVault() || u.isVaultLocked() || u.state.Section != appstate.SectionEntries {
return false
}
return u.buildSyncMenuModel().showRemoteSyncSettingsShortcut()
return u.buildSyncMenuModel().ShowRemoteSyncSettingsShortcut()
}
func (u *ui) remoteSyncSettingsShortcutLabel() string {
return u.buildSyncMenuModel().remoteSyncSettingsShortcutLabel()
return u.buildSyncMenuModel().RemoteSyncSettingsShortcutLabel()
}
func (u *ui) shouldShowRemoveRemoteSyncShortcut() bool {
return u.buildSyncMenuModel().showRemoveRemoteSyncShortcut()
return u.buildSyncMenuModel().ShowRemoveRemoteSyncShortcut()
}
func (u *ui) removeRemoteSyncShortcutLabel() string {
return u.buildSyncMenuModel().removeRemoteSyncShortcutLabel()
return u.buildSyncMenuModel().RemoveRemoteSyncShortcutLabel()
}
func (u *ui) shouldShowRemoteSyncSetupShortcut() bool {
if !u.hasOpenVault() || u.isVaultLocked() || u.state.Section != appstate.SectionEntries {
return false
}
return u.buildSyncMenuModel().showRemoteSyncSetupShortcut()
return u.buildSyncMenuModel().ShowRemoteSyncSetupShortcut()
}
func (u *ui) remoteSyncSetupShortcutLabel() string {
return u.buildSyncMenuModel().remoteSyncSetupShortcutLabel()
return u.buildSyncMenuModel().RemoteSyncSetupShortcutLabel()
}
func (u *ui) syncMenuActionLabels() []string {
return u.buildSyncMenuModel().actionLabels()
return u.buildSyncMenuModel().ActionLabels()
}
func remoteBindingSuffix(baseURL, path, username string) string {
@@ -2901,11 +2901,11 @@ func (u *ui) removeSelectedRemoteBindingAction() error {
}
func (u *ui) saveCurrentRemoteBindingHeading() string {
return u.buildSyncMenuModel().saveCurrentRemoteBindingHeading()
return u.buildSyncMenuModel().SaveCurrentRemoteBindingHeading()
}
func (u *ui) saveCurrentRemoteBindingButtonLabel() string {
return u.buildSyncMenuModel().saveCurrentRemoteBindingButtonLabel()
return u.buildSyncMenuModel().SaveCurrentRemoteBindingButtonLabel()
}
func (u *ui) materializeCurrentRemoteCache() error {
+31 -31
View File
@@ -439,14 +439,14 @@ func TestBuildSyncMenuModelForUnboundVault(t *testing.T) {
u.state.Section = appstate.SectionEntries
model := u.buildSyncMenuModel()
if !model.showRemoteSyncSetupShortcut() {
t.Fatal("model.showRemoteSyncSetupShortcut() = false, want true for an unbound open vault")
if !model.ShowRemoteSyncSetupShortcut() {
t.Fatal("model.ShowRemoteSyncSetupShortcut() = false, want true for an unbound open vault")
}
if model.showDirectRemoteSyncShortcut() {
t.Fatal("model.showDirectRemoteSyncShortcut() = true, want false without a saved binding")
if model.ShowDirectRemoteSyncShortcut() {
t.Fatal("model.ShowDirectRemoteSyncShortcut() = true, want false without a saved binding")
}
if got := model.actionLabels(); !slices.Equal(got, []string{"Open Advanced Sync", "Set Up Remote Sync"}) {
t.Fatalf("model.actionLabels() = %v, want [Open Advanced Sync Set Up Remote Sync]", got)
if got := model.ActionLabels(); !slices.Equal(got, []string{"Open Advanced Sync", "Set Up Remote Sync"}) {
t.Fatalf("model.ActionLabels() = %v, want [Open Advanced Sync Set Up Remote Sync]", got)
}
}
@@ -477,30 +477,30 @@ func TestBuildSyncMenuModelForBoundVault(t *testing.T) {
u.selectedVaultRemoteSyncMode = appstate.SyncModeAutomaticOnOpenSave
model := u.buildSyncMenuModel()
if model.showRemoteSyncSetupShortcut() {
t.Fatal("model.showRemoteSyncSetupShortcut() = true, want false for a bound vault")
if model.ShowRemoteSyncSetupShortcut() {
t.Fatal("model.ShowRemoteSyncSetupShortcut() = true, want false for a bound vault")
}
if !model.showDirectRemoteSyncShortcut() {
t.Fatal("model.showDirectRemoteSyncShortcut() = false, want true for a bound vault")
if !model.ShowDirectRemoteSyncShortcut() {
t.Fatal("model.ShowDirectRemoteSyncShortcut() = false, want true for a bound vault")
}
if !model.showRemoteSyncSettingsShortcut() {
t.Fatal("model.showRemoteSyncSettingsShortcut() = false, want true for a bound vault")
if !model.ShowRemoteSyncSettingsShortcut() {
t.Fatal("model.ShowRemoteSyncSettingsShortcut() = false, want true for a bound vault")
}
if !model.showRemoveRemoteSyncShortcut() {
t.Fatal("model.showRemoveRemoteSyncShortcut() = false, want true for a bound vault")
if !model.ShowRemoveRemoteSyncShortcut() {
t.Fatal("model.ShowRemoveRemoteSyncShortcut() = false, want true for a bound vault")
}
summary := model.savedBindingSummary
if !summary.ok {
t.Fatal("model.savedBindingSummary.ok = false, want true")
summary := model.SavedBindingSummary
if !summary.OK {
t.Fatal("model.SavedBindingSummary.OK = false, want true")
}
if summary.profileLabel != "Downtown Mint" {
t.Fatalf("model.savedBindingSummary.profileLabel = %q, want Downtown Mint", summary.profileLabel)
if summary.ProfileLabel != "Downtown Mint" {
t.Fatalf("model.SavedBindingSummary.ProfileLabel = %q, want Downtown Mint", summary.ProfileLabel)
}
if summary.credentialLabel != "Mint Credentials · verbal-kint" {
t.Fatalf("model.savedBindingSummary.credentialLabel = %q, want Mint Credentials · verbal-kint", summary.credentialLabel)
if summary.CredentialLabel != "Mint Credentials · verbal-kint" {
t.Fatalf("model.SavedBindingSummary.CredentialLabel = %q, want Mint Credentials · verbal-kint", summary.CredentialLabel)
}
if summary.syncLabel != "Syncs automatically on open and save." {
t.Fatalf("model.savedBindingSummary.syncLabel = %q, want automatic-sync summary", summary.syncLabel)
if summary.SyncLabel != "Syncs automatically on open and save." {
t.Fatalf("model.SavedBindingSummary.SyncLabel = %q, want automatic-sync summary", summary.SyncLabel)
}
}
@@ -515,8 +515,8 @@ func TestBuildSyncMenuModelShowsSaveCurrentBindingOnlyWithCompleteRemoteInput(t
u.state.Section = appstate.SectionEntries
model := u.buildSyncMenuModel()
if model.showSaveCurrentBinding {
t.Fatal("model.showSaveCurrentBinding = true, want false without remote input")
if model.ShowSaveCurrentBinding {
t.Fatal("model.ShowSaveCurrentBinding = true, want false without remote input")
}
u.remoteBaseURL.SetText("https://mint.example.invalid/remote.php/dav")
@@ -525,14 +525,14 @@ func TestBuildSyncMenuModelShowsSaveCurrentBindingOnlyWithCompleteRemoteInput(t
u.remotePassword.SetText("kobayashi")
model = u.buildSyncMenuModel()
if !model.showSaveCurrentBinding {
t.Fatal("model.showSaveCurrentBinding = false, want true with complete remote input")
if !model.ShowSaveCurrentBinding {
t.Fatal("model.ShowSaveCurrentBinding = false, want true with complete remote input")
}
if got := model.saveCurrentRemoteBindingHeading(); got != "Bind this local vault to the current remote target" {
t.Fatalf("model.saveCurrentRemoteBindingHeading() = %q, want vault binding guidance", got)
if got := model.SaveCurrentRemoteBindingHeading(); got != "Bind this local vault to the current remote target" {
t.Fatalf("model.SaveCurrentRemoteBindingHeading() = %q, want vault binding guidance", got)
}
if got := model.saveCurrentRemoteBindingButtonLabel(); got != "Save Remote In Vault" {
t.Fatalf("model.saveCurrentRemoteBindingButtonLabel() = %q, want Save Remote In Vault", got)
if got := model.SaveCurrentRemoteBindingButtonLabel(); got != "Save Remote In Vault" {
t.Fatalf("model.SaveCurrentRemoteBindingButtonLabel() = %q, want Save Remote In Vault", got)
}
}
+28 -28
View File
@@ -206,29 +206,29 @@ func (u *ui) syncMenu(gtx layout.Context) layout.Dimensions {
return tonedButton(gtx, u.theme, &u.openAdvancedSync, "Open Advanced Sync")
},
}
if model.showShare {
if model.ShowShare {
actionRows = append(actionRows, func(gtx layout.Context) layout.Dimensions {
return tonedButton(gtx, u.theme, &u.shareCurrentVault, "Share Vault")
})
}
if model.showRemoteSyncSetupShortcut() {
if model.ShowRemoteSyncSetupShortcut() {
actionRows = append(actionRows, func(gtx layout.Context) layout.Dimensions {
return tonedButton(gtx, u.theme, &u.useSavedAdvancedSyncRemote, model.remoteSyncSetupShortcutLabel())
return tonedButton(gtx, u.theme, &u.useSavedAdvancedSyncRemote, model.RemoteSyncSetupShortcutLabel())
})
}
if model.showDirectRemoteSyncShortcut() {
if model.ShowDirectRemoteSyncShortcut() {
actionRows = append(actionRows, func(gtx layout.Context) layout.Dimensions {
return tonedButton(gtx, u.theme, &u.openSelectedVaultRemote, model.directRemoteSyncShortcutLabel())
return tonedButton(gtx, u.theme, &u.openSelectedVaultRemote, model.DirectRemoteSyncShortcutLabel())
})
}
if model.showRemoteSyncSettingsShortcut() {
if model.ShowRemoteSyncSettingsShortcut() {
actionRows = append(actionRows, func(gtx layout.Context) layout.Dimensions {
return tonedButton(gtx, u.theme, &u.useSavedAdvancedSyncRemote, model.remoteSyncSettingsShortcutLabel())
return tonedButton(gtx, u.theme, &u.useSavedAdvancedSyncRemote, model.RemoteSyncSettingsShortcutLabel())
})
}
if model.showRemoveRemoteSyncShortcut() {
if model.ShowRemoveRemoteSyncShortcut() {
actionRows = append(actionRows, func(gtx layout.Context) layout.Dimensions {
return tonedButton(gtx, u.theme, &u.removeSelectedRemoteBinding, model.removeRemoteSyncShortcutLabel())
return tonedButton(gtx, u.theme, &u.removeSelectedRemoteBinding, model.RemoveRemoteSyncShortcutLabel())
})
}
actionWidth := menuActionWidth(gtx, actionRows)
@@ -241,7 +241,7 @@ func (u *ui) syncMenu(gtx layout.Context) layout.Dimensions {
}),
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
if !model.showShare {
if !model.ShowShare {
return layout.Dimensions{}
}
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
@@ -259,42 +259,42 @@ func (u *ui) syncMenu(gtx layout.Context) layout.Dimensions {
})
}),
}
if model.showRemoteSyncSetupShortcut() {
if model.ShowRemoteSyncSetupShortcut() {
rows = append(rows,
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return rightAlignedMenuAction(gtx, actionWidth, func(gtx layout.Context) layout.Dimensions {
return tonedButton(gtx, u.theme, &u.useSavedAdvancedSyncRemote, model.remoteSyncSetupShortcutLabel())
return tonedButton(gtx, u.theme, &u.useSavedAdvancedSyncRemote, model.RemoteSyncSetupShortcutLabel())
})
}),
)
}
if model.showDirectRemoteSyncShortcut() {
if model.ShowDirectRemoteSyncShortcut() {
rows = append(rows,
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return rightAlignedMenuAction(gtx, actionWidth, func(gtx layout.Context) layout.Dimensions {
return tonedButton(gtx, u.theme, &u.openSelectedVaultRemote, model.directRemoteSyncShortcutLabel())
return tonedButton(gtx, u.theme, &u.openSelectedVaultRemote, model.DirectRemoteSyncShortcutLabel())
})
}),
)
}
if model.showRemoteSyncSettingsShortcut() {
if model.ShowRemoteSyncSettingsShortcut() {
rows = append(rows,
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return rightAlignedMenuAction(gtx, actionWidth, func(gtx layout.Context) layout.Dimensions {
return tonedButton(gtx, u.theme, &u.useSavedAdvancedSyncRemote, model.remoteSyncSettingsShortcutLabel())
return tonedButton(gtx, u.theme, &u.useSavedAdvancedSyncRemote, model.RemoteSyncSettingsShortcutLabel())
})
}),
)
}
if model.showRemoveRemoteSyncShortcut() {
if model.ShowRemoveRemoteSyncShortcut() {
rows = append(rows,
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return rightAlignedMenuAction(gtx, actionWidth, func(gtx layout.Context) layout.Dimensions {
return tonedButton(gtx, u.theme, &u.removeSelectedRemoteBinding, model.removeRemoteSyncShortcutLabel())
return tonedButton(gtx, u.theme, &u.removeSelectedRemoteBinding, model.RemoveRemoteSyncShortcutLabel())
})
}),
)
@@ -303,36 +303,36 @@ func (u *ui) syncMenu(gtx layout.Context) layout.Dimensions {
rows = append(rows,
layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
lbl := material.Label(u.theme, unit.Sp(11), model.savedBindingHeading())
lbl := material.Label(u.theme, unit.Sp(11), model.SavedBindingHeading())
lbl.Color = mutedColor
return lbl.Layout(gtx)
}),
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
)
if !model.showSelectors {
if !model.ShowSelectors {
rows = append(rows,
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
summary := model.savedBindingSummary
if !summary.ok {
summary := model.SavedBindingSummary
if !summary.OK {
return layout.Dimensions{}
}
return layout.Background{}.Layout(gtx, fill(color.NRGBA{R: 242, G: 245, B: 240, A: 255}), func(gtx layout.Context) layout.Dimensions {
return layout.UniformInset(unit.Dp(10)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
lbl := material.Label(u.theme, unit.Sp(13), summary.profileLabel)
lbl := material.Label(u.theme, unit.Sp(13), summary.ProfileLabel)
lbl.Color = accentColor
return lbl.Layout(gtx)
}),
layout.Rigid(layout.Spacer{Height: unit.Dp(2)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
lbl := material.Label(u.theme, unit.Sp(12), "Credential: "+summary.credentialLabel)
lbl := material.Label(u.theme, unit.Sp(12), "Credential: "+summary.CredentialLabel)
lbl.Color = mutedColor
return lbl.Layout(gtx)
}),
layout.Rigid(layout.Spacer{Height: unit.Dp(2)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
lbl := material.Label(u.theme, unit.Sp(12), summary.syncLabel)
lbl := material.Label(u.theme, unit.Sp(12), summary.SyncLabel)
lbl.Color = mutedColor
return lbl.Layout(gtx)
}),
@@ -395,17 +395,17 @@ func (u *ui) syncMenu(gtx layout.Context) layout.Dimensions {
}
}
}
if model.showSaveCurrentBinding {
if model.ShowSaveCurrentBinding {
rows = append(rows,
layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
lbl := material.Label(u.theme, unit.Sp(11), model.saveCurrentRemoteBindingHeading())
lbl := material.Label(u.theme, unit.Sp(11), model.SaveCurrentRemoteBindingHeading())
lbl.Color = mutedColor
return lbl.Layout(gtx)
}),
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return tonedButton(gtx, u.theme, &u.saveCurrentRemoteBinding, model.saveCurrentRemoteBindingButtonLabel())
return tonedButton(gtx, u.theme, &u.saveCurrentRemoteBinding, model.SaveCurrentRemoteBindingButtonLabel())
}),
)
}
+51
View File
@@ -0,0 +1,51 @@
package appui
import (
"runtime"
"strings"
"git.julianfamily.org/keepassgo/internal/appstate"
appuiactions "git.julianfamily.org/keepassgo/internal/appui/actions"
)
func (u *ui) buildSyncMenuModel() appuiactions.SyncMenuModel {
model := appuiactions.SyncMenuModel{
HasOpenVault: u.hasOpenVault(),
ShowSelectors: u.shouldShowSavedRemoteBindingSelectors(),
ShowShare: supportsVaultShare(runtime.GOOS) && u.vaultSharer != nil && strings.TrimSpace(u.currentShareableVaultPath()) != "",
RemoteBaseURL: strings.TrimSpace(u.remoteBaseURL.Text()),
RemotePath: strings.TrimSpace(u.remotePath.Text()),
RemoteUsername: strings.TrimSpace(u.remoteUsername.Text()),
RemotePassword: u.remotePassword.Text(),
SelectedVaultSyncMode: normalizeUISyncMode(u.selectedVaultRemoteSyncMode),
}
_, model.HasSelectedBinding = u.selectedVaultRemoteBinding()
model.SavedBindingSummary = u.computeSavedRemoteBindingSummary()
model.ShowSaveCurrentBinding = model.HasOpenVault && model.RemoteBaseURL != "" && model.RemotePath != "" && model.RemoteUsername != "" && model.RemotePassword != ""
return model
}
func (u *ui) computeSavedRemoteBindingSummary() appuiactions.SyncMenuBindingSummary {
profile, ok := u.selectedVaultRemoteProfile()
if !ok {
return appuiactions.SyncMenuBindingSummary{}
}
entry, ok := u.selectedVaultRemoteCredentialEntry()
if !ok {
return appuiactions.SyncMenuBindingSummary{}
}
credentialLabel := entry.Title
if strings.TrimSpace(entry.Username) != "" {
credentialLabel += " · " + strings.TrimSpace(entry.Username)
}
syncLabel := "Sync manually when you choose Use Remote Sync."
if normalizeUISyncMode(u.selectedVaultRemoteSyncMode) == appstate.SyncModeAutomaticOnOpenSave {
syncLabel = "Syncs automatically on open and save."
}
return appuiactions.SyncMenuBindingSummary{
ProfileLabel: profile.Name,
CredentialLabel: credentialLabel,
SyncLabel: syncLabel,
OK: true,
}
}
-142
View File
@@ -1,142 +0,0 @@
package appui
import (
"runtime"
"strings"
"git.julianfamily.org/keepassgo/internal/appstate"
)
type syncMenuModel struct {
hasOpenVault bool
hasSelectedBinding bool
showSelectors bool
showShare bool
showSaveCurrentBinding bool
savedBindingSummary syncMenuBindingSummary
remoteBaseURL string
remotePath string
remoteUsername string
remotePassword string
selectedVaultSyncMode appstate.SyncMode
}
type syncMenuBindingSummary struct {
profileLabel string
credentialLabel string
syncLabel string
ok bool
}
func (u *ui) buildSyncMenuModel() syncMenuModel {
model := syncMenuModel{
hasOpenVault: u.hasOpenVault(),
showSelectors: u.shouldShowSavedRemoteBindingSelectors(),
showShare: supportsVaultShare(runtime.GOOS) && u.vaultSharer != nil && strings.TrimSpace(u.currentShareableVaultPath()) != "",
remoteBaseURL: strings.TrimSpace(u.remoteBaseURL.Text()),
remotePath: strings.TrimSpace(u.remotePath.Text()),
remoteUsername: strings.TrimSpace(u.remoteUsername.Text()),
remotePassword: u.remotePassword.Text(),
selectedVaultSyncMode: normalizeUISyncMode(u.selectedVaultRemoteSyncMode),
}
_, model.hasSelectedBinding = u.selectedVaultRemoteBinding()
model.savedBindingSummary = u.computeSavedRemoteBindingSummary()
model.showSaveCurrentBinding = model.hasOpenVault && model.remoteBaseURL != "" && model.remotePath != "" && model.remoteUsername != "" && model.remotePassword != ""
return model
}
func (u *ui) computeSavedRemoteBindingSummary() syncMenuBindingSummary {
profile, ok := u.selectedVaultRemoteProfile()
if !ok {
return syncMenuBindingSummary{}
}
entry, ok := u.selectedVaultRemoteCredentialEntry()
if !ok {
return syncMenuBindingSummary{}
}
credentialLabel := entry.Title
if strings.TrimSpace(entry.Username) != "" {
credentialLabel += " · " + strings.TrimSpace(entry.Username)
}
syncLabel := "Sync manually when you choose Use Remote Sync."
if normalizeUISyncMode(u.selectedVaultRemoteSyncMode) == appstate.SyncModeAutomaticOnOpenSave {
syncLabel = "Syncs automatically on open and save."
}
return syncMenuBindingSummary{
profileLabel: profile.Name,
credentialLabel: credentialLabel,
syncLabel: syncLabel,
ok: true,
}
}
func (m syncMenuModel) savedBindingHeading() string {
if !m.showSelectors {
return "Use this vault's saved remote sync target"
}
return "Use a saved remote profile from this vault"
}
func (m syncMenuModel) openSelectedButtonLabel() string {
if !m.showSelectors {
return "Use Remote Sync"
}
return "Open Saved Remote"
}
func (m syncMenuModel) showDirectRemoteSyncShortcut() bool {
return m.hasOpenVault && m.hasSelectedBinding
}
func (m syncMenuModel) directRemoteSyncShortcutLabel() string {
return "Use Remote Sync"
}
func (m syncMenuModel) showRemoteSyncSettingsShortcut() bool {
return m.hasOpenVault && m.hasSelectedBinding
}
func (m syncMenuModel) remoteSyncSettingsShortcutLabel() string {
return "Remote Sync Settings"
}
func (m syncMenuModel) showRemoveRemoteSyncShortcut() bool {
return m.showRemoteSyncSettingsShortcut()
}
func (m syncMenuModel) removeRemoteSyncShortcutLabel() string {
return "Stop Using Remote Sync"
}
func (m syncMenuModel) showRemoteSyncSetupShortcut() bool {
return m.hasOpenVault && !m.hasSelectedBinding
}
func (m syncMenuModel) remoteSyncSetupShortcutLabel() string {
return "Set Up Remote Sync"
}
func (m syncMenuModel) actionLabels() []string {
labels := []string{"Open Advanced Sync"}
if m.showRemoteSyncSetupShortcut() {
labels = append(labels, m.remoteSyncSetupShortcutLabel())
}
if m.showDirectRemoteSyncShortcut() {
labels = append(labels, m.directRemoteSyncShortcutLabel())
}
if m.showRemoteSyncSettingsShortcut() {
labels = append(labels, m.remoteSyncSettingsShortcutLabel())
}
if m.showRemoveRemoteSyncShortcut() {
labels = append(labels, m.removeRemoteSyncShortcutLabel())
}
return labels
}
func (m syncMenuModel) saveCurrentRemoteBindingHeading() string {
return "Bind this local vault to the current remote target"
}
func (m syncMenuModel) saveCurrentRemoteBindingButtonLabel() string {
return "Save Remote In Vault"
}