Match Advanced Sync credentials by URL
This commit is contained in:
@@ -395,6 +395,7 @@ type ui struct {
|
||||
recentRemoteClicks []widget.Clickable
|
||||
vaultRemoteProfileClicks []widget.Clickable
|
||||
vaultRemoteCredentialClicks []widget.Clickable
|
||||
syncRemoteCredentialClicks []widget.Clickable
|
||||
removeCustomFields []widget.Clickable
|
||||
state appstate.State
|
||||
visible []entry
|
||||
@@ -444,6 +445,7 @@ type ui struct {
|
||||
syncRemotePath widget.Editor
|
||||
syncRemoteUsername widget.Editor
|
||||
syncRemotePassword widget.Editor
|
||||
selectedSyncRemoteCredentialEntryID string
|
||||
syncDialogOpen bool
|
||||
syncMenuOpen bool
|
||||
mainMenuOpen bool
|
||||
@@ -2177,6 +2179,36 @@ func (u *ui) availableRemoteCredentialEntries() []vault.Entry {
|
||||
return entries
|
||||
}
|
||||
|
||||
func normalizeRemoteCredentialURL(raw string) string {
|
||||
raw = strings.TrimSpace(raw)
|
||||
raw = strings.TrimRight(raw, "/")
|
||||
return raw
|
||||
}
|
||||
|
||||
func (u *ui) matchingAdvancedSyncRemoteCredentialEntries() []vault.Entry {
|
||||
if sanitizeSyncSourceMode(u.syncSourceMode) != syncSourceRemote {
|
||||
return nil
|
||||
}
|
||||
baseURL := normalizeRemoteCredentialURL(u.syncRemoteBaseURL.Text())
|
||||
if baseURL == "" {
|
||||
return nil
|
||||
}
|
||||
var matches []vault.Entry
|
||||
for _, entry := range u.availableRemoteCredentialEntries() {
|
||||
if normalizeRemoteCredentialURL(entry.URL) != baseURL {
|
||||
continue
|
||||
}
|
||||
matches = append(matches, entry)
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
func (u *ui) applyAdvancedSyncRemoteCredentialEntry(entry vault.Entry) {
|
||||
u.selectedSyncRemoteCredentialEntryID = strings.TrimSpace(entry.ID)
|
||||
u.syncRemoteUsername.SetText(strings.TrimSpace(entry.Username))
|
||||
u.syncRemotePassword.SetText(entry.Password)
|
||||
}
|
||||
|
||||
func (u *ui) selectVaultRemoteProfile(id string) {
|
||||
id = strings.TrimSpace(id)
|
||||
u.selectedVaultRemoteProfileID = id
|
||||
@@ -4023,6 +4055,14 @@ func (u *ui) layout(gtx layout.Context) layout.Dimensions {
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := range u.syncRemoteCredentialClicks {
|
||||
for u.syncRemoteCredentialClicks[i].Clicked(gtx) {
|
||||
entries := u.matchingAdvancedSyncRemoteCredentialEntries()
|
||||
if i < len(entries) {
|
||||
u.applyAdvancedSyncRemoteCredentialEntry(entries[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
for u.openSelectedVaultRemote.Clicked(gtx) {
|
||||
if u.lifecycleBusy() {
|
||||
continue
|
||||
@@ -4766,6 +4806,10 @@ func (u *ui) approvalDialogContent(gtx layout.Context) layout.Dimensions {
|
||||
}
|
||||
|
||||
func (u *ui) syncDialogContent(gtx layout.Context) layout.Dimensions {
|
||||
matchingCredentials := u.matchingAdvancedSyncRemoteCredentialEntries()
|
||||
if len(u.syncRemoteCredentialClicks) < len(matchingCredentials) {
|
||||
u.syncRemoteCredentialClicks = make([]widget.Clickable, len(matchingCredentials))
|
||||
}
|
||||
return material.List(u.theme, &u.lifecycleList).Layout(gtx, 1, func(gtx layout.Context, _ int) layout.Dimensions {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
@@ -4814,7 +4858,7 @@ func (u *ui) syncDialogContent(gtx layout.Context) layout.Dimensions {
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if u.syncSourceMode == syncSourceRemote {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
children := []layout.FlexChild{
|
||||
layout.Rigid(labeledEditorHelp(u.theme, "Remote Base URL", "WebDAV base URL for the other source.", &u.syncRemoteBaseURL, false)),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(labeledEditorHelp(u.theme, "Remote Path", "Path to the other remote .kdbx file.", &u.syncRemotePath, false)),
|
||||
@@ -4824,6 +4868,40 @@ func (u *ui) syncDialogContent(gtx layout.Context) layout.Dimensions {
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return u.syncPasswordField(gtx)
|
||||
}),
|
||||
}
|
||||
if len(matchingCredentials) > 0 {
|
||||
children = append(children,
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
lbl := material.Label(u.theme, unit.Sp(11), "Matching vault credentials")
|
||||
lbl.Color = mutedColor
|
||||
return lbl.Layout(gtx)
|
||||
}),
|
||||
)
|
||||
for i, entry := range matchingCredentials {
|
||||
i := i
|
||||
entry := entry
|
||||
children = append(children,
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(4)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
label := entry.Title
|
||||
if strings.TrimSpace(entry.Username) != "" {
|
||||
label += " · " + strings.TrimSpace(entry.Username)
|
||||
}
|
||||
selected := strings.TrimSpace(u.selectedSyncRemoteCredentialEntryID) == entry.ID
|
||||
return recentSelectionCard(gtx, selected, func(gtx layout.Context) layout.Dimensions {
|
||||
return u.syncRemoteCredentialClicks[i].Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
lbl := material.Label(u.theme, unit.Sp(13), label)
|
||||
lbl.Color = accentColor
|
||||
return lbl.Layout(gtx)
|
||||
})
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
children...,
|
||||
)
|
||||
}
|
||||
if supportsDesktopFilePicker(runtime.GOOS) {
|
||||
|
||||
Reference in New Issue
Block a user