From bf7720ebd3cc3063a6f4d30d1d083e3f813e1921 Mon Sep 17 00:00:00 2001 From: airkjw Date: Fri, 12 Jun 2026 07:33:41 +0900 Subject: [PATCH] fix: clear season flow when starting Classic so tutorial/theme can't leak - Add SeasonFlowNotifier.clear() to null out the flow state - Call clear() in HomeScreen's Classic button before startStage() - Broaden tutorial-end guard to next.phase != GamePhase.playing (covers stuck) - Add regression test: clear() resets flow to null Co-Authored-By: Claude Sonnet 4.6 --- lib/state/season_flow_notifier.dart | 4 ++++ lib/ui/screens/game_screen.dart | 2 +- lib/ui/screens/home_screen.dart | 1 + test/state/season_flow_test.dart | 16 ++++++++++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/state/season_flow_notifier.dart b/lib/state/season_flow_notifier.dart index 749cbd4..eede482 100644 --- a/lib/state/season_flow_notifier.dart +++ b/lib/state/season_flow_notifier.dart @@ -41,4 +41,8 @@ class SeasonFlowNotifier extends Notifier { if (flow == null || !flow.hasNext) return; startSeasonStage(flow.pack, flow.index + 1); } + + /// Leaving season play (e.g. starting a Classic run) clears the flow so + /// stale stage context can't leak into other modes. + void clear() => state = null; } diff --git a/lib/ui/screens/game_screen.dart b/lib/ui/screens/game_screen.dart index ea4fc00..695dbd9 100644 --- a/lib/ui/screens/game_screen.dart +++ b/lib/ui/screens/game_screen.dart @@ -150,7 +150,7 @@ class _GameScreenState extends ConsumerState if (prev?.phase != next.phase) { // A finished stage ends the tutorial; otherwise the overlay would sit // on top of the result card and leak into the next stage. - if (next.phase == GamePhase.won || next.phase == GamePhase.lost) { + if (next.phase != GamePhase.playing) { ref.read(tutorialProvider.notifier).skip(); } if (next.phase == GamePhase.won) { diff --git a/lib/ui/screens/home_screen.dart b/lib/ui/screens/home_screen.dart index b048cff..47a6863 100644 --- a/lib/ui/screens/home_screen.dart +++ b/lib/ui/screens/home_screen.dart @@ -84,6 +84,7 @@ class HomeScreen extends ConsumerWidget { ), onPressed: () { if (!(ModalRoute.of(context)?.isCurrent ?? true)) return; + ref.read(seasonFlowProvider.notifier).clear(); ref.read(gameSessionProvider.notifier).startStage( StageConfig.endless( seed: DateTime.now().millisecondsSinceEpoch, diff --git a/test/state/season_flow_test.dart b/test/state/season_flow_test.dart index 53bc8e3..35fc5bc 100644 --- a/test/state/season_flow_test.dart +++ b/test/state/season_flow_test.dart @@ -77,4 +77,20 @@ void main() { expect(container.read(seasonFlowProvider)!.hasNext, isFalse); expect(container.read(gameSessionProvider)!.phase, GamePhase.playing); }); + + test('clear() resets flow to null so Classic mode has no stale season context', + () async { + final container = await _container(); + final flow = container.read(seasonFlowProvider.notifier); + flow.startSeasonStage(_pack(), 0); + + // State is non-null after starting a season stage. + expect(container.read(seasonFlowProvider), isNotNull); + + flow.clear(); + + // After clear(), the flow must be null so GameScreen's tutorial/theme + // checks can't fire for a Classic (endless) session. + expect(container.read(seasonFlowProvider), isNull); + }); }