diff --git a/README.md b/README.md index 00b1c64..b336761 100644 --- a/README.md +++ b/README.md @@ -100,8 +100,7 @@ autoread: true ``` ### Ordering - -Set the default sort ordering of the list +Set the default sort ordering of the list. Ordering can be one of "asc", "desc" or "length". Length will sort by article content length in ascending order, unread items still show at the top. ```yaml ordering: asc diff --git a/internal/commands/list.go b/internal/commands/list.go index 5e95cde..305128a 100644 --- a/internal/commands/list.go +++ b/internal/commands/list.go @@ -95,10 +95,13 @@ func (m *model) UpdateList() tea.Cmd { func sortList(m model) func() tea.Msg { return func() tea.Msg { // reverse sorting order - if m.commands.config.Ordering == constants.AscendingOrdering { + switch m.commands.config.Ordering { + case constants.AscendingOrdering: m.commands.config.Ordering = constants.DescendingOrdering - } else { + case constants.DescendingOrdering: m.commands.config.Ordering = constants.AscendingOrdering + default: + // noop } items, err := m.commands.GetAllFeeds() diff --git a/internal/constants/store.go b/internal/constants/store.go index d45c450..835e50d 100644 --- a/internal/constants/store.go +++ b/internal/constants/store.go @@ -4,5 +4,6 @@ package constants const ( AscendingOrdering = "asc" DescendingOrdering = "desc" + LengthOrdering = "length" DefaultOrdering = AscendingOrdering ) diff --git a/internal/store/store.go b/internal/store/store.go index b9da04e..d51b58d 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -257,15 +257,21 @@ func (sls *SQLiteStore) upsertItem(db statementPreparer, item *Item) error { // TODO: pagination func (sls SQLiteStore) GetAllItems(ordering string) ([]Item, error) { itemStmt := ` - select id, feedurl, guid, link, title, content, author, readat, favourite, publishedat, createdat, updatedat from items order by readat is not null asc, coalesce(publishedat, createdat) %s; + select id, feedurl, guid, link, title, content, author, readat, favourite, publishedat, createdat, updatedat from items order by readat is not null asc, %s; ` var stmt string + var timeStmt string + switch ordering { + case constants.LengthOrdering: + stmt = fmt.Sprintf(itemStmt, "length(content) asc") case constants.DescendingOrdering: - stmt = fmt.Sprintf(itemStmt, constants.DescendingOrdering) + timeStmt = fmt.Sprintf("coalesce(publishedat, createdat) %s", constants.DescendingOrdering) + stmt = fmt.Sprintf(itemStmt, timeStmt) default: - stmt = fmt.Sprintf(itemStmt, constants.DefaultOrdering) + timeStmt = fmt.Sprintf("coalesce(publishedat, createdat) %s", constants.DefaultOrdering) + stmt = fmt.Sprintf(itemStmt, timeStmt) } rows, err := sls.db.Query(stmt) diff --git a/internal/store/store_test.go b/internal/store/store_test.go new file mode 100644 index 0000000..ba5c37a --- /dev/null +++ b/internal/store/store_test.go @@ -0,0 +1,107 @@ +package store + +import ( + "testing" + "time" +) + +func TestStore_GetAllItemsOrder(t *testing.T) { + now := time.Now() + testCases := []struct { + name string + ordering string + items []Item + expectedOrder []string + }{ + { + name: "test descending_by_new", + ordering: "desc", + items: []Item{ + { + PublishedAt: now.Add(-2 * time.Hour), + CreatedAt: now.Add(-2 * time.Hour), + Title: "First Item", + Content: "A short string", + FeedURL: "example.com", + Link: "example.com/endpoint", + }, + { + PublishedAt: now.Add(-23 * time.Hour), + CreatedAt: now.Add(-24 * time.Hour), + Title: "Second Item", + Content: "A really long string", + FeedURL: "example.com", + Link: "example.com/api", + }, + { + PublishedAt: now.Add(-20 * time.Hour), + CreatedAt: now.Add(-20 * time.Hour), + Title: "Third Item", + Content: "Some boring string. Use lorem ipsum", + FeedURL: "example.com", + Link: "example.com/anotherExample", + }, + }, + expectedOrder: []string{"First Item", "Second Item", "Third Item"}, + }, + { + name: "test_order_by_content_size", + ordering: "length", + items: []Item{ + { + PublishedAt: now.Add(-2 * time.Hour), + CreatedAt: now.Add(-2 * time.Hour), + Title: "First Item", + Content: "AA", + FeedURL: "example.com", + Link: "example.com/endpoint", + }, + { + PublishedAt: now.Add(-23 * time.Hour), + CreatedAt: now.Add(-24 * time.Hour), + Title: "Second Item", + Content: "AAA", + FeedURL: "example.com", + Link: "example.com/api", + }, + { + PublishedAt: now.Add(-20 * time.Hour), + CreatedAt: now.Add(-20 * time.Hour), + Title: "Third Item", + Content: "A", + FeedURL: "example.com", + Link: "example.com/anotherExample", + }, + }, + expectedOrder: []string{"First Item", "Second Item", "Third Item"}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + sls, err := NewInMemorySQLiteStore() + if err != nil { + t.Fatalf("Error initailizing db: %v", err) + } + for _, item := range tc.items { + err = sls.UpsertItem(&item) + if item.Title == "Third Item" { + sls.ToggleRead(item.ID) + } + if err != nil { + t.Errorf("Error insert %v: %v", item, err) + } + } + allItems, err := sls.GetAllItems(tc.ordering) + if err != nil { + t.Fatalf("Error fetching all items: %v", err) + } + for i := range allItems { + if allItems[i].Title != tc.expectedOrder[i] { + t.Fatalf("Unexpected item order. Want: %v Got: %v", tc.expectedOrder[i], allItems[i].Title) + } + } + + }) + } +}