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"
|
tab = "all"
|
||||||
items = reading
|
items = reading
|
||||||
}
|
}
|
||||||
// The strip is the head of the same updated_at DESC reading list the All tab
|
// The strip is scoped to series with a chapter waiting, which is the one
|
||||||
// renders, so it only earns its ~230px of the first phone screen where it is
|
// question the list below it does not already answer: the list is ordered by
|
||||||
// not a pure duplicate of what sits right below it: on All, with more series
|
// reading recency, so the head of it *is* the strip whenever the strip is
|
||||||
// than it shows. On the other tabs it either repeats the list or, worse,
|
// just "the most recent rows". Only on All — on Updated it would render the
|
||||||
// puts reading series above the archived and finished buckets.
|
// 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
|
var recent []Bookmark
|
||||||
if tab == "all" && len(reading) > recentCount {
|
if tab == "all" {
|
||||||
recent = reading[:recentCount]
|
recent = withNew
|
||||||
|
if len(recent) > recentCount {
|
||||||
|
recent = recent[:recentCount]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return listView{Tab: tab, Recent: recent, Items: items, NewCount: len(withNew)}, nil
|
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
|
return body
|
||||||
}
|
}
|
||||||
|
|
||||||
// The strip is the head of the same list the All tab renders, so it only shows
|
// The strip carries the series with a chapter waiting — the one thing the
|
||||||
// where it is not a pure duplicate: on All, with more reading series than it
|
// updated_at-ordered list below it does not already say — and only on All.
|
||||||
// holds.
|
func TestRecentStripCarriesUnreadOnlyAndOnlyOnAll(t *testing.T) {
|
||||||
func TestRecentStripOnlyOnAllAndOnlyWhenItAddsSomething(t *testing.T) {
|
|
||||||
cfg := webConfig()
|
cfg := webConfig()
|
||||||
srv, store := newWebTestServer(t, cfg)
|
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"} {
|
// A reading series that is caught up has nothing waiting, so it stays out.
|
||||||
if strings.Contains(stripOf(t, srv, cfg, tab), `class="recent"`) {
|
caught := Bookmark{
|
||||||
t.Fatalf("tab %s rendered the strip for a library of one reading series", tab)
|
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++ {
|
for i := 0; i <= recentCount; i++ {
|
||||||
b := Bookmark{
|
b := Bookmark{
|
||||||
Key: fmt.Sprintf("asura:more%d", i), Site: "asura",
|
Key: fmt.Sprintf("asura:new%d", i), Site: "asura",
|
||||||
SeriesID: fmt.Sprintf("more%d", i), Title: fmt.Sprintf("MoreReading%d", i),
|
SeriesID: fmt.Sprintf("new%d", i), Title: fmt.Sprintf("Waiting%d", i),
|
||||||
Status: statusReading, LastChapterNum: 1, UpdatedAt: time.Now().UnixMilli(),
|
Status: statusReading, LastChapterNum: 1, LatestChapter: "2",
|
||||||
|
LatestChapterNum: floatPtr(2), UpdatedAt: time.Now().UnixMilli() + int64(i),
|
||||||
}
|
}
|
||||||
if _, err := store.Upsert(b); err != nil {
|
if _, err := store.Upsert(b); err != nil {
|
||||||
t.Fatalf("seed %s: %v", b.Key, err)
|
t.Fatalf("seed %s: %v", b.Key, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if got := strings.Count(stripOf(t, srv, cfg, "all"), "recent-card"); got != recentCount {
|
||||||
strip := stripOf(t, srv, cfg, "all")
|
t.Fatalf("strip rendered %d cards, want %d", got, recentCount)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user