Clarify start, locked, and recycle flows
This commit is contained in:
@@ -1599,6 +1599,10 @@ func (u *ui) listEmptyMessage() string {
|
||||
query := strings.TrimSpace(u.search.Text())
|
||||
if query != "" {
|
||||
switch u.state.Section {
|
||||
case appstate.SectionAPITokens:
|
||||
return fmt.Sprintf("No API tokens match %q. Clear or refine the search.", query)
|
||||
case appstate.SectionAPIAudit:
|
||||
return fmt.Sprintf("No audit events match %q. Clear or refine the search.", query)
|
||||
case appstate.SectionTemplates:
|
||||
return fmt.Sprintf("No templates match %q. Clear or refine the search.", query)
|
||||
case appstate.SectionRecycleBin:
|
||||
@@ -1615,10 +1619,10 @@ func (u *ui) listEmptyMessage() string {
|
||||
case appstate.SectionTemplates:
|
||||
return "Templates are not available in this build."
|
||||
case appstate.SectionRecycleBin:
|
||||
return "Recycle Bin is empty."
|
||||
return "Recycle Bin is empty. Deleted entries will appear here until restored."
|
||||
default:
|
||||
if len(u.displayPath()) > 0 {
|
||||
return "No entries in this group yet. Add one or open a subgroup."
|
||||
return "No entries in this group yet. Add one, search below this point, or open a subgroup."
|
||||
}
|
||||
return "Create or open a vault, then add an entry to get started."
|
||||
}
|
||||
@@ -1635,14 +1639,20 @@ func (u *ui) detailPlaceholderMessage() string {
|
||||
}
|
||||
switch u.state.Section {
|
||||
case appstate.SectionAPITokens:
|
||||
return "Select an API token or issue a new one."
|
||||
return "Select an API token, issue a new one, or search to narrow the list."
|
||||
case appstate.SectionAPIAudit:
|
||||
return "Select an audit event to inspect it."
|
||||
return "Select an audit event to inspect it, or filter the list with Search vault."
|
||||
case appstate.SectionTemplates:
|
||||
return "Select a template or start a reusable entry."
|
||||
case appstate.SectionRecycleBin:
|
||||
return "Select a recycle-bin entry to review or restore it."
|
||||
return "Select a deleted entry to review or restore it."
|
||||
default:
|
||||
if strings.TrimSpace(u.search.Text()) != "" {
|
||||
return "Select a matching entry from the filtered list or clear the search."
|
||||
}
|
||||
if len(u.displayPath()) == 0 {
|
||||
return "Select an entry from the vault root or open a group."
|
||||
}
|
||||
return "Select an entry or start a new one."
|
||||
}
|
||||
}
|
||||
@@ -2480,13 +2490,23 @@ func (u *ui) headerActions(gtx layout.Context) layout.Dimensions {
|
||||
}
|
||||
|
||||
func (u *ui) syncButtonGroup(gtx layout.Context) layout.Dimensions {
|
||||
label := "Synchronize"
|
||||
spacing := unit.Dp(4)
|
||||
if u.mode == "phone" {
|
||||
label = "Sync"
|
||||
spacing = unit.Dp(3)
|
||||
}
|
||||
return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
btn := material.Button(u.theme, &u.synchronizeVault, "Synchronize")
|
||||
btn := material.Button(u.theme, &u.synchronizeVault, label)
|
||||
btn.CornerRadius = unit.Dp(10)
|
||||
if u.mode == "phone" {
|
||||
btn.TextSize = unit.Sp(13)
|
||||
btn.Inset = layout.Inset{Top: 8, Bottom: 8, Left: 12, Right: 12}
|
||||
}
|
||||
return btn.Layout(gtx)
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(4)}.Layout),
|
||||
layout.Rigid(layout.Spacer{Width: spacing}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return u.syncMenuToggle(gtx)
|
||||
}),
|
||||
@@ -2505,6 +2525,10 @@ func (u *ui) syncMenuToggle(gtx layout.Context) layout.Dimensions {
|
||||
btn.Color = color.NRGBA{R: 255, G: 252, B: 247, A: 255}
|
||||
btn.Size = unit.Dp(18)
|
||||
btn.Inset = layout.UniformInset(unit.Dp(8))
|
||||
if u.mode == "phone" {
|
||||
btn.Size = unit.Dp(16)
|
||||
btn.Inset = layout.UniformInset(unit.Dp(7))
|
||||
}
|
||||
return btn.Layout(gtx)
|
||||
}
|
||||
|
||||
@@ -2712,6 +2736,7 @@ func (u *ui) entryRow(gtx layout.Context, click *widget.Clickable, idx int, item
|
||||
}
|
||||
row := func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.UniformInset(inset).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
showPath := strings.TrimSpace(u.search.Text()) != "" || len(u.displayPath()) == 0 || u.state.Section == appstate.SectionRecycleBin
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
lbl := material.Label(u.theme, titleSize, item.Title)
|
||||
@@ -2730,10 +2755,10 @@ func (u *ui) entryRow(gtx layout.Context, click *widget.Clickable, idx int, item
|
||||
return lbl.Layout(gtx)
|
||||
}),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if strings.TrimSpace(u.search.Text()) == "" {
|
||||
if !showPath {
|
||||
return layout.Dimensions{}
|
||||
}
|
||||
lbl := material.Label(u.theme, unit.Sp(11), strings.Join(item.Path, " / "))
|
||||
lbl := material.Label(u.theme, unit.Sp(11), strings.Join(u.displayEntryPath(item.Path), " / "))
|
||||
lbl.Color = mutedColor
|
||||
return lbl.Layout(gtx)
|
||||
}),
|
||||
@@ -2761,6 +2786,10 @@ func (u *ui) entryRow(gtx layout.Context, click *widget.Clickable, idx int, item
|
||||
}
|
||||
fillColor := selectedColor
|
||||
edgeColor := selectedEdge
|
||||
if u.state.Section == appstate.SectionRecycleBin {
|
||||
fillColor = color.NRGBA{R: 245, G: 234, B: 226, A: 255}
|
||||
edgeColor = color.NRGBA{R: 144, G: 74, B: 49, A: 255}
|
||||
}
|
||||
if u.isFocused(listFocusID(idx)) && item.ID != u.state.SelectedEntryID {
|
||||
fillColor = color.NRGBA{R: 235, G: 241, B: 238, A: 255}
|
||||
edgeColor = accentColor
|
||||
@@ -3265,7 +3294,7 @@ func (u *ui) historyRow(gtx layout.Context, click *widget.Clickable, index int,
|
||||
|
||||
func (u *ui) pathBar(gtx layout.Context) layout.Dimensions {
|
||||
if u.state.Section == appstate.SectionRecycleBin {
|
||||
lbl := material.Label(u.theme, unit.Sp(13), "Recycle Bin")
|
||||
lbl := material.Label(u.theme, unit.Sp(13), "Recycle Bin / Deleted entries")
|
||||
lbl.Color = mutedColor
|
||||
return lbl.Layout(gtx)
|
||||
}
|
||||
|
||||
@@ -426,9 +426,19 @@ func (u *ui) apiAuditRow(gtx layout.Context, click *widget.Clickable, idx int, e
|
||||
func (u *ui) apiTokenListPanel(gtx layout.Context) layout.Dimensions {
|
||||
tokens := u.apiTokens()
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
lbl := material.Label(u.theme, unit.Sp(12), "Grant scoped gRPC access to external tools. Search matches token name, client, or expiration.")
|
||||
lbl.Color = mutedColor
|
||||
return lbl.Layout(gtx)
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
|
||||
if len(tokens) == 0 {
|
||||
lbl := material.Label(u.theme, unit.Sp(14), "No API tokens match the current filter.")
|
||||
text := "No API tokens yet."
|
||||
if strings.TrimSpace(u.search.Text()) != "" {
|
||||
text = "No API tokens match the current filter."
|
||||
}
|
||||
lbl := material.Label(u.theme, unit.Sp(14), text)
|
||||
lbl.Color = mutedColor
|
||||
return lbl.Layout(gtx)
|
||||
}
|
||||
|
||||
+118
-63
@@ -15,6 +15,12 @@ import (
|
||||
|
||||
func (u *ui) lifecycleControls(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(12), "OPEN OR CREATE VAULT")
|
||||
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 layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
@@ -69,6 +75,12 @@ func (u *ui) lifecycleControls(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(12), "VAULT FILE")
|
||||
lbl.Color = mutedColor
|
||||
return lbl.Layout(gtx)
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(4)}.Layout),
|
||||
layout.Rigid(selectorEditorHelp(u.theme, "Vault Path", "Choose the existing .kdbx file to open.", &u.vaultPath, &u.pickVaultPath, "Choose File", false)),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
@@ -179,41 +191,41 @@ func (u *ui) recentVaultList(gtx layout.Context) layout.Dimensions {
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, func() []layout.FlexChild {
|
||||
children := make([]layout.FlexChild, 0, len(u.recentVaults)*2)
|
||||
for i, path := range u.recentVaults {
|
||||
index := i
|
||||
label := path
|
||||
if friendly := friendlyRecentVaultLabel(path); friendly != "" {
|
||||
label = friendly
|
||||
}
|
||||
children = append(children, layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return compactCard(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
return u.recentVaultClicks[index].Layout(gtx, 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(14), label)
|
||||
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(11), path)
|
||||
lbl.Color = mutedColor
|
||||
return lbl.Layout(gtx)
|
||||
}),
|
||||
)
|
||||
})
|
||||
maxY := gtx.Dp(unit.Dp(180))
|
||||
if gtx.Constraints.Max.Y > maxY {
|
||||
gtx.Constraints.Max.Y = maxY
|
||||
}
|
||||
if gtx.Constraints.Min.Y > gtx.Constraints.Max.Y {
|
||||
gtx.Constraints.Min.Y = gtx.Constraints.Max.Y
|
||||
}
|
||||
return material.List(u.theme, &u.lifecycleList).Layout(gtx, len(u.recentVaults), func(gtx layout.Context, i int) layout.Dimensions {
|
||||
path := u.recentVaults[i]
|
||||
label := path
|
||||
if friendly := friendlyRecentVaultLabel(path); friendly != "" {
|
||||
label = friendly
|
||||
}
|
||||
return layout.Inset{Bottom: unit.Dp(6)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
return compactCard(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
return u.recentVaultClicks[i].Layout(gtx, 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(14), label)
|
||||
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(11), path)
|
||||
lbl.Color = mutedColor
|
||||
return lbl.Layout(gtx)
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
}))
|
||||
if i < len(u.recentVaults)-1 {
|
||||
children = append(children, layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout))
|
||||
}
|
||||
}
|
||||
return children
|
||||
}()...)
|
||||
})
|
||||
})
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
@@ -233,38 +245,38 @@ func (u *ui) recentRemoteList(gtx layout.Context) layout.Dimensions {
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, func() []layout.FlexChild {
|
||||
children := make([]layout.FlexChild, 0, len(u.recentRemotes)*2)
|
||||
for i, record := range u.recentRemotes {
|
||||
index := i
|
||||
label := friendlyRecentRemoteLabel(record)
|
||||
children = append(children, layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return compactCard(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
return u.recentRemoteClicks[index].Layout(gtx, 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(14), label)
|
||||
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(11), strings.TrimSpace(record.BaseURL))
|
||||
lbl.Color = mutedColor
|
||||
return lbl.Layout(gtx)
|
||||
}),
|
||||
)
|
||||
})
|
||||
maxY := gtx.Dp(unit.Dp(180))
|
||||
if gtx.Constraints.Max.Y > maxY {
|
||||
gtx.Constraints.Max.Y = maxY
|
||||
}
|
||||
if gtx.Constraints.Min.Y > gtx.Constraints.Max.Y {
|
||||
gtx.Constraints.Min.Y = gtx.Constraints.Max.Y
|
||||
}
|
||||
return material.List(u.theme, &u.lifecycleList).Layout(gtx, len(u.recentRemotes), func(gtx layout.Context, i int) layout.Dimensions {
|
||||
record := u.recentRemotes[i]
|
||||
label := friendlyRecentRemoteLabel(record)
|
||||
return layout.Inset{Bottom: unit.Dp(6)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
return compactCard(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
return u.recentRemoteClicks[i].Layout(gtx, 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(14), label)
|
||||
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(11), strings.TrimSpace(record.BaseURL))
|
||||
lbl.Color = mutedColor
|
||||
return lbl.Layout(gtx)
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
}))
|
||||
if i < len(u.recentRemotes)-1 {
|
||||
children = append(children, layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout))
|
||||
}
|
||||
}
|
||||
return children
|
||||
}()...)
|
||||
})
|
||||
})
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
@@ -707,7 +719,50 @@ func selectorEditorHelp(th *material.Theme, label, help string, editor *widget.E
|
||||
}
|
||||
|
||||
func (u *ui) unlockPanel(gtx layout.Context) layout.Dimensions {
|
||||
targetLabel := "Locked vault"
|
||||
targetValue := "Unlock the active vault to continue."
|
||||
if u.state.Session != nil {
|
||||
if strings.TrimSpace(u.remoteBaseURL.Text()) != "" || strings.TrimSpace(u.remotePath.Text()) != "" {
|
||||
baseURL := strings.TrimSpace(u.remoteBaseURL.Text())
|
||||
path := strings.TrimSpace(u.remotePath.Text())
|
||||
targetLabel = "Remote vault"
|
||||
targetValue = friendlyRecentRemoteLabel(recentRemoteRecord{BaseURL: baseURL, Path: path})
|
||||
if strings.TrimSpace(targetValue) == "" {
|
||||
targetValue = "Remote WebDAV vault"
|
||||
}
|
||||
} else {
|
||||
path := strings.TrimSpace(u.vaultPath.Text())
|
||||
targetLabel = "Local vault"
|
||||
targetValue = friendlyRecentVaultLabel(path)
|
||||
if strings.TrimSpace(path) != "" {
|
||||
targetValue = targetValue + "\n" + path
|
||||
}
|
||||
if strings.TrimSpace(targetValue) == "" {
|
||||
targetValue = "Local vault file"
|
||||
}
|
||||
}
|
||||
}
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return compactCard(gtx, 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(12), strings.ToUpper(targetLabel))
|
||||
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.Body1(u.theme, targetValue)
|
||||
lbl.Color = accentColor
|
||||
return lbl.Layout(gtx)
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return u.masterPasswordField(gtx, "Used alone or together with a key file to unlock the vault.")
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user