Collapse phone subgroup browser after navigation

This commit is contained in:
Joe Julian
2026-04-02 09:41:33 -07:00
parent 7fc0661b32
commit e4ebb2e723
2 changed files with 84 additions and 4 deletions
+44 -4
View File
@@ -312,6 +312,7 @@ type ui struct {
cancelDeleteGroup widget.Clickable cancelDeleteGroup widget.Clickable
addCustomField widget.Clickable addCustomField widget.Clickable
toggleGroupControls widget.Clickable toggleGroupControls widget.Clickable
togglePhoneGroupBrowser widget.Clickable
toggleLifecycleAdvanced widget.Clickable toggleLifecycleAdvanced widget.Clickable
toggleHistory widget.Clickable toggleHistory widget.Clickable
togglePasswordInline widget.Clickable togglePasswordInline widget.Clickable
@@ -386,6 +387,7 @@ type ui struct {
splitBase float32 splitBase float32
splitStartY float32 splitStartY float32
phoneSpan int phoneSpan int
phoneGroupBrowserExpanded bool
eyeIcon *widget.Icon eyeIcon *widget.Icon
eyeOffIcon *widget.Icon eyeOffIcon *widget.Icon
copyIcon *widget.Icon copyIcon *widget.Icon
@@ -585,6 +587,7 @@ func newUIWithState(mode string, sess appstate.CurrentSession, paths statePaths)
apiPolicyGroupScope: true, apiPolicyGroupScope: true,
autofillNoticePreference: autofillNoticeAll, autofillNoticePreference: autofillNoticeAll,
backgroundResults: make(chan backgroundActionResult, 8), backgroundResults: make(chan backgroundActionResult, 8),
phoneGroupBrowserExpanded: true,
} }
u.apiPolicyAllow.Value = true u.apiPolicyAllow.Value = true
u.apiPolicyGroupScopeW.Value = true u.apiPolicyGroupScopeW.Value = true
@@ -2616,10 +2619,18 @@ func (u *ui) ensureNavClickables() {
} }
} }
func (u *ui) syncPhoneGroupBrowser(path []string) {
if u.mode != "phone" {
return
}
u.phoneGroupBrowserExpanded = len(u.displayEntryPath(path)) == 0
}
func (u *ui) setCurrentPath(path []string) { func (u *ui) setCurrentPath(path []string) {
u.currentPath = append([]string(nil), path...) u.currentPath = append([]string(nil), path...)
u.state.NavigateToPath(path) u.state.NavigateToPath(path)
u.syncedPath = append([]string(nil), path...) u.syncedPath = append([]string(nil), path...)
u.syncPhoneGroupBrowser(path)
u.noteCurrentVaultPath() u.noteCurrentVaultPath()
u.clearDeleteGroupConfirmation() u.clearDeleteGroupConfirmation()
} }
@@ -5176,11 +5187,28 @@ func (u *ui) groupBar(gtx layout.Context) layout.Dimensions {
atRoot := len(displayPath) == 0 atRoot := len(displayPath) == 0
return compactCard(gtx, func(gtx layout.Context) layout.Dimensions { return compactCard(gtx, func(gtx layout.Context) layout.Dimensions {
if u.mode == "phone" { if u.mode == "phone" {
if atRoot {
u.phoneGroupBrowserExpanded = true
}
toggleLabel := "Browse"
if u.phoneGroupBrowserExpanded {
toggleLabel = "Hide"
}
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions { layout.Rigid(func(gtx layout.Context) layout.Dimensions {
lbl := material.Label(u.theme, unit.Sp(12), "GROUPS") return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
lbl.Color = mutedColor layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
return lbl.Layout(gtx) lbl := material.Label(u.theme, unit.Sp(12), "GROUPS")
lbl.Color = mutedColor
return lbl.Layout(gtx)
}),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
if len(groups) == 0 && atRoot {
return layout.Dimensions{}
}
return tonedButton(gtx, u.theme, &u.togglePhoneGroupBrowser, toggleLabel)
}),
)
}), }),
layout.Rigid(func(gtx layout.Context) layout.Dimensions { layout.Rigid(func(gtx layout.Context) layout.Dimensions {
if atRoot { if atRoot {
@@ -5211,8 +5239,19 @@ func (u *ui) groupBar(gtx layout.Context) layout.Dimensions {
) )
}) })
}), }),
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions { layout.Rigid(func(gtx layout.Context) layout.Dimensions {
for u.togglePhoneGroupBrowser.Clicked(gtx) {
u.phoneGroupBrowserExpanded = !u.phoneGroupBrowserExpanded
}
if !u.phoneGroupBrowserExpanded {
return layout.Dimensions{}
}
return layout.Spacer{Height: unit.Dp(8)}.Layout(gtx)
}),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
if !u.phoneGroupBrowserExpanded {
return layout.Dimensions{}
}
if len(groups) == 0 { if len(groups) == 0 {
lbl := material.Label(u.theme, unit.Sp(12), "No subgroups here.") lbl := material.Label(u.theme, unit.Sp(12), "No subgroups here.")
lbl.Color = mutedColor lbl.Color = mutedColor
@@ -5232,6 +5271,7 @@ func (u *ui) groupBar(gtx layout.Context) layout.Dimensions {
for u.groupClicks[idx].Clicked(gtx) { for u.groupClicks[idx].Clicked(gtx) {
u.state.EnterGroup(name) u.state.EnterGroup(name)
u.currentPath = append([]string(nil), u.state.CurrentPath...) u.currentPath = append([]string(nil), u.state.CurrentPath...)
u.syncPhoneGroupBrowser(u.currentPath)
u.filter() u.filter()
} }
return tonedButton(gtx, u.theme, &u.groupClicks[idx], name) return tonedButton(gtx, u.theme, &u.groupClicks[idx], name)
+40
View File
@@ -734,6 +734,46 @@ func TestUIPhoneGroupBarWithChildGroupsDoesNotPanic(t *testing.T) {
_ = u.groupBar(gtx) _ = u.groupBar(gtx)
} }
func TestUIPhoneGroupBrowserStartsExpandedAtRootAndCollapsesInVisibleSubgroups(t *testing.T) {
t.Parallel()
u := newUIWithModel("phone", vault.Model{
Groups: [][]string{
{"Crew"},
{"Crew", "Internet"},
},
})
u.setCurrentPath(nil)
if !u.phoneGroupBrowserExpanded {
t.Fatal("phoneGroupBrowserExpanded = false at root, want true")
}
u.setCurrentPath([]string{"Crew", "Internet"})
if u.phoneGroupBrowserExpanded {
t.Fatal("phoneGroupBrowserExpanded = true inside visible subgroup, want false")
}
}
func TestUIPhoneGroupBrowserToggleDoesNotChangeCurrentGroupToolsState(t *testing.T) {
t.Parallel()
u := newUIWithModel("phone", vault.Model{
Groups: [][]string{
{"Crew"},
{"Crew", "Internet"},
},
})
u.groupControlsHidden = true
u.setCurrentPath([]string{"Crew"})
u.phoneGroupBrowserExpanded = false
u.phoneGroupBrowserExpanded = !u.phoneGroupBrowserExpanded
if !u.groupControlsHidden {
t.Fatal("groupControlsHidden = false, want phone group browser toggle to stay independent")
}
}
func TestUIAPIAuditSectionShowsRecordedEvents(t *testing.T) { func TestUIAPIAuditSectionShowsRecordedEvents(t *testing.T) {
t.Parallel() t.Parallel()