Add local vault lifecycle UI coverage

This commit is contained in:
Joe Julian
2026-03-29 11:21:30 -07:00
parent 3f29fae12f
commit 2b535a90e4
2 changed files with 307 additions and 14 deletions
+59 -14
View File
@@ -134,6 +134,8 @@ type ui struct {
state appstate.State
masterKeyMode vault.MasterKeyMode
visible []entry
currentPath []string
syncedPath []string
selectedHistoryIndex int
showPassword bool
togglePassword widget.Clickable
@@ -161,6 +163,11 @@ var (
selectedEdge = color.NRGBA{R: 73, G: 123, B: 100, A: 255}
)
const (
errVaultPathRequired = "vault path is required"
errSaveAsPathRequired = "save-as path is required"
)
func newUI(mode string) *ui {
return newUIWithSession(mode, &session.Manager{})
}
@@ -233,6 +240,7 @@ func newUIWithState(mode string, sess appstate.CurrentSession) *ui {
func (u *ui) filter() {
u.state.SearchQuery = u.search.Text()
u.syncCurrentPath()
visible, err := u.state.VisibleEntries()
if err != nil {
u.visible = nil
@@ -281,24 +289,25 @@ func (u *ui) selectedAttachmentNames() []string {
func (u *ui) showEntriesSection() {
u.state.Section = appstate.SectionEntries
u.state.NavigateToPath(nil)
u.setCurrentPath(nil)
u.filter()
}
func (u *ui) showTemplatesSection() {
u.state.Section = appstate.SectionTemplates
u.state.NavigateToPath(nil)
u.setCurrentPath(nil)
u.filter()
}
func (u *ui) showRecycleBinSection() {
u.state.Section = appstate.SectionRecycleBin
u.state.NavigateToPath(nil)
u.setCurrentPath(nil)
u.filter()
}
func (u *ui) childGroups() []string {
u.state.SearchQuery = u.search.Text()
u.syncCurrentPath()
groups, err := u.state.ChildGroups()
if err != nil {
return nil
@@ -374,10 +383,12 @@ func (u *ui) currentMasterKey() (vault.MasterKey, error) {
return vault.MasterKey{}, fmt.Errorf("key file is required")
}
default:
if password == "" {
return vault.MasterKey{}, fmt.Errorf("master password is required")
if path == "" {
if password == "" {
return vault.MasterKey{}, fmt.Errorf("master password is required")
}
return vault.MasterKey{Password: password}, nil
}
return vault.MasterKey{Password: password}, nil
}
content, err := os.ReadFile(path)
@@ -406,6 +417,7 @@ func (u *ui) createVaultAction() error {
if err := u.state.CreateVault(key); err != nil {
return err
}
u.currentPath = append([]string(nil), u.state.CurrentPath...)
u.filter()
return nil
}
@@ -415,9 +427,14 @@ func (u *ui) openVaultAction() error {
if err != nil {
return err
}
if err := u.state.OpenVault(strings.TrimSpace(u.vaultPath.Text()), key); err != nil {
path := strings.TrimSpace(u.vaultPath.Text())
if path == "" {
return errors.New(errVaultPathRequired)
}
if err := u.state.OpenVault(path, key); err != nil {
return err
}
u.currentPath = append([]string(nil), u.state.CurrentPath...)
u.filter()
return nil
}
@@ -431,7 +448,12 @@ func (u *ui) saveAction() error {
}
func (u *ui) saveAsAction() error {
if err := u.state.SaveAs(strings.TrimSpace(u.saveAsPath.Text())); err != nil {
path := strings.TrimSpace(u.saveAsPath.Text())
if path == "" {
return errors.New(errSaveAsPathRequired)
}
if err := u.state.SaveAs(path); err != nil {
return err
}
u.filter()
@@ -459,6 +481,7 @@ func (u *ui) lockAction() error {
if err := u.state.Lock(); err != nil {
return err
}
u.currentPath = append([]string(nil), u.state.CurrentPath...)
u.showPassword = false
u.filter()
return nil
@@ -472,6 +495,7 @@ func (u *ui) unlockAction() error {
if err := u.state.Unlock(key); err != nil {
return err
}
u.currentPath = append([]string(nil), u.state.CurrentPath...)
u.filter()
return nil
}
@@ -592,11 +616,30 @@ func (u *ui) detailPlaceholderMessage() string {
}
func (u *ui) ensureNavClickables() {
if len(u.breadcrumbs) < len(u.state.CurrentPath)+1 {
u.breadcrumbs = make([]widget.Clickable, len(u.state.CurrentPath)+1)
u.syncCurrentPath()
if len(u.breadcrumbs) < len(u.currentPath)+1 {
u.breadcrumbs = make([]widget.Clickable, len(u.currentPath)+1)
}
}
func (u *ui) setCurrentPath(path []string) {
u.currentPath = append([]string(nil), path...)
u.state.NavigateToPath(path)
u.syncedPath = append([]string(nil), path...)
}
func (u *ui) syncCurrentPath() {
switch {
case slices.Equal(u.currentPath, u.syncedPath) && !slices.Equal(u.state.CurrentPath, u.syncedPath):
u.currentPath = append([]string(nil), u.state.CurrentPath...)
case !slices.Equal(u.currentPath, u.syncedPath) && slices.Equal(u.state.CurrentPath, u.syncedPath):
u.state.CurrentPath = append([]string(nil), u.currentPath...)
case !slices.Equal(u.currentPath, u.syncedPath) && !slices.Equal(u.state.CurrentPath, u.syncedPath):
u.state.CurrentPath = append([]string(nil), u.currentPath...)
}
u.syncedPath = append([]string(nil), u.currentPath...)
}
func (u *ui) layout(gtx layout.Context) layout.Dimensions {
u.processShortcuts(gtx)
for u.createVault.Clicked(gtx) {
@@ -1271,9 +1314,10 @@ func (u *ui) pathBar(gtx layout.Context) layout.Dimensions {
return lbl.Layout(gtx)
}
crumbs := append([]string{"Vault"}, append([]string{}, u.state.CurrentPath...)...)
u.syncCurrentPath()
crumbs := append([]string{"Vault"}, append([]string{}, u.currentPath...)...)
if u.state.Section == appstate.SectionTemplates {
crumbs = append([]string{"Templates"}, append([]string{}, u.state.CurrentPath...)...)
crumbs = append([]string{"Templates"}, append([]string{}, u.currentPath...)...)
}
return layout.Flex{Alignment: layout.Middle}.Layout(gtx, func() []layout.FlexChild {
children := make([]layout.FlexChild, 0, len(crumbs)*2)
@@ -1283,9 +1327,9 @@ func (u *ui) pathBar(gtx layout.Context) layout.Dimensions {
children = append(children, layout.Rigid(func(gtx layout.Context) layout.Dimensions {
for u.breadcrumbs[index].Clicked(gtx) {
if index == 0 {
u.state.NavigateToPath(nil)
u.setCurrentPath(nil)
} else {
u.state.NavigateToPath(crumbs[1 : index+1])
u.setCurrentPath(crumbs[1 : index+1])
}
u.filter()
}
@@ -1323,6 +1367,7 @@ func (u *ui) groupBar(gtx layout.Context) layout.Dimensions {
children = append(children, layout.Rigid(func(gtx layout.Context) layout.Dimensions {
for u.groupClicks[idx].Clicked(gtx) {
u.state.EnterGroup(name)
u.currentPath = append([]string(nil), u.state.CurrentPath...)
u.filter()
}
btn := material.Button(u.theme, &u.groupClicks[idx], "Folder: "+name)