diff --git a/backend/web.go b/backend/web.go index 83ed317..7ec0d3d 100644 --- a/backend/web.go +++ b/backend/web.go @@ -200,14 +200,21 @@ func (h *webHandler) buildListView(tab string) (listView, error) { tab = "all" items = reading } - // The strip is the head of the same updated_at DESC reading list the All tab - // renders, so it only earns its ~230px of the first phone screen where it is - // not a pure duplicate of what sits right below it: on All, with more series - // than it shows. On the other tabs it either repeats the list or, worse, - // puts reading series above the archived and finished buckets. + // The strip is scoped to series with a chapter waiting, which is the one + // question the list below it does not already answer: the list is ordered by + // reading recency, so the head of it *is* the strip whenever the strip is + // just "the most recent rows". Only on All — on Updated it would render the + // same set twice, and on the other tabs it would contradict the bucket. + // + // It therefore disappears entirely on a library with nothing new. That is + // the intended reading: an empty strip has nothing to say, and the ~240px it + // costs on a phone belongs to the list. var recent []Bookmark - if tab == "all" && len(reading) > recentCount { - recent = reading[:recentCount] + if tab == "all" { + recent = withNew + if len(recent) > recentCount { + recent = recent[:recentCount] + } } return listView{Tab: tab, Recent: recent, Items: items, NewCount: len(withNew)}, nil } diff --git a/backend/web_test.go b/backend/web_test.go index f7988e6..90a8648 100644 --- a/backend/web_test.go +++ b/backend/web_test.go @@ -594,43 +594,69 @@ func stripOf(t *testing.T, srv http.Handler, cfg Config, tab string) string { return body } -// The strip is the head of the same list the All tab renders, so it only shows -// where it is not a pure duplicate: on All, with more reading series than it -// holds. -func TestRecentStripOnlyOnAllAndOnlyWhenItAddsSomething(t *testing.T) { +// The strip carries the series with a chapter waiting — the one thing the +// updated_at-ordered list below it does not already say — and only on All. +func TestRecentStripCarriesUnreadOnlyAndOnlyOnAll(t *testing.T) { cfg := webConfig() srv, store := newWebTestServer(t, cfg) - seedStatusRows(t, store) + seedStatusRows(t, store) // ReadingOne is at 10 with 11 out; the rest are not reading - for _, tab := range []string{"all", "new", "fav", "archived", "finished"} { - if strings.Contains(stripOf(t, srv, cfg, tab), `class="recent"`) { - t.Fatalf("tab %s rendered the strip for a library of one reading series", tab) + // A reading series that is caught up has nothing waiting, so it stays out. + caught := Bookmark{ + Key: "asura:caught", Site: "asura", SeriesID: "caught", Title: "CaughtUpOne", + Status: statusReading, LastChapterNum: 40, LatestChapter: "40", + LatestChapterNum: floatPtr(40), UpdatedAt: time.Now().UnixMilli(), + } + if _, err := store.Upsert(caught); err != nil { + t.Fatalf("seed %s: %v", caught.Key, err) + } + + strip := stripOf(t, srv, cfg, "all") + if !strings.Contains(strip, "ReadingOne") { + t.Fatal("strip dropped the series with an unread chapter") + } + for _, unwanted := range []string{"CaughtUpOne", "ArchivedOne", "FinishedOne"} { + if strings.Contains(strip, unwanted) { + t.Fatalf("strip included %s", unwanted) + } + } + for _, tab := range []string{"new", "fav", "archived", "finished"} { + if strings.Contains(stripOf(t, srv, cfg, tab), "ReadingOne") { + t.Fatalf("tab %s rendered the strip", tab) } } - // Past recentCount the strip stops repeating the whole list. + // Nothing new anywhere: the strip has nothing to say and does not render. + reading, _, err := store.Get("asura:reading") + if err != nil { + t.Fatalf("Get: %v", err) + } + reading.LatestChapterNum = floatPtr(reading.LastChapterNum) + if _, err := store.Upsert(reading); err != nil { + t.Fatalf("Upsert: %v", err) + } + if strings.Contains(stripOf(t, srv, cfg, "all"), `class="recent"`) { + t.Fatal("strip rendered with no unread chapters anywhere") + } +} + +// The strip never grows past recentCount, however many series are waiting. +func TestRecentStripCapped(t *testing.T) { + cfg := webConfig() + srv, store := newWebTestServer(t, cfg) for i := 0; i <= recentCount; i++ { b := Bookmark{ - Key: fmt.Sprintf("asura:more%d", i), Site: "asura", - SeriesID: fmt.Sprintf("more%d", i), Title: fmt.Sprintf("MoreReading%d", i), - Status: statusReading, LastChapterNum: 1, UpdatedAt: time.Now().UnixMilli(), + Key: fmt.Sprintf("asura:new%d", i), Site: "asura", + SeriesID: fmt.Sprintf("new%d", i), Title: fmt.Sprintf("Waiting%d", i), + Status: statusReading, LastChapterNum: 1, LatestChapter: "2", + LatestChapterNum: floatPtr(2), UpdatedAt: time.Now().UnixMilli() + int64(i), } if _, err := store.Upsert(b); err != nil { t.Fatalf("seed %s: %v", b.Key, err) } } - - strip := stripOf(t, srv, cfg, "all") - if !strings.Contains(strip, `class="recent"`) { - t.Fatal("strip missing on All with more reading series than it shows") - } - if strings.Contains(strip, "ArchivedOne") || strings.Contains(strip, "FinishedOne") { - t.Fatal("recent strip included an archived or finished series") - } - for _, tab := range []string{"new", "fav", "archived", "finished"} { - if strings.Contains(stripOf(t, srv, cfg, tab), `class="recent"`) { - t.Fatalf("tab %s rendered the strip", tab) - } + if got := strings.Count(stripOf(t, srv, cfg, "all"), "recent-card"); got != recentCount { + t.Fatalf("strip rendered %d cards, want %d", got, recentCount) } }