nandi/gleanpublic⑂ Fork 0
⑂ 89afff9
Commits
⬇ Clone ▾
git clone https://git.rickub.com/nandi/glean.git
git clone ssh://git@rickub.com/nandi/glean.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Fix pagination: replace load more by actual pagesUnverified

Julien Robert committed 2026-04-20T18:06:43+02:00 Browse files
89afff9 parent: 72289c4
modified internal/server/annotations_handler.go +25 -13
@@ -14,36 +14,48 @@ import (
1414 func (s *Server) handleLibrary(w http.ResponseWriter, r *http.Request) {
1515 user := currentUser(r)
1616
17- likedOffset, _ := strconv.Atoi(r.URL.Query().Get("liked_offset"))
18- if likedOffset < 0 {
19- likedOffset = 0
17+ limit := 20
18+
19+ likedPageNum, _ := strconv.Atoi(r.URL.Query().Get("liked_page"))
20+ if likedPageNum < 1 {
21+ likedPageNum = 1
2022 }
21- annotOffset, _ := strconv.Atoi(r.URL.Query().Get("annot_offset"))
22- if annotOffset < 0 {
23- annotOffset = 0
23+ annotPageNum, _ := strconv.Atoi(r.URL.Query().Get("annot_page"))
24+ if annotPageNum < 1 {
25+ annotPageNum = 1
2426 }
25- limit := 20
2627
27- articles, _ := s.db.ListLikedArticles(r.Context(), user.DID, limit+1, likedOffset)
28+ likedPage := Pagination{Page: likedPageNum, PageSize: limit}
29+ annotPage := Pagination{Page: annotPageNum, PageSize: limit}
30+
31+ articles, _ := s.db.ListLikedArticles(r.Context(), user.DID, limit+1, likedPage.Offset())
2832 likedHasMore := len(articles) > limit
2933 if likedHasMore {
3034 articles = articles[:limit]
3135 }
36+ likedPage = likedPage.Paginate(len(articles))
37+ if likedHasMore {
38+ likedPage.HasNext = true
39+ likedPage.NextPage = likedPage.Page + 1
40+ }
3241
33- annotations, _ := s.db.ListAnnotations(r.Context(), "", "", user.DID, limit+1, annotOffset)
42+ annotations, _ := s.db.ListAnnotations(r.Context(), "", "", user.DID, limit+1, annotPage.Offset())
3443 annotHasMore := len(annotations) > limit
3544 if annotHasMore {
3645 annotations = annotations[:limit]
3746 }
47+ annotPage = annotPage.Paginate(len(annotations))
48+ if annotHasMore {
49+ annotPage.HasNext = true
50+ annotPage.NextPage = annotPage.Page + 1
51+ }
3852
3953 s.render(w, r, "library.html", map[string]any{
4054 "User": user,
4155 "Articles": articles,
4256 "Annotations": annotations,
43- "LikedHasMore": likedHasMore,
44- "AnnotHasMore": annotHasMore,
45- "NextLiked": likedOffset + limit,
46- "NextAnnot": annotOffset + limit,
57+ "LikedPage": likedPage,
58+ "AnnotPage": annotPage,
4759 })
4860 }
4961
@@ -14,36 +14,48 @@ import (
14 func (s *Server) handleLibrary(w http.ResponseWriter, r *http.Request) {14 func (s *Server) handleLibrary(w http.ResponseWriter, r *http.Request) {
15 user := currentUser(r)15 user := currentUser(r)
16 16
17- likedOffset, _ := strconv.Atoi(r.URL.Query().Get("liked_offset"))17+ limit := 20
18- if likedOffset < 0 {18+
19- likedOffset = 019+ likedPageNum, _ := strconv.Atoi(r.URL.Query().Get("liked_page"))
20+ if likedPageNum < 1 {
21+ likedPageNum = 1
20 }22 }
21- annotOffset, _ := strconv.Atoi(r.URL.Query().Get("annot_offset"))23+ annotPageNum, _ := strconv.Atoi(r.URL.Query().Get("annot_page"))
22- if annotOffset < 0 {24+ if annotPageNum < 1 {
23- annotOffset = 025+ annotPageNum = 1
24 }26 }
25- limit := 20
26 27
27- articles, _ := s.db.ListLikedArticles(r.Context(), user.DID, limit+1, likedOffset)28+ likedPage := Pagination{Page: likedPageNum, PageSize: limit}
29+ annotPage := Pagination{Page: annotPageNum, PageSize: limit}
30+
31+ articles, _ := s.db.ListLikedArticles(r.Context(), user.DID, limit+1, likedPage.Offset())
28 likedHasMore := len(articles) > limit32 likedHasMore := len(articles) > limit
29 if likedHasMore {33 if likedHasMore {
30 articles = articles[:limit]34 articles = articles[:limit]
31 }35 }
36+ likedPage = likedPage.Paginate(len(articles))
37+ if likedHasMore {
38+ likedPage.HasNext = true
39+ likedPage.NextPage = likedPage.Page + 1
40+ }
32 41
33- annotations, _ := s.db.ListAnnotations(r.Context(), "", "", user.DID, limit+1, annotOffset)42+ annotations, _ := s.db.ListAnnotations(r.Context(), "", "", user.DID, limit+1, annotPage.Offset())
34 annotHasMore := len(annotations) > limit43 annotHasMore := len(annotations) > limit
35 if annotHasMore {44 if annotHasMore {
36 annotations = annotations[:limit]45 annotations = annotations[:limit]
37 }46 }
47+ annotPage = annotPage.Paginate(len(annotations))
48+ if annotHasMore {
49+ annotPage.HasNext = true
50+ annotPage.NextPage = annotPage.Page + 1
51+ }
38 52
39 s.render(w, r, "library.html", map[string]any{53 s.render(w, r, "library.html", map[string]any{
40 "User": user,54 "User": user,
41 "Articles": articles,55 "Articles": articles,
42 "Annotations": annotations,56 "Annotations": annotations,
43- "LikedHasMore": likedHasMore,57+ "LikedPage": likedPage,
44- "AnnotHasMore": annotHasMore,58+ "AnnotPage": annotPage,
45- "NextLiked": likedOffset + limit,
46- "NextAnnot": annotOffset + limit,
47 })59 })
48 }60 }
49 61
modified internal/server/articles_handler.go +11 -8
@@ -38,23 +38,26 @@ func (s *Server) handleArticles(w http.ResponseWriter, r *http.Request) {
3838 feedURL := r.URL.Query().Get("feed")
3939
4040 page := pageFromRequest(r, 50)
41- articles, err := s.db.ListArticles(r.Context(), user.DID, feedURL, page.FetchLimit(), page.Offset)
41+ articles, err := s.db.ListArticles(r.Context(), user.DID, feedURL, page.Limit()+1, page.Offset())
4242 if err != nil {
4343 s.logger.Error("failed to list articles", "error", err)
4444 http.Error(w, err.Error(), http.StatusInternalServerError)
4545 return
4646 }
4747
48- page = page.Paginate(len(articles))
49- if page.HasMore {
50- articles = articles[:page.Limit]
48+ totalFetched := len(articles)
49+ page = page.Paginate(totalFetched)
50+ if page.HasNext {
51+ articles = articles[:page.PageSize]
5152 }
5253
5354 s.render(w, r, "articles.html", map[string]any{
54- "User": user,
55- "Articles": articles,
56- "FeedURL": feedURL,
57- "Page": page,
55+ "User": user,
56+ "Articles": articles,
57+ "FeedURL": feedURL,
58+ "Page": page,
59+ "BaseURL": "/articles",
60+ "QueryParams": buildQueryParams(map[string]string{"feed": feedURL}),
5861 })
5962 }
6063
@@ -38,23 +38,26 @@ func (s *Server) handleArticles(w http.ResponseWriter, r *http.Request) {
38 feedURL := r.URL.Query().Get("feed")38 feedURL := r.URL.Query().Get("feed")
39 39
40 page := pageFromRequest(r, 50)40 page := pageFromRequest(r, 50)
41- articles, err := s.db.ListArticles(r.Context(), user.DID, feedURL, page.FetchLimit(), page.Offset)41+ articles, err := s.db.ListArticles(r.Context(), user.DID, feedURL, page.Limit()+1, page.Offset())
42 if err != nil {42 if err != nil {
43 s.logger.Error("failed to list articles", "error", err)43 s.logger.Error("failed to list articles", "error", err)
44 http.Error(w, err.Error(), http.StatusInternalServerError)44 http.Error(w, err.Error(), http.StatusInternalServerError)
45 return45 return
46 }46 }
47 47
48- page = page.Paginate(len(articles))48+ totalFetched := len(articles)
49- if page.HasMore {49+ page = page.Paginate(totalFetched)
50- articles = articles[:page.Limit]50+ if page.HasNext {
51+ articles = articles[:page.PageSize]
51 }52 }
52 53
53 s.render(w, r, "articles.html", map[string]any{54 s.render(w, r, "articles.html", map[string]any{
54- "User": user,55+ "User": user,
55- "Articles": articles,56+ "Articles": articles,
56- "FeedURL": feedURL,57+ "FeedURL": feedURL,
57- "Page": page,58+ "Page": page,
59+ "BaseURL": "/articles",
60+ "QueryParams": buildQueryParams(map[string]string{"feed": feedURL}),
58 })61 })
59 }62 }
60 63
modified internal/server/dashboard_handler.go +7 -4
@@ -12,10 +12,11 @@ func (s *Server) handleDashboard(w http.ResponseWriter, r *http.Request) {
1212 subCount, _ := s.db.GetSubscriptionCount(r.Context(), user.DID)
1313
1414 page := pageFromRequest(r, 25)
15- articles, _ := s.db.ListUnreadArticles(r.Context(), user.DID, "", page.FetchLimit(), page.Offset)
16- page = page.Paginate(len(articles))
17- if page.HasMore {
18- articles = articles[:page.Limit]
15+ articles, _ := s.db.ListUnreadArticles(r.Context(), user.DID, "", page.Limit()+1, page.Offset())
16+ totalFetched := len(articles)
17+ page = page.Paginate(totalFetched)
18+ if page.HasNext {
19+ articles = articles[:page.PageSize]
1920 }
2021
2122 articleRecs, _ := s.db.GetArticleRecommendations(r.Context(), user.DID, 5)
@@ -35,5 +36,7 @@ func (s *Server) handleDashboard(w http.ResponseWriter, r *http.Request) {
3536 "PeopleRecommendations": peopleRecs,
3637 "PersonalTrending": personalTrending,
3738 "Page": page,
39+ "BaseURL": "/dashboard",
40+ "QueryParams": map[string]string{},
3841 })
3942 }
@@ -12,10 +12,11 @@ func (s *Server) handleDashboard(w http.ResponseWriter, r *http.Request) {
12 subCount, _ := s.db.GetSubscriptionCount(r.Context(), user.DID)12 subCount, _ := s.db.GetSubscriptionCount(r.Context(), user.DID)
13 13
14 page := pageFromRequest(r, 25)14 page := pageFromRequest(r, 25)
15- articles, _ := s.db.ListUnreadArticles(r.Context(), user.DID, "", page.FetchLimit(), page.Offset)15+ articles, _ := s.db.ListUnreadArticles(r.Context(), user.DID, "", page.Limit()+1, page.Offset())
16- page = page.Paginate(len(articles))16+ totalFetched := len(articles)
17- if page.HasMore {17+ page = page.Paginate(totalFetched)
18- articles = articles[:page.Limit]18+ if page.HasNext {
19+ articles = articles[:page.PageSize]
19 }20 }
20 21
21 articleRecs, _ := s.db.GetArticleRecommendations(r.Context(), user.DID, 5)22 articleRecs, _ := s.db.GetArticleRecommendations(r.Context(), user.DID, 5)
@@ -35,5 +36,7 @@ func (s *Server) handleDashboard(w http.ResponseWriter, r *http.Request) {
35 "PeopleRecommendations": peopleRecs,36 "PeopleRecommendations": peopleRecs,
36 "PersonalTrending": personalTrending,37 "PersonalTrending": personalTrending,
37 "Page": page,38 "Page": page,
39+ "BaseURL": "/dashboard",
40+ "QueryParams": map[string]string{},
38 })41 })
39 }42 }
modified internal/server/feeds_handler.go +7 -4
@@ -19,10 +19,11 @@ func (s *Server) handleFeeds(w http.ResponseWriter, r *http.Request) {
1919 category := r.URL.Query().Get("category")
2020
2121 page := pageFromRequest(r, 50)
22- subs, _ := s.db.ListSubscriptions(r.Context(), user.DID, category, page.FetchLimit(), page.Offset)
23- page = page.Paginate(len(subs))
24- if page.HasMore {
25- subs = subs[:page.Limit]
22+ subs, _ := s.db.ListSubscriptions(r.Context(), user.DID, category, page.Limit()+1, page.Offset())
23+ totalFetched := len(subs)
24+ page = page.Paginate(totalFetched)
25+ if page.HasNext {
26+ subs = subs[:page.PageSize]
2627 }
2728
2829 allSubs, _ := s.db.ListSubscriptions(r.Context(), user.DID, "", 1000, 0)
@@ -49,6 +50,8 @@ func (s *Server) handleFeeds(w http.ResponseWriter, r *http.Request) {
4950 "PeopleRecommendations": peopleRecs,
5051 "DeadFeeds": deadFeeds,
5152 "Page": page,
53+ "BaseURL": "/feeds",
54+ "QueryParams": buildQueryParams(map[string]string{"category": category}),
5255 })
5356 }
5457
@@ -19,10 +19,11 @@ func (s *Server) handleFeeds(w http.ResponseWriter, r *http.Request) {
19 category := r.URL.Query().Get("category")19 category := r.URL.Query().Get("category")
20 20
21 page := pageFromRequest(r, 50)21 page := pageFromRequest(r, 50)
22- subs, _ := s.db.ListSubscriptions(r.Context(), user.DID, category, page.FetchLimit(), page.Offset)22+ subs, _ := s.db.ListSubscriptions(r.Context(), user.DID, category, page.Limit()+1, page.Offset())
23- page = page.Paginate(len(subs))23+ totalFetched := len(subs)
24- if page.HasMore {24+ page = page.Paginate(totalFetched)
25- subs = subs[:page.Limit]25+ if page.HasNext {
26+ subs = subs[:page.PageSize]
26 }27 }
27 28
28 allSubs, _ := s.db.ListSubscriptions(r.Context(), user.DID, "", 1000, 0)29 allSubs, _ := s.db.ListSubscriptions(r.Context(), user.DID, "", 1000, 0)
@@ -49,6 +50,8 @@ func (s *Server) handleFeeds(w http.ResponseWriter, r *http.Request) {
49 "PeopleRecommendations": peopleRecs,50 "PeopleRecommendations": peopleRecs,
50 "DeadFeeds": deadFeeds,51 "DeadFeeds": deadFeeds,
51 "Page": page,52 "Page": page,
53+ "BaseURL": "/feeds",
54+ "QueryParams": buildQueryParams(map[string]string{"category": category}),
52 })55 })
53 }56 }
54 57
modified internal/server/pagination.go +38 -16
@@ -8,29 +8,51 @@ import (
88 const defaultPageSize = 25
99
1010 type Pagination struct {
11- Offset int
12- Limit int
13- HasMore bool
14- NextOffset int
11+ Page int
12+ PageSize int
13+ HasPrev bool
14+ HasNext bool
15+ PrevPage int
16+ NextPage int
1517 }
1618
17-func pageFromRequest(r *http.Request, limit int) Pagination {
18- if limit <= 0 {
19- limit = defaultPageSize
19+func pageFromRequest(r *http.Request, pageSize int) Pagination {
20+ if pageSize <= 0 {
21+ pageSize = defaultPageSize
2022 }
21- offset, _ := strconv.Atoi(r.URL.Query().Get("offset"))
22- if offset < 0 {
23- offset = 0
23+ page, _ := strconv.Atoi(r.URL.Query().Get("page"))
24+ if page < 1 {
25+ page = 1
2426 }
25- return Pagination{Offset: offset, Limit: limit}
27+ return Pagination{Page: page, PageSize: pageSize}
2628 }
2729
28-func (p Pagination) Paginate(count int) Pagination {
29- p.HasMore = count > p.Limit
30- p.NextOffset = p.Offset + p.Limit
30+func (p Pagination) Paginate(totalFetched int) Pagination {
31+ if totalFetched > p.PageSize {
32+ p.HasNext = true
33+ p.NextPage = p.Page + 1
34+ }
35+ if p.Page > 1 {
36+ p.HasPrev = true
37+ p.PrevPage = p.Page - 1
38+ }
3139 return p
3240 }
3341
34-func (p Pagination) FetchLimit() int {
35- return p.Limit + 1
42+func (p Pagination) Offset() int {
43+ return (p.Page - 1) * p.PageSize
44+}
45+
46+func (p Pagination) Limit() int {
47+ return p.PageSize
48+}
49+
50+func buildQueryParams(params map[string]string) map[string]string {
51+ result := make(map[string]string)
52+ for k, v := range params {
53+ if v != "" {
54+ result[k] = v
55+ }
56+ }
57+ return result
3658 }
@@ -8,29 +8,51 @@ import (
8 const defaultPageSize = 258 const defaultPageSize = 25
9 9
10 type Pagination struct {10 type Pagination struct {
11- Offset int11+ Page int
12- Limit int12+ PageSize int
13- HasMore bool13+ HasPrev bool
14- NextOffset int14+ HasNext bool
15+ PrevPage int
16+ NextPage int
15 }17 }
16 18
17-func pageFromRequest(r *http.Request, limit int) Pagination {19+func pageFromRequest(r *http.Request, pageSize int) Pagination {
18- if limit <= 0 {20+ if pageSize <= 0 {
19- limit = defaultPageSize21+ pageSize = defaultPageSize
20 }22 }
21- offset, _ := strconv.Atoi(r.URL.Query().Get("offset"))23+ page, _ := strconv.Atoi(r.URL.Query().Get("page"))
22- if offset < 0 {24+ if page < 1 {
23- offset = 025+ page = 1
24 }26 }
25- return Pagination{Offset: offset, Limit: limit}27+ return Pagination{Page: page, PageSize: pageSize}
26 }28 }
27 29
28-func (p Pagination) Paginate(count int) Pagination {30+func (p Pagination) Paginate(totalFetched int) Pagination {
29- p.HasMore = count > p.Limit31+ if totalFetched > p.PageSize {
30- p.NextOffset = p.Offset + p.Limit32+ p.HasNext = true
33+ p.NextPage = p.Page + 1
34+ }
35+ if p.Page > 1 {
36+ p.HasPrev = true
37+ p.PrevPage = p.Page - 1
38+ }
31 return p39 return p
32 }40 }
33 41
34-func (p Pagination) FetchLimit() int {42+func (p Pagination) Offset() int {
35- return p.Limit + 143+ return (p.Page - 1) * p.PageSize
44+}
45+
46+func (p Pagination) Limit() int {
47+ return p.PageSize
48+}
49+
50+func buildQueryParams(params map[string]string) map[string]string {
51+ result := make(map[string]string)
52+ for k, v := range params {
53+ if v != "" {
54+ result[k] = v
55+ }
56+ }
57+ return result
36 }58 }
modified internal/server/server.go +15 -0
@@ -7,6 +7,7 @@ import (
77 "html/template"
88 "log/slog"
99 "net/http"
10+ "net/url"
1011 "path/filepath"
1112 "strings"
1213 "time"
@@ -208,6 +209,20 @@ func (s *Server) loadTemplates() {
208209 }
209210 return template.HTML(`<input type="hidden" name="csrf_token" value="` + s + `">`)
210211 },
212+ "paginationURL": func(baseURL string, page int, queryParams map[string]string) string {
213+ u, _ := url.Parse(baseURL)
214+ q := u.Query()
215+ for k, v := range queryParams {
216+ q.Set(k, v)
217+ }
218+ if page > 1 {
219+ q.Set("page", fmt.Sprintf("%d", page))
220+ } else {
221+ q.Del("page")
222+ }
223+ u.RawQuery = q.Encode()
224+ return u.String()
225+ },
211226 }
212227
213228 var allFiles []string
@@ -7,6 +7,7 @@ import (
7 "html/template"7 "html/template"
8 "log/slog"8 "log/slog"
9 "net/http"9 "net/http"
10+ "net/url"
10 "path/filepath"11 "path/filepath"
11 "strings"12 "strings"
12 "time"13 "time"
@@ -208,6 +209,20 @@ func (s *Server) loadTemplates() {
208 }209 }
209 return template.HTML(`<input type="hidden" name="csrf_token" value="` + s + `">`)210 return template.HTML(`<input type="hidden" name="csrf_token" value="` + s + `">`)
210 },211 },
212+ "paginationURL": func(baseURL string, page int, queryParams map[string]string) string {
213+ u, _ := url.Parse(baseURL)
214+ q := u.Query()
215+ for k, v := range queryParams {
216+ q.Set(k, v)
217+ }
218+ if page > 1 {
219+ q.Set("page", fmt.Sprintf("%d", page))
220+ } else {
221+ q.Del("page")
222+ }
223+ u.RawQuery = q.Encode()
224+ return u.String()
225+ },
211 }226 }
212 227
213 var allFiles []string228 var allFiles []string
modified internal/tmpl/articles.html +15 -8
@@ -17,14 +17,21 @@
1717 {{end}}
1818 </div>
1919
20-{{if .Page.HasMore}}
21-<div class="text-center py-4">
22- <button hx-get="/articles?offset={{.Page.NextOffset}}{{if .FeedURL}}&feed={{.FeedURL}}{{end}}"
23- hx-target="#article-list" hx-swap="beforeend"
24- hx-select="article"
25- class="text-sm text-spot-secondary hover:text-spot-green transition">
26- Load more
27- </button>
20+{{if or .Page.HasPrev .Page.HasNext}}
21+<div class="flex items-center justify-center gap-3 py-6">
22+ {{if .Page.HasPrev}}
23+ <a href="{{paginationURL .BaseURL .Page.PrevPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">&laquo; Prev</a>
24+ {{else}}
25+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">&laquo; Prev</span>
26+ {{end}}
27+
28+ <span class="text-sm text-spot-secondary">Page {{.Page.Page}}</span>
29+
30+ {{if .Page.HasNext}}
31+ <a href="{{paginationURL .BaseURL .Page.NextPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next &raquo;</a>
32+ {{else}}
33+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next &raquo;</span>
34+ {{end}}
2835 </div>
2936 {{end}}
3037
@@ -17,14 +17,21 @@
17 {{end}}17 {{end}}
18 </div>18 </div>
19 19
20-{{if .Page.HasMore}}20+{{if or .Page.HasPrev .Page.HasNext}}
21-<div class="text-center py-4">21+<div class="flex items-center justify-center gap-3 py-6">
22- <button hx-get="/articles?offset={{.Page.NextOffset}}{{if .FeedURL}}&feed={{.FeedURL}}{{end}}"22+ {{if .Page.HasPrev}}
23- hx-target="#article-list" hx-swap="beforeend"23+ <a href="{{paginationURL .BaseURL .Page.PrevPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">&laquo; Prev</a>
24- hx-select="article"24+ {{else}}
25- class="text-sm text-spot-secondary hover:text-spot-green transition">25+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">&laquo; Prev</span>
26- Load more26+ {{end}}
27- </button>27+
28+ <span class="text-sm text-spot-secondary">Page {{.Page.Page}}</span>
29+
30+ {{if .Page.HasNext}}
31+ <a href="{{paginationURL .BaseURL .Page.NextPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next &raquo;</a>
32+ {{else}}
33+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next &raquo;</span>
34+ {{end}}
28 </div>35 </div>
29 {{end}}36 {{end}}
30 37
modified internal/tmpl/dashboard.html +15 -8
@@ -38,14 +38,21 @@
3838 {{end}}
3939 </div>
4040
41- {{if .Page.HasMore}}
42- <div class="text-center py-4">
43- <button hx-get="/dashboard?offset={{.Page.NextOffset}}"
44- hx-target="#article-list" hx-swap="beforeend"
45- hx-select="article"
46- class="text-sm text-spot-secondary hover:text-spot-green transition">
47- Load more
48- </button>
41+ {{if or .Page.HasPrev .Page.HasNext}}
42+ <div class="flex items-center justify-center gap-3 py-6">
43+ {{if .Page.HasPrev}}
44+ <a href="{{paginationURL .BaseURL .Page.PrevPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">&laquo; Prev</a>
45+ {{else}}
46+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">&laquo; Prev</span>
47+ {{end}}
48+
49+ <span class="text-sm text-spot-secondary">Page {{.Page.Page}}</span>
50+
51+ {{if .Page.HasNext}}
52+ <a href="{{paginationURL .BaseURL .Page.NextPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next &raquo;</a>
53+ {{else}}
54+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next &raquo;</span>
55+ {{end}}
4956 </div>
5057 {{end}}
5158 </div>
@@ -38,14 +38,21 @@
38 {{end}}38 {{end}}
39 </div>39 </div>
40 40
41- {{if .Page.HasMore}}41+ {{if or .Page.HasPrev .Page.HasNext}}
42- <div class="text-center py-4">42+ <div class="flex items-center justify-center gap-3 py-6">
43- <button hx-get="/dashboard?offset={{.Page.NextOffset}}"43+ {{if .Page.HasPrev}}
44- hx-target="#article-list" hx-swap="beforeend"44+ <a href="{{paginationURL .BaseURL .Page.PrevPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">&laquo; Prev</a>
45- hx-select="article"45+ {{else}}
46- class="text-sm text-spot-secondary hover:text-spot-green transition">46+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">&laquo; Prev</span>
47- Load more47+ {{end}}
48- </button>48+
49+ <span class="text-sm text-spot-secondary">Page {{.Page.Page}}</span>
50+
51+ {{if .Page.HasNext}}
52+ <a href="{{paginationURL .BaseURL .Page.NextPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next &raquo;</a>
53+ {{else}}
54+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next &raquo;</span>
55+ {{end}}
49 </div>56 </div>
50 {{end}}57 {{end}}
51 </div>58 </div>
modified internal/tmpl/feeds.html +15 -8
@@ -92,14 +92,21 @@
9292 {{end}}
9393 </div>
9494
95- {{if .Page.HasMore}}
96- <div class="text-center py-4">
97- <button hx-get="/feeds?offset={{.Page.NextOffset}}{{if .Category}}&category={{.Category}}{{end}}"
98- hx-target="#feed-list" hx-swap="beforeend"
99- hx-select=".feed-item"
100- class="text-sm text-spot-secondary hover:text-spot-green transition">
101- Load more
102- </button>
95+ {{if or .Page.HasPrev .Page.HasNext}}
96+ <div class="flex items-center justify-center gap-3 py-6">
97+ {{if .Page.HasPrev}}
98+ <a href="{{paginationURL .BaseURL .Page.PrevPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">&laquo; Prev</a>
99+ {{else}}
100+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">&laquo; Prev</span>
101+ {{end}}
102+
103+ <span class="text-sm text-spot-secondary">Page {{.Page.Page}}</span>
104+
105+ {{if .Page.HasNext}}
106+ <a href="{{paginationURL .BaseURL .Page.NextPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next &raquo;</a>
107+ {{else}}
108+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next &raquo;</span>
109+ {{end}}
103110 </div>
104111 {{end}}
105112 </div>
@@ -92,14 +92,21 @@
92 {{end}}92 {{end}}
93 </div>93 </div>
94 94
95- {{if .Page.HasMore}}95+ {{if or .Page.HasPrev .Page.HasNext}}
96- <div class="text-center py-4">96+ <div class="flex items-center justify-center gap-3 py-6">
97- <button hx-get="/feeds?offset={{.Page.NextOffset}}{{if .Category}}&category={{.Category}}{{end}}"97+ {{if .Page.HasPrev}}
98- hx-target="#feed-list" hx-swap="beforeend"98+ <a href="{{paginationURL .BaseURL .Page.PrevPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">&laquo; Prev</a>
99- hx-select=".feed-item"99+ {{else}}
100- class="text-sm text-spot-secondary hover:text-spot-green transition">100+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">&laquo; Prev</span>
101- Load more101+ {{end}}
102- </button>102+
103+ <span class="text-sm text-spot-secondary">Page {{.Page.Page}}</span>
104+
105+ {{if .Page.HasNext}}
106+ <a href="{{paginationURL .BaseURL .Page.NextPage .QueryParams}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next &raquo;</a>
107+ {{else}}
108+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next &raquo;</span>
109+ {{end}}
103 </div>110 </div>
104 {{end}}111 {{end}}
105 </div>112 </div>
modified internal/tmpl/library.html +30 -16
@@ -19,14 +19,21 @@
1919 {{end}}
2020 </div>
2121
22- {{if .LikedHasMore}}
23- <div class="text-center py-4">
24- <button hx-get="/library?liked_offset={{.NextLiked}}"
25- hx-target="#liked-list" hx-swap="beforeend"
26- hx-select="article"
27- class="text-sm text-spot-secondary hover:text-spot-green transition">
28- Load more
29- </button>
22+ {{if or .LikedPage.HasPrev .LikedPage.HasNext}}
23+ <div class="flex items-center justify-center gap-3 py-6">
24+ {{if .LikedPage.HasPrev}}
25+ <a href="/library?liked_page={{.LikedPage.PrevPage}}{{if gt .AnnotPage.Page 1}}&annot_page={{.AnnotPage.Page}}{{end}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">&laquo; Prev</a>
26+ {{else}}
27+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">&laquo; Prev</span>
28+ {{end}}
29+
30+ <span class="text-sm text-spot-secondary">Page {{.LikedPage.Page}}</span>
31+
32+ {{if .LikedPage.HasNext}}
33+ <a href="/library?liked_page={{.LikedPage.NextPage}}{{if gt .AnnotPage.Page 1}}&annot_page={{.AnnotPage.Page}}{{end}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next &raquo;</a>
34+ {{else}}
35+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next &raquo;</span>
36+ {{end}}
3037 </div>
3138 {{end}}
3239 </div>
@@ -51,14 +58,21 @@
5158 {{end}}
5259 </div>
5360
54- {{if .AnnotHasMore}}
55- <div class="text-center py-4">
56- <button hx-get="/library?annot_offset={{.NextAnnot}}"
57- hx-target="#annotations-list" hx-swap="beforeend"
58- hx-select=".bg-spot-surface"
59- class="text-sm text-spot-secondary hover:text-spot-green transition">
60- Load more
61- </button>
61+ {{if or .AnnotPage.HasPrev .AnnotPage.HasNext}}
62+ <div class="flex items-center justify-center gap-3 py-6">
63+ {{if .AnnotPage.HasPrev}}
64+ <a href="/library?annot_page={{.AnnotPage.PrevPage}}{{if gt .LikedPage.Page 1}}&liked_page={{.LikedPage.Page}}{{end}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">&laquo; Prev</a>
65+ {{else}}
66+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">&laquo; Prev</span>
67+ {{end}}
68+
69+ <span class="text-sm text-spot-secondary">Page {{.AnnotPage.Page}}</span>
70+
71+ {{if .AnnotPage.HasNext}}
72+ <a href="/library?annot_page={{.AnnotPage.NextPage}}{{if gt .LikedPage.Page 1}}&liked_page={{.LikedPage.Page}}{{end}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next &raquo;</a>
73+ {{else}}
74+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next &raquo;</span>
75+ {{end}}
6276 </div>
6377 {{end}}
6478 </div>
@@ -19,14 +19,21 @@
19 {{end}}19 {{end}}
20 </div>20 </div>
21 21
22- {{if .LikedHasMore}}22+ {{if or .LikedPage.HasPrev .LikedPage.HasNext}}
23- <div class="text-center py-4">23+ <div class="flex items-center justify-center gap-3 py-6">
24- <button hx-get="/library?liked_offset={{.NextLiked}}"24+ {{if .LikedPage.HasPrev}}
25- hx-target="#liked-list" hx-swap="beforeend"25+ <a href="/library?liked_page={{.LikedPage.PrevPage}}{{if gt .AnnotPage.Page 1}}&annot_page={{.AnnotPage.Page}}{{end}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">&laquo; Prev</a>
26- hx-select="article"26+ {{else}}
27- class="text-sm text-spot-secondary hover:text-spot-green transition">27+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">&laquo; Prev</span>
28- Load more28+ {{end}}
29- </button>29+
30+ <span class="text-sm text-spot-secondary">Page {{.LikedPage.Page}}</span>
31+
32+ {{if .LikedPage.HasNext}}
33+ <a href="/library?liked_page={{.LikedPage.NextPage}}{{if gt .AnnotPage.Page 1}}&annot_page={{.AnnotPage.Page}}{{end}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next &raquo;</a>
34+ {{else}}
35+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next &raquo;</span>
36+ {{end}}
30 </div>37 </div>
31 {{end}}38 {{end}}
32 </div>39 </div>
@@ -51,14 +58,21 @@
51 {{end}}58 {{end}}
52 </div>59 </div>
53 60
54- {{if .AnnotHasMore}}61+ {{if or .AnnotPage.HasPrev .AnnotPage.HasNext}}
55- <div class="text-center py-4">62+ <div class="flex items-center justify-center gap-3 py-6">
56- <button hx-get="/library?annot_offset={{.NextAnnot}}"63+ {{if .AnnotPage.HasPrev}}
57- hx-target="#annotations-list" hx-swap="beforeend"64+ <a href="/library?annot_page={{.AnnotPage.PrevPage}}{{if gt .LikedPage.Page 1}}&liked_page={{.LikedPage.Page}}{{end}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">&laquo; Prev</a>
58- hx-select=".bg-spot-surface"65+ {{else}}
59- class="text-sm text-spot-secondary hover:text-spot-green transition">66+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">&laquo; Prev</span>
60- Load more67+ {{end}}
61- </button>68+
69+ <span class="text-sm text-spot-secondary">Page {{.AnnotPage.Page}}</span>
70+
71+ {{if .AnnotPage.HasNext}}
72+ <a href="/library?annot_page={{.AnnotPage.NextPage}}{{if gt .LikedPage.Page 1}}&liked_page={{.LikedPage.Page}}{{end}}" class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-text hover:bg-spot-hover-50 transition">Next &raquo;</a>
73+ {{else}}
74+ <span class="px-3 py-1.5 rounded-lg text-sm bg-spot-hover text-spot-muted cursor-not-allowed">Next &raquo;</span>
75+ {{end}}
62 </div>76 </div>
63 {{end}}77 {{end}}
64 </div>78 </div>
modified readme.md +3 -1
@@ -1 +1,3 @@
1-# [Glean.at](https://glean.at)
1+# Glean.at
2+
3+[See live](https://glean.at)
@@ -1 +1,3 @@
1-# [Glean.at](https://glean.at)1+# Glean.at
2+
3+[See live](https://glean.at)