Commit dbec077010

dbec077010394f2955c9b20cfd81700ef6b2bdb9

parent: ce4ca463ba

Verified · cmc ci/build: success ci/test: success ci/vuln: success

cmc <hello@cleberg.net> · 2026-09-04 03:03 UTC

web: issue and merge request lists page at fifty

The lists rendered every row; a repository with a long history got one
page of hundreds. Fifty at a time now, with an "older" link that keeps
every active filter and pages on the number, the same keyset cursor the
commands use.

Ref #118

Layout: unified · split

internal/httpd/web.go +30 −4
@@ -1584,6 +1584,18 @@ func (s *Server) labelColors(repoID int64) map[string]template.CSS {
15841584 return out
15851585}
15861586
1587// listPage is how many issues or merge requests a list page shows before
1588// it offers the older ones (#118). Keyset paging on the number, the same
1589// cursor the commands use, so every filter carries across pages.
1590const listPage = 50
1591
1592// olderLink is the current URL with before=<number> set.
1593func olderLink(r *http.Request, before int64) string {
1594 q := r.URL.Query()
1595 q.Set("before", strconv.FormatInt(before, 10))
1596 return "?" + q.Encode()
1597}
1598
15871599func (s *Server) issues(w http.ResponseWriter, r *http.Request) {
15881600 p, ok := s.repoFor(w, r, "")
15891601 if !ok {
@@ -1598,12 +1610,18 @@ func (s *Server) issues(w http.ResponseWriter, r *http.Request) {
15981610 // label chips and author links point here.
15991611 qv := r.URL.Query()
16001612 f := store.IssueFilter{State: state, Label: qv.Get("label"), Assignee: qv.Get("assignee"),
1601 Author: qv.Get("author"), Milestone: qv.Get("milestone")}
1613 Author: qv.Get("author"), Milestone: qv.Get("milestone"), Limit: listPage + 1}
1614 f.Before, _ = strconv.ParseInt(qv.Get("before"), 10, 64)
16021615 issues, err := s.st.QueryIssues(p.Repo.ID, f)
16031616 if err != nil {
16041617 http.Error(w, "internal error", http.StatusInternalServerError)
16051618 return
16061619 }
1620 older := ""
1621 if len(issues) > listPage {
1622 issues = issues[:listPage]
1623 older = olderLink(r, issues[len(issues)-1].Number)
1624 }
16071625 if labels, err := s.st.ListIssueLabels(p.Repo.ID); err == nil {
16081626 for i := range issues {
16091627 issues[i].Labels = labels[issues[i].ID]
@@ -1616,8 +1634,9 @@ func (s *Server) issues(w http.ResponseWriter, r *http.Request) {
16161634 Filters []listFilter
16171635 Issues []store.Issue
16181636 LabelColors map[string]template.CSS
1637 Older string
16191638 }{p, state, f.Label, activeFilters(state, [][2]string{{"label", f.Label}, {"assignee", f.Assignee}, {"author", f.Author}, {"milestone", f.Milestone}}),
1620 issues, s.labelColors(p.Repo.ID)})
1639 issues, s.labelColors(p.Repo.ID), older})
16211640}
16221641
16231642func (s *Server) issue(w http.ResponseWriter, r *http.Request) {
@@ -1703,18 +1722,25 @@ func (s *Server) mrs(w http.ResponseWriter, r *http.Request) {
17031722 state = "open"
17041723 }
17051724 qv := r.URL.Query()
1706 mf := store.MRFilter{State: state, Author: qv.Get("author"), Milestone: qv.Get("milestone")}
1725 mf := store.MRFilter{State: state, Author: qv.Get("author"), Milestone: qv.Get("milestone"), Limit: listPage + 1}
1726 mf.Before, _ = strconv.ParseInt(qv.Get("before"), 10, 64)
17071727 mrs, err := s.st.QueryMRs(p.Repo.ID, mf)
17081728 if err != nil {
17091729 http.Error(w, "internal error", http.StatusInternalServerError)
17101730 return
17111731 }
1732 older := ""
1733 if len(mrs) > listPage {
1734 mrs = mrs[:listPage]
1735 older = olderLink(r, mrs[len(mrs)-1].Number)
1736 }
17121737 s.render(w, "mrs.html", struct {
17131738 repoPage
17141739 State string
17151740 Filters []listFilter
17161741 MRs []store.MR
1717 }{p, state, activeFilters(state, [][2]string{{"author", mf.Author}, {"milestone", mf.Milestone}}), mrs})
1742 Older string
1743 }{p, state, activeFilters(state, [][2]string{{"author", mf.Author}, {"milestone", mf.Milestone}}), mrs, older})
17181744}
17191745
17201746func (s *Server) mr(w http.ResponseWriter, r *http.Request) {
internal/web/templates/issues.html +1
@@ -22,4 +22,5 @@
2222</li>
2323{{else}}<li class="empty">no {{if ne .State "all"}}{{.State}} {{end}}issues — open one with <code>gitbay issue create {{.Repo.OwnerName}}/{{.Repo.Name}} --title "..."</code></li>{{end}}
2424</ul>
25{{if .Older}}<p class="pager"><a href="{{.Older}}">older →</a></p>{{end}}
2526{{end}}
internal/web/templates/mrs.html +1
@@ -21,4 +21,5 @@
2121</li>
2222{{else}}<li class="empty">no {{if ne .State "all"}}{{.State}} {{end}}merge requests — open one with <code>gitbay mr create {{.Repo.OwnerName}}/{{.Repo.Name}} --source ... --target {{.Repo.DefaultBranch}}</code></li>{{end}}
2323</ul>
24{{if .Older}}<p class="pager"><a href="{{.Older}}">older →</a></p>{{end}}
2425{{end}}