website/pkg/models/pagination.go
Noah 500456c05e Recent Forum Posts
* Add a "Newest" tab to the Forums landing page to order ALL forum posts
  (comments) by most recent, paginated.
* Add a "Views" cooldown in Redis: viewing the same post multiple times
  within 1 hour doesn't ++ the view count with every page load, per user
  per thread ID.
* Update the paginators to handle unlimited numbers of pages: shows max
  7 page buttons with your current page towards the middle.
* General ability to jump to the "last page" of anything: use a negative
  page size like ?page=-1 and it acts like the last page.
2022-08-30 22:13:57 -07:00

120 lines
2.2 KiB
Go

package models
import (
"math"
"net/http"
"strconv"
"code.nonshy.com/nonshy/website/pkg/config"
)
// Pagination result object.
type Pagination struct {
Page int // provide <0 to mean "last page"
PerPage int
Total int64
Sort string
// privates
lastPage bool
}
// Page for Iter.
type Page struct {
Page int
IsCurrent bool
}
// Load the page from form or query parameters.
func (p *Pagination) ParsePage(r *http.Request) {
raw := r.FormValue("page")
a, err := strconv.Atoi(raw)
if err == nil {
if a <= 0 {
p.lastPage = true
a = 1
}
p.Page = a
} else {
p.Page = 1
}
}
// Iter the pages, for templates.
func (p *Pagination) Iter() []Page {
var (
pages = []Page{}
pageIdx int
total = p.Pages()
)
for i := 1; i <= total; i++ {
pages = append(pages, Page{
Page: i,
IsCurrent: i == p.Page,
})
if i == p.Page {
pageIdx = i
}
}
// Do we have A LOT of pages?
if len(pages) > config.PagerButtonLimit {
// We return a slide only N pages long. Where is our current page in the offset?
if pageIdx <= config.PagerButtonLimit/2 {
// We are near the front, return the first N pages.
return pages[:config.PagerButtonLimit+1]
}
// Are we near the end?
if pageIdx > len(pages)-(config.PagerButtonLimit/2) {
// We are near the end, return the last N pages.
return pages[len(pages)-config.PagerButtonLimit-1:]
}
// We are somewhere in the middle.
var result = []Page{}
for i := pageIdx - (config.PagerButtonLimit / 2) - 1; i < pageIdx+(config.PagerButtonLimit/2); i++ {
if i >= 0 && i < len(pages) {
result = append(result, pages[i])
}
}
return result
}
return pages
}
func (p *Pagination) Pages() int {
return int(math.Ceil(float64(p.Total) / float64(p.PerPage)))
}
func (p *Pagination) GetOffset() int {
// Are we looking for the FINAL page?
if p.lastPage && p.Pages() >= 1 {
p.Page = p.Pages()
}
return (p.Page - 1) * p.PerPage
}
func (p *Pagination) HasNext() bool {
return p.Page < p.Pages()
}
func (p *Pagination) HasPrevious() bool {
return p.Page > 1
}
func (p *Pagination) Next() int {
if p.Page >= p.Pages() {
return p.Pages()
}
return p.Page + 1
}
func (p *Pagination) Previous() int {
if p.Page > 1 {
return p.Page - 1
}
return 1
}