Unify open flow around local vaults

This commit is contained in:
Joe Julian
2026-04-06 20:24:41 -07:00
parent 99ff89de32
commit 57d6a304db
3 changed files with 240 additions and 199 deletions
+84 -8
View File
@@ -217,6 +217,13 @@ const (
syncDirectionPush syncDirection = "push"
)
type syncDialogPurpose string
const (
syncDialogPurposeAdvanced syncDialogPurpose = "advanced"
syncDialogPurposeRemoteSetup syncDialogPurpose = "remote-setup"
)
type ui struct {
mode string
theme *material.Theme
@@ -286,6 +293,7 @@ type ui struct {
toggleSyncMenu widget.Clickable
toggleMainMenu widget.Clickable
openAdvancedSync widget.Clickable
useSavedAdvancedSyncRemote widget.Clickable
openSelectedVaultRemote widget.Clickable
saveCurrentRemoteBinding widget.Clickable
openSecuritySettings widget.Clickable
@@ -446,6 +454,7 @@ type ui struct {
syncRemoteUsername widget.Editor
syncRemotePassword widget.Editor
selectedSyncRemoteCredentialEntryID string
syncDialogPurpose syncDialogPurpose
syncDialogOpen bool
syncMenuOpen bool
mainMenuOpen bool
@@ -1361,11 +1370,28 @@ func (u *ui) openAdvancedSyncDialog() {
u.syncDialogOpen = true
u.syncMenuOpen = false
u.showSyncPassword = false
u.syncDialogPurpose = syncDialogPurposeAdvanced
u.syncSourceMode = u.syncDefaultSourceMode
u.syncDirection = u.syncDefaultDirection
if strings.TrimSpace(u.syncLocalPath.Text()) == "" {
u.syncLocalPath.SetText(strings.TrimSpace(u.vaultPath.Text()))
}
u.syncSavedRemoteBindingSelection()
u.prefillAdvancedSyncRemoteFromSavedBinding()
}
func (u *ui) openRemoteSyncSetupDialog() {
u.syncDialogOpen = true
u.syncMenuOpen = false
u.showSyncPassword = false
u.syncDialogPurpose = syncDialogPurposeRemoteSetup
u.syncSourceMode = syncSourceRemote
u.syncDirection = syncDirectionPush
if strings.TrimSpace(u.syncLocalPath.Text()) == "" {
u.syncLocalPath.SetText(strings.TrimSpace(u.vaultPath.Text()))
}
u.syncSavedRemoteBindingSelection()
u.prefillAdvancedSyncRemoteFromSavedBinding()
}
func (u *ui) clearSyncLocalImport() {
@@ -2012,12 +2038,15 @@ func (u *ui) restoreStartupLifecycleTarget() {
remoteRecord, hasRemote, remoteUsedAt := u.latestRecentRemote()
switch {
case hasRemote && (localPath == "" || remoteUsedAt.After(localUsedAt)):
u.lifecycleMode = "remote"
u.applyRecentRemoteRecord(remoteRecord)
case hasRemote && strings.TrimSpace(remoteRecord.LocalVaultPath) != "" && (localPath == "" || remoteUsedAt.After(localUsedAt)):
u.lifecycleMode = "local"
u.vaultPath.SetText(strings.TrimSpace(remoteRecord.LocalVaultPath))
case localPath != "":
u.lifecycleMode = "local"
u.vaultPath.SetText(localPath)
case hasRemote:
u.lifecycleMode = "remote"
u.applyRecentRemoteRecord(remoteRecord)
}
}
@@ -2251,6 +2280,27 @@ func (u *ui) applyAdvancedSyncRemoteCredentialEntry(entry vault.Entry) {
u.syncRemotePassword.SetText(entry.Password)
}
func (u *ui) savedAdvancedSyncRemoteBinding() (appstate.ResolvedRemoteBinding, bool) {
if !u.hasOpenVault() {
return appstate.ResolvedRemoteBinding{}, false
}
_, resolved, ok, err := u.resolvedSelectedVaultRemoteBinding()
if err != nil || !ok {
return appstate.ResolvedRemoteBinding{}, false
}
return resolved, true
}
func (u *ui) prefillAdvancedSyncRemoteFromSavedBinding() {
resolved, ok := u.savedAdvancedSyncRemoteBinding()
if !ok {
return
}
u.syncRemoteBaseURL.SetText(resolved.Profile.BaseURL)
u.syncRemotePath.SetText(resolved.Profile.Path)
u.applyAdvancedSyncRemoteCredentialEntry(resolved.Credentials)
}
func (u *ui) selectVaultRemoteProfile(id string) {
id = strings.TrimSpace(id)
u.selectedVaultRemoteProfileID = id
@@ -2484,6 +2534,18 @@ func (u *ui) directRemoteSyncShortcutLabel() string {
return "Use Remote Sync"
}
func (u *ui) shouldShowRemoteSyncSetupShortcut() bool {
if !u.hasOpenVault() || u.isVaultLocked() || u.state.Section != appstate.SectionEntries {
return false
}
_, ok := u.selectedVaultRemoteBinding()
return !ok
}
func (u *ui) remoteSyncSetupShortcutLabel() string {
return "Set Up Remote Sync"
}
func remoteBindingSuffix(baseURL, path, username string) string {
sum := sha256.Sum256([]byte(strings.TrimSpace(baseURL) + "\n" + strings.TrimSpace(path) + "\n" + strings.TrimSpace(username)))
return hex.EncodeToString(sum[:8])
@@ -4105,6 +4167,9 @@ func (u *ui) layout(gtx layout.Context) layout.Dimensions {
}
}
}
for u.useSavedAdvancedSyncRemote.Clicked(gtx) {
u.openRemoteSyncSetupDialog()
}
for u.openSelectedVaultRemote.Clicked(gtx) {
if u.lifecycleBusy() {
continue
@@ -6646,16 +6711,27 @@ func (u *ui) pathBar(gtx layout.Context) layout.Dimensions {
return children
}()...)
}
if !u.shouldShowDirectRemoteSyncShortcut() {
if !u.shouldShowDirectRemoteSyncShortcut() && !u.shouldShowRemoteSyncSetupShortcut() {
return crumbBar(gtx)
}
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
children := []layout.FlexChild{
layout.Rigid(crumbBar),
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
}
if u.shouldShowDirectRemoteSyncShortcut() {
children = append(children, layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return tonedButton(gtx, u.theme, &u.openSelectedVaultRemote, u.directRemoteSyncShortcutLabel())
}),
)
}))
}
if u.shouldShowRemoteSyncSetupShortcut() {
if u.shouldShowDirectRemoteSyncShortcut() {
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.remoteSyncSetupShortcutLabel())
}))
}
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, children...)
}
func (u *ui) visibleBreadcrumbs(displayPath []string) ([]string, []int) {