diff --git a/ui/sort.go b/ui/sort.go index 4f389fb..508e142 100644 --- a/ui/sort.go +++ b/ui/sort.go @@ -3,10 +3,47 @@ package ui import ( "cmp" "slices" + "time" ) -func sortMarkdowns(mds []*markdown) { - slices.SortStableFunc(mds, func(a, b *markdown) int { - return cmp.Compare(a.Note, b.Note) - }) +type sortOrder bool + +const ( + ascending sortOrder = true + descending sortOrder = false +) + +func sortMarkdowns(mds []*markdown, sortByDate bool, ascending sortOrder) { + if sortByDate { + if ascending { + slices.SortStableFunc(mds, func(a, b *markdown) int { + return compareTime(a.Modtime, b.Modtime) + }) + } else { + slices.SortStableFunc(mds, func(a, b *markdown) int { + return -compareTime(a.Modtime, b.Modtime) + }) + } + } else { + if ascending { + slices.SortStableFunc(mds, func(a, b *markdown) int { + return cmp.Compare(a.Note, b.Note) + }) + } else { + slices.SortStableFunc(mds, func(a, b *markdown) int { + return -cmp.Compare(a.Note, b.Note) + }) + } + } +} + +func compareTime(a, b time.Time) int { + switch { + case a.Before(b): + return -1 + case a.After(b): + return 1 + default: + return 0 + } } diff --git a/ui/stash.go b/ui/stash.go index c3739b2..80e9e88 100644 --- a/ui/stash.go +++ b/ui/stash.go @@ -147,6 +147,8 @@ type stashModel struct { showStatusMessage bool statusMessage statusMessage statusMessageTimer *time.Timer + sortByDate bool + sortAscending bool // Available document sections we can cycle through. We use a slice, rather // than a map, because order is important. @@ -220,7 +222,7 @@ func (m *stashModel) resetFiltering() { m.filterInput.Reset() m.filteredMarkdowns = nil - sortMarkdowns(m.markdowns) + sortMarkdowns(m.markdowns, m.sortByDate, sortOrder(m.sortAscending)) // If the filtered section is present (it's always at the end) slice it out // of the sections slice to remove it from the UI. @@ -293,15 +295,9 @@ func (m stashModel) selectedMarkdown() *markdown { // Adds markdown documents to the model. func (m *stashModel) addMarkdowns(mds ...*markdown) { - if len(mds) == 0 { - return - } - m.markdowns = append(m.markdowns, mds...) - if !m.filterApplied() { - sortMarkdowns(m.markdowns) - } - + m.filteredMarkdowns = m.markdowns + sortMarkdowns(m.filteredMarkdowns, m.sortByDate, sortOrder(m.sortAscending)) m.updatePagination() } @@ -389,11 +385,13 @@ func newStashModel(common *commonModel) stashModel { } m := stashModel{ - common: common, - spinner: sp, - filterInput: si, - serverPage: 1, - sections: s, + common: common, + spinner: sp, + filterInput: si, + serverPage: 1, + sections: s, + sortAscending: true, + sortByDate: false, } return m @@ -568,6 +566,42 @@ func (m *stashModel) handleDocumentBrowsing(msg tea.Msg) tea.Cmd { m.viewState = stashStateShowingError return nil } + + case "y": + m.sortByDate = true + m.sortAscending = !m.sortAscending + sortMarkdowns(m.filteredMarkdowns, m.sortByDate, sortOrder(m.sortAscending)) + m.statusMessage = statusMessage{ + status: normalStatusMessage, + message: fmt.Sprintf("Sorted by date %s", + map[bool]string{ + true: "(oldest first)", + false: "(newest first)", + }[m.sortAscending], + ), + } + m.showStatusMessage = true + cmds = append(cmds, func() tea.Msg { + return statusMessageTimeoutMsg(stashContext) + }) + + case "t": + m.sortByDate = false + m.sortAscending = !m.sortAscending + sortMarkdowns(m.filteredMarkdowns, m.sortByDate, sortOrder(m.sortAscending)) + m.statusMessage = statusMessage{ + status: normalStatusMessage, + message: fmt.Sprintf("Sorted by title %s", + map[bool]string{ + true: "(A to Z)", + false: "(Z to A)", + }[m.sortAscending], + ), + } + m.showStatusMessage = true + cmds = append(cmds, func() tea.Msg { + return statusMessageTimeoutMsg(stashContext) + }) } } @@ -583,7 +617,7 @@ func (m *stashModel) handleDocumentBrowsing(msg tea.Msg) tea.Cmd { switch key.String() { case "b", "u": m.paginator().PrevPage() - case "f", "d": + case "d", "f": m.paginator().NextPage() } } @@ -867,8 +901,10 @@ func loadLocalMarkdown(md *markdown) tea.Cmd { func filterMarkdowns(m stashModel) tea.Cmd { return func() tea.Msg { - if m.filterInput.Value() == "" || !m.filterApplied() { - return filteredMarkdownMsg(m.markdowns) // return everything + if m.filterInput.Value() == "" { + m.filteredMarkdowns = m.markdowns + sortMarkdowns(m.filteredMarkdowns, m.sortByDate, sortOrder(m.sortAscending)) + return filteredMarkdownMsg(m.filteredMarkdowns) } targets := []string{} diff --git a/ui/stashhelp.go b/ui/stashhelp.go index 6c0b474..83b6602 100644 --- a/ui/stashhelp.go +++ b/ui/stashhelp.go @@ -106,12 +106,21 @@ func (m stashModel) helpView() (string, int) { editHelp []string sectionHelp []string appHelp []string + sortHelp []string ) if numDocs > 0 && m.showFullHelp { navHelp = []string{"enter", "open", "j/k ↑/↓", "choose"} } + // Only show sort help in full help view + if numDocs > 0 && m.showFullHelp { + sortHelp = []string{ + "y", "toggle date sort", + "t", "toggle title sort", + } + } + if len(m.sections) > 1 { if m.showFullHelp { navHelp = append(navHelp, "tab/shift+tab", "section") @@ -149,7 +158,7 @@ func (m stashModel) helpView() (string, int) { if m.filterState != filtering { appHelp = append(appHelp, "?", "close help") } - return m.renderHelp(navHelp, filterHelp, append(selectionHelp, editHelp...), sectionHelp, appHelp) + return m.renderHelp(navHelp, sortHelp, filterHelp, append(selectionHelp, editHelp...), sectionHelp, appHelp) } // Mini help