feat(web): gate the point layer, and say what an account opens - #513
Conversation
50cdd66 to
0fb130a
Compare
089e6ff to
f81e96f
Compare
|
The maintainer has taken the call (2026-08-29): yes, the point layer goes behind a member account. So the direction here is settled and it is worth finishing. It cannot merge as it stands, and the conflict is not mechanical, which is why I left it rather than rebasing it for you. This branch gates
The rest of your reasoning stands and I checked it: the hex heat is not windowed for anyone since #466, so a visitor loses nothing they could otherwise see, and the split copy for guest versus hunter is the right one given #174. Ping me when it is pushed and I will review it properly. |
The layer toggle cycled points, hex, both for every role. Below member /api/points returns the last 24 hours and at most 500 rows (server/internal/httpapi/degrade.go), so a guest pressing it landed on a thin or empty layer with nothing on the control saying why, and the thinness read as a property of the data rather than of the account. The hex heat is not windowed for anyone since efiten#466, so the gate costs a visitor nothing they could otherwise see. auth.js gains three helpers beside canSeeLocate, which this mirrors: canSeePointLayer, a member gate. modeForRole, which holds a degraded role on hex whatever ?mode= asked for. The mode is restored before /api/auth/me resolves, so it is re-derived once the role is known, the same deferral ?locate=1 uses. pointLayerReason, the copy on the disabled button. Two audiences, the split guestNotice already makes: a guest has no account, a hunter has one and needs an admin to verify it, so "log in" is a dead end for them (efiten#174). The button stays visible and disabled rather than hidden: a visitor should be able to see that the layer exists and what it takes to open it. Nothing in style.css styled :disabled, so the gated toggle rendered identical to a live one, colour, opacity and cursor alike, and only the tooltip said otherwise, which on a phone is nothing at all. Measured, then fixed: `#bar button:disabled` at (1,1,1), because `#bar select, #bar input, than the attribute. The guest notice named "individual receptions" as a map layer, which is no longer true for these roles: the ticker keeps its own /api/points feed and is where they meet receptions. Rewritten to name it, and kept short on purpose. Measured at 390x844 with a guest role, against master: | bar | master | this branch | |---|---|---| | 390x844 guest | 321px | 321px | | 1280x720 guest | 124px | 124px | | 1280x720 hunter | 147px | 124px | The first draft of that sentence was 9 characters over a line break and cost 15px on a phone, which is where efiten#423 is fighting for room. roles no longer have, so it names the ticker instead and no longer depends on which layer is drawn. e2e/auth.spec.js's "guest popup has no Locate button" opened a point popup as a guest, which is now unreachable. It is split: a guest has no popup to put a button in, and a member's popup still offers Locate, so the Locate gate it was really about is still asserted. 433 unit tests, 171 e2e, eslint clean. Mutation-checked: making the gate a hunter gate, dropping modeForRole, giving both roles the same copy, not calling the gate from applyRole, disabling the button without re-deriving the mode, dropping the reason from the title, and removing the :disabled rule each turn the intended test red. Stacked on efiten#512: this branch starts from it, since both change what applyRole does and efiten#512's clamp note is the one being moved here. Closes efiten#493 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
f81e96f to
074218e
Compare
|
Pushed, The gate moved onto the segmented group. Points and Both are disabled below member, Hex stays live, and the reason renders as a line under the group (
One more resolution worth naming: the seg click handler on master calls The bar-height table in the PR body was measured on the pre-#541 bar and no longer described anything, so it is replaced: measured on the current bar at 390x844 and 1280x720, the old and new guest and hunter strings render identical bar heights, so the copy rewrite costs no room. Verification on the new top: web 509 unit, e2e 199/199 with --fail-on-flaky-tests (exit 0), app 848 unit, eslint clean. Three mutations on the segment build turn |
efiten
left a comment
There was a problem hiding this comment.
The gate reads well on the segmented control, which is the part that needed rethinking rather than rebasing. Points and Both disabled with Hex live is the right shape: a visitor can see the layer exists and that it is not theirs yet, which is more honest than hiding it and much more honest than the thin layer they used to get with nothing saying why.
Verified with master merged in: web/ 509 unit and 199 playwright, eslint clean.
Both halves of the gate are pinned, checked by breaking them:
- The split copy. Collapsing the hunter branch so everyone gets the guest wording turns 3 unit tests red. That split is the whole of #174: telling a hunter to log in is a dead end, they are logged in, they are waiting on a person.
- The reason actually reaching the reader. Disabling the segments without wiring
aria-describedbyturns an e2e red. Worth having as its own assertion, since a disabled control with an unreachable explanation is the failure mode this is meant to fix, and it is invisible to anyone testing with a mouse.
A line under the group rather than a title is the right call for the reason you give: on a phone there is no hover, so a title is not an explanation, it is a secret.
One thing I checked rather than assumed, since it is what makes this defensible: the hex heat has not been windowed for anyone since #466, so a visitor loses nothing they could otherwise see. The maintainer accepted that trade on 2026-08-29.
# Conflicts: # app/changelog.json # web/changelog.json
🤖 I have created a release *beep* *boop* --- <details><summary>app: 1.24.0</summary> ## [1.24.0](app-v1.23.0...app-v1.24.0) (2026-08-30) ### Features * **app:** give the theme three states and a memory, and end the Settings tab ([#566](#566)) ([e67c319](e67c319)) * **app:** keep the SNR the repeater heard us at ([#489](#489)) ([0cd0a7c](0cd0a7c)) * **app:** make every recorded reception audible, one instrument per type (+ sound tweaks) ([#470](#470)) ([6244c0f](6244c0f)) * **web:** gate the point layer, and say what an account opens ([#513](#513)) ([71856f1](71856f1)) ### Bug Fixes * **app:** take the gate's coach marks out of #splash's stacking context ([#565](#565)) ([6fba59e](6fba59e)) </details> <details><summary>web: 1.20.0</summary> ## [1.20.0](web-v1.19.0...web-v1.20.0) (2026-08-30) ### Features * **app:** give the theme three states and a memory, and end the Settings tab ([#566](#566)) ([e67c319](e67c319)) * **app:** keep the SNR the repeater heard us at ([#489](#489)) ([0cd0a7c](0cd0a7c)) * **app:** make every recorded reception audible, one instrument per type (+ sound tweaks) ([#470](#470)) ([6244c0f](6244c0f)) * **web:** gate the point layer, and say what an account opens ([#513](#513)) ([71856f1](71856f1)) ### Bug Fixes * **app:** take the gate's coach marks out of #splash's stacking context ([#565](#565)) ([6fba59e](6fba59e)) </details> --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
The layer control offered points, hex, both for every role. Below member
/api/points returns the last 24 hours and at most 500 rows
(server/internal/httpapi/degrade.go), so a guest switching to Points landed
on a thin or empty layer with nothing on the control saying why, and the
thinness read as a property of the data rather than of the account.
The hex heat is not windowed for anyone since #466, so the gate costs a
visitor nothing they could otherwise see.
The gate
auth.js gains three helpers beside canSeeLocate, which this mirrors:
canSeePointLayer, a member gate.
modeForRole, which holds a degraded role on hex whatever ?mode= asked
for. The mode is restored before /api/auth/me resolves, so it is
re-derived once the role is known, the same deferral ?locate=1 uses.
pointLayerReason, the line under the layer segments. Two audiences, the
split guestNotice already makes: a guest has no account, a hunter has one
and needs an admin to verify it, so "log in" is a dead end for them
(#174).
Since #539 the layer control is the #layer-seg segmented group in the
filter panel, so the gate lands there: Points and Both are disabled below
member, Hex stays live, and the reason renders as #layer-gate-note directly
under the group, with aria-describedby on the two disabled segments. A line
rather than a title, because on a phone a tooltip is nothing at all. The
segments stay visible rather than hidden: a visitor should be able to see
that the layer exists and what it takes to open it.
The seg click handler loses its syncTimeUi() call: the clamp note stopped
being layer-dependent when rangeLabelFor dropped showsPoints, so switching
layers no longer changes the range label.
A disabled attribute is not a disabled button
Nothing in style.css styled :disabled, so a gated segment rendered
identical to a live one, colour, opacity and cursor alike.
#bar button:disabledat (1,1,1), because#bar select, #bar input, #bar buttonsets colour at (1,0,1) and
.bf-seg button.activesits at (0,2,1). e2easserts the rendered colour, opacity and cursor rather than the attribute.
Copy
The guest notice named "individual receptions" as a map layer, which is no
longer true for these roles: the ticker keeps its own /api/points feed and
is where they meet receptions. Rewritten to name it. Measured on the
current bar at 390x844 and 1280x720, the old and new strings render the
same bar heights for both roles, so the rewrite costs no room.
#492's clamp note moves with it: it named the point layer, which these
roles no longer have, so it names the ticker instead and no longer depends
on which layer is drawn.
A test that could not survive
e2e/auth.spec.js's "guest popup has no Locate button" opened a point popup
as a guest, which is now unreachable. It is split: a guest has no popup to
put a button in, and a member's popup still offers Locate, so the Locate
gate it was really about is still asserted.
Verified
web 509 unit, e2e 199/199 with --fail-on-flaky-tests, app 848 unit, eslint
clean, on ae9ba97. Mutation-checked on the segment build: dropping the
disable, dropping the mode re-derive, and emptying the note each turn
auth.spec red. The auth helper tests (gate boundary at hunter, per-role
copy, deep-link hold) carry over unchanged from the first round.
Closes #493
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
🤖 Generated with Claude Code