feat: refresh on network state change and cancel retry when offline

This commit is contained in:
Kingkor Roy Tirtho
2023-06-21 22:10:39 +06:00
parent 72ca4cece4
commit 8c7b19c0e5
7 changed files with 121 additions and 12 deletions
@@ -10,6 +10,7 @@ abstract class DefaultConstants {
static const RetryConfig retryConfig = RetryConfig(
maxRetries: 3,
retryDelay: Duration(seconds: 1),
cancelWhenOffline: true,
);
static const RefreshConfig refreshConfig = RefreshConfig(
@@ -17,6 +18,7 @@ abstract class DefaultConstants {
refreshInterval: Duration.zero,
refreshOnMount: false,
refreshOnQueryFnChange: false,
refreshOnNetworkStateChange: true,
);
static const Duration cacheDuration = Duration(minutes: 5);
@@ -7,12 +7,14 @@ class RefreshConfig {
final Duration refreshInterval;
final bool refreshOnMount;
final bool refreshOnQueryFnChange;
final bool refreshOnNetworkStateChange;
const RefreshConfig({
required this.staleDuration,
required this.refreshInterval,
required this.refreshOnMount,
required this.refreshOnQueryFnChange,
required this.refreshOnNetworkStateChange,
});
factory RefreshConfig.withDefaults(
@@ -49,6 +51,7 @@ class RefreshConfig {
Duration? refreshInterval,
bool? refreshOnMount,
bool? refreshOnQueryFnChange,
bool? refreshOnNetworkStateChange,
}) {
return RefreshConfig(
staleDuration: staleDuration ?? this.staleDuration,
@@ -56,6 +59,8 @@ class RefreshConfig {
refreshOnMount: refreshOnMount ?? this.refreshOnMount,
refreshOnQueryFnChange:
refreshOnQueryFnChange ?? this.refreshOnQueryFnChange,
refreshOnNetworkStateChange:
refreshOnNetworkStateChange ?? this.refreshOnNetworkStateChange,
);
}
@@ -5,8 +5,13 @@ import 'package:flutter/material.dart';
class RetryConfig {
final int maxRetries;
final Duration retryDelay;
final bool cancelWhenOffline;
const RetryConfig({required this.maxRetries, required this.retryDelay});
const RetryConfig({
required this.maxRetries,
required this.retryDelay,
required this.cancelWhenOffline,
});
factory RetryConfig.withDefaults(
BuildContext context, {
@@ -32,10 +37,12 @@ class RetryConfig {
RetryConfig copyWith({
int? maxRetries,
Duration? retryDelay,
bool? cancelWhenOffline,
}) {
return RetryConfig(
maxRetries: maxRetries ?? this.maxRetries,
retryDelay: retryDelay ?? this.retryDelay,
cancelWhenOffline: cancelWhenOffline ?? this.cancelWhenOffline,
);
}
+1 -1
View File
@@ -523,7 +523,7 @@ class QueryClient {
required ConnectivityAdapter connectivity,
String? cacheDir,
}) async {
connectivity = connectivity;
QueryClient.connectivity = connectivity;
await Hive.initFlutter(cacheDir);
_cachePrefix = cachePrefix;
await Hive.openLazyBox(queryCachePrefix);
@@ -170,12 +170,37 @@ class InfiniteQuery<DataType, ErrorType, PageType>
}),
);
});
// Listen to network changes and cancel any ongoing operations
bool wasConnected = true;
_connectivitySubscription = QueryClient.connectivity.onConnectivityChanged
.listen((isConnected) async {
try {
if (isConnected &&
!wasConnected &&
refreshConfig.refreshOnNetworkStateChange) {
for (final page in state.pages) {
if (page.isStale) {
await refresh(page.page);
}
}
} else if (!isConnected &&
_mutex.isLocked &&
retryConfig.cancelWhenOffline) {
await _operation?.cancel();
}
} finally {
wasConnected = isConnected;
}
});
}
final _mutex = Mutex();
final LazyBox _box;
final StreamController<PageEvent<DataType, PageType>> _dataController;
final StreamController<PageEvent<ErrorType, PageType>> _errorController;
StreamSubscription<bool>? _connectivitySubscription;
CancelableOperation<void>? _operation;
@@ -421,6 +446,15 @@ class InfiniteQuery<DataType, ErrorType, PageType>
return super.addListener(listener, fireImmediately: fireImmediately);
}
@override
void dispose() {
_operation?.cancel();
_connectivitySubscription?.cancel();
_errorController.close();
_dataController.close();
super.dispose();
}
@override
operator ==(Object other) =>
identical(this, other) || other is InfiniteQuery && key == other.key;
+40 -10
View File
@@ -2,6 +2,7 @@ import 'dart:async';
import 'package:async/async.dart';
import 'package:fl_query/src/collections/retry_config.dart';
import 'package:fl_query/src/core/client.dart';
import 'package:fl_query/src/core/mixins/retryer.dart';
import 'package:mutex/mutex.dart';
import 'package:state_notifier/state_notifier.dart';
@@ -43,13 +44,38 @@ class Mutation<DataType, ErrorType, VariablesType>
MutationFn<DataType, VariablesType> _mutationFn;
Mutation(this.key, MutationFn<DataType, VariablesType> mutationFn,
{required this.retryConfig})
: _dataController = StreamController.broadcast(),
Mutation(
this.key,
MutationFn<DataType, VariablesType> mutationFn, {
required this.retryConfig,
}) : _dataController = StreamController.broadcast(),
_errorController = StreamController.broadcast(),
_mutationController = StreamController.broadcast(),
_mutationFn = mutationFn,
super(MutationState<DataType, ErrorType, VariablesType>());
super(MutationState<DataType, ErrorType, VariablesType>()) {
// Listen to network changes and cancel any ongoing operations
bool wasConnected = true;
_connectivitySubscription = QueryClient.connectivity.onConnectivityChanged
.listen((isConnected) async {
try {
if (!isConnected &&
wasConnected &&
_mutex.isLocked &&
retryConfig.cancelWhenOffline) {
await _operation?.cancel();
}
} finally {
wasConnected = isConnected;
}
});
}
final _mutex = Mutex();
final StreamController<VariablesType> _mutationController;
final StreamController<DataType> _dataController;
final StreamController<ErrorType> _errorController;
CancelableOperation<void>? _operation;
late final StreamSubscription<bool>? _connectivitySubscription;
bool get isInactive => !hasListeners;
bool get isMutating => _mutex.isLocked;
@@ -62,12 +88,6 @@ class Mutation<DataType, ErrorType, VariablesType>
Stream<ErrorType> get errorStream => _errorController.stream;
Stream<VariablesType> get mutationStream => _mutationController.stream;
final _mutex = Mutex();
final StreamController<VariablesType> _mutationController;
final StreamController<DataType> _dataController;
final StreamController<ErrorType> _errorController;
CancelableOperation<void>? _operation;
Future<void> _operate(VariablesType variables) {
return _mutex.protect(() async {
state = state.copyWith();
@@ -113,6 +133,16 @@ class Mutation<DataType, ErrorType, VariablesType>
state = MutationState<DataType, ErrorType, VariablesType>();
}
@override
void dispose() {
_operation?.cancel();
_connectivitySubscription?.cancel();
_dataController.close();
_errorController.close();
_mutationController.close();
super.dispose();
}
@override
operator ==(Object other) {
return identical(this, other) || (other is Mutation && key == other.key);
+31
View File
@@ -92,6 +92,27 @@ class Query<DataType, ErrorType>
await refresh();
}
});
// Listen to network changes and cancel any ongoing operations
bool wasConnected = true;
_connectivitySubscription = QueryClient.connectivity.onConnectivityChanged
.listen((isConnected) async {
try {
if (isConnected &&
!wasConnected &&
state.isStale &&
refreshConfig.refreshOnNetworkStateChange) {
await refresh();
} else if (!isConnected &&
_mutex.isLocked &&
retryConfig.cancelWhenOffline) {
await _operation?.cancel();
}
} finally {
wasConnected = isConnected;
}
});
}
DataType? _initial;
@@ -99,6 +120,7 @@ class Query<DataType, ErrorType>
final _mutex = Mutex();
final StreamController<DataType> _dataController;
final StreamController<ErrorType> _errorController;
StreamSubscription<bool>? _connectivitySubscription;
bool get isInitial => hasData && state.data == _initial;
bool get isLoading => isInitial ? _mutex.isLocked : !hasData && !hasError;
@@ -183,6 +205,15 @@ class Query<DataType, ErrorType>
return super.addListener(listener, fireImmediately: fireImmediately);
}
@override
void dispose() {
_operation?.cancel();
_connectivitySubscription?.cancel();
_dataController.close();
_errorController.close();
super.dispose();
}
@override
operator ==(Object other) {
return identical(this, other) || (other is Query && key == other.key);