diff --git a/analysis_options.yaml b/analysis_options.yaml index 96d9c2b2e..d862ca315 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -25,7 +25,7 @@ analyzer: errors: invalid_annotation_target: ignore plugins: - - custom_lint + - riverpod_lint formatter: page_width: 100 @@ -39,7 +39,3 @@ linter: avoid_redundant_argument_values: false sort_pub_dependencies: true sort_unnamed_constructors_first: false - -custom_lint: - rules: - - avoid_manual_providers_as_generated_provider_dependency: false diff --git a/lib/src/model/analysis/analysis_controller.dart b/lib/src/model/analysis/analysis_controller.dart index ed7792f32..8c489124b 100644 --- a/lib/src/model/analysis/analysis_controller.dart +++ b/lib/src/model/analysis/analysis_controller.dart @@ -110,6 +110,12 @@ final analysisControllerProvider = AsyncNotifierProvider.autoDispose name: 'AnalysisControllerProvider', ); +Root? _savedStandaloneRoot; + +void clearSavedStandaloneAnalysis() { + _savedStandaloneRoot = null; +} + class AnalysisController extends AsyncNotifier with EngineEvaluationMixin implements PgnTreeNotifier { @@ -183,6 +189,13 @@ class AnalysisController extends AsyncNotifier serverAnalysis = null; division = null; activeCorrespondenceGame = null; + + // We want to keep the standalone analysis session alive even if the user navigates away + ref.onCancel(() { + if (_root.mainline.isNotEmpty) { + _savedStandaloneRoot = _root; + } + }); } case ActiveCorrespondenceGame(:final gameFullId): { @@ -229,21 +242,24 @@ class AnalysisController extends AsyncNotifier final List> openingFutures = []; - _root = Root.fromPgnGame( - game, - isLichessAnalysis: options.isLichessGameAnalysis, - onVisitNode: (root, branch, isMainline) { - if (isMainline && - options.initialMoveCursor != null && - branch.position.ply <= root.position.ply + options.initialMoveCursor!) { - path = path + branch.id; - lastMove = branch.sanMove.move; - } - if (isMainline && opening == null && branch.position.ply <= 10) { - openingFutures.add(_fetchOpening(branch.position.fen, path)); - } - }, - ); + _root = switch (options) { + Standalone() when _savedStandaloneRoot != null => _savedStandaloneRoot!, + _ => Root.fromPgnGame( + game, + isLichessAnalysis: options.isLichessGameAnalysis, + onVisitNode: (root, branch, isMainline) { + if (isMainline && + options.initialMoveCursor != null && + branch.position.ply <= root.position.ply + options.initialMoveCursor!) { + path = path + branch.id; + lastMove = branch.sanMove.move; + } + if (isMainline && opening == null && branch.position.ply <= 10) { + openingFutures.add(_fetchOpening(branch.position.fen, path)); + } + }, + ), + }; // wait for the opening to be fetched to recompute the branch opening Future.wait(openingFutures) @@ -345,6 +361,11 @@ class AnalysisController extends AsyncNotifier return analysisState; } + void clearSavedStandaloneAnalysis() { + _savedStandaloneRoot = null; + ref.invalidateSelf(); + } + Future onFocusRegained() async { if (options case ActiveCorrespondenceGame(:final gameFullId)) { final updatedGame = await _gameRepository.getActiveCorrespondenceGame(gameFullId); diff --git a/lib/src/view/analysis/analysis_screen.dart b/lib/src/view/analysis/analysis_screen.dart index b82e983f7..d905d91ce 100644 --- a/lib/src/view/analysis/analysis_screen.dart +++ b/lib/src/view/analysis/analysis_screen.dart @@ -553,6 +553,13 @@ class _BottomBar extends ConsumerWidget { return showAdaptiveActionSheet( context: context, actions: [ + if (options case Standalone()) + BottomSheetAction( + makeLabel: (context) => Text(context.l10n.clearSavedMoves), + onPressed: () => ref + .read(analysisControllerProvider(options).notifier) + .clearSavedStandaloneAnalysis(), + ), if (analysisState.isEngineAvailable(evalPrefs)) BottomSheetAction( makeLabel: (context) => Text( diff --git a/pubspec.lock b/pubspec.lock index 316b231bb..7c4bd54e4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.65" + analysis_server_plugin: + dependency: transitive + description: + name: analysis_server_plugin + sha256: "26844e7f977087567135d62532b67d5639fe206c5194c3f410ba75e1a04a2747" + url: "https://pub.dev" + source: hosted + version: "0.3.3" analyzer: dependency: transitive description: @@ -314,7 +322,7 @@ packages: source: hosted version: "1.0.8" custom_lint: - dependency: "direct dev" + dependency: transitive description: name: custom_lint sha256: "751ee9440920f808266c3ec2553420dea56d3c7837dd2d62af76b11be3fcece5" @@ -663,10 +671,10 @@ packages: dependency: "direct main" description: name: flutter_riverpod - sha256: "9e2d6907f12cc7d23a846847615941bddee8709bf2bfd274acdf5e80bcf22fde" + sha256: "38ec6c303e2c83ee84512f5fc2a82ae311531021938e63d7137eccc107bf3c02" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.1.0" flutter_secure_storage: dependency: "direct main" description: @@ -1338,26 +1346,26 @@ packages: dependency: transitive description: name: riverpod - sha256: c406de02bff19d920b832bddfb8283548bfa05ce41c59afba57ce643e116aa59 + sha256: "16ff608d21e8ea64364f2b7c049c94a02ab81668f78845862b6e88b71dd4935a" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.1.0" riverpod_analyzer_utils: dependency: transitive description: name: riverpod_analyzer_utils - sha256: a0f68adb078b790faa3c655110a017f9a7b7b079a57bbd40f540e80dce5fcd29 + sha256: "947b05d04c52a546a2ac6b19ef2a54b08520ff6bdf9f23d67957a4c8df1c3bc0" url: "https://pub.dev" source: hosted - version: "1.0.0-dev.7" + version: "1.0.0-dev.8" riverpod_lint: dependency: "direct dev" description: name: riverpod_lint - sha256: "7ef9c43469e9b5ac4e4c3b24d7c30642e47ce1b12cd7dcdd643534db0a72ed13" + sha256: "4d2eb0d19bbe7e3323bd0ce4553b2e6170d161a13914bfdd85a3612329edcb43" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.1.0" rxdart: dependency: transitive description: @@ -1883,6 +1891,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.3" + yaml_edit: + dependency: transitive + description: + name: yaml_edit + sha256: ec709065bb2c911b336853b67f3732dd13e0336bd065cc2f1061d7610ddf45e3 + url: "https://pub.dev" + source: hosted + version: "2.2.3" sdks: dart: ">=3.10.0 <4.0.0" flutter: ">=3.38.0 <4.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index e1630a43f..e87dc6dff 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -78,7 +78,6 @@ dependencies: dev_dependencies: build_runner: ^2.3.2 - custom_lint: ^0.8.1 fake_async: ^1.3.1 flutter_test: sdk: flutter diff --git a/test/view/analysis/analysis_screen_test.dart b/test/view/analysis/analysis_screen_test.dart index fd292f2b4..960a266e6 100644 --- a/test/view/analysis/analysis_screen_test.dart +++ b/test/view/analysis/analysis_screen_test.dart @@ -35,6 +35,10 @@ void main() { // ignore: avoid_dynamic_calls final sanMoves = jsonDecode(gameResponse)['moves'] as String; + tearDown(() { + clearSavedStandaloneAnalysis(); + }); + group('Analysis Screen', () { testWidgets('displays correct move and position', (tester) async { final app = await makeTestProviderScopeApp(