appview: improve pagination.Page usage #519

closed
opened by ptr.pet targeting master from ptr.pet/core: pipeline-paginated
Changed files
+112 -58
appview
db
issues
middleware
pages
templates
repo
issues
pagination
repo
+6 -6
appview/db/issues.go
···
// 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 {
···
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()...)
···
}
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) {
+19 -8
appview/issues/issues.go
···
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 {
···
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),
)
···
RepoInfo: f.RepoInfo(user),
Issues: issues,
FilteringByOpen: isOpen,
-
Page: page,
+
Pagination: paginate,
})
}
+1 -1
appview/issues/router.go
···
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())
+24 -22
appview/middleware/middleware.go
···
}
}
-
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 {
+1 -1
appview/pages/pages.go
···
RepoInfo repoinfo.RepoInfo
Active string
Issues []db.Issue
-
Page pagination.Page
+
Pagination pagination.Pagination
FilteringByOpen bool
}
+7 -6
appview/pages/templates/repo/issues/issues.html
···
{{ end }}
{{ define "pagination" }}
+
{{ $currentPage := .Pagination.CurrentPage }}
<div class="flex justify-end mt-4 gap-2">
{{ $currentState := "closed" }}
{{ if .FilteringByOpen }}
{{ $currentState = "open" }}
{{ end }}
-
{{ if gt .Page.Offset 0 }}
-
{{ $prev := .Page.Previous }}
+
{{ if gt $currentPage.No 0 }}
+
{{ $prev := .Pagination.PreviousPage }}
<a
class="btn flex items-center gap-2 no-underline hover:no-underline dark:text-white dark:hover:bg-gray-700"
hx-boost="true"
-
href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&offset={{ $prev.Offset }}&limit={{ $prev.Limit }}"
+
href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&page={{ add $prev.No 1 }}&count={{ $prev.Count }}"
>
{{ i "chevron-left" "w-4 h-4" }}
previous
···
<div></div>
{{ end }}
-
{{ if eq (len .Issues) .Page.Limit }}
-
{{ $next := .Page.Next }}
+
{{ if lt (add $currentPage.No 1) .Pagination.TotalPageCount }}
+
{{ $next := .Pagination.NextPage }}
<a
class="btn flex items-center gap-2 no-underline hover:no-underline dark:text-white dark:hover:bg-gray-700"
hx-boost="true"
-
href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&offset={{ $next.Offset }}&limit={{ $next.Limit }}"
+
href = "/{{ $.RepoInfo.FullName }}/issues?state={{ $currentState }}&page={{ add $next.No 1 }}&count={{ $next.Count }}"
>
next
{{ i "chevron-right" "w-4 h-4" }}
+53 -13
appview/pagination/page.go
···
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
+
}
+1 -1
appview/repo/feed.go
···
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 {