From 1af31ddd5ad59834b0ac7c049b70900dba20f621 Mon Sep 17 00:00:00 2001 From: dusk Date: Wed, 20 Aug 2025 23:27:09 +0300 Subject: [PATCH] appview: improve pagination API Change-Id: llstyrzusrmvlkxnnyunzszpvnyoqvlp Signed-off-by: dusk --- appview/db/issues.go | 12 ++-- appview/issues/issues.go | 27 +++++--- appview/issues/router.go | 2 +- appview/middleware/middleware.go | 46 ++++++------- appview/pages/pages.go | 2 +- .../pages/templates/repo/issues/issues.html | 13 ++-- appview/pagination/page.go | 66 +++++++++++++++---- appview/repo/feed.go | 2 +- 8 files changed, 112 insertions(+), 58 deletions(-) diff --git a/appview/db/issues.go b/appview/db/issues.go index 75f47a7f..e437a6b3 100644 --- a/appview/db/issues.go +++ b/appview/db/issues.go @@ -216,9 +216,9 @@ func createNewIssue(tx *sql.Tx, issue *Issue) error { // get next issue_id var newIssueId int err := tx.QueryRow(` - update repo_issue_seqs - set next_issue_id = next_issue_id + 1 - where repo_at = ? + update repo_issue_seqs + set next_issue_id = next_issue_id + 1 + where repo_at = ? returning next_issue_id - 1 `, issue.RepoAt).Scan(&newIssueId) if err != nil { @@ -261,8 +261,8 @@ func GetIssuesPaginated(e Execer, page pagination.Page, filters ...filter) ([]Is whereClause = " where " + strings.Join(conditions, " and ") } - pLower := FilterGte("row_num", page.Offset+1) - pUpper := FilterLte("row_num", page.Offset+page.Limit) + pLower := FilterGte("row_num", page.Count*page.No+1) + pUpper := FilterLte("row_num", page.Count*(page.No+1)) args = append(args, pLower.Arg()...) args = append(args, pUpper.Arg()...) @@ -396,7 +396,7 @@ func GetIssuesPaginated(e Execer, page pagination.Page, filters ...filter) ([]Is } func GetIssues(e Execer, filters ...filter) ([]Issue, error) { - return GetIssuesPaginated(e, pagination.FirstPage(), filters...) + return GetIssuesPaginated(e, pagination.Page{No: 0, Count: 10}, filters...) } func GetIssue(e Execer, repoAt syntax.ATURI, issueId int) (*Issue, error) { diff --git a/appview/issues/issues.go b/appview/issues/issues.go index 6643d293..ac85a2cc 100644 --- a/appview/issues/issues.go +++ b/appview/issues/issues.go @@ -743,12 +743,6 @@ func (rp *Issues) RepoIssues(w http.ResponseWriter, r *http.Request) { isOpen = true } - page, ok := r.Context().Value("page").(pagination.Page) - if !ok { - log.Println("failed to get page") - page = pagination.FirstPage() - } - user := rp.oauth.GetUser(r) f, err := rp.repoResolver.Resolve(r) if err != nil { @@ -756,13 +750,30 @@ func (rp *Issues) RepoIssues(w http.ResponseWriter, r *http.Request) { return } + issueCounts, err := db.GetIssueCount(rp.db, f.RepoAt()) + if err != nil { + log.Println("failed to get issue count", err) + rp.pages.Notice(w, "issues", "Failed to load issues. Try again later.") + return + } + page, ok := r.Context().Value("page").(pagination.Page) + if !ok { + log.Println("failed to get page") + page = pagination.Page{No: 0, Count: 10} + } + issueCount := issueCounts.Closed + if isOpen { + issueCount = issueCounts.Open + } + paginate := pagination.NewFromPage(page, issueCount) + openVal := 0 if isOpen { openVal = 1 } issues, err := db.GetIssuesPaginated( rp.db, - page, + paginate.CurrentPage(), db.FilterEq("repo_at", f.RepoAt()), db.FilterEq("open", openVal), ) @@ -777,7 +788,7 @@ func (rp *Issues) RepoIssues(w http.ResponseWriter, r *http.Request) { RepoInfo: f.RepoInfo(user), Issues: issues, FilteringByOpen: isOpen, - Page: page, + Pagination: paginate, }) } diff --git a/appview/issues/router.go b/appview/issues/router.go index 58492585..93e866ad 100644 --- a/appview/issues/router.go +++ b/appview/issues/router.go @@ -11,7 +11,7 @@ func (i *Issues) Router(mw *middleware.Middleware) http.Handler { r := chi.NewRouter() r.Route("/", func(r chi.Router) { - r.With(middleware.Paginate).Get("/", i.RepoIssues) + r.With(middleware.Paginate(10)).Get("/", i.RepoIssues) r.Route("/{issue}", func(r chi.Router) { r.Use(mw.ResolveIssue()) diff --git a/appview/middleware/middleware.go b/appview/middleware/middleware.go index 7bb4eee6..f20fe800 100644 --- a/appview/middleware/middleware.go +++ b/appview/middleware/middleware.go @@ -81,33 +81,35 @@ func AuthMiddleware(a *oauth.OAuth) middlewareFunc { } } -func Paginate(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - page := pagination.FirstPage() +func Paginate(paginationCount int) func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + page := pagination.Page{No: 0, Count: paginationCount} - offsetVal := r.URL.Query().Get("offset") - if offsetVal != "" { - offset, err := strconv.Atoi(offsetVal) - if err != nil { - log.Println("invalid offset") - } else { - page.Offset = offset + pageNoVal := r.URL.Query().Get("page") + if pageNoVal != "" { + pageNo, err := strconv.Atoi(pageNoVal) + if err != nil { + log.Println("invalid page no") + } else if pageNo > 0 { + page.No = pageNo - 1 + } } - } - limitVal := r.URL.Query().Get("limit") - if limitVal != "" { - limit, err := strconv.Atoi(limitVal) - if err != nil { - log.Println("invalid limit") - } else { - page.Limit = limit + pageCountVal := r.URL.Query().Get("count") + if pageCountVal != "" { + pageCount, err := strconv.Atoi(pageCountVal) + if err != nil { + log.Println("invalid page count") + } else { + page.Count = pageCount + } } - } - ctx := context.WithValue(r.Context(), "page", page) - next.ServeHTTP(w, r.WithContext(ctx)) - }) + ctx := context.WithValue(r.Context(), "page", page) + next.ServeHTTP(w, r.WithContext(ctx)) + }) + } } func (mw Middleware) knotRoleMiddleware(group string) middlewareFunc { diff --git a/appview/pages/pages.go b/appview/pages/pages.go index 6d80f96d..7b165d3e 100644 --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -866,7 +866,7 @@ type RepoIssuesParams struct { RepoInfo repoinfo.RepoInfo Active string Issues []db.Issue - Page pagination.Page + Pagination pagination.Pagination FilteringByOpen bool } diff --git a/appview/pages/templates/repo/issues/issues.html b/appview/pages/templates/repo/issues/issues.html index d76a64fd..c2b9546a 100644 --- a/appview/pages/templates/repo/issues/issues.html +++ b/appview/pages/templates/repo/issues/issues.html @@ -87,18 +87,19 @@ {{ end }} {{ define "pagination" }} +{{ $currentPage := .Pagination.CurrentPage }}
{{ $currentState := "closed" }} {{ if .FilteringByOpen }} {{ $currentState = "open" }} {{ end }} - {{ if gt .Page.Offset 0 }} - {{ $prev := .Page.Previous }} + {{ if gt $currentPage.No 0 }} + {{ $prev := .Pagination.PreviousPage }} {{ i "chevron-left" "w-4 h-4" }} previous @@ -107,12 +108,12 @@
{{ end }} - {{ if eq (len .Issues) .Page.Limit }} - {{ $next := .Page.Next }} + {{ if lt (add $currentPage.No 1) .Pagination.TotalPageCount }} + {{ $next := .Pagination.NextPage }}
next {{ i "chevron-right" "w-4 h-4" }} diff --git a/appview/pagination/page.go b/appview/pagination/page.go index e9f47fa1..62065f4b 100644 --- a/appview/pagination/page.go +++ b/appview/pagination/page.go @@ -1,31 +1,71 @@ package pagination -type Page struct { +type Pagination struct { Offset int // where to start from - Limit int // number of items in a page + Limit int // number of items on a single page + Total int // total number of items +} + +type Page struct { + No int // page number + Count int // number of items on this page +} + +func New(offset, limit, total int) Pagination { + return Pagination{ + Offset: offset, + Limit: limit, + Total: total, + } +} + +func NewFromPage(page Page, total int) Pagination { + return New(page.No*page.Count, page.Count, total) } -func FirstPage() Page { +func (p Pagination) FirstPage() Page { return Page{ - Offset: 0, - Limit: 10, + No: 0, + Count: p.Limit, } } -func (p Page) Previous() Page { +func (p Pagination) CurrentPage() Page { + return Page{ + No: p.Offset / p.Limit, + Count: p.Limit, + } +} + +func (p Pagination) LastPage() Page { + return Page{ + No: (p.Total - 1) / p.Limit, + Count: p.Limit, + } +} + +func (p Pagination) PreviousPage() Page { if p.Offset-p.Limit < 0 { - return FirstPage() + return p.FirstPage() } else { return Page{ - Offset: p.Offset - p.Limit, - Limit: p.Limit, + No: (p.Offset - p.Limit) / p.Limit, + Count: p.Limit, } } } -func (p Page) Next() Page { - return Page{ - Offset: p.Offset + p.Limit, - Limit: p.Limit, +func (p Pagination) NextPage() Page { + if p.Offset+p.Limit >= p.Total { + return p.LastPage() + } else { + return Page{ + No: (p.Offset + p.Limit) / p.Limit, + Count: p.Limit, + } } } + +func (p Pagination) TotalPageCount() int { + return (p.Total + p.Limit - 1) / p.Limit +} diff --git a/appview/repo/feed.go b/appview/repo/feed.go index d4e99854..1aaa0bf5 100644 --- a/appview/repo/feed.go +++ b/appview/repo/feed.go @@ -26,7 +26,7 @@ func (rp *Repo) getRepoFeed(ctx context.Context, f *reporesolver.ResolvedRepo) ( issues, err := db.GetIssuesPaginated( rp.db, - pagination.Page{Limit: feedLimitPerType}, + pagination.Page{No: 0, Count: feedLimitPerType}, db.FilterEq("repo_at", f.RepoAt()), ) if err != nil { -- 2.43.0