Trigger background sync on connectivity recovery
This commit is contained in:
@@ -6,6 +6,9 @@ import 'package:relationship_saver/core/network/reachability/network_reachabilit
|
||||
abstract class NetworkReachability {
|
||||
/// Returns `true` when network looks reachable.
|
||||
Future<bool> isReachable({Duration timeout = const Duration(seconds: 2)});
|
||||
|
||||
/// Emits reachability updates when platform network status changes.
|
||||
Stream<bool> watch({Duration timeout = const Duration(seconds: 2)});
|
||||
}
|
||||
|
||||
/// Returns platform implementation for network reachability probing.
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:relationship_saver/core/network/reachability/network_reachability.dart';
|
||||
|
||||
class IoNetworkReachability implements NetworkReachability {
|
||||
IoNetworkReachability({Connectivity? connectivity})
|
||||
: _connectivity = connectivity ?? Connectivity();
|
||||
|
||||
final Connectivity _connectivity;
|
||||
|
||||
@override
|
||||
Future<bool> isReachable({
|
||||
Duration timeout = const Duration(seconds: 2),
|
||||
@@ -19,6 +25,35 @@ class IoNetworkReachability implements NetworkReachability {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<bool> watch({Duration timeout = const Duration(seconds: 2)}) {
|
||||
final Stream<dynamic> changes =
|
||||
_connectivity.onConnectivityChanged as Stream<dynamic>;
|
||||
return changes
|
||||
.asyncMap((dynamic event) async {
|
||||
final Iterable<ConnectivityResult> results =
|
||||
_normalizeConnectivityResults(event);
|
||||
final bool hasTransport = results.any(
|
||||
(ConnectivityResult result) => result != ConnectivityResult.none,
|
||||
);
|
||||
if (!hasTransport) {
|
||||
return false;
|
||||
}
|
||||
return isReachable(timeout: timeout);
|
||||
})
|
||||
.distinct();
|
||||
}
|
||||
|
||||
Iterable<ConnectivityResult> _normalizeConnectivityResults(dynamic event) {
|
||||
if (event is ConnectivityResult) {
|
||||
return <ConnectivityResult>[event];
|
||||
}
|
||||
if (event is List<ConnectivityResult>) {
|
||||
return event;
|
||||
}
|
||||
return const <ConnectivityResult>[];
|
||||
}
|
||||
}
|
||||
|
||||
NetworkReachability createPlatformNetworkReachability() =>
|
||||
|
||||
@@ -7,6 +7,11 @@ class StubNetworkReachability implements NetworkReachability {
|
||||
}) async {
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<bool> watch({Duration timeout = const Duration(seconds: 2)}) {
|
||||
return const Stream<bool>.empty();
|
||||
}
|
||||
}
|
||||
|
||||
NetworkReachability createPlatformNetworkReachability() =>
|
||||
|
||||
@@ -8,6 +8,11 @@ class WebNetworkReachability implements NetworkReachability {
|
||||
// Web environments vary in available probes; keep optimistic fallback.
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<bool> watch({Duration timeout = const Duration(seconds: 2)}) {
|
||||
return const Stream<bool>.empty();
|
||||
}
|
||||
}
|
||||
|
||||
NetworkReachability createPlatformNetworkReachability() =>
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:async';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:relationship_saver/core/config/app_config.dart';
|
||||
import 'package:relationship_saver/core/network/reachability/network_reachability.dart';
|
||||
import 'package:relationship_saver/core/network/reachability/network_reachability_provider.dart';
|
||||
import 'package:relationship_saver/features/sync/sync_auto_trigger_controller.dart';
|
||||
import 'package:relationship_saver/features/sync/sync_coordinator.dart';
|
||||
@@ -22,10 +23,13 @@ class _SyncBackgroundRunnerState extends ConsumerState<SyncBackgroundRunner>
|
||||
with WidgetsBindingObserver {
|
||||
late final SyncAutoTriggerController _controller;
|
||||
Timer? _periodicTimer;
|
||||
StreamSubscription<bool>? _reachabilitySubscription;
|
||||
bool? _lastReachable;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final reachability = ref.read(networkReachabilityProvider);
|
||||
_controller = SyncAutoTriggerController(
|
||||
syncNow: () => ref.read(syncCoordinatorProvider).syncNow(),
|
||||
now: DateTime.now,
|
||||
@@ -36,11 +40,12 @@ class _SyncBackgroundRunnerState extends ConsumerState<SyncBackgroundRunner>
|
||||
if (AppConfig.useFakeBackend) {
|
||||
return true;
|
||||
}
|
||||
return ref.read(networkReachabilityProvider).isReachable();
|
||||
return reachability.isReachable();
|
||||
},
|
||||
);
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
_configurePeriodicRunner();
|
||||
_configureReachabilityRunner(reachability);
|
||||
if (AppConfig.enableBackgroundSync) {
|
||||
unawaited(_controller.trigger(ignoreCooldown: true));
|
||||
}
|
||||
@@ -60,6 +65,7 @@ class _SyncBackgroundRunnerState extends ConsumerState<SyncBackgroundRunner>
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
_periodicTimer?.cancel();
|
||||
_reachabilitySubscription?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -80,4 +86,22 @@ class _SyncBackgroundRunnerState extends ConsumerState<SyncBackgroundRunner>
|
||||
unawaited(_controller.trigger());
|
||||
});
|
||||
}
|
||||
|
||||
void _configureReachabilityRunner(NetworkReachability reachability) {
|
||||
if (!AppConfig.enableBackgroundSync) {
|
||||
return;
|
||||
}
|
||||
_reachabilitySubscription = reachability.watch().listen((bool reachable) {
|
||||
final bool? previous = _lastReachable;
|
||||
_lastReachable = reachable;
|
||||
if (reachable && previous == false) {
|
||||
unawaited(_controller.trigger(ignoreCooldown: true));
|
||||
}
|
||||
});
|
||||
unawaited(_primeReachabilityState(reachability));
|
||||
}
|
||||
|
||||
Future<void> _primeReachabilityState(NetworkReachability reachability) async {
|
||||
_lastReachable = await reachability.isReachable();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user