feat: refresh on network state change and cancel retry when offline
This commit is contained in:
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user