appview: improve pagination.Page usage #519

closed
opened by ptr.pet targeting master from [deleted fork]: pipeline-paginated
Changed files
+112 -58
appview
db
issues
middleware
pages
templates
repo
issues
pagination
repo
+6 -6
appview/db/issues.go
··· 216 216 // get next issue_id 217 217 var newIssueId int 218 218 err := tx.QueryRow(` 219 - update repo_issue_seqs 220 - set next_issue_id = next_issue_id + 1 221 - where repo_at = ? 219 + update repo_issue_seqs 220 + set next_issue_id = next_issue_id + 1 221 + where repo_at = ? 222 222 returning next_issue_id - 1 223 223 `, issue.RepoAt).Scan(&newIssueId) 224 224 if err != nil { ··· 261 261 whereClause = " where " + strings.Join(conditions, " and ") 262 262 } 263 263 264 - pLower := FilterGte("row_num", page.Offset+1) 265 - pUpper := FilterLte("row_num", page.Offset+page.Limit) 264 + pLower := FilterGte("row_num", page.Count*page.No+1) 265 + pUpper := FilterLte("row_num", page.Count*(page.No+1)) 266 266 267 267 args = append(args, pLower.Arg()...) 268 268 args = append(args, pUpper.Arg()...) ··· 396 396 } 397 397 398 398 func GetIssues(e Execer, filters ...filter) ([]Issue, error) { 399 - return GetIssuesPaginated(e, pagination.FirstPage(), filters...) 399 + return GetIssuesPaginated(e, pagination.Page{No: 0, Count: 10}, filters...) 400 400 } 401 401 402 402 func GetIssue(e Execer, repoAt syntax.ATURI, issueId int) (*Issue, error) {
+19 -8
appview/issues/issues.go
··· 743 743 isOpen = true 744 744 } 745 745 746 - page, ok := r.Context().Value("page").(pagination.Page) 747 - if !ok { 748 - log.Println("failed to get page") 749 - page = pagination.FirstPage() 750 - } 751 - 752 746 user := rp.oauth.GetUser(r) 753 747 f, err := rp.repoResolver.Resolve(r) 754 748 if err != nil { ··· 756 750 return 757 751 } 758 752 753 + issueCounts, err := db.GetIssueCount(rp.db, f.RepoAt()) 754 + if err != nil { 755 + log.Println("failed to get issue count", err) 756 + rp.pages.Notice(w, "issues", "Failed to load issues. Try again later.") 757 + return 758 + } 759 + page, ok := r.Context().Value("page").(pagination.Page) 760 + if !ok { 761 + log.Println("failed to get page") 762 + page = pagination.Page{No: 0, Count: 10} 763 + } 764 + issueCount := issueCounts.Closed 765 + if isOpen { 766 + issueCount = issueCounts.Open 767 + } 768 + paginate := pagination.NewFromPage(page, issueCount) 769 + 759 770 openVal := 0 760 771 if isOpen { 761 772 openVal = 1 762 773 } 763 774 issues, err := db.GetIssuesPaginated( 764 775 rp.db, 765 - page, 776 + paginate.CurrentPage(), 766 777 db.FilterEq("repo_at", f.RepoAt()), 767 778 db.FilterEq("open", openVal), 768 779 ) ··· 777 788 RepoInfo: f.RepoInfo(user), 778 789 Issues: issues, 779 790 FilteringByOpen: isOpen, 780 - Page: page, 791 + Pagination: paginate, 781 792 }) 782 793 } 783 794
+1 -1
appview/issues/router.go
··· 11 11 r := chi.NewRouter() 12 12 13 13 r.Route("/", func(r chi.Router) { 14 - r.With(middleware.Paginate).Get("/", i.RepoIssues) 14 + r.With(middleware.Paginate(10)).Get("/", i.RepoIssues) 15 15 16 16 r.Route("/{issue}", func(r chi.Router) { 17 17 r.Use(mw.ResolveIssue())
+24 -22
appview/middleware/middleware.go
··· 81 81 } 82 82 } 83 83 84 - func Paginate(next http.Handler) http.Handler { 85 - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 86 - page := pagination.FirstPage() 84 + func Paginate(paginationCount int) func(next http.Handler) http.Handler { 85 + return func(next http.Handler) http.Handler { 86 + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 87 + page := pagination.Page{No: 0, Count: paginationCount} 87 88 88 - offsetVal := r.URL.Query().Get("offset") 89 - if offsetVal != "" { 90 - offset, err := strconv.Atoi(offsetVal) 91 - if err != nil { 92 - log.Println("invalid offset") 93 - } else { 94 - page.Offset = offset 89 + pageNoVal := r.URL.Query().Get("page") 90 + if pageNoVal != "" { 91 + pageNo, err := strconv.Atoi(pageNoVal) 92 + if err != nil { 93 + log.Println("invalid page no") 94 + } else if pageNo > 0 { 95 + page.No = pageNo - 1 96 + } 95 97 } 96 - } 97 98 98 - limitVal := r.URL.Query().Get("limit") 99 - if limitVal != "" { 100 - limit, err := strconv.Atoi(limitVal) 101 - if err != nil { 102 - log.Println("invalid limit") 103 - } else { 104 - page.Limit = limit 99 + pageCountVal := r.URL.Query().Get("count") 100 + if pageCountVal != "" { 101 + pageCount, err := strconv.Atoi(pageCountVal) 102 + if err != nil { 103 + log.Println("invalid page count") 104 + } else { 105 + page.Count = pageCount 106 + } 105 107 } 106 - } 107 108 108 - ctx := context.WithValue(r.Context(), "page", page) 109 - next.ServeHTTP(w, r.WithContext(ctx)) 110 - }) 109 + ctx := context.WithValue(r.Context(), "page", page) 110 + next.ServeHTTP(w, r.WithContext(ctx)) 111 + }) 112 + } 111 113 } 112 114 113 115 func (mw Middleware) knotRoleMiddleware(group string) middlewareFunc {
+1 -1
appview/pages/pages.go
··· 866 866 RepoInfo repoinfo.RepoInfo 867 867 Active string 868 868 Issues []db.Issue 869 - Page pagination.Page 869 + Pagination pagination.Pagination 870 870 FilteringByOpen bool 871 871 } 872 872
+7 -6
appview/pages/templates/repo/issues/issues.html
··· 87 87 {{ end }} 88 88 89 89 {{ define "pagination" }} 90 + {{ $currentPage := .Pagination.CurrentPage }} 90 91 <div class="flex justify-end mt-4 gap-2"> 91 92 {{ $currentState := "closed" }} 92 93 {{ if .FilteringByOpen }} 93 94 {{ $currentState = "open" }} 94 95 {{ end }} 95 96 96 - {{ if gt .Page.Offset 0 }} 97 - {{ $prev := .Page.Previous }} 97 + {{ if gt $currentPage.No 0 }} 98 + {{ $prev := .Pagination.PreviousPage }} 98 99 <a 99 100 class="btn flex items-center gap-2 no-underline hover:no-underline dark:text-white dark:hover:bg-gray-700" 100 101 hx-boost="true" 101 - href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&offset={{ $prev.Offset }}&limit={{ $prev.Limit }}" 102 + href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&page={{ add $prev.No 1 }}&count={{ $prev.Count }}" 102 103 > 103 104 {{ i "chevron-left" "w-4 h-4" }} 104 105 previous ··· 107 108 <div></div> 108 109 {{ end }} 109 110 110 - {{ if eq (len .Issues) .Page.Limit }} 111 - {{ $next := .Page.Next }} 111 + {{ if lt (add $currentPage.No 1) .Pagination.TotalPageCount }} 112 + {{ $next := .Pagination.NextPage }} 112 113 <a 113 114 class="btn flex items-center gap-2 no-underline hover:no-underline dark:text-white dark:hover:bg-gray-700" 114 115 hx-boost="true" 115 - href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&offset={{ $next.Offset }}&limit={{ $next.Limit }}" 116 + href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&page={{ add $next.No 1 }}&count={{ $next.Count }}" 116 117 > 117 118 next 118 119 {{ i "chevron-right" "w-4 h-4" }}
+53 -13
appview/pagination/page.go
··· 1 1 package pagination 2 2 3 - type Page struct { 3 + type Pagination struct { 4 4 Offset int // where to start from 5 - Limit int // number of items in a page 5 + Limit int // number of items on a single page 6 + Total int // total number of items 7 + } 8 + 9 + type Page struct { 10 + No int // page number 11 + Count int // number of items on this page 12 + } 13 + 14 + func New(offset, limit, total int) Pagination { 15 + return Pagination{ 16 + Offset: offset, 17 + Limit: limit, 18 + Total: total, 19 + } 20 + } 21 + 22 + func NewFromPage(page Page, total int) Pagination { 23 + return New(page.No*page.Count, page.Count, total) 6 24 } 7 25 8 - func FirstPage() Page { 26 + func (p Pagination) FirstPage() Page { 9 27 return Page{ 10 - Offset: 0, 11 - Limit: 10, 28 + No: 0, 29 + Count: p.Limit, 12 30 } 13 31 } 14 32 15 - func (p Page) Previous() Page { 33 + func (p Pagination) CurrentPage() Page { 34 + return Page{ 35 + No: p.Offset / p.Limit, 36 + Count: p.Limit, 37 + } 38 + } 39 + 40 + func (p Pagination) LastPage() Page { 41 + return Page{ 42 + No: (p.Total - 1) / p.Limit, 43 + Count: p.Limit, 44 + } 45 + } 46 + 47 + func (p Pagination) PreviousPage() Page { 16 48 if p.Offset-p.Limit < 0 { 17 - return FirstPage() 49 + return p.FirstPage() 18 50 } else { 19 51 return Page{ 20 - Offset: p.Offset - p.Limit, 21 - Limit: p.Limit, 52 + No: (p.Offset - p.Limit) / p.Limit, 53 + Count: p.Limit, 22 54 } 23 55 } 24 56 } 25 57 26 - func (p Page) Next() Page { 27 - return Page{ 28 - Offset: p.Offset + p.Limit, 29 - Limit: p.Limit, 58 + func (p Pagination) NextPage() Page { 59 + if p.Offset+p.Limit >= p.Total { 60 + return p.LastPage() 61 + } else { 62 + return Page{ 63 + No: (p.Offset + p.Limit) / p.Limit, 64 + Count: p.Limit, 65 + } 30 66 } 31 67 } 68 + 69 + func (p Pagination) TotalPageCount() int { 70 + return (p.Total + p.Limit - 1) / p.Limit 71 + }
+1 -1
appview/repo/feed.go
··· 26 26 27 27 issues, err := db.GetIssuesPaginated( 28 28 rp.db, 29 - pagination.Page{Limit: feedLimitPerType}, 29 + pagination.Page{No: 0, Count: feedLimitPerType}, 30 30 db.FilterEq("repo_at", f.RepoAt()), 31 31 ) 32 32 if err != nil {