From 5dde200c84836aba676708c2dd2682a861edb289 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Thu, 23 Feb 2023 13:14:42 +0600 Subject: [PATCH] feat: safe cancellation of running operation on reset --- .../fl_query/.flutter-plugins-dependencies | 2 +- packages/fl_query/example/pubspec.lock | 16 --------- .../fl_query/lib/src/core/infinite_query.dart | 36 ++++++++++++++----- .../lib/src/core/{ => mixins}/retryer.dart | 17 +++++++++ .../lib/src/core/{ => mixins}/validation.dart | 0 packages/fl_query/lib/src/core/mutation.dart | 9 +++-- packages/fl_query/lib/src/core/query.dart | 19 +++++++--- packages/fl_query/pubspec.yaml | 3 +- .../.flutter-plugins-dependencies | 2 +- 9 files changed, 67 insertions(+), 37 deletions(-) rename packages/fl_query/lib/src/core/{ => mixins}/retryer.dart (72%) rename packages/fl_query/lib/src/core/{ => mixins}/validation.dart (100%) diff --git a/packages/fl_query/.flutter-plugins-dependencies b/packages/fl_query/.flutter-plugins-dependencies index 8d0c185..2cf8b3f 100644 --- a/packages/fl_query/.flutter-plugins-dependencies +++ b/packages/fl_query/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/","native_build":true,"dependencies":[]}],"android":[{"name":"connectivity_plus","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_android-2.0.22/","native_build":true,"dependencies":[]}],"macos":[{"name":"connectivity_plus_macos","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_macos-1.2.6/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/","native_build":true,"dependencies":[]}],"linux":[{"name":"connectivity_plus_linux","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_linux-1.3.1/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_linux-2.1.8/","native_build":false,"dependencies":[]}],"windows":[{"name":"connectivity_plus_windows","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_windows-1.2.2/","native_build":true,"dependencies":[]},{"name":"path_provider_windows","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_windows-2.1.3/","native_build":false,"dependencies":[]}],"web":[{"name":"connectivity_plus_web","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_web-1.2.5/","dependencies":[]}]},"dependencyGraph":[{"name":"connectivity_plus","dependencies":["connectivity_plus_linux","connectivity_plus_macos","connectivity_plus_web","connectivity_plus_windows"]},{"name":"connectivity_plus_linux","dependencies":[]},{"name":"connectivity_plus_macos","dependencies":[]},{"name":"connectivity_plus_web","dependencies":[]},{"name":"connectivity_plus_windows","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2023-02-21 09:32:52.281806","version":"3.7.3"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/","native_build":true,"dependencies":[]}],"android":[{"name":"connectivity_plus","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_android-2.0.22/","native_build":true,"dependencies":[]}],"macos":[{"name":"connectivity_plus_macos","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_macos-1.2.6/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/","native_build":true,"dependencies":[]}],"linux":[{"name":"connectivity_plus_linux","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_linux-1.3.1/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_linux-2.1.8/","native_build":false,"dependencies":[]}],"windows":[{"name":"connectivity_plus_windows","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_windows-1.2.2/","native_build":true,"dependencies":[]},{"name":"path_provider_windows","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_windows-2.1.3/","native_build":false,"dependencies":[]}],"web":[{"name":"connectivity_plus_web","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_web-1.2.5/","dependencies":[]}]},"dependencyGraph":[{"name":"connectivity_plus","dependencies":["connectivity_plus_linux","connectivity_plus_macos","connectivity_plus_web","connectivity_plus_windows"]},{"name":"connectivity_plus_linux","dependencies":[]},{"name":"connectivity_plus_macos","dependencies":[]},{"name":"connectivity_plus_web","dependencies":[]},{"name":"connectivity_plus_windows","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2023-02-23 13:13:41.719080","version":"3.7.3"} \ No newline at end of file diff --git a/packages/fl_query/example/pubspec.lock b/packages/fl_query/example/pubspec.lock index 21dd23c..7e35f4a 100644 --- a/packages/fl_query/example/pubspec.lock +++ b/packages/fl_query/example/pubspec.lock @@ -359,14 +359,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.2.4" - queue: - dependency: transitive - description: - name: queue - sha256: "9a41ecadc15db79010108c06eae229a45c56b18db699760f34e8c9ac9b831ff9" - url: "https://pub.dev" - source: hosted - version: "3.1.0+2" sky_engine: dependency: transitive description: flutter @@ -436,14 +428,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" - uuid: - dependency: transitive - description: - name: uuid - sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" - url: "https://pub.dev" - source: hosted - version: "3.0.7" vector_math: dependency: transitive description: diff --git a/packages/fl_query/lib/src/core/infinite_query.dart b/packages/fl_query/lib/src/core/infinite_query.dart index 77e4b09..fd99676 100644 --- a/packages/fl_query/lib/src/core/infinite_query.dart +++ b/packages/fl_query/lib/src/core/infinite_query.dart @@ -1,9 +1,10 @@ import 'dart:async'; +import 'package:async/async.dart'; import 'package:collection/collection.dart'; import 'package:fl_query/fl_query.dart'; -import 'package:fl_query/src/core/retryer.dart'; -import 'package:fl_query/src/core/validation.dart'; +import 'package:fl_query/src/core/mixins/retryer.dart'; +import 'package:fl_query/src/core/mixins/validation.dart'; import 'package:hive_flutter/adapters.dart'; import 'package:mutex/mutex.dart'; import 'package:state_notifier/state_notifier.dart'; @@ -108,6 +109,8 @@ class InfiniteQuery final RefreshConfig refreshConfig; final JsonConfig? jsonConfig; + final PageType _initialParam; + InfiniteQuery( this.key, InfiniteQueryFn queryFn, { @@ -116,7 +119,8 @@ class InfiniteQuery this.retryConfig = DefaultConstants.retryConfig, this.refreshConfig = DefaultConstants.refreshConfig, this.jsonConfig, - }) : _dataController = StreamController.broadcast(), + }) : _initialParam = initialParam, + _dataController = StreamController.broadcast(), _errorController = StreamController.broadcast(), _box = Hive.lazyBox(QueryClient.infiniteQueryCachePrefix), super(InfiniteQueryState( @@ -175,6 +179,8 @@ class InfiniteQuery final StreamController> _dataController; final StreamController> _errorController; + CancelableOperation? _operation; + List get pages => state.pages.map((e) => e.data).whereType().toList(); List get errors => @@ -197,10 +203,10 @@ class InfiniteQuery bool get hasNextPage => state.hasNextPage; - Future _operation(PageType page) { + Future _operate(PageType page) { return _mutex.protect(() async { state = state.copyWith(); - return await retryOperation( + _operation = cancellableRetryOperation( () => state.queryFn(page), config: retryConfig, onSuccessful: (data) async { @@ -262,14 +268,14 @@ class InfiniteQuery final lastPage = state.lastPage; if (_mutex.isLocked || hasPageData || hasPageError) return state.pages.last.data; - return await _operation(lastPage).then((_) => state.pages.last.data); + return await _operate(lastPage).then((_) => state.pages.last.data); } Future refresh([PageType? page]) async { page ??= lastPage; if (_mutex.isLocked) return state.pages.firstWhereOrNull((e) => e.page == page)?.data; - return await _operation(page!).then((_) { + return await _operate(page!).then((_) { return state.pages.firstWhereOrNull((e) => e.page == page)?.data; }); } @@ -277,7 +283,7 @@ class InfiniteQuery Future?> refreshAll() async { if (_mutex.isLocked) return pages; return await Future.wait( - state.pages.map((e) => _operation(e.page)), + state.pages.map((e) => _operate(e.page)), ).then((_) => pages); } @@ -286,7 +292,7 @@ class InfiniteQuery if (_mutex.isLocked || nextPage == null) { return state.pages.lastOrNull?.data; } - return await _operation(nextPage).then((_) { + return await _operate(nextPage).then((_) { return state.pages.firstWhereOrNull((e) => e.page == nextPage)?.data; }); } @@ -331,6 +337,18 @@ class InfiniteQuery ); } + Future reset() async { + await _operation?.cancel(); + state = state.copyWith(pages: { + InfiniteQueryPage( + page: _initialParam, + updatedAt: DateTime.now(), + staleDuration: refreshConfig.staleDuration, + ) + }); + _box.delete(key); + } + @override RemoveListener addListener( Listener> listener, { diff --git a/packages/fl_query/lib/src/core/retryer.dart b/packages/fl_query/lib/src/core/mixins/retryer.dart similarity index 72% rename from packages/fl_query/lib/src/core/retryer.dart rename to packages/fl_query/lib/src/core/mixins/retryer.dart index 71208ed..c3bcc58 100644 --- a/packages/fl_query/lib/src/core/retryer.dart +++ b/packages/fl_query/lib/src/core/mixins/retryer.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:async/async.dart'; import 'package:fl_query/src/collections/retry_config.dart'; import 'package:flutter/material.dart'; @@ -38,4 +39,20 @@ mixin Retryer { } } } + + CancelableOperation cancellableRetryOperation( + FutureOr Function() operation, { + required RetryConfig config, + required void Function(T?) onSuccessful, + required void Function(E?) onFailed, + }) { + return CancelableOperation.fromFuture( + retryOperation( + operation, + config: config, + onSuccessful: onSuccessful, + onFailed: onFailed, + ), + ); + } } diff --git a/packages/fl_query/lib/src/core/validation.dart b/packages/fl_query/lib/src/core/mixins/validation.dart similarity index 100% rename from packages/fl_query/lib/src/core/validation.dart rename to packages/fl_query/lib/src/core/mixins/validation.dart diff --git a/packages/fl_query/lib/src/core/mutation.dart b/packages/fl_query/lib/src/core/mutation.dart index 2771f21..c3151f1 100644 --- a/packages/fl_query/lib/src/core/mutation.dart +++ b/packages/fl_query/lib/src/core/mutation.dart @@ -1,8 +1,9 @@ import 'dart:async'; +import 'package:async/async.dart'; import 'package:fl_query/src/collections/default_configs.dart'; import 'package:fl_query/src/collections/retry_config.dart'; -import 'package:fl_query/src/core/retryer.dart'; +import 'package:fl_query/src/core/mixins/retryer.dart'; import 'package:mutex/mutex.dart'; import 'package:state_notifier/state_notifier.dart'; @@ -74,11 +75,12 @@ class Mutation final StreamController _mutationController; final StreamController _dataController; final StreamController _errorController; + CancelableOperation? _operation; Future _operate(VariablesType variables) { return _mutex.protect(() async { state = state.copyWith(); - return await retryOperation( + _operation = await cancellableRetryOperation( () { _mutationController.add(variables); return state.mutationFn(variables); @@ -115,7 +117,8 @@ class Mutation state = state.copyWith(mutationFn: mutationFn, updatedAt: state.updatedAt); } - void reset() { + Future reset() async { + await _operation?.cancel(); state = MutationState( mutationFn: state.mutationFn, ); diff --git a/packages/fl_query/lib/src/core/query.dart b/packages/fl_query/lib/src/core/query.dart index b4c074d..a8427a9 100644 --- a/packages/fl_query/lib/src/core/query.dart +++ b/packages/fl_query/lib/src/core/query.dart @@ -5,11 +5,12 @@ import 'package:fl_query/src/collections/json_config.dart'; import 'package:fl_query/src/collections/refresh_config.dart'; import 'package:fl_query/src/collections/retry_config.dart'; import 'package:fl_query/src/core/client.dart'; -import 'package:fl_query/src/core/retryer.dart'; -import 'package:fl_query/src/core/validation.dart'; +import 'package:fl_query/src/core/mixins/retryer.dart'; +import 'package:fl_query/src/core/mixins/validation.dart'; import 'package:hive_flutter/adapters.dart'; import 'package:mutex/mutex.dart'; import 'package:state_notifier/state_notifier.dart'; +import 'package:async/async.dart'; typedef QueryFn = FutureOr Function(); @@ -73,7 +74,7 @@ class Query )) { if (jsonConfig != null) { _mutex.protect(() async { - final json = await _box.get(key.toString()); + final json = await _box.get(key); if (json != null) { _initial = jsonConfig!.fromJson( Map.castFrom(json), @@ -116,10 +117,12 @@ class Query Stream get dataStream => _dataController.stream; Stream get errorStream => _errorController.stream; + CancelableOperation? _operation; + Future _operate() { return _mutex.protect(() async { state = state.copyWith(); - return await retryOperation( + _operation = cancellableRetryOperation( state.queryFn, config: retryConfig, onSuccessful: (DataType? data) { @@ -131,7 +134,7 @@ class Query _dataController.add(data); if (jsonConfig != null) { _box.put( - key.toString(), + key, jsonConfig!.toJson(data), ); } @@ -168,6 +171,12 @@ class Query state = state.copyWith(data: data, updatedAt: DateTime.now()); } + Future reset() async { + await _operation?.cancel(); + state = state.copyWith(data: _initial, updatedAt: DateTime.now()); + _box.delete(key); + } + @override RemoveListener addListener(Listener> listener, {bool fireImmediately = true}) { diff --git a/packages/fl_query/pubspec.yaml b/packages/fl_query/pubspec.yaml index 45f239e..d210f3e 100644 --- a/packages/fl_query/pubspec.yaml +++ b/packages/fl_query/pubspec.yaml @@ -20,8 +20,7 @@ dependencies: hive_flutter: ^1.1.0 state_notifier: ^0.7.2+1 mutex: ^3.0.1 - queue: ^3.1.0+1 - uuid: ^3.0.6 + async: ^2.10.0 dev_dependencies: flutter_test: diff --git a/packages/fl_query_hooks/.flutter-plugins-dependencies b/packages/fl_query_hooks/.flutter-plugins-dependencies index 86c7133..b454cbd 100644 --- a/packages/fl_query_hooks/.flutter-plugins-dependencies +++ b/packages/fl_query_hooks/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/","native_build":true,"dependencies":[]}],"android":[{"name":"connectivity_plus","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_android-2.0.22/","native_build":true,"dependencies":[]}],"macos":[{"name":"connectivity_plus_macos","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_macos-1.2.6/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/","native_build":true,"dependencies":[]}],"linux":[{"name":"connectivity_plus_linux","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_linux-1.3.1/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_linux-2.1.8/","native_build":false,"dependencies":[]}],"windows":[{"name":"connectivity_plus_windows","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_windows-1.2.2/","native_build":true,"dependencies":[]},{"name":"path_provider_windows","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_windows-2.1.3/","native_build":false,"dependencies":[]}],"web":[{"name":"connectivity_plus_web","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_web-1.2.5/","dependencies":[]}]},"dependencyGraph":[{"name":"connectivity_plus","dependencies":["connectivity_plus_linux","connectivity_plus_macos","connectivity_plus_web","connectivity_plus_windows"]},{"name":"connectivity_plus_linux","dependencies":[]},{"name":"connectivity_plus_macos","dependencies":[]},{"name":"connectivity_plus_web","dependencies":[]},{"name":"connectivity_plus_windows","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2023-02-21 09:32:49.296117","version":"3.7.3"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/","native_build":true,"dependencies":[]}],"android":[{"name":"connectivity_plus","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_android-2.0.22/","native_build":true,"dependencies":[]}],"macos":[{"name":"connectivity_plus_macos","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_macos-1.2.6/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/","native_build":true,"dependencies":[]}],"linux":[{"name":"connectivity_plus_linux","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_linux-1.3.1/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_linux-2.1.8/","native_build":false,"dependencies":[]}],"windows":[{"name":"connectivity_plus_windows","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_windows-1.2.2/","native_build":true,"dependencies":[]},{"name":"path_provider_windows","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_windows-2.1.3/","native_build":false,"dependencies":[]}],"web":[{"name":"connectivity_plus_web","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_web-1.2.5/","dependencies":[]}]},"dependencyGraph":[{"name":"connectivity_plus","dependencies":["connectivity_plus_linux","connectivity_plus_macos","connectivity_plus_web","connectivity_plus_windows"]},{"name":"connectivity_plus_linux","dependencies":[]},{"name":"connectivity_plus_macos","dependencies":[]},{"name":"connectivity_plus_web","dependencies":[]},{"name":"connectivity_plus_windows","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2023-02-23 13:13:38.929384","version":"3.7.3"} \ No newline at end of file