diff --git a/main.go b/main.go index 9b9d762..4f585b9 100644 --- a/main.go +++ b/main.go @@ -312,6 +312,7 @@ type ui struct { cancelDeleteGroup widget.Clickable addCustomField widget.Clickable toggleGroupControls widget.Clickable + togglePhoneGroupBrowser widget.Clickable toggleLifecycleAdvanced widget.Clickable toggleHistory widget.Clickable togglePasswordInline widget.Clickable @@ -386,6 +387,7 @@ type ui struct { splitBase float32 splitStartY float32 phoneSpan int + phoneGroupBrowserExpanded bool eyeIcon *widget.Icon eyeOffIcon *widget.Icon copyIcon *widget.Icon @@ -585,6 +587,7 @@ func newUIWithState(mode string, sess appstate.CurrentSession, paths statePaths) apiPolicyGroupScope: true, autofillNoticePreference: autofillNoticeAll, backgroundResults: make(chan backgroundActionResult, 8), + phoneGroupBrowserExpanded: true, } u.apiPolicyAllow.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) { u.currentPath = append([]string(nil), path...) u.state.NavigateToPath(path) u.syncedPath = append([]string(nil), path...) + u.syncPhoneGroupBrowser(path) u.noteCurrentVaultPath() u.clearDeleteGroupConfirmation() } @@ -5176,11 +5187,28 @@ func (u *ui) groupBar(gtx layout.Context) layout.Dimensions { atRoot := len(displayPath) == 0 return compactCard(gtx, func(gtx layout.Context) layout.Dimensions { if u.mode == "phone" { + if atRoot { + u.phoneGroupBrowserExpanded = true + } + toggleLabel := "Browse" + if u.phoneGroupBrowserExpanded { + toggleLabel = "Hide" + } return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(func(gtx layout.Context) layout.Dimensions { - lbl := material.Label(u.theme, unit.Sp(12), "GROUPS") - lbl.Color = mutedColor - return lbl.Layout(gtx) + return layout.Flex{Alignment: layout.Middle}.Layout(gtx, + layout.Flexed(1, func(gtx layout.Context) layout.Dimensions { + 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 { 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 { + 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 { lbl := material.Label(u.theme, unit.Sp(12), "No subgroups here.") lbl.Color = mutedColor @@ -5232,6 +5271,7 @@ func (u *ui) groupBar(gtx layout.Context) layout.Dimensions { for u.groupClicks[idx].Clicked(gtx) { u.state.EnterGroup(name) u.currentPath = append([]string(nil), u.state.CurrentPath...) + u.syncPhoneGroupBrowser(u.currentPath) u.filter() } return tonedButton(gtx, u.theme, &u.groupClicks[idx], name) diff --git a/main_test.go b/main_test.go index 458e225..235a2a2 100644 --- a/main_test.go +++ b/main_test.go @@ -734,6 +734,46 @@ func TestUIPhoneGroupBarWithChildGroupsDoesNotPanic(t *testing.T) { _ = u.groupBar(gtx) } +func TestUIPhoneGroupBrowserStartsExpandedAtRootAndCollapsesInVisibleSubgroups(t *testing.T) { + t.Parallel() + + u := newUIWithModel("phone", vault.Model{ + Groups: [][]string{ + {"Joe"}, + {"Joe", "Internet"}, + }, + }) + + u.setCurrentPath(nil) + if !u.phoneGroupBrowserExpanded { + t.Fatal("phoneGroupBrowserExpanded = false at root, want true") + } + + u.setCurrentPath([]string{"Joe", "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{ + {"Joe"}, + {"Joe", "Internet"}, + }, + }) + u.groupControlsHidden = true + u.setCurrentPath([]string{"Joe"}) + 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) { t.Parallel()