Commit 464a15c39a
Verified · cmc
Layout: unified · split
app/api.go +4 −3
| @@ -20,13 +20,14 @@ type info struct { | ||
| 20 | 20 | Settings settingsParams `json:"settings"` |
| 21 | 21 | } |
| 22 | 22 | |
| 23 | // Info responds with this instance's version and its proxy/NSFW settings. | |
| 23 | // Info responds with this instance's version and its proxy/NSFW/hide-ai settings. | |
| 24 | 24 | func (a API) Info() { |
| 25 | 25 | json, err := json.Marshal(info{ |
| 26 | 26 | Version: a.main.Version, |
| 27 | 27 | Settings: settingsParams{ |
| 28 | Nsfw: CFG.Nsfw, | |
| 29 | Proxy: CFG.Proxy, | |
| 28 | Nsfw: CFG.Nsfw, | |
| 29 | Proxy: CFG.Proxy, | |
| 30 | HideAI: CFG.HideAI, | |
| 30 | 31 | }, |
| 31 | 32 | }) |
| 32 | 33 | try(err) |
app/cli.go +6 −4
| @@ -55,8 +55,9 @@ type settingsUrls struct { | ||
| 55 | 55 | } |
| 56 | 56 | |
| 57 | 57 | type settingsParams struct { |
| 58 | Nsfw bool `json:"nsfw"` | |
| 59 | Proxy bool `json:"proxy"` | |
| 58 | Nsfw bool `json:"nsfw"` | |
| 59 | Proxy bool `json:"proxy"` | |
| 60 | HideAI bool `json:"hide-ai"` | |
| 60 | 61 | } |
| 61 | 62 | |
| 62 | 63 | type settings struct { |
| @@ -112,8 +113,9 @@ func addInstance() { | ||
| 112 | 113 | Country: prompt("Country", true), |
| 113 | 114 | ModifiedSrc: prompt("Link to modified sources", false), |
| 114 | 115 | Settings: settingsParams{ |
| 115 | Nsfw: CFG.Nsfw, | |
| 116 | Proxy: CFG.Proxy, | |
| 116 | Nsfw: CFG.Nsfw, | |
| 117 | Proxy: CFG.Proxy, | |
| 118 | HideAI: CFG.HideAI, | |
| 117 | 119 | }, |
| 118 | 120 | Urls: settingsUrls{ |
| 119 | 121 | Clearnet: prompt("Clearnet link", false), |
app/config.go +3 −2
| @@ -134,8 +134,9 @@ func ExecuteConfig() { | ||
| 134 | 134 | } |
| 135 | 135 | |
| 136 | 136 | About = instanceAbout{ |
| 137 | Proxy: CFG.Proxy, | |
| 138 | Nsfw: CFG.Nsfw, | |
| 137 | Proxy: CFG.Proxy, | |
| 138 | Nsfw: CFG.Nsfw, | |
| 139 | HideAI: CFG.HideAI, | |
| 139 | 140 | } |
| 140 | 141 | |
| 141 | 142 | static.StaticPath = CFG.StaticPath |
app/util.go +13 −4
| @@ -72,6 +72,7 @@ func RefreshInstances() { | ||
| 72 | 72 | type instanceAbout struct { |
| 73 | 73 | Proxy bool `json:"proxy"` |
| 74 | 74 | Nsfw bool `json:"nsfw"` |
| 75 | HideAI bool `json:"hide-ai"` | |
| 75 | 76 | Instances []settings `json:"instances"` |
| 76 | 77 | } |
| 77 | 78 | |
| @@ -337,9 +338,6 @@ type DeviationList struct { | ||
| 337 | 338 | } |
| 338 | 339 | |
| 339 | 340 | // NavBase renders the page navigation bar for a list. |
| 340 | // | |
| 341 | // FIXME: on some artworks the first page can make the navigation panel disappear | |
| 342 | // entirely. | |
| 343 | 341 | func (s skunkyart) NavBase(c DeviationList) string { |
| 344 | 342 | var list strings.Builder |
| 345 | 343 | |
| @@ -379,7 +377,18 @@ func (s skunkyart) NavBase(c DeviationList) string { | ||
| 379 | 377 | p = 1 |
| 380 | 378 | } |
| 381 | 379 | |
| 382 | for i, x := p-6, 0; (i <= c.Pages && i <= p+6) && x < 12; i++ { | |
| 380 | // The window runs to the last page or the current one, whichever is further | |
| 381 | // out. Callers that cannot count pages pass Pages: 0 — the comment list on an | |
| 382 | // artwork is one — and bounding purely by Pages then ended the loop before | |
| 383 | // i reached 1, so page one rendered no numbers at all. With nothing before it | |
| 384 | // to link back to and no further page to link on, the whole panel came out as | |
| 385 | // a bare <br>. | |
| 386 | last := c.Pages | |
| 387 | if p > last { | |
| 388 | last = p | |
| 389 | } | |
| 390 | ||
| 391 | for i, x := p-6, 0; (i <= last && i <= p+6) && x < 12; i++ { | |
| 383 | 392 | if i > 0 { |
| 384 | 393 | var onPage bool |
| 385 | 394 | if i == p { |
app/util_test.go added +74
| @@ -0,0 +1,74 @@ | ||
| 1 | package app | |
| 2 | ||
| 3 | import ( | |
| 4 | "strings" | |
| 5 | "testing" | |
| 6 | ) | |
| 7 | ||
| 8 | // TestNavBaseRendersFirstPageWithoutPageCount is the regression test for the | |
| 9 | // navigation panel vanishing on some artworks. The comment list on a deviation | |
| 10 | // cannot count its pages, so it passes Pages: 0; on page one, with no further | |
| 11 | // page to offer, the panel used to render as nothing but a <br>. | |
| 12 | func TestNavBaseRendersFirstPageWithoutPageCount(t *testing.T) { | |
| 13 | s := skunkyart{Page: 1, _pth: "/deviation/1"} | |
| 14 | ||
| 15 | out := s.NavBase(DeviationList{Pages: 0, More: false}) | |
| 16 | ||
| 17 | if strings.TrimSpace(strings.TrimPrefix(out, "<br>")) == "" { | |
| 18 | t.Fatalf("navigation panel is empty, want the current page rendered: %q", out) | |
| 19 | } | |
| 20 | if !strings.Contains(out, "1") { | |
| 21 | t.Errorf("current page number missing from %q", out) | |
| 22 | } | |
| 23 | } | |
| 24 | ||
| 25 | // TestNavBaseFirstPageOffersNextWhenMore covers the same Pages: 0 case when a | |
| 26 | // further page does exist: the current page must still appear alongside Next, | |
| 27 | // rather than Next standing on its own with nothing to anchor it. | |
| 28 | func TestNavBaseFirstPageOffersNextWhenMore(t *testing.T) { | |
| 29 | s := skunkyart{Page: 1, _pth: "/deviation/1"} | |
| 30 | ||
| 31 | out := s.NavBase(DeviationList{Pages: 0, More: true}) | |
| 32 | ||
| 33 | if !strings.Contains(out, "Next") { | |
| 34 | t.Errorf("Next link missing from %q", out) | |
| 35 | } | |
| 36 | if !strings.Contains(out, "1") { | |
| 37 | t.Errorf("current page number missing from %q", out) | |
| 38 | } | |
| 39 | } | |
| 40 | ||
| 41 | // TestNavBaseKnownPageCountUnchanged pins the ordinary path: when the caller | |
| 42 | // does know the page count, the window is still bounded by it. | |
| 43 | func TestNavBaseKnownPageCountUnchanged(t *testing.T) { | |
| 44 | s := skunkyart{Page: 1, _pth: "/gallery"} | |
| 45 | ||
| 46 | out := s.NavBase(DeviationList{Pages: 3, More: true}) | |
| 47 | ||
| 48 | for _, want := range []string{"1", "2", "3"} { | |
| 49 | if !strings.Contains(out, want) { | |
| 50 | t.Errorf("page %s missing from %q", want, out) | |
| 51 | } | |
| 52 | } | |
| 53 | if strings.Contains(out, "p=4") { | |
| 54 | t.Errorf("linked past the last page in %q", out) | |
| 55 | } | |
| 56 | } | |
| 57 | ||
| 58 | // TestInstanceAboutCarriesHideAI covers the About page and /api/instance both | |
| 59 | // reading hide-ai from config, so a visitor can tell whether an instance | |
| 60 | // filters AI-generated works without inferring it from absent results. | |
| 61 | func TestInstanceAboutCarriesHideAI(t *testing.T) { | |
| 62 | hide := CFG.HideAI | |
| 63 | defer func() { CFG.HideAI = hide }() | |
| 64 | ||
| 65 | for _, on := range []bool{true, false} { | |
| 66 | CFG.HideAI = on | |
| 67 | if got := (instanceAbout{HideAI: CFG.HideAI}).HideAI; got != on { | |
| 68 | t.Errorf("instanceAbout.HideAI = %v, want %v", got, on) | |
| 69 | } | |
| 70 | if got := (settingsParams{HideAI: CFG.HideAI}).HideAI; got != on { | |
| 71 | t.Errorf("settingsParams.HideAI = %v, want %v", got, on) | |
| 72 | } | |
| 73 | } | |
| 74 | } | |
static/html/about.htm +1
| @@ -11,6 +11,7 @@ | ||
| 11 | 11 | <ul> |
| 12 | 12 | <li><b>NSFW</b>: <span class="about-{{.Templates.About.Nsfw}}">{{if .Templates.About.Nsfw}}YES{{else}}NO{{end}}</span></li> |
| 13 | 13 | <li><b>Proxyfing</b>: <span class="about-{{.Templates.About.Proxy}}">{{if .Templates.About.Proxy}}YES{{else}}NO{{end}}</span></li> |
| 14 | <li><b>Hide AI</b>: <span class="about-{{.Templates.About.HideAI}}">{{if .Templates.About.HideAI}}YES{{else}}NO{{end}}</span></li> | |
| 14 | 15 | </ul> |
| 15 | 16 | <details> |
| 16 | 17 | <summary><b>Instances:</b></summary> |