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.

Refresh the repository tab counts while the page is open

a2ec95616f4216c743cb1ae358ef8e9449ad8d1f · Keenan Nemetz · 9d ago · parent 0789f22e

Refresh the repository tab counts while the page is open

The header's open counts were only as fresh as the page load, so a reader
watching a repository had to reload to find out that an issue or a change had
been opened.

The tabs now poll /r/{name}/counts every 30s while the page is visible. The
response is the two badges as out-of-band updates, so the current tab, the
clone menu and anything the reader has typed stay where they are. A badge that
moves keeps one short ring, because a number changing elsewhere on the screen
is easy to miss. The endpoint answers 404 for a repository the viewer cannot
read, like every other page under /r/{name}.

server/web/templates/chrome.html +23−3

@@ -93,6 +93,18 @@
93 93 </html>
94 94 {{end}}
95 95
96 {{/* The open counts beside the Issues and Changes tabs. They are templates of
97 their own so the header's poll can replace the badges and nothing else: the
98 tab the reader is on, the clone menu and any open form stay where they are.
99 The ids are what the out-of-band swap matches, so the page and the poll
100 agree on where a count goes. */}}
101 {{define "repo_issue_count"}}<span class="chip-count" id="repo-issue-count"{{if .Live}} hx-swap-oob="outerHTML"{{end}}>{{.OpenIssues}}</span>{{end}}
102 {{define "repo_request_count"}}<span class="chip-count" id="repo-request-count"{{if .Live}} hx-swap-oob="outerHTML"{{end}}>{{.OpenRequests}}</span>{{end}}
103 {{define "repo_counts_live"}}
104 {{template "repo_issue_count" .}}
105 {{template "repo_request_count" .}}
106 {{end}}
107
96 108 {{define "repohead"}}
97 109 <div class="repohead">
98 110 <div class="repo-title">
@@ -119,12 +131,20 @@
119 131 </div>
120 132 {{if .Description}}<p class="repo-desc">{{.Description}}</p>{{end}}
121 133 </div>
122 <nav class="tabs" aria-label="Repository">
134 {{/* One poll for the header's counts. The response is out-of-band updates to
135 the two badges, so the tabs are not re-rendered and the page keeps its
136 state. It pauses while the tab is hidden, so a forgotten page does not
137 keep asking. */}}
138 <nav class="tabs" aria-label="Repository"
139 hx-get="/r/{{.Name}}/counts"
140 hx-trigger="every 30s [document.visibilityState === 'visible']"
141 hx-sync="this:replace"
142 hx-swap="none">
123 143 <a class="repotab{{if eq .Tab "overview"}} active{{end}}" href="/r/{{.Name}}"{{if eq .Tab "overview"}} aria-current="page"{{end}}>{{icon "layout-dashboard"}}Overview</a>
124 144 <a href="/r/{{.Name}}/activity"{{if eq .Tab "activity"}} class="active" aria-current="page"{{end}}>{{icon "activity"}}Activity</a>
125 145 <a href="/r/{{.Name}}/code"{{if eq .Tab "code"}} class="active" aria-current="page"{{end}}>{{icon "code"}}Code</a>
126 <a href="/r/{{.Name}}/issues"{{if eq .Tab "issues"}} class="active" aria-current="page"{{end}}>{{icon "circle-dot"}}Issues <span class="chip-count">{{.OpenIssues}}</span></a>
127 <a href="/r/{{.Name}}/requests"{{if eq .Tab "requests"}} class="active" aria-current="page"{{end}}>{{icon "git-pull-request"}}Changes <span class="chip-count">{{.OpenRequests}}</span></a>
146 <a href="/r/{{.Name}}/issues"{{if eq .Tab "issues"}} class="active" aria-current="page"{{end}}>{{icon "circle-dot"}}Issues {{template "repo_issue_count" .}}</a>
147 <a href="/r/{{.Name}}/requests"{{if eq .Tab "requests"}} class="active" aria-current="page"{{end}}>{{icon "git-pull-request"}}Changes {{template "repo_request_count" .}}</a>
128 148 <a href="/r/{{.Name}}/releases"{{if eq .Tab "releases"}} class="active" aria-current="page"{{end}}>{{icon "tag"}}Releases</a>
129 149 <a href="/r/{{.Name}}/runs"{{if eq .Tab "runs"}} class="active" aria-current="page"{{end}}>{{icon "play"}}Runs</a>
130 150 <a href="/r/{{.Name}}/locks"{{if eq .Tab "locks"}} class="active" aria-current="page"{{end}}>{{icon "lock"}}Locks</a>