package web import ( "net/http" "net/url" "strconv" ) const defaultCollectionPageSize = 20 type paginationData struct { Page int TotalPages int PreviousURL string NextURL string } func paginateCollection[T any](r *http.Request, values []T) ([]T, *paginationData) { pageSize := collectionPageSize(r) totalPages := (len(values) + pageSize - 1) / pageSize if totalPages <= 1 { return values, nil } page, _ := strconv.Atoi(r.URL.Query().Get("page")) if page < 1 { page = 1 } if page > totalPages { page = totalPages } start := (page - 1) * pageSize end := min(start+pageSize, len(values)) pagination := &paginationData{Page: page, TotalPages: totalPages} if page > 1 { pagination.PreviousURL = collectionPageURL(r.URL, page-1) } if page < totalPages { pagination.NextURL = collectionPageURL(r.URL, page+1) } return values[start:end], pagination } func collectionPageSize(r *http.Request) int { userSuffix := "" if user, ok := userFromContext(r.Context()); ok { userSuffix = "." + strconv.Itoa(user.ID) } for _, cookieName := range []string{"gardomatic.entries-per-page" + userSuffix, "gardomatic.tasks-per-page" + userSuffix} { cookie, err := r.Cookie(cookieName) if err != nil { continue } pageSize, err := strconv.Atoi(cookie.Value) if err == nil && validCollectionPageSize(pageSize) { return pageSize } } return defaultCollectionPageSize } func validCollectionPageSize(pageSize int) bool { switch pageSize { case 10, 20, 50, 100: return true default: return false } } func collectionPageURL(current *url.URL, page int) string { query := current.Query() query.Set("page", strconv.Itoa(page)) path := current.Path if path == "" { path = "?" } else { path += "?" } return path + query.Encode() }