Replace level-up text modal with confetti glass popover (L2+) #508

Merged
PlasticDigits merged 2 commits from issue-335-level-up-celebration-popover into main 2026-06-19 08:14:34 +00:00
PlasticDigits commented 2026-06-19 06:31:16 +00:00 (Migrated from gitlab.com)

Summary

Replaces the auto-triggered level-up text modal with a glass confetti popover for Level 2+ unlocks on play / (#335).

  • New LevelUpCelebrationPopover — cyberminimalist glass panel, subtle canvas-confetti burst (≤2s), headline Level Up, concise unlock line (≤80 chars).
  • useArenaLevelUpCelebration + detectUnseenLevelUpFeature wire auto-open from ArenaSimplePage for L2–L5 only; L1 / wallet connect alone does not celebrate.
  • FeatureMechanicModal unchanged for explicit ? help (full mechanic copy preserved).
  • markFeatureTutorialSeen / localStorage keys unchanged — repeats do not spam.
  • prefers-reduced-motion: confetti omitted; popover remains.
  • Docs: INV-FRONTEND-335-LEVEL-UP-CELEBRATION, arena-views §335.

Acceptance checklist

Criterion Verification Result
L1→L2+ shows confetti popover with concise unlock text Anvil stack + mock wallet; on-chain starter CRED buy while page open; data-testid="level-up-celebration" visible PASS
L1 / wallet connect alone does not celebrate detectUnseenLevelUpFeature(undefined, 1) + arenaLevelUpCelebration.test.ts PASS
Reduced-motion: no confetti LevelUpCelebrationPopover.test.tsx (data-reduced-motion="true", no confetti canvas) PASS
Glass styling matches command-console primitives GlassPanel + .level-up-celebration__* CSS tokens; screenshots below PASS
Screenshots (desktop / tablet / mobile) on #335 Playwright capture script + uploads on issue thread PASS
cd frontend && npm run typecheck && npm run lint && npm test CI-local run PASS

Third-party verification

  1. git checkout issue-335-level-up-celebration-popover
  2. bash scripts/start-local-anvil-stack.sh --no-frontend (or full QA stack)
  3. cd frontend && ANVIL_E2E=1 VITE_E2E_MOCK_WALLET=1 npm run build && npm run preview -- --host 127.0.0.1 --port 4173
  4. cd frontend && node scripts/capture-level-up-celebration-screenshots.mjs — writes PNGs under /tmp/yieldomega-level-up-screenshots-335/
  5. cd frontend && npm run typecheck && npm run lint && npm test
  6. Optional E2E: ANVIL_E2E=1 bash scripts/e2e-anvil.sh (wallet write path)
  7. Manual: connect wallet on /, buy to cross L2; confirm popover once; click ? on a mechanic — full FeatureMechanicModal still opens.

Test plan notes

  • Unit: arenaLevelUpCelebration.test.ts, LevelUpCelebrationPopover.test.tsx
  • Visual: screenshots attached to #335
## Summary Replaces the auto-triggered level-up **text modal** with a **glass confetti popover** for **Level 2+** unlocks on play `/` ([#335](https://gitlab.com/PlasticDigits/yieldomega/-/issues/335)). - New `LevelUpCelebrationPopover` — cyberminimalist glass panel, subtle `canvas-confetti` burst (≤2s), headline **Level Up**, concise unlock line (≤80 chars). - `useArenaLevelUpCelebration` + `detectUnseenLevelUpFeature` wire auto-open from `ArenaSimplePage` for L2–L5 only; **L1 / wallet connect alone does not celebrate**. - `FeatureMechanicModal` unchanged for explicit `?` help (full mechanic copy preserved). - `markFeatureTutorialSeen` / localStorage keys unchanged — repeats do not spam. - `prefers-reduced-motion`: confetti omitted; popover remains. - Docs: `INV-FRONTEND-335-LEVEL-UP-CELEBRATION`, [arena-views §335](docs/frontend/arena-views.md#level-up-celebration-gitlab-335). ## Acceptance checklist | Criterion | Verification | Result | |-----------|--------------|--------| | L1→L2+ shows confetti popover with concise unlock text | Anvil stack + mock wallet; on-chain starter CRED buy while page open; `data-testid="level-up-celebration"` visible | **PASS** | | L1 / wallet connect alone does not celebrate | `detectUnseenLevelUpFeature(undefined, 1)` + `arenaLevelUpCelebration.test.ts` | **PASS** | | Reduced-motion: no confetti | `LevelUpCelebrationPopover.test.tsx` (`data-reduced-motion="true"`, no confetti canvas) | **PASS** | | Glass styling matches command-console primitives | `GlassPanel` + `.level-up-celebration__*` CSS tokens; screenshots below | **PASS** | | Screenshots (desktop / tablet / mobile) on #335 | Playwright capture script + uploads on issue thread | **PASS** | | `cd frontend && npm run typecheck && npm run lint && npm test` | CI-local run | **PASS** | ## Third-party verification 1. `git checkout issue-335-level-up-celebration-popover` 2. `bash scripts/start-local-anvil-stack.sh --no-frontend` (or full QA stack) 3. `cd frontend && ANVIL_E2E=1 VITE_E2E_MOCK_WALLET=1 npm run build && npm run preview -- --host 127.0.0.1 --port 4173` 4. `cd frontend && node scripts/capture-level-up-celebration-screenshots.mjs` — writes PNGs under `/tmp/yieldomega-level-up-screenshots-335/` 5. `cd frontend && npm run typecheck && npm run lint && npm test` 6. Optional E2E: `ANVIL_E2E=1 bash scripts/e2e-anvil.sh` (wallet write path) 7. Manual: connect wallet on `/`, buy to cross L2; confirm popover once; click `?` on a mechanic — full `FeatureMechanicModal` still opens. ## Test plan notes - Unit: `arenaLevelUpCelebration.test.ts`, `LevelUpCelebrationPopover.test.tsx` - Visual: screenshots attached to [#335](https://gitlab.com/PlasticDigits/yieldomega/-/issues/335)
PlasticDigits commented 2026-06-19 06:31:26 +00:00 (Migrated from gitlab.com)

mentioned in issue #335

mentioned in issue #335
PlasticDigits commented 2026-06-19 06:34:52 +00:00 (Migrated from gitlab.com)

Security review — MR !508

Commit reviewed: 859b4a9ff1ed689d674407f536a9109043392217
Scope: Frontend level-up UX — LevelUpCelebrationPopover, useArenaLevelUpCelebration, arenaLevelUpCelebration, ArenaSimplePage wiring, CSS, docs/invariants, dev screenshot script (capture-level-up-celebration-screenshots.mjs). No contract/indexer/backend changes.

Outcome: FINDINGS: 0 medium+ — Security review: no medium+ findings on this diff.

Method

  • Diff + data-flow trace from playerLevel (indexer → useArenaPlayerLevel / clampPlayerLevel) through detectUnseenLevelUpFeature → levelUpCelebrationUnlockLine → JSX render and canvas-confetti burst.
  • Compared to prior auto-open FeatureMechanicModal path removed from ArenaSimplePage.
  • Checked for XSS sinks, injection in new scripts, new dependencies, authz/on-chain surface changes.
  • Prior MR notes: none from prior security-review runs (only issue mention).

Areas reviewed (no issues at medium+)

Area Result
XSS / HTML injection unlockLine is a closed switch on ArenaFeatureKey with static strings; rendered as React text children ({unlockLine}), not dangerouslySetInnerHTML. Feature keys derive only from featureKeyForUnlockLevel (levels 2–5), not raw indexer strings.
Indexer / level tampering useArenaPlayerLevel clamps level to 1–5 before levelBigint reaches the celebration hook; no unbounded loop or arbitrary key injection from poisoned stats.level.
Supply chain No new npm deps; canvas-confetti already used (TimerHeroParticles) with same useWorker: true pattern.
EVM / auth / secrets No smart-contract, auth, or secret-handling changes in this MR.
Dev screenshot script execSync/cast against local .env.local + Anvil — developer-only QA tooling; not a production attack surface (would require compromising the operator’s local env).

Inline threads: none (no medium+ findings).

## Security review — MR !508 **Commit reviewed:** `859b4a9ff1ed689d674407f536a9109043392217` **Scope:** Frontend level-up UX — `LevelUpCelebrationPopover`, `useArenaLevelUpCelebration`, `arenaLevelUpCelebration`, `ArenaSimplePage` wiring, CSS, docs/invariants, dev screenshot script (`capture-level-up-celebration-screenshots.mjs`). No contract/indexer/backend changes. **Outcome:** `FINDINGS: 0` medium+ — **Security review: no medium+ findings on this diff.** ### Method - Diff + data-flow trace from `playerLevel` (indexer → `useArenaPlayerLevel` / `clampPlayerLevel`) through `detectUnseenLevelUpFeature` → `levelUpCelebrationUnlockLine` → JSX render and `canvas-confetti` burst. - Compared to prior auto-open `FeatureMechanicModal` path removed from `ArenaSimplePage`. - Checked for XSS sinks, injection in new scripts, new dependencies, authz/on-chain surface changes. - Prior MR notes: none from prior security-review runs (only issue mention). ### Areas reviewed (no issues at medium+) | Area | Result | |------|--------| | **XSS / HTML injection** | `unlockLine` is a closed `switch` on `ArenaFeatureKey` with static strings; rendered as React text children (`{unlockLine}`), not `dangerouslySetInnerHTML`. Feature keys derive only from `featureKeyForUnlockLevel` (levels 2–5), not raw indexer strings. | | **Indexer / level tampering** | `useArenaPlayerLevel` clamps level to 1–5 before `levelBigint` reaches the celebration hook; no unbounded loop or arbitrary key injection from poisoned `stats.level`. | | **Supply chain** | No new npm deps; `canvas-confetti` already used (`TimerHeroParticles`) with same `useWorker: true` pattern. | | **EVM / auth / secrets** | No smart-contract, auth, or secret-handling changes in this MR. | | **Dev screenshot script** | `execSync`/`cast` against local `.env.local` + Anvil — developer-only QA tooling; not a production attack surface (would require compromising the operator’s local env). | **Inline threads:** none (no medium+ findings).
PlasticDigits commented 2026-06-19 07:48:12 +00:00 (Migrated from gitlab.com)

added 9 commits

  • 859b4a9f...e1ebea74 - 8 commits from branch main
  • 2c5b37ee - Merge origin/main into issue-335-level-up-celebration-popover

Compare with previous version

added 9 commits <ul><li>859b4a9f...e1ebea74 - 8 commits from branch <code>main</code></li><li>2c5b37ee - Merge origin/main into issue-335-level-up-celebration-popover</li></ul> [Compare with previous version](/PlasticDigits/yieldomega/-/merge_requests/138/diffs?diff_id=1860567044&start_sha=859b4a9ff1ed689d674407f536a9109043392217)
PlasticDigits commented 2026-06-19 07:48:17 +00:00 (Migrated from gitlab.com)

Resolved merge conflicts with main.

Conflict: frontend/src/pages/arena/ArenaSimplePage.tsx import block — kept shouldShowLevelLock from main (warbow level-lock UI) while retaining the MR’s LevelUpCelebrationPopover / useArenaLevelUpCelebration path (no readFeatureTutorialSeen auto-modal).

Checks: cd frontend && npm run typecheck && npm run lint && npm test — PASS.

Resolved merge conflicts with `main`. **Conflict:** `frontend/src/pages/arena/ArenaSimplePage.tsx` import block — kept `shouldShowLevelLock` from main (warbow level-lock UI) while retaining the MR’s `LevelUpCelebrationPopover` / `useArenaLevelUpCelebration` path (no `readFeatureTutorialSeen` auto-modal). **Checks:** `cd frontend && npm run typecheck && npm run lint && npm test` — PASS.
PlasticDigits commented 2026-06-19 07:51:07 +00:00 (Migrated from gitlab.com)

Security review — MR !508

Commit reviewed: 2c5b37ee4c2f8b829d3ce92d8a56076fe26e16ff
Scope: Frontend level-up UX — LevelUpCelebrationPopover, useArenaLevelUpCelebration, arenaLevelUpCelebration, ArenaSimplePage wiring, CSS, docs/invariants, dev screenshot script (capture-level-up-celebration-screenshots.mjs). No contract/indexer/backend changes. Re-reviewed after merge from main (prior review on 859b4a9f).

Outcome: FINDINGS: 0 medium+ — Security review: no medium+ findings on this diff.

Method

  • Full MR diff + data-flow trace: playerLevel (indexer → useArenaPlayerLevel / clampPlayerLevel) → detectUnseenLevelUpFeature → levelUpCelebrationUnlockLine → JSX render and canvas-confetti burst.
  • Compared to removed auto-open FeatureMechanicModal path in ArenaSimplePage.
  • Checked XSS sinks, shell injection in new scripts, dependency/supply-chain changes, authz/on-chain surface.
  • Prior security-review note on this MR (0 findings on earlier SHA); re-validated on current HEAD — no new exposure from merge resolution.

Areas reviewed (no issues at medium+)

Area Result
XSS / HTML injection unlockLine is a closed switch on ArenaFeatureKey with static strings; rendered as React text children ({unlockLine}), not dangerouslySetInnerHTML. Feature keys derive only from featureKeyForUnlockLevel (levels 2–5), not raw indexer strings.
Indexer / level tampering useArenaPlayerLevel clamps level to 1–5 before levelBigint reaches the celebration hook; loop in detectUnseenLevelUpFeature is bounded (≤4 iterations). No arbitrary key injection from poisoned stats.level.
Supply chain No new npm deps; canvas-confetti already used (TimerHeroParticles) with same useWorker: true pattern.
EVM / auth / secrets No smart-contract, auth, or secret-handling changes in this MR.
Dev screenshot script execSync/cast against local .env.local + Anvil — developer-only QA tooling; not a production attack surface (would require compromising the operator’s local env).

Inline threads: none (no medium+ findings).

## Security review — MR !508 **Commit reviewed:** `2c5b37ee4c2f8b829d3ce92d8a56076fe26e16ff` **Scope:** Frontend level-up UX — `LevelUpCelebrationPopover`, `useArenaLevelUpCelebration`, `arenaLevelUpCelebration`, `ArenaSimplePage` wiring, CSS, docs/invariants, dev screenshot script (`capture-level-up-celebration-screenshots.mjs`). No contract/indexer/backend changes. Re-reviewed after merge from `main` (prior review on `859b4a9f`). **Outcome:** `FINDINGS: 0` medium+ — **Security review: no medium+ findings on this diff.** ### Method - Full MR diff + data-flow trace: `playerLevel` (indexer → `useArenaPlayerLevel` / `clampPlayerLevel`) → `detectUnseenLevelUpFeature` → `levelUpCelebrationUnlockLine` → JSX render and `canvas-confetti` burst. - Compared to removed auto-open `FeatureMechanicModal` path in `ArenaSimplePage`. - Checked XSS sinks, shell injection in new scripts, dependency/supply-chain changes, authz/on-chain surface. - Prior security-review note on this MR (0 findings on earlier SHA); re-validated on current HEAD — no new exposure from merge resolution. ### Areas reviewed (no issues at medium+) | Area | Result | |------|--------| | **XSS / HTML injection** | `unlockLine` is a closed `switch` on `ArenaFeatureKey` with static strings; rendered as React text children (`{unlockLine}`), not `dangerouslySetInnerHTML`. Feature keys derive only from `featureKeyForUnlockLevel` (levels 2–5), not raw indexer strings. | | **Indexer / level tampering** | `useArenaPlayerLevel` clamps level to 1–5 before `levelBigint` reaches the celebration hook; loop in `detectUnseenLevelUpFeature` is bounded (≤4 iterations). No arbitrary key injection from poisoned `stats.level`. | | **Supply chain** | No new npm deps; `canvas-confetti` already used (`TimerHeroParticles`) with same `useWorker: true` pattern. | | **EVM / auth / secrets** | No smart-contract, auth, or secret-handling changes in this MR. | | **Dev screenshot script** | `execSync`/`cast` against local `.env.local` + Anvil — developer-only QA tooling; not a production attack surface (would require compromising the operator’s local env). | **Inline threads:** none (no medium+ findings).
PlasticDigits commented 2026-06-19 08:14:34 +00:00 (Migrated from gitlab.com)

mentioned in commit 1411dc491b

mentioned in commit 1411dc491b96ba2790904f18a9ec098f804b0d28
PlasticDigits (Migrated from gitlab.com) merged commit 1411dc491b into main 2026-06-19 08:14:34 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
code/yieldomega!508
No description provided.