Skip to content

Tribe name boost purchases in the store - #4739

Merged
evanpelle merged 5 commits into
mainfrom
feat/tribe-name-boosts
Jul 28, 2026
Merged

Tribe name boost purchases in the store#4739
evanpelle merged 5 commits into
mainfrom
feat/tribe-name-boosts

Conversation

@evanpelle

Copy link
Copy Markdown
Collaborator

What

Adds the boost purchase flow to the store's Tribes tab (follow-up to #4715, per the boost handoff doc):

  • Boost button on every active (pending/live) name, priced from cosmetics.jsontribeNames.boostPriceHard — never hardcoded. If the config block is absent (older API), boost purchasing hides entirely; rejected/revoked names never show the button (they'd 404).
  • Boost state per name: "3× boosted · until Aug 23" from the list endpoint's activeBoosts (+1 = displayed multiplier, since draw weight is 1 + activeBoosts) and boostExpiresAt. Both fields are optional in the schema so older responses parse.
  • Confirm dialog whose copy sells what a boost actually buys — "appear more often in other players' games" — rather than implying a guaranteed slot.

Purchase flow (mirrors the API doc's recommendation)

  1. Balance pre-check against /users/@me currency.hard — a shortfall opens the existing InsufficientCurrencyDialog with its top-up CTA instead of submitting into a guaranteed 400.
  2. POST /users/@me/tribe_names/:id/boosts with a fresh Idempotency-Key UUID per click and all boost buttons disabled until it settles — the endpoint has no rate limit and boosts stack, so this is what prevents double-charges.
  3. 201 → success notice, then re-fetch the name list and /users/@me (boost state is only served by the list endpoint — never reconstructed client-side).
  4. 400 insufficient (balance moved under us) → refresh + top-up dialog. 404 (name taken down mid-session) → notice + list refresh so the rejected state and review reason appear.

Schemas

  • ApiSchemas: TribeNameSchema + activeBoosts/boostExpiresAt (optional), new PostTribeBoostResponseSchema (stringified bigints stay strings).
  • CosmeticSchemas: optional tribeNames { priceHard, boostPriceHard, boostDurationDays } block on CosmeticsSchema.

Verification

  • Schema tests for the new fields, the documented 201 shape, and the cosmetics config block (present/absent/malformed).
  • Verified live in the running app against stubbed API routes: list rendering with badges, confirm dialog, 201 → success + refresh, exactly one POST carrying a UUID Idempotency-Key, and the insufficient path showing the top-up dialog without issuing a request.
  • Full suite (2367 + 287), tsc --noEmit, eslint, prettier all green; en.json keys sorted (EnJsonSorted).

🤖 Generated with Claude Code

Adds the boost flow to the store's Tribes tab: each active name gets a
Boost button priced from cosmetics.json (tribeNames.boostPriceHard —
never hardcoded; the button hides when the block is absent), a
"N× boosted · until <date>" line from the list endpoint's activeBoosts
/ boostExpiresAt, and a confirm dialog whose copy sells what boosts
actually do (appear more often in other players' games).

Purchases POST /users/@me/tribe_names/:id/boosts with a fresh
Idempotency-Key UUID per click — the endpoint has no rate limit and
boosts stack, so the key is what stops a double-submit from charging
twice. Balance is pre-checked against /users/@me and shortfalls reuse
the InsufficientCurrencyDialog top-up path; a 400 (balance moved), 404
(name taken down), or failure each map to the flow the API doc
recommends. Boost state is re-fetched after purchase, never
reconstructed client-side.

Verified live against stubbed API routes: list rendering, confirm,
201 refresh, idempotency header, and the no-POST insufficient path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 69165b4b-1771-48c0-b8c4-5a20d568a688

📥 Commits

Reviewing files that changed from the base of the PR and between 68bd053 and 732afae.

📒 Files selected for processing (3)
  • src/client/components/TribesPanel.ts
  • src/core/ApiSchemas.ts
  • tests/ApiSchemas.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/ApiSchemas.test.ts
  • src/core/ApiSchemas.ts
  • src/client/components/TribesPanel.ts

Walkthrough

Adds tribe-name boost schemas, API handling, cosmetics pricing, and a complete TribesPanel flow with balance checks, confirmation, status rendering, refreshes, notices, and localization.

Changes

Tribe boost feature

Layer / File(s) Summary
Boost data contracts and validation
src/core/ApiSchemas.ts, src/core/CosmeticSchemas.ts, tests/ApiSchemas.test.ts, tests/CosmeticSchemas.test.ts
Adds boost fields, response schemas, cosmetics pricing configuration, and validation coverage.
Boost API operation
src/client/Api.ts
Adds boostTribeName with idempotency headers, typed result codes, status mapping, logout handling, and response validation.
Tribes panel boost flow
src/client/components/TribesPanel.ts, resources/lang/en.json
Loads boost settings, checks balance, confirms purchases, renders boost status and controls, refreshes data, and displays localized feedback.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  actor Player
  participant TribesPanel
  participant boostTribeName
  participant TribeBoostEndpoint
  Player->>TribesPanel: Select boost
  TribesPanel->>TribesPanel: Check balance and confirm purchase
  TribesPanel->>boostTribeName: Submit tribe id and idempotency key
  boostTribeName->>TribeBoostEndpoint: POST request
  TribeBoostEndpoint-->>boostTribeName: Return boost response or error status
  boostTribeName-->>TribesPanel: Return typed result
  TribesPanel->>TribesPanel: Refresh tribe data and show notice
Loading

Possibly related PRs

Suggested labels: Feature

Poem

Tribe names glow with boosted might,
A button sparks the purchase light.
Balance checks and notices chime,
Expiry dates keep watch on time,
Fresh data makes the panel bright.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding tribe name boost purchases to the store.
Description check ✅ Passed The description matches the changeset and describes the boost purchase flow, schemas, and tests.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/core/CosmeticSchemas.ts (1)

435-444: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

Optional: consider guarding against malformed pricing config.

priceHard, boostPriceHard, and boostDurationDays accept any number, including negative or zero values. A typo in cosmetics.json (e.g. boostDurationDays: -30) would still pass validation and surface a nonsensical boost duration/price in the UI.

🛡️ Optional tightening
     tribeNames: z
       .object({
-        priceHard: z.number(),
-        boostPriceHard: z.number(),
-        boostDurationDays: z.number(),
+        priceHard: z.number().positive(),
+        boostPriceHard: z.number().positive(),
+        boostDurationDays: z.number().positive(),
       })
       .optional(),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/CosmeticSchemas.ts` around lines 435 - 444, Strengthen the
`tribeNames` schema in `CosmeticSchemas` by validating `priceHard`,
`boostPriceHard`, and `boostDurationDays` as strictly positive numbers,
rejecting zero and negative configuration values while preserving the existing
optional handling for older `cosmetics.json` files.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/core/CosmeticSchemas.ts`:
- Around line 435-444: Strengthen the `tribeNames` schema in `CosmeticSchemas`
by validating `priceHard`, `boostPriceHard`, and `boostDurationDays` as strictly
positive numbers, rejecting zero and negative configuration values while
preserving the existing optional handling for older `cosmetics.json` files.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2042c94a-ccc8-496f-984e-c549828f7f6e

📥 Commits

Reviewing files that changed from the base of the PR and between 4e6e8d2 and fe65045.

📒 Files selected for processing (7)
  • resources/lang/en.json
  • src/client/Api.ts
  • src/client/components/TribesPanel.ts
  • src/core/ApiSchemas.ts
  • src/core/CosmeticSchemas.ts
  • tests/ApiSchemas.test.ts
  • tests/CosmeticSchemas.test.ts

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 27, 2026
The API moved the name purchase price to cosmetics.json and dropped
priceHard from GET /users/@me/tribe_names; the required
z.coerce.number() coerced the absent field to NaN and failed the whole
list parse, breaking the tab. Make the field optional and prefer the
cosmetics.json price, keeping the response field as a legacy fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 27, 2026
Boosted rows now read "2 boosts · next expires Aug 23" (count instead
of the multiplier, per-repo singular/_plural convention) and the list
sorts most-boosted first — stable, so equal counts keep the API's
newest-first order and rejected names sink with the unboosted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 27, 2026
The list endpoint builds boostExpiresAt with a raw SQL max() that
bypasses the ORM's Date mapping, so the wire carried pg text
("2026-08-23 18:04:11+00") and the strict z.iso.datetime() failed the
whole list parse. The field is now a plain string and the renderer
skips dates the browser can't parse, showing the count alone.

Also relabel the date from "next expires" to "until": the API serves
the LAST boost's lapse (max), the moment the name stops being boosted
entirely, not the next one to drop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 27, 2026
cosmetics.json (tribeNames.priceHard) is the only price source now; the
list endpoint serves just the player's names. A legacy response with
priceHard still parses — the unknown key is stripped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@evanpelle evanpelle added this to the v33 milestone Jul 28, 2026
@evanpelle
evanpelle merged commit 7a9e3bd into main Jul 28, 2026
12 of 13 checks passed
@evanpelle
evanpelle deleted the feat/tribe-name-boosts branch July 28, 2026 00:16
@github-project-automation github-project-automation Bot moved this from Triage to Complete in OpenFront Release Management Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Complete

Development

Successfully merging this pull request may close these issues.

1 participant