Skip to content

Fix #1312: restore map thumbnail on expanded server cards - #1326

Merged
rumblefrog merged 1 commit into
mainfrom
fix/issue-1312-server-card-map-thumbnails
May 10, 2026
Merged

rumblefrog merged 1 commit into
mainfrom
fix/issue-1312-server-card-map-thumbnails

Conversation

@rumblefrog

Copy link
Copy Markdown
Member

Summary

Fixes #1312.

The v2.0.0 redesign at #1123 D1 rebuilt page_servers.tpl from the handoff card grid and dropped the legacy <img id="mapimg_{\$server.sid}"> slot. The API handler still returns the mapimg URL (api_servers_host_players → GetMapImage()), so the field had no consumer — every expanded server card showed the map name as text only.

This PR restores the surface by:

  • Adding a hidden <img data-testid="server-map-img"> inside the players panel (so it rides the same [hidden] toggle that surfaces the player list — the issue's "expanded server card" framing).
  • Wiring the inline initializer to patch src from r.data.mapimg. The slot only paints once onload fires; on error it stays hidden so missing files (a fork without nomap.jpg, an unknown map without a screenshot) degrade to "no thumbnail" instead of a broken-image icon. nomap.jpg already ships in web/images/maps/ so the standard fallback path renders the bundled placeholder.
  • Documenting the new render path in AGENTS.md's "Where to find what" table.

No backend / API contract changes — the handler already emits mapimg. PHP GetMapImage() (web/includes/system-functions.php) is unchanged.

Regression guards

  • web/tests/integration/ServerMapImageRenderTest.php — static contract: handler still emits mapimg, template ships the <img> slot inside the players panel, inline JS wires d.mapimg + onload / onerror, GetMapImage() falls back to nomap and the bundled nomap.jpg placeholder ships.
  • web/tests/e2e/specs/flows/server-map-thumbnail.spec.ts — runtime observable across the three terminal paths: bundled image loads + becomes visible, missing image keeps the slot hidden, server-offline keeps the slot hidden.

Test plan

Quality gates run inside the parallel Docker stack scoped to sbpp-task-1312:

  • ./sbpp.sh phpstan — pass (228/228, no errors)
  • ./sbpp.sh test — pass (408 tests, 1777 assertions)
  • ./sbpp.sh test --filter ServerMapImageRenderTest — pass (5 tests, 12 assertions, all green)
  • ./sbpp.sh ts-check — pass (no .js files touched; the inline <script> in the .tpl is already covered by the existing JSDoc)
  • ./sbpp.sh e2e specs/flows/server-map-thumbnail.spec.ts --workers=1 — pass (6 tests across both chromium + mobile-chromium projects)
  • ./sbpp.sh e2e specs/smoke/servers.spec.ts specs/flows/empty-states.spec.ts --workers=1 — pass (no regressions on the related surfaces)
  • ./sbpp.sh e2e --grep "servers" specs/a11y/routes.spec.ts --workers=1 — pass (0 critical axe violations on ?p=servers light + dark)

API contract regen not required — no handler signatures changed.

The v2.0.0 redesign at #1123 D1 rebuilt page_servers.tpl from the
handoff card grid and dropped the legacy `<img id="mapimg_{$server.sid}">`
slot. The API handler still returns the `mapimg` URL
(api_servers_host_players → GetMapImage), so the field had no
consumer — every expanded card showed the map name as text only.

Restore the surface by adding a hidden `<img data-testid="server-map-img">`
inside the players panel and wiring the inline initializer to
patch `src` from `r.data.mapimg`. The slot only paints once
`onload` fires; on `error` it stays hidden so missing files (a
fork without `nomap.jpg`, an unknown map without a screenshot)
degrade to "no thumbnail" instead of a broken-image icon.

Regression guards:
  - web/tests/integration/ServerMapImageRenderTest.php pins the
    static contract: handler still emits `mapimg`, template ships
    the `<img>` slot inside the players panel, inline JS wires
    `d.mapimg` + `onload`/`onerror`, GetMapImage falls back to
    `nomap` and the bundled `nomap.jpg` placeholder ships.
  - web/tests/e2e/specs/flows/server-map-thumbnail.spec.ts pins the
    runtime observable across the three terminal paths: bundled
    image loads + becomes visible, missing image keeps the slot
    hidden, server-offline keeps the slot hidden.

Documents the new render path in AGENTS.md's "Where to find what"
table so the surface stays discoverable.
@rumblefrog
rumblefrog added this pull request to the merge queue May 10, 2026
Merged via the queue into main with commit 80de4c3 May 10, 2026
4 checks passed
@rumblefrog
rumblefrog deleted the fix/issue-1312-server-card-map-thumbnails branch May 10, 2026 21:19
rumblefrog added a commit that referenced this pull request May 10, 2026
Two test-only fixes surfaced when the PR rebased on top of #1320 +
#1329 landing in main:

- `BanListIpColumnTest::renderBanList` (from #1320) constructs a
  `BanListView` with positional + named arguments. This PR adds a 28th
  constructor parameter (`is_advanced_search_open`) which the test
  must now pass — default it to `false` so the IP-column suite stays
  exercising the column-render contract it was written for, not the
  disclosure state.
- `ServerMapImageRenderTest::testHandlerStillEmitsMapimgField` (from
  #1326) asserted a literal `"'mapimg'   => GetMapImage("` substring
  in `web/api/handlers/servers.php`. #1329's cache + debounce rewrite
  shifted the surrounding column alignment to 5 spaces, breaking the
  brittle match without changing the contract the test guards (the
  field is still emitted). Switch to a regex that tolerates any
  whitespace between the key and `=>` so a future alignment shift
  doesn't silently re-trigger the same false positive.
rumblefrog added a commit that referenced this pull request May 10, 2026
Two test-only fixes surfaced when the PR rebased on top of #1320 +

- `BanListIpColumnTest::renderBanList` (from #1320) constructs a
  `BanListView` with positional + named arguments. This PR adds a 28th
  constructor parameter (`is_advanced_search_open`) which the test
  must now pass — default it to `false` so the IP-column suite stays
  exercising the column-render contract it was written for, not the
  disclosure state.
- `ServerMapImageRenderTest::testHandlerStillEmitsMapimgField` (from
  #1326) asserted a literal `"'mapimg'   => GetMapImage("` substring
  in `web/api/handlers/servers.php`. #1329's cache + debounce rewrite
  shifted the surrounding column alignment to 5 spaces, breaking the
  brittle match without changing the contract the test guards (the
  field is still emitted). Switch to a regex that tolerates any
  whitespace between the key and `=>` so a future alignment shift
  doesn't silently re-trigger the same false positive.
Rushaway pushed a commit to srcdslab/sourcebans-pp that referenced this pull request May 15, 2026
…ban-meta + Re-apply + collapsible adv search) (sbpp#1330)

* fix(banlist+commslist): restore unban-meta line + Re-apply + collapsible advanced search (sbpp#1315)

The v2.0 redesign collapsed the sourcebans.js-driven legacy public lists
into Smarty-only `page_bans.tpl` / `page_comms.tpl` and dropped three
power-user surfaces along the way. This PR restores them.

  1. Inline unban-meta line on admin-lifted rows. The reason cell on the
     desktop table emits "Unbanned by <admin>: <reason>"
     (`[data-testid="ban-unban-meta"]`) when `state == 'unbanned'`;
     mobile cards mirror with the `-mobile` suffix. Same shape on the
     commslist for `state == 'unmuted'` — higher priority there because
     no drawer fallback exists. Both are gated on `!$hideadminname` so
     anonymous viewers under a hidden-admins config don't get the admin
     name leaked. Read-side render only — no overlap with sbpp#1301 / sbpp#1323.
  2. Re-apply (Reban) icon affordance for expired / unbanned rows on
     the banlist desktop, gated on `ADMIN_OWNER | ADMIN_ADD_BAN`.
     Deep-links the smart-default `?p=admin&c=bans&section=add-ban&rebanid=<bid>`
     URL the existing `BansPrepareReban` JSON action already
     pre-populates. Mobile parity deferred — drawer is canonical.
  3. Advanced-search disclosure on both lists. Default-collapsed
     `<details class="filters-details">` wrapping the legacy multi-
     criterion search form (loaded via `{load_template
     file="admin.bans.search"}`). Auto-opens on a post-submit
     `?advType=&advSearch=` paint via a new
     `BanListView::$is_advanced_search_open` /
     `CommsListView::$is_advanced_search_open` boolean. Reuses the
     `.filters-details` chrome sbpp#1303 introduced for admin-admins
     (CSS rules ship in this PR; will dedupe harmlessly when sbpp#1318
     merges).

Tests:

  - PHPUnit: `PublicBanListRegressionTest` (7 cases — disclosure
    default-closed, auto-open, unban-meta render, Re-apply gating
    + URL shape, banlist + commslist parity). RunInSeparateProcess
    per method to dodge the page-handler `setPostKey()` redeclare
    trap, mirroring `Php82DeprecationsTest`'s pattern.
  - Playwright: `flows/public-banlist-regressions.spec.ts` (4 cases
    across chromium + mobile-chromium — disclosure default-closed,
    click-to-open, post-submit auto-open via URL navigation;
    closed-state axe coverage). Re-apply / unban-meta DB-state
    assertions live in the PHPUnit test where seeding is cleaner.

AGENTS.md "Where to find what" gains three rows for the new patterns.

* fix(tests): update integration tests after rebase on main

Two test-only fixes surfaced when the PR rebased on top of sbpp#1320 +

- `BanListIpColumnTest::renderBanList` (from sbpp#1320) constructs a
  `BanListView` with positional + named arguments. This PR adds a 28th
  constructor parameter (`is_advanced_search_open`) which the test
  must now pass — default it to `false` so the IP-column suite stays
  exercising the column-render contract it was written for, not the
  disclosure state.
- `ServerMapImageRenderTest::testHandlerStillEmitsMapimgField` (from
  sbpp#1326) asserted a literal `"'mapimg'   => GetMapImage("` substring
  in `web/api/handlers/servers.php`. sbpp#1329's cache + debounce rewrite
  shifted the surrounding column alignment to 5 spaces, breaking the
  brittle match without changing the contract the test guards (the
  field is still emitted). Switch to a regex that tolerates any
  whitespace between the key and `=>` so a future alignment shift
  doesn't silently re-trigger the same false positive.

* fix(tests): login as admin in BanListIpColumnTest hideplayerips=false case

sbpp#1315's `<details class="filters-details">` disclosure on the public
ban list `{load_template file="admin.bans.search"}`s the legacy
advanced-search partial. The partial's backing `admin.bans.search.php`
re-derives `hideplayerips` / `hideadminname` from
`Config::getBool('banlist.hideplayerips') && !$userbank->is_admin()`
and `Renderer::render`s the result into Smarty, overwriting whatever
the parent BanListView had assigned. In production this is a no-op
because the parent (`page.banlist.php`) computes the SAME formula —
both halves converge. But `BanListIpColumnTest` hand-crafts
`BanListView::$hideplayerips` to exercise the column-gating contract
in isolation, so the partial's overwrite collapses the test's
fixture-controlled value down to whatever the test environment's
Config + $userbank happen to evaluate to.

Fix by logging in as admin in the two test methods that pass
`hideplayerips: false`. With `is_admin()=true`, the partial's
formula reduces to `(…) && !true = false`, matching the View's
hand-crafted `false`. The suppressed-state test
(`hideplayerips: true`) stays anonymous so the formula reduces to
`Config && true` — `true` under `data.sql`'s
`banlist.hideplayerips=1` default — also matching the View's input.

The test pre-dates sbpp#1315; it was written against the v2.0 template
that didn't yet `{load_template}` the partial, so the parent's
assigns were never overwritten. The new disclosure brings the
partial back into the render tree and the test has to maintain
the production invariant ("BanListView's hideplayerips matches
AdminBansSearchView's recomputed value") explicitly.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Map images / thumbnails no longer displayed on expanded server cards

1 participant