Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

A free and open-source forge that lives in your Git repo.

Show open work on repository tabs and overview

a196e404c86a5b9607bd226c7229ffc89a37d47f · Keenan Nemetz · 11d ago · parent 0e35a6a0

Show open work on repository tabs and overview

The repository header now counts open issues and change requests, and the overview sidebar lists the newest open issues and change requests with links to their full lists. Closed and merged records stay out of both: the counts and the overview describe what is still being worked on.

Issue counts come from the derived index; change requests are read from the metadata branch, where their state lives. A failed read leaves a zero or an empty section rather than turning the overview into an error page.

server/web/page.go +38−9

@@ -9,6 +9,8 @@ import (
9 9 "strings"
10 10
11 11 "github.com/rodent-software/vole/forge/access"
12 "github.com/rodent-software/vole/forge/repo"
13 "github.com/rodent-software/vole/metadata"
12 14 )
13 15
14 16 // Page is embedded by every view. Exported so html/template can promote its
@@ -70,10 +72,11 @@ func (w *Web) who(name string) string {
70 72 }
71 73
72 74 // repoNav is the repository header's data: which repository, which tab is
73 // current, and whether anonymous readers can see it. Visibility comes from the
74 // same ACL rule the handlers use, so the lock/globe icon cannot disagree with
75 // whether an anonymous clone works. Templates call it as
76 // {{template "repohead" (nav .Name "issues")}}.
75 // current, whether anonymous readers can see it, and how many open issues and
76 // change requests its tabs should advertise. Visibility comes from the same ACL
77 // rule the handlers use, so the lock/globe icon cannot disagree with whether an
78 // anonymous clone works. Templates call it as
79 // {{template "repohead" (nav .Name "issues" .CanAdmin)}}.
77 80 func (w *Web) repoNav(name, tab string, canAdmin bool) repoNavData {
78 81 n := repoNavData{Name: name, Tab: tab, Private: true, CanAdmin: canAdmin}
79 82 m, err := w.Repos.Open(name)
@@ -84,13 +87,39 @@ func (w *Web) repoNav(name, tab string, canAdmin bool) repoNavData {
84 87 if acl, err := m.ACL(); err == nil {
85 88 n.Private = !acl.Can("", access.Read)
86 89 }
90 n.OpenIssues, n.OpenRequests = w.openCounts(m)
87 91 return n
88 92 }
89 93
94 // openCounts returns the counts the repository tabs show. They describe the
95 // whole repository, not a filtered page: a badge that changed when you searched
96 // would be a lie. A read that fails leaves a zero rather than failing the page
97 // the reader asked for; the tab still links to the list, which reports its own
98 // error if the same read fails there.
99 func (w *Web) openCounts(m *repo.Meta) (issues, requests int) {
100 if w.Index != nil {
101 if err := w.Index.Ensure(m); err == nil {
102 if n, err := w.Index.IssueCount(m.ID, "open"); err == nil {
103 issues = n
104 }
105 }
106 }
107 if list, err := metadata.LoadRequests(m.Git(), metadata.Ref); err == nil {
108 for _, req := range list {
109 if req.State == "open" {
110 requests++
111 }
112 }
113 }
114 return issues, requests
115 }
116
90 117 type repoNavData struct {
91 Name string
92 Description string
93 Tab string
94 Private bool
95 CanAdmin bool
118 Name string
119 Description string
120 Tab string
121 Private bool
122 CanAdmin bool
123 OpenIssues int
124 OpenRequests int
96 125 }