From 9837c89075cb2c2bf651ec2a3dd2f0bc34666c93 Mon Sep 17 00:00:00 2001 From: Admin Date: Sun, 1 Mar 2026 21:47:39 +0500 Subject: [PATCH] fix: add server-side pagination to ranking page (?page=N) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously all cached ranking items were dumped into one page and the page buttons only triggered refetches from novelfire — not navigation. - handleRanking now reads ?page=N query param (default 1) - Items sliced server-side at 20 per page (rankingPageSize constant) - Template receives DisplayNums/CurrentPage/TotalPages for browse nav - Display pagination bar renders GET /ranking?page=N HTMX links with the active page highlighted in amber; hidden when only 1 page - Fetch section collapsed into a
to reduce visual noise - FetchNums replaces old PageNums field name in template data --- scraper/internal/server/ui.go | 126 ++++++++++++++++++++++++++-------- 1 file changed, 99 insertions(+), 27 deletions(-) diff --git a/scraper/internal/server/ui.go b/scraper/internal/server/ui.go index 9127a63..d1fb22f 100644 --- a/scraper/internal/server/ui.go +++ b/scraper/internal/server/ui.go @@ -557,33 +557,66 @@ const rankingTmpl = ` - -
-
- Fetch up to page: - {{range .PageNums}} + + {{if gt .TotalPages 1}} +
+
+ Page: + {{$cur := .CurrentPage}} + {{range .DisplayNums}} {{if eq .Num 0}} + {{else if eq .Num $cur}} + {{.Num}} {{else}} -
- - -
+ + {{.Num}} + {{end}} {{end}}
-

- Each page = ~20 novels from - novelfire.net popular -

+

Showing {{.TotalItems}} cached novels · page {{.CurrentPage}} of {{.TotalPages}}

+ {{end}} + + +
+ + + Fetch more from novelfire.net + +
+
+ Fetch up to page: + {{range .FetchNums}} + {{if eq .Num 0}} + + {{else}} +
+ + +
+ {{end}} + {{end}} +
+

+ Each page ≈ 20 novels from + novelfire.net popular +

+
+
@@ -777,8 +810,11 @@ func rankingPageNums(total int) []pageNum { return out } -// handleRanking serves the ranking page from the cached ranking.md file. +const rankingPageSize = 20 + +// handleRanking serves the ranking page from the cached ranking.json file. // It does NOT trigger a live scrape; use POST /ranking/refresh for that. +// Supports ?page=N for browsing through cached items (20 per page). func (s *Server) handleRanking(w http.ResponseWriter, r *http.Request) { rankingItems, err := s.writer.ReadRankingItems() if err != nil { @@ -790,16 +826,52 @@ func (s *Server) handleRanking(w http.ResponseWriter, r *http.Request) { cachedAt = info.ModTime().Format("Jan 2, 2006 at 15:04") } + // Parse requested display page (1-indexed). + currentPage := 1 + if p := r.URL.Query().Get("page"); p != "" { + if n, err2 := strconv.Atoi(p); err2 == nil && n > 0 { + currentPage = n + } + } + + totalItems := len(rankingItems) + totalPages := 1 + if totalItems > 0 { + totalPages = (totalItems + rankingPageSize - 1) / rankingPageSize + } + if currentPage > totalPages { + currentPage = totalPages + } + + // Slice items for the current display page. + start := (currentPage - 1) * rankingPageSize + end := start + rankingPageSize + if end > totalItems { + end = totalItems + } + pageItems := rankingItems + if totalItems > 0 { + pageItems = rankingItems[start:end] + } + t := template.Must(template.New("ranking").Parse(rankingTmpl)) var buf bytes.Buffer _ = t.Execute(&buf, struct { - Books interface{} - CachedAt string - PageNums []pageNum + Books interface{} + CachedAt string + FetchNums []pageNum + DisplayNums []pageNum + CurrentPage int + TotalPages int + TotalItems int }{ - Books: toRankingViewItems(rankingItems, s.writer.LocalSlugs()), - CachedAt: cachedAt, - PageNums: rankingPageNums(100), + Books: toRankingViewItems(pageItems, s.writer.LocalSlugs()), + CachedAt: cachedAt, + FetchNums: rankingPageNums(100), + DisplayNums: rankingPageNums(totalPages), + CurrentPage: currentPage, + TotalPages: totalPages, + TotalItems: totalItems, }) s.respond(w, r, "Rankings", buf.String()) }