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/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

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

Celebrate a landed change request, once

c8fa5759b293126f1b247abc746bc339791568cb · Keenan Nemetz · 13d ago · parent 53ff967b

Celebrate a landed change request, once

A successful landing now redirects with ?merged=1, and app.js answers that
parameter by fetching static/confetti.browser.js (canvas-confetti 1.9.3, ISC)
and firing one burst. The flag is removed from the address as soon as it is
read, so a reload, a bookmark or a shared link is an ordinary visit, and no
page without the parameter ever requests the bundle. The library honours
prefers-reduced-motion through disableForReducedMotion, and reset() before
firing means a page restored from the back/forward cache cannot stack a
second canvas.

The asset is vendored the way htmx and Lucide are: the file is unmodified
from the published npm tarball, with the license beside it, so script-src
'self' stays as it is. It is deliberately not in the page shell, because
Static() serves assets uncompressed and a shell script would make every page
pay for a celebration only one page uses.

Refs #01M5XZ7T4R88BA9KV1KTSV7ZXP

server/ui_test.go +29

@@ -84,6 +84,35 @@ func TestStaticAssetsAreServedFromTheBinary(t *testing.T) {
84 84 }
85 85 }
86 86
87 // The celebration library is a static asset like any other, but it is not in the
88 // shell: the shell would make every page load fetch it. app.js names it, and names
89 // it once, so the only request is the one a landing asks for.
90 func TestConfettiIsFetchedOnlyForALanding(t *testing.T) {
91 t.Parallel()
92 f := newFixture(t)
93 f.publish(f.repo)
94 code, body := f.get("/static/confetti.browser.js")
95 if code != 200 {
96 t.Fatalf("/static/confetti.browser.js = %d", code)
97 }
98 if !strings.Contains(body, "canvas-confetti v1.9.3") {
99 t.Fatalf("the vendored script is not canvas-confetti 1.9.3:\n%s", body[:min(len(body), 200)])
100 }
101 code, app := f.get("/static/app.js")
102 if code != 200 {
103 t.Fatalf("/static/app.js = %d", code)
104 }
105 if n := strings.Count(app, "/static/confetti.browser.js"); n != 1 {
106 t.Fatalf("app.js names the confetti asset %d times, want 1", n)
107 }
108 for _, page := range []string{"/", "/r/" + f.repo + "/requests"} {
109 if code, html := f.get(page); code != 200 {
110 t.Fatalf("%s = %d", page, code)
111 } else if strings.Contains(html, "confetti") {
112 t.Fatalf("%s loads the celebration from the shell:\n%s", page, html)
113 }
114 }
115 }
87 116 func TestRunsPageListsQueuedWork(t *testing.T) {
88 117 t.Parallel()
89 118 f := newFixture(t)