Skip to content

feat(supporter): render compact supporter chip in TopBar#144

Merged
ntatschner merged 1 commit into
nextfrom
feat/supporter-chip-topbar
Jun 1, 2026
Merged

feat(supporter): render compact supporter chip in TopBar#144
ntatschner merged 1 commit into
nextfrom
feat/supporter-chip-topbar

Conversation

@ntatschner
Copy link
Copy Markdown
Collaborator

Summary

Surfaces the supporter pill on every signed-in page (not just /u/<handle> self view from PR #143) by piggybacking on the existing layout-level Promise.allSettled scaffold. getSupporterStatus joins the existing location / shared-with-me / catalog fan-out so the chip costs one extra fetch per shell render — request-level cached by React anyway.

Continuation of the chip rollout: PR #143 (profile self) → #144 (topbar, this PR)#145 (public/friend profile) → #146 (discover/profiles).

Web changes

  • layout.tsx adds getSupporterStatus to the Promise.allSettled block, fail-soft to null on error so a /v1/me/supporter hiccup doesn't blank the chrome (same posture as the other shell fetches).
  • TopBar.tsx accepts an optional supporter prop, renders <SupporterChip size="sm" /> next to the @handle pill. Compact palette size mirrors the existing topbar density.

E2E fixture

Required per the Playwright fixture default rule (CLAUDE.md): any new authenticated layout-level fetch needs a default fixture in scenarioFor()'s base map, otherwise every existing scenario 599s.

  • api-mock.ts gets a default /v1/me/supporter fixture pointing at state=none so existing scenarios that don't care about supporter logic don't 599 on the new layout fetch.
  • New supporterStatus(tier, plate) helper for tests that DO want to exercise the chip.

Test plan

  • pnpm --filter web run test:run — 40 tests pass (no new tests; SupporterChip already covered in PR feat(supporter): render tier-styled chip on self profile view #143)
  • pnpm --filter web run typecheck clean
  • pnpm --filter web run lint — no new warnings
  • Smoke after platform promote: chip visible in topbar on /dashboard, /journey, /sharing — not just /u/

Follow-ups

Surfaces the supporter pill on every signed-in page (not just
/u/<handle> self view) by piggybacking on the existing layout-level
Promise.allSettled scaffold. `getSupporterStatus` joins the existing
location / shared / catalog fan-out so the chip costs one extra
fetch per shell render — request-level cached by React anyway.

Web changes:
  - layout.tsx adds getSupporterStatus to the Promise.allSettled
    block, fail-soft to null on error so a /v1/me/supporter hiccup
    doesn't blank the chrome (same posture as the other shell
    fetches).
  - TopBar.tsx accepts an optional `supporter` prop, renders
    <SupporterChip size="sm" /> next to the @handle pill. Compact
    palette size mirrors the existing topbar density.

E2E fixture (required per `Playwright fixture default rule`):
  - api-mock.ts gets a default `/v1/me/supporter` fixture pointing
    at state=none so existing scenarios that don't care about
    supporter logic don't 599 on the new layout fetch.
  - New `supporterStatus(tier, plate)` helper for tests that DO
    want to exercise the chip.

Self-profile chip from PR #143 continues to work; the topbar chip
just makes the recognition visible on /dashboard, /journey, /sharing
etc. without forcing the user to navigate to their profile page.

Follow-up: public/friend profile chip (#145), discover/profiles
chip (#146).
@ntatschner ntatschner merged commit 99250eb into next Jun 1, 2026
9 checks passed
@ntatschner ntatschner deleted the feat/supporter-chip-topbar branch June 1, 2026 01:37
ntatschner pushed a commit that referenced this pull request Jun 1, 2026
Extends the supporter recognition pill (PR #143 self-only, PR #144
topbar) to anyone visiting a public profile or accepted-share view.
Continuation of the chip rollout: #143#144 → **#145 (this PR)** →
#146 (discover bulk).

Server changes:

  - `SupporterStore` gains `get_by_handle_public(handle)` —
    single-query JOIN of `users` + `supporter_status` + LATERAL most-
    recent completed `revolut_orders`. Returns `Ok(None)` for unknown
    handles, missing rows, or `state=none`; returns `Ok(Some(_))`
    only for `active`/`lapsed`. Case-insensitive handle match
    mirrors the rest of the project.

  - `PublicSummaryResponse` gains `supporter: Option<PublicSupporterInfo>`.
    `PublicSupporterInfo` is a deliberate projection of the store
    shape — only `state` + `current_tier_key` + `name_plate` are
    surfaced. `grace_until`, payment timestamps, and `cancelled_at`
    are explicitly NOT exposed to strangers (fingerprinting + churn
    leak avoidance).

  - `render_summary` + `render_summary_scoped` now take a
    `&dyn SupporterStore` and populate `supporter` on every public
    summary response. Fail-soft on lookup error (warn log; chip
    just doesn't render). Three handler call sites updated:
    `public_summary`, `friend_summary`, `preview_summary`. Each
    takes `Extension(Arc<dyn SupporterStore>)`; same dyn-cast
    pattern as `share_metadata_dyn`.

  - `main.rs` wires `supporter_store_dyn` + adds the Extension
    layer alongside `share_metadata_dyn`.

  - `openapi.rs` registers `PublicSupporterInfo` so the TS client
    regen picks it up.

Web changes:

  - `/u/[handle]/page.tsx` extends the supporter wiring from PR #143.
    Self path keeps fetching `/v1/me/supporter` for full DTO. Public
    + shared paths now read `data.supporter` from the extended
    PublicSummaryResponse. Both feed the same `<SupporterChip>` via
    a `ChipStatus` type alias (the three-field overlap between
    `SupporterStatusDto` and `PublicSupporterInfo`).

Tests:

  - 4 new SupporterStore tests on the memstore: unknown handle,
    unbound handle (row exists but no handle binding — simulates
    Postgres JOIN miss), bound handle returns active row with
    case-insensitive lookup, state=none filtered out.
  - `MemorySupporterStore` gains `bind_handle(handle, user_id)` so
    tests can simulate the users-table side of the JOIN.
  - All existing supporter + sharing tests still pass (35 sharing,
    8 supporter, 666 filtered).

Follow-up: PR #146 — discover/profiles chip (needs bulk
`get_many_public(user_ids)` to avoid N+1).
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.

1 participant