Scope the strip to series with a chapter waiting
"Continue reading" was the head of the same updated_at DESC list rendered directly beneath it, so on a small library it said nothing new for ~240px of the first phone screen. Scoping it to series with an unread chapter gives it the one question the list does not already answer, since the list's order is reading recency and its head *is* the old strip. Consequence, accepted deliberately: with nothing new anywhere the strip does not render at all, and the list starts at the top of the page. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+14
-7
@@ -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
|
||||
}
|
||||
|
||||
+50
-24
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user