fix: query, infinite_query fetching/refetching when offline

This commit is contained in:
Kingkor Roy Tirtho
2022-12-16 21:52:25 +06:00
parent 074175ed39
commit 02d11d2f46
11 changed files with 87 additions and 36 deletions
@@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_ios","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/path_provider_ios-2.0.11/","native_build":true,"dependencies":[]}],"android":[{"name":"connectivity_plus","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/path_provider_android-2.0.22/","native_build":true,"dependencies":[]}],"macos":[{"name":"connectivity_plus_macos","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_macos-1.2.4/","native_build":true,"dependencies":[]},{"name":"path_provider_macos","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-2.0.6/","native_build":true,"dependencies":[]}],"linux":[{"name":"connectivity_plus_linux","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_linux-1.3.1/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-2.1.7/","native_build":false,"dependencies":[]}],"windows":[{"name":"connectivity_plus_windows","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_windows-1.2.2/","native_build":true,"dependencies":[]},{"name":"path_provider_windows","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-2.1.3/","native_build":false,"dependencies":[]}],"web":[{"name":"connectivity_plus_web","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/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_ios","path_provider_linux","path_provider_macos","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_ios","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2022-12-15 14:54:08.206645","version":"3.3.8"}
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_ios","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/path_provider_ios-2.0.11/","native_build":true,"dependencies":[]}],"android":[{"name":"connectivity_plus","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/path_provider_android-2.0.22/","native_build":true,"dependencies":[]}],"macos":[{"name":"connectivity_plus_macos","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_macos-1.2.4/","native_build":true,"dependencies":[]},{"name":"path_provider_macos","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-2.0.6/","native_build":true,"dependencies":[]}],"linux":[{"name":"connectivity_plus_linux","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_linux-1.3.1/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-2.1.7/","native_build":false,"dependencies":[]}],"windows":[{"name":"connectivity_plus_windows","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_windows-1.2.2/","native_build":true,"dependencies":[]},{"name":"path_provider_windows","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-2.1.3/","native_build":false,"dependencies":[]}],"web":[{"name":"connectivity_plus_web","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/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_ios","path_provider_linux","path_provider_macos","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_ios","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2022-12-16 21:09:35.483382","version":"3.3.8"}
@@ -66,8 +66,9 @@ class _InfiniteQueryDiskCacheExampleState
job: infiniteQueryDiskCacheExampleQuery,
externalData: null,
builder: (context, query) {
final data =
query.pages.expand((page) => page!.toList()).toList();
final data = query.pages
.expand((page) => page?.toList() ?? [])
.toList();
return Scaffold(
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.download_rounded),
+1
View File
@@ -14,6 +14,7 @@ export 'src/query_bowl.dart';
export 'src/query_builder.dart';
export 'src/mutation.dart';
export 'src/mutation_builder.dart';
export 'src/always_online_connectivity.dart';
export 'src/models/infinite_query_job.dart';
export 'src/models/query_job.dart';
export 'src/models/mutation_job.dart';
@@ -0,0 +1,20 @@
import 'package:connectivity_plus/connectivity_plus.dart';
final _alwaysOnlineConnectivity = AlwaysOnlineConnectivity._();
/// make this singleton
class AlwaysOnlineConnectivity implements Connectivity {
AlwaysOnlineConnectivity._();
factory AlwaysOnlineConnectivity() {
return _alwaysOnlineConnectivity;
}
@override
Future<ConnectivityResult> checkConnectivity() {
return Future.value(ConnectivityResult.ethernet);
}
@override
Stream<ConnectivityResult> get onConnectivityChanged => Stream.empty();
}
+29 -1
View File
@@ -1,3 +1,5 @@
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:fl_query/src/utils.dart';
import 'package:flutter/widgets.dart';
abstract class BaseOperation<Data, Error> extends ChangeNotifier {
@@ -10,6 +12,8 @@ abstract class BaseOperation<Data, Error> extends ChangeNotifier {
@protected
Duration cacheTime;
Connectivity _connectivity;
// all properties
Data? data;
Error? error;
@@ -31,7 +35,9 @@ abstract class BaseOperation<Data, Error> extends ChangeNotifier {
required this.retries,
required this.retryDelay,
this.data,
}) : updatedAt = DateTime.now();
Connectivity? connectivity,
}) : updatedAt = DateTime.now(),
_connectivity = connectivity ?? Connectivity();
void mount(ValueKey<String> uKey) {
_mounts.add(uKey);
@@ -52,6 +58,28 @@ abstract class BaseOperation<Data, Error> extends ChangeNotifier {
Set<ValueKey<String>> get mounts => _mounts;
/// checks if the application is connected to internet in any mean
///
/// It's true when any one this is connected -
/// - ethernet
/// - mobile
/// - wifi
///
/// Deprecated: Use [isNetworkOnline] instead
@deprecated
Future<bool> isInternetConnected() async {
return isNetworkOnline;
}
/// checks if the application is connected to internet in any mean
///
/// It's true when any one this is connected -
/// - ethernet
/// - mobile
/// - wifi
Future<bool> get isNetworkOnline =>
_connectivity.checkConnectivity().then((v) => isConnectedToInternet(v));
bool get isInactive => mounts.isEmpty;
bool get hasData => data != null;
bool get hasError => error != null;
+6 -18
View File
@@ -1,10 +1,8 @@
import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:fl_query/fl_query.dart';
import 'package:fl_query/src/base_operation.dart';
import 'package:fl_query/src/mixins/autocast.dart';
import 'package:fl_query/src/utils.dart';
import 'package:flutter/widgets.dart';
import 'package:hive/hive.dart';
@@ -45,8 +43,6 @@ abstract class BaseQuery<T extends Object, Outside, Error>
bool? refetchOnApplicationResume;
bool? refetchOnWindowFocus;
Connectivity _connectivity;
T? _previousData;
BaseQuery({
@@ -57,6 +53,7 @@ abstract class BaseQuery<T extends Object, Outside, Error>
required super.retries,
required super.retryDelay,
required this.status,
super.connectivity,
this.refetchOnMount,
this.refetchOnReconnect,
this.refetchInterval,
@@ -64,14 +61,12 @@ abstract class BaseQuery<T extends Object, Outside, Error>
this.refetchOnWindowFocus,
this.enabled = true,
T? previousData,
Connectivity? connectivity,
T? initialData,
onData,
onError,
}) : _staleTime = staleTime,
_initialData = initialData,
_externalData = externalData,
_connectivity = connectivity ?? Connectivity(),
_previousData = previousData,
super(data: previousData ?? initialData) {
if (onData != null) onDataListeners.add(onData);
@@ -203,7 +198,10 @@ abstract class BaseQuery<T extends Object, Outside, Error>
/// if isLoading/isRefetching is true that means its already fetching/
/// refetching. So [_execute] again can create a race condition
if (isLoading || isRefetching || (hasData && !isPreviousData)) return data;
if (isLoading ||
isRefetching ||
!(await isNetworkOnline) ||
(hasData && !isPreviousData)) return data;
status = QueryStatus.loading;
notifyListeners();
return execute().then((_) {
@@ -227,7 +225,7 @@ abstract class BaseQuery<T extends Object, Outside, Error>
Future<T?> refetch() async {
/// if isLoading/isRefetching is true that means its already fetching/
/// refetching. So [_execute] again can create a race condition
if (isRefetching || isLoading) return data;
if (isRefetching || isLoading || !(await isNetworkOnline)) return data;
if (enabled && !fetched) return await fetch();
status = QueryStatus.refetching;
refetchCount++;
@@ -349,16 +347,6 @@ abstract class BaseQuery<T extends Object, Outside, Error>
if (updated) notifyListeners();
}
/// checks if the application is connected to internet in any mean
///
/// It's true when any one this is connected -
/// - ethernet
/// - mobile
/// - wifi
Future<bool> isInternetConnected() async {
return isConnectedToInternet(await _connectivity.checkConnectivity());
}
/// can be used to update the data manually. Can be useful when used
/// together with mutations to perform optimistic updates or manual data
/// updates
@@ -129,7 +129,7 @@ class InfiniteQuery<T extends Object, Outside, PageParam extends Object>
(_) async {
// only refetch if its connected to the internet or refetch will
// always result in error while there's no internet
if (isStale && await isInternetConnected()) await refetchPages();
if (isStale && await isNetworkOnline) await refetchPages();
},
);
}
@@ -241,7 +241,7 @@ class InfiniteQuery<T extends Object, Outside, PageParam extends Object>
/// also checking if the is stale or not
/// no need to refetch a valid query for no reason
if (refetchOnMount == true && isStale) {
this.isInternetConnected().then((isConnected) async {
isNetworkOnline.then((isConnected) async {
if (isConnected) await refetchPages();
});
}
@@ -1,3 +1,4 @@
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:fl_query/fl_query.dart';
class MutationJob<T extends Object, V> {
@@ -6,10 +7,12 @@ class MutationJob<T extends Object, V> {
final int? retries;
final Duration? retryDelay;
final Duration? cacheTime;
final Connectivity? connectivity;
MutationJob({
required String mutationKey,
required this.task,
this.connectivity,
this.retries,
this.retryDelay,
this.cacheTime,
@@ -28,6 +31,7 @@ class MutationJob<T extends Object, V> {
int? retries,
Duration? retryDelay,
Duration? cacheTime,
Connectivity? connectivity,
}) {
return (String mutationKey) {
if (preMutationKey != null) mutationKey = "$preMutationKey#$mutationKey";
@@ -37,6 +41,7 @@ class MutationJob<T extends Object, V> {
retries: retries,
retryDelay: retryDelay,
cacheTime: cacheTime,
connectivity: connectivity,
);
};
}
+12 -4
View File
@@ -50,6 +50,7 @@ class Mutation<T extends Object, V> extends BaseOperation<T, dynamic>
required this.task,
required super.retries,
required super.retryDelay,
super.connectivity,
required Duration cacheTime,
MutationListener<T, V>? onData,
MutationListener<dynamic, V>? onError,
@@ -73,6 +74,7 @@ class Mutation<T extends Object, V> extends BaseOperation<T, dynamic>
retries: options.retries ?? 3,
retryDelay: options.retryDelay ?? const Duration(milliseconds: 200),
cacheTime: options.cacheTime ?? const Duration(minutes: 5),
connectivity: options.connectivity,
) {
if (onData != null) _onDataListeners.add(onData);
if (onError != null) _onErrorListeners.add(onError);
@@ -105,6 +107,7 @@ class Mutation<T extends Object, V> extends BaseOperation<T, dynamic>
onError(error, variables, _sideEffectContext);
}
notifyListeners();
throw e;
} else {
// retrying for retry count if failed for the first time
while (retryAttempts <= retries) {
@@ -128,6 +131,7 @@ class Mutation<T extends Object, V> extends BaseOperation<T, dynamic>
onError(error, variables, _sideEffectContext);
}
notifyListeners();
throw e;
}
retryAttempts++;
}
@@ -168,10 +172,14 @@ class Mutation<T extends Object, V> extends BaseOperation<T, dynamic>
_variables = variables;
if (onData != null) _onDataListeners.add(onData);
if (onError != null) _onErrorListeners.add(onError);
_execute(variables).then((_) {
_onDataListeners.remove(onData);
_onErrorListeners.remove(onError);
});
try {
_execute(variables).then((_) {
_onDataListeners.remove(onData);
_onErrorListeners.remove(onError);
});
} catch (e) {
return;
}
}
Future<T?> mutateAsync(V variables) async {
+2 -2
View File
@@ -108,7 +108,7 @@ class Query<T extends Object, Outside> extends BaseQuery<T, Outside, dynamic> {
(_) async {
// only refetch if its connected to the internet or refetch will
// always result in error while there's no internet
if (isStale && await isInternetConnected()) await refetch();
if (isStale && await isNetworkOnline) await refetch();
},
);
}
@@ -123,7 +123,7 @@ class Query<T extends Object, Outside> extends BaseQuery<T, Outside, dynamic> {
/// also checking if the is stale or not
/// no need to refetch a valid query for no reason
if (refetchOnMount == true && isStale) {
this.isInternetConnected().then((isConnected) async {
isNetworkOnline.then((isConnected) async {
if (isConnected) await refetch();
});
}
+6 -6
View File
@@ -112,12 +112,12 @@ class QueryBowl {
.onConnectivityChanged
.listen((ConnectivityResult result) async {
if (isConnectedToInternet(result)) {
for (final query in this.cache.queries) {
for (final query in this.cache.queries.toList()) {
if (query.refetchOnReconnect == false || !query.enabled) continue;
await query.refetch();
await Future.delayed(refetchOnReconnectDelay);
}
for (final infiniteQuery in this.cache.infiniteQueries) {
for (final infiniteQuery in this.cache.infiniteQueries.toList()) {
if (infiniteQuery.refetchOnReconnect == false ||
!infiniteQuery.enabled) continue;
await infiniteQuery.refetchPages();
@@ -130,13 +130,13 @@ class QueryBowl {
SystemChannels.lifecycle.setMessageHandler((msg) async {
if (msg == 'AppLifecycleState.resumed') {
if (_canNotRefetchAfterWeGotTheApp) return null;
for (final query in this.cache.queries) {
for (final query in this.cache.queries.toList()) {
if (query.refetchOnApplicationResume == false || !query.enabled)
continue;
await query.refetch();
await Future.delayed(refetchOnApplicationResumeDelay);
}
for (final infiniteQuery in this.cache.infiniteQueries) {
for (final infiniteQuery in this.cache.infiniteQueries.toList()) {
if (infiniteQuery.refetchOnApplicationResume == false ||
!infiniteQuery.enabled) continue;
await infiniteQuery.refetchPages();
@@ -185,12 +185,12 @@ class QueryBowl {
@protected
notifyWindowFocused() async {
if (kIsMobile || _canNotRefetchAfterWeGotTheApp) return;
for (final query in this.cache.queries) {
for (final query in this.cache.queries.toList()) {
if (query.refetchOnWindowFocus == false || !query.enabled) continue;
await query.refetch();
await Future.delayed(refetchOnWindowFocusDelay);
}
for (final infiniteQuery in this.cache.infiniteQueries) {
for (final infiniteQuery in this.cache.infiniteQueries.toList()) {
if (infiniteQuery.refetchOnWindowFocus == false || !infiniteQuery.enabled)
continue;
await infiniteQuery.refetchPages();