fix(boosters): address final-review findings

- daily claim: record the claim before granting boosters, so a crash
  mid-claim forfeits at most one reward instead of allowing a re-claim
  (booster farming) on next launch.
- game screen: disarm the booster target synchronously before awaiting,
  so a rapid second board tap can't double-fire a use or stack a dialog.
- new players: seed one of each booster once (idempotent persisted flag),
  fulfilling the spec's starting inventory. Wired in main().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-18 19:36:24 +09:00
parent 412cc08167
commit fa2784519b
5 changed files with 44 additions and 4 deletions
+4 -3
View File
@@ -169,10 +169,13 @@ class _GameScreenState extends ConsumerState<GameScreen>
final y = (local.dy / cell).floor();
if (x < 0 || x >= GridState.size || y < 0 || y >= GridState.size) return;
// Disarm synchronously, before any await, so a rapid second tap on the
// board is a no-op rather than a redundant booster use / stacked dialog.
setState(() => _arming = null);
final session = ref.read(gameSessionProvider.notifier);
if (armed == BoosterType.hammer) {
await session.useHammer(x, y);
if (mounted) setState(() => _arming = null);
} else if (armed == BoosterType.lineBomb) {
final axis = await _chooseLineAxis();
if (axis == _LineAxis.row) {
@@ -180,8 +183,6 @@ class _GameScreenState extends ConsumerState<GameScreen>
} else if (axis == _LineAxis.col) {
await session.useLineBomb(col: x);
}
// A dismissed chooser cancels the use but still clears the armed state.
if (mounted) setState(() => _arming = null);
}
}