Restore per-vault groups and scroll edit forms
This commit is contained in:
@@ -80,6 +80,11 @@ type statePaths struct {
|
||||
RecentVaultsPath string
|
||||
}
|
||||
|
||||
type recentVaultRecord struct {
|
||||
Path string `json:"path"`
|
||||
LastGroup []string `json:"lastGroup,omitempty"`
|
||||
}
|
||||
|
||||
type ui struct {
|
||||
mode string
|
||||
theme *material.Theme
|
||||
@@ -179,6 +184,7 @@ type ui struct {
|
||||
recentVaultsPath string
|
||||
editingEntry bool
|
||||
recentVaults []string
|
||||
recentVaultGroups map[string][]string
|
||||
deleteGroupPath []string
|
||||
statusExpiresAt time.Time
|
||||
now func() time.Time
|
||||
@@ -262,6 +268,7 @@ func newUIWithState(mode string, sess appstate.CurrentSession, paths statePaths)
|
||||
lifecycleMode: "local",
|
||||
defaultSaveAsPath: paths.DefaultSaveAsPath,
|
||||
recentVaultsPath: paths.RecentVaultsPath,
|
||||
recentVaultGroups: map[string][]string{},
|
||||
now: time.Now,
|
||||
}
|
||||
u.state.Session = sess
|
||||
@@ -491,7 +498,7 @@ func (u *ui) openVaultAction() error {
|
||||
}
|
||||
u.noteRecentVault(path)
|
||||
u.currentPath = append([]string(nil), u.state.CurrentPath...)
|
||||
u.enterHiddenVaultRoot()
|
||||
u.restoreRecentVaultGroup(path)
|
||||
u.editingEntry = false
|
||||
u.filter()
|
||||
return nil
|
||||
@@ -597,6 +604,14 @@ func (u *ui) noteRecentVault(path string) {
|
||||
if path == "" {
|
||||
return
|
||||
}
|
||||
if u.recentVaultGroups == nil {
|
||||
u.recentVaultGroups = map[string][]string{}
|
||||
}
|
||||
if len(u.currentPath) > 0 {
|
||||
u.recentVaultGroups[path] = append([]string(nil), u.currentPath...)
|
||||
} else if _, ok := u.recentVaultGroups[path]; !ok {
|
||||
u.recentVaultGroups[path] = nil
|
||||
}
|
||||
next := []string{path}
|
||||
for _, existing := range u.recentVaults {
|
||||
if existing == path {
|
||||
@@ -622,19 +637,40 @@ func (u *ui) loadRecentVaults() {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var paths []string
|
||||
if err := json.Unmarshal(content, &paths); err != nil {
|
||||
u.recentVaults = nil
|
||||
u.recentVaultGroups = map[string][]string{}
|
||||
var records []recentVaultRecord
|
||||
switch {
|
||||
case json.Unmarshal(content, &records) == nil:
|
||||
u.applyRecentVaultRecords(records)
|
||||
return
|
||||
default:
|
||||
var paths []string
|
||||
if err := json.Unmarshal(content, &paths); err != nil {
|
||||
return
|
||||
}
|
||||
records = make([]recentVaultRecord, 0, len(paths))
|
||||
for _, path := range paths {
|
||||
records = append(records, recentVaultRecord{Path: path})
|
||||
}
|
||||
u.applyRecentVaultRecords(records)
|
||||
}
|
||||
filtered := make([]string, 0, len(paths))
|
||||
}
|
||||
|
||||
func (u *ui) applyRecentVaultRecords(records []recentVaultRecord) {
|
||||
filtered := make([]string, 0, len(records))
|
||||
seen := map[string]bool{}
|
||||
for _, path := range paths {
|
||||
path = strings.TrimSpace(path)
|
||||
for _, record := range records {
|
||||
path := strings.TrimSpace(record.Path)
|
||||
if path == "" || seen[path] {
|
||||
continue
|
||||
}
|
||||
seen[path] = true
|
||||
filtered = append(filtered, path)
|
||||
if u.recentVaultGroups == nil {
|
||||
u.recentVaultGroups = map[string][]string{}
|
||||
}
|
||||
u.recentVaultGroups[path] = append([]string(nil), record.LastGroup...)
|
||||
if len(filtered) == 6 {
|
||||
break
|
||||
}
|
||||
@@ -652,13 +688,27 @@ func (u *ui) saveRecentVaults() {
|
||||
if err := os.MkdirAll(filepath.Dir(u.recentVaultsPath), 0o700); err != nil {
|
||||
return
|
||||
}
|
||||
content, err := json.MarshalIndent(u.recentVaults, "", " ")
|
||||
records := make([]recentVaultRecord, 0, len(u.recentVaults))
|
||||
for _, path := range u.recentVaults {
|
||||
records = append(records, recentVaultRecord{
|
||||
Path: path,
|
||||
LastGroup: append([]string(nil), u.recentVaultGroups[path]...),
|
||||
})
|
||||
}
|
||||
content, err := json.MarshalIndent(records, "", " ")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = os.WriteFile(u.recentVaultsPath, content, 0o600)
|
||||
}
|
||||
|
||||
func (u *ui) recentVaultGroup(path string) []string {
|
||||
if u.recentVaultGroups == nil {
|
||||
return nil
|
||||
}
|
||||
return append([]string(nil), u.recentVaultGroups[strings.TrimSpace(path)]...)
|
||||
}
|
||||
|
||||
func (u *ui) hiddenVaultRoot() string {
|
||||
if u.state.Section != appstate.SectionEntries {
|
||||
return ""
|
||||
@@ -685,6 +735,29 @@ func (u *ui) enterHiddenVaultRoot() {
|
||||
u.setCurrentPath([]string{root})
|
||||
}
|
||||
|
||||
func (u *ui) restoreRecentVaultGroup(path string) {
|
||||
saved := u.recentVaultGroup(path)
|
||||
if len(saved) == 0 {
|
||||
u.enterHiddenVaultRoot()
|
||||
return
|
||||
}
|
||||
model, err := u.state.Session.Current()
|
||||
if err != nil {
|
||||
u.enterHiddenVaultRoot()
|
||||
return
|
||||
}
|
||||
root := u.hiddenVaultRoot()
|
||||
if len(saved) == 1 && root != "" && saved[0] == root {
|
||||
u.setCurrentPath(saved)
|
||||
return
|
||||
}
|
||||
if len(model.EntriesInPath(saved)) > 0 || len(model.ChildGroups(saved)) > 0 || hasExactGroup(model, saved) {
|
||||
u.setCurrentPath(saved)
|
||||
return
|
||||
}
|
||||
u.enterHiddenVaultRoot()
|
||||
}
|
||||
|
||||
func (u *ui) displayPath() []string {
|
||||
path := append([]string(nil), u.currentPath...)
|
||||
root := u.hiddenVaultRoot()
|
||||
@@ -709,6 +782,15 @@ func pathHasPrefix(path, prefix []string) bool {
|
||||
return slices.Equal(path[:len(prefix)], prefix)
|
||||
}
|
||||
|
||||
func hasExactGroup(model vault.Model, path []string) bool {
|
||||
for _, group := range model.Groups {
|
||||
if slices.Equal(group, path) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *ui) currentGroupDeletionState() (bool, string) {
|
||||
u.syncCurrentPath()
|
||||
if u.state.Section != appstate.SectionEntries || len(u.displayPath()) == 0 || u.state.Session == nil {
|
||||
@@ -924,6 +1006,7 @@ func (u *ui) setCurrentPath(path []string) {
|
||||
u.currentPath = append([]string(nil), path...)
|
||||
u.state.NavigateToPath(path)
|
||||
u.syncedPath = append([]string(nil), path...)
|
||||
u.noteCurrentVaultPath()
|
||||
u.clearDeleteGroupConfirmation()
|
||||
}
|
||||
|
||||
@@ -937,11 +1020,28 @@ func (u *ui) syncCurrentPath() {
|
||||
u.state.CurrentPath = append([]string(nil), u.currentPath...)
|
||||
}
|
||||
u.syncedPath = append([]string(nil), u.currentPath...)
|
||||
u.noteCurrentVaultPath()
|
||||
if len(u.deleteGroupPath) > 0 && !slices.Equal(u.deleteGroupPath, u.currentPath) {
|
||||
u.clearDeleteGroupConfirmation()
|
||||
}
|
||||
}
|
||||
|
||||
func (u *ui) noteCurrentVaultPath() {
|
||||
status, ok := u.state.Session.(sessionStatus)
|
||||
if !ok || status.IsRemote() || status.IsLocked() {
|
||||
return
|
||||
}
|
||||
path := strings.TrimSpace(u.vaultPath.Text())
|
||||
if path == "" {
|
||||
return
|
||||
}
|
||||
if u.recentVaultGroups == nil {
|
||||
u.recentVaultGroups = map[string][]string{}
|
||||
}
|
||||
u.recentVaultGroups[path] = append([]string(nil), u.currentPath...)
|
||||
u.saveRecentVaults()
|
||||
}
|
||||
|
||||
func (u *ui) layout(gtx layout.Context) layout.Dimensions {
|
||||
u.processShortcuts(gtx)
|
||||
for u.createVault.Clicked(gtx) {
|
||||
@@ -1479,8 +1579,8 @@ func (u *ui) detailPanel(gtx layout.Context) layout.Dimensions {
|
||||
)
|
||||
}
|
||||
if u.editingEntry {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
rows := []layout.Widget{
|
||||
func(gtx layout.Context) layout.Dimensions {
|
||||
title := "New Entry"
|
||||
if ok {
|
||||
title = "Edit Entry"
|
||||
@@ -1488,10 +1588,13 @@ func (u *ui) detailPanel(gtx layout.Context) layout.Dimensions {
|
||||
lbl := material.Label(u.theme, unit.Sp(18), title)
|
||||
lbl.Color = accentColor
|
||||
return lbl.Layout(gtx)
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(u.entryEditorPanel),
|
||||
)
|
||||
},
|
||||
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||
u.entryEditorPanel,
|
||||
}
|
||||
return material.List(u.theme, &u.detailList).Layout(gtx, len(rows), func(gtx layout.Context, i int) layout.Dimensions {
|
||||
return rows[i](gtx)
|
||||
})
|
||||
}
|
||||
password := u.detailPasswordValue()
|
||||
titleSize := unit.Sp(26)
|
||||
|
||||
Reference in New Issue
Block a user