feat: refine GM day controls - #96
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a pending eliminations system for the day phase, allowing GMs to undo accidental player removals before they are finalized. It also adds controls to adjust the discussion timer during the game and implements alphabetical sorting for roles in the setup and reference screens. Furthermore, the "optional rules" feature has been removed. Feedback was provided to simplify a redundant calculation in the Timer component.
There was a problem hiding this comment.
Pull request overview
This PR refines GM-facing day-phase controls by making Day eliminations reversible until they’re finalized (Night start / win confirmation), improving visibility of pending eliminations, and enhancing timer/role UX consistency.
Changes:
- Add reversible Day eliminations with “Recently eliminated” UI + undo, and defer Day-elimination log entries until finalization.
- Add in-game discussion timer duration adjustment controls (±30s) with styling + i18n.
- Alphabetize role chooser/reference lists and remove the “optional rules” feature, updating UI and E2E coverage accordingly.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/e2e/white-wolf-endgame.spec.ts | Asserts pending Day eliminations are finalized/cleared and log entries exist at endgame. |
| tests/e2e/vote-cap.spec.ts | Updates Day-phase expectations for pending eliminations, undo behavior, and log finalization. |
| tests/e2e/tie-breaker.spec.ts | Updates tie-breaker flow expectations to align with pending eliminations + undo. |
| tests/e2e/role-first-workflow.spec.ts | Adds coverage ensuring role chooser/reference are alphabetized and optional rules are absent. |
| tests/e2e/day-timer-adjustment.spec.ts | Adds E2E coverage for adjusting the discussion timer mid-game without console errors. |
| tests/e2e/base-game-flow.spec.ts | Updates baseline flow assertions for “alive-only” rendering + recent eliminations UI. |
| src/types/game.types.ts | Adds PendingDayElimination and pendingDayEliminations; removes optional rule fields. |
| src/styles/timer.css | Adds layout/styling for timer duration adjustment controls. |
| src/styles/setup.css | Removes styling for the deleted optional-rules UI. |
| src/styles/roles.css | Removes styling for the deleted optional action display. |
| src/styles/day.css | Adds styling for the “Recently eliminated” panel. |
| src/store/gameStore.ts | Implements pending Day eliminations + undo/finalize and adjustable discussion timer; bumps persisted version. |
| src/i18n.ts | Removes optional-rules strings; adds strings for recent eliminations and timer adjustment controls. |
| src/data/roles.ts | Removes optional rule text from roles; adds sortRolesByName() helper. |
| src/components/Setup/SetupScreen.tsx | Sorts roles alphabetically, removes optional-rules UI, and uses shared timer constants. |
| src/components/Roles/RoleReference.tsx | Sorts role reference cards alphabetically and removes optional-rule rendering. |
| src/components/Game/Timer.tsx | Adds timer duration adjustment UI + test ids and integrates store adjust action. |
| src/components/Game/TieBreaker.tsx | Removes logging callback usage to align with deferred/finalized Day logs. |
| src/components/Game/RoleTray.tsx | Shows only alive players in the GM role tray (chips removed for eliminated players). |
| src/components/Game/GameBoard.tsx | Finalizes pending Day eliminations when confirming the winner. |
| src/components/Game/DayPhase.tsx | Renders only alive player cards; adds “Recently eliminated” panel + undo action. |
Comments suppressed due to low confidence (1)
src/store/gameStore.ts:795
- The v3 persist
migratenormalizes several fields, but it doesn't initialize the newly addedpendingDayEliminations. If an older persisted state is loaded and the persist middleware doesn’t fully merge defaults,pendingDayEliminationscan end upundefinedat runtime (e.g.,DayPhasecalls.length). Consider explicitly settingrest.pendingDayEliminations = [](and optionally clamping/normalizingdiscussionTimeSeconds/timerRemainingto the new min/max) as part of the migration output so the migrated state always matches the currentGameStoreshape.
delete rest.votes;
delete rest.optionalRules;
rest.rolePowerOverrides = normalizeOverrides(rest.rolePowerOverrides);
if (typeof rest.firstDayExecutionDone !== 'boolean') rest.firstDayExecutionDone = false;
rest.loversIds = normalizeLoversIds(rest.loversIds);
if (Array.isArray(rest.roleIds)) rest.roleIds = rest.roleIds.map(sanitizeRoleId);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Addressed Copilot's suppressed migration note in 99c401f by explicitly clearing stale pending Day eliminations during migration and normalizing persisted discussion timer fields. Verified locally with npm run lint, npm run build, and npx playwright test tests/e2e/vote-cap.spec.ts tests/e2e/day-timer-adjustment.spec.ts --project=chromium. |
|
Added follow-up migration regression coverage in ef0ed70 after an independent subagent audit. The new Playwright test seeds a v2 persisted store and verifies migration initializes pendingDayEliminations, removes optionalRules, clamps timer fields, sanitizes removed roles, and renders Day safely. Verified locally with npm run lint, npm run build, and npx playwright test tests/e2e/persisted-state-migration.spec.ts tests/e2e/vote-cap.spec.ts tests/e2e/day-timer-adjustment.spec.ts --project=chromium. |
Summary
shared | web | mobilemain | release/web | release/mobilefeature | fix | ci | docs | releaseValidation
npm run lintnpm run buildnpm run test:e2eandroid/gradlew testDebugUnitTestRelease Notes