refactor(offline): single connectivity subscription in OfflineModeProvider
This commit is contained in:
+31
-33
@@ -444,7 +444,11 @@ class _MainAppState extends State<MainApp> with WidgetsBindingObserver {
|
||||
late final OfflineWatchSyncService _offlineWatchSyncService;
|
||||
late final AppLifecycleListener _appLifecycleListener;
|
||||
StreamSubscription<WatchStateEvent>? _watchStateSubscription;
|
||||
StreamSubscription<List<ConnectivityResult>>? _connectivitySubscription;
|
||||
|
||||
/// WiFi-reconnect sync trigger, listening on [OfflineModeProvider] — the
|
||||
/// app's single connectivity subscription lives there.
|
||||
VoidCallback? _connectivitySyncListener;
|
||||
OfflineModeProvider? _connectivitySyncProvider;
|
||||
Timer? _syncDebounce;
|
||||
final Set<String> _pendingSyncKeys = <String>{};
|
||||
bool _isAutoDeleteRunning = false;
|
||||
@@ -507,7 +511,7 @@ class _MainAppState extends State<MainApp> with WidgetsBindingObserver {
|
||||
|
||||
_syncDebounce?.cancel();
|
||||
await _watchStateSubscription?.cancel();
|
||||
await _connectivitySubscription?.cancel();
|
||||
_removeConnectivitySyncListener();
|
||||
_memoryCheckTimer?.cancel();
|
||||
|
||||
_downloadManager.dispose();
|
||||
@@ -528,7 +532,7 @@ class _MainAppState extends State<MainApp> with WidgetsBindingObserver {
|
||||
void dispose() {
|
||||
_syncDebounce?.cancel();
|
||||
_watchStateSubscription?.cancel();
|
||||
_connectivitySubscription?.cancel();
|
||||
_removeConnectivitySyncListener();
|
||||
_memoryCheckTimer?.cancel();
|
||||
_appLifecycleListener.dispose();
|
||||
if (!_shutdownStarted) {
|
||||
@@ -552,38 +556,32 @@ class _MainAppState extends State<MainApp> with WidgetsBindingObserver {
|
||||
}
|
||||
|
||||
/// Fires [_autoDeleteAndSync] on each WiFi/Ethernet reconnect so rules run
|
||||
/// as soon as the device is back online. Rapid flapping is bounded by the
|
||||
/// executor's cooldown.
|
||||
void _startConnectivitySyncTrigger(DownloadProvider downloadProvider) {
|
||||
Future<void> setup() async {
|
||||
try {
|
||||
final initial = await Connectivity().checkConnectivity();
|
||||
_lastConnectivityWasWifi = _hasWifiOrEthernet(initial);
|
||||
} catch (e) {
|
||||
appLogger.w('Initial connectivity read failed, defaulting to false: $e');
|
||||
_lastConnectivityWasWifi = false;
|
||||
/// as soon as the device is back online. Listens on [OfflineModeProvider],
|
||||
/// which owns the app's single connectivity subscription and notifies on
|
||||
/// connection-type changes. Rapid flapping is bounded by the executor's
|
||||
/// cooldown.
|
||||
void _startConnectivitySyncTrigger(DownloadProvider downloadProvider, OfflineModeProvider offlineModeProvider) {
|
||||
_removeConnectivitySyncListener();
|
||||
_lastConnectivityWasWifi = offlineModeProvider.hasWifiOrEthernet;
|
||||
_connectivitySyncProvider = offlineModeProvider;
|
||||
_connectivitySyncListener = () {
|
||||
final hasWifi = offlineModeProvider.hasWifiOrEthernet;
|
||||
final transitioned = hasWifi && !_lastConnectivityWasWifi;
|
||||
_lastConnectivityWasWifi = hasWifi;
|
||||
if (transitioned) {
|
||||
appLogger.d('Connectivity moved onto WiFi/Ethernet — triggering sync pass');
|
||||
_autoDeleteAndSync(downloadProvider);
|
||||
}
|
||||
|
||||
try {
|
||||
_connectivitySubscription = Connectivity().onConnectivityChanged.listen((results) {
|
||||
final hasWifi = _hasWifiOrEthernet(results);
|
||||
final transitioned = hasWifi && !_lastConnectivityWasWifi;
|
||||
_lastConnectivityWasWifi = hasWifi;
|
||||
if (transitioned) {
|
||||
appLogger.d('Connectivity moved onto WiFi/Ethernet — triggering sync pass');
|
||||
_autoDeleteAndSync(downloadProvider);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
appLogger.w('Could not subscribe to connectivity changes: $e');
|
||||
}
|
||||
}
|
||||
|
||||
setup();
|
||||
};
|
||||
offlineModeProvider.addListener(_connectivitySyncListener!);
|
||||
}
|
||||
|
||||
static bool _hasWifiOrEthernet(List<ConnectivityResult> results) =>
|
||||
results.contains(ConnectivityResult.wifi) || results.contains(ConnectivityResult.ethernet);
|
||||
void _removeConnectivitySyncListener() {
|
||||
final listener = _connectivitySyncListener;
|
||||
if (listener != null) _connectivitySyncProvider?.removeListener(listener);
|
||||
_connectivitySyncListener = null;
|
||||
_connectivitySyncProvider = null;
|
||||
}
|
||||
|
||||
/// Run auto-delete (if enabled) and then a sync-rule pass.
|
||||
///
|
||||
@@ -820,7 +818,7 @@ class _MainAppState extends State<MainApp> with WidgetsBindingObserver {
|
||||
});
|
||||
});
|
||||
|
||||
_startConnectivitySyncTrigger(downloadProvider);
|
||||
_startConnectivitySyncTrigger(downloadProvider, offlineModeProvider);
|
||||
|
||||
// Thread the offline flag into services so queue/resume paths can
|
||||
// short-circuit instead of hitting the network and failing.
|
||||
|
||||
@@ -28,6 +28,18 @@ class OfflineModeProvider extends ChangeNotifier with DisposableChangeNotifierMi
|
||||
bool _lastOfflineState = false;
|
||||
bool _isInitialized = false;
|
||||
|
||||
/// Latest raw connectivity results. This provider owns the app's single
|
||||
/// `Connectivity()` subscription; consumers needing the connection *type*
|
||||
/// (e.g. the WiFi-reconnect sync trigger in main.dart) read it from here
|
||||
/// instead of subscribing themselves.
|
||||
List<ConnectivityResult> _lastConnectivityResults = const [];
|
||||
bool _lastWifiOrEthernetState = false;
|
||||
|
||||
/// Whether the current connection is WiFi or Ethernet (unmetered-ish).
|
||||
bool get hasWifiOrEthernet =>
|
||||
_lastConnectivityResults.contains(ConnectivityResult.wifi) ||
|
||||
_lastConnectivityResults.contains(ConnectivityResult.ethernet);
|
||||
|
||||
/// True once [MultiServerManager] has emitted its first server-status
|
||||
/// snapshot. Until then we don't actually know whether any server is
|
||||
/// online — the binder hasn't finished its first connect yet — so we
|
||||
@@ -98,6 +110,8 @@ class OfflineModeProvider extends ChangeNotifier with DisposableChangeNotifierMi
|
||||
const Duration(seconds: 3),
|
||||
onTimeout: () => [ConnectivityResult.other],
|
||||
);
|
||||
_lastConnectivityResults = connectivityResult;
|
||||
_lastWifiOrEthernetState = hasWifiOrEthernet;
|
||||
_hasNetworkConnection = !connectivityResult.contains(ConnectivityResult.none);
|
||||
} catch (e) {
|
||||
// connectivity_plus can throw PlatformException on Windows (NetworkManager::StartListen)
|
||||
@@ -139,8 +153,18 @@ class OfflineModeProvider extends ChangeNotifier with DisposableChangeNotifierMi
|
||||
() {
|
||||
_connectivitySubscription = Connectivity().onConnectivityChanged.listen(
|
||||
(results) {
|
||||
_lastConnectivityResults = results;
|
||||
_hasNetworkConnection = !results.contains(ConnectivityResult.none);
|
||||
_notifyIfOfflineChanged();
|
||||
// Notify on connection-type changes too (WiFi <-> cellular), not
|
||||
// just offline flips — type consumers listen through this provider.
|
||||
final wifiNow = hasWifiOrEthernet;
|
||||
if (wifiNow != _lastWifiOrEthernetState) {
|
||||
_lastWifiOrEthernetState = wifiNow;
|
||||
_lastOfflineState = isOffline;
|
||||
safeNotifyListeners();
|
||||
} else {
|
||||
_notifyIfOfflineChanged();
|
||||
}
|
||||
},
|
||||
onError: (e) {
|
||||
_hasNetworkConnection = true;
|
||||
|
||||
Reference in New Issue
Block a user