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'; /// Runs lightweight background sync triggers while authenticated. class SyncBackgroundRunner extends ConsumerStatefulWidget { const SyncBackgroundRunner({required this.child, super.key}); final Widget child; @override ConsumerState createState() => _SyncBackgroundRunnerState(); } class _SyncBackgroundRunnerState extends ConsumerState with WidgetsBindingObserver { late final SyncAutoTriggerController _controller; Timer? _periodicTimer; StreamSubscription? _reachabilitySubscription; bool? _lastReachable; @override void initState() { super.initState(); final reachability = ref.read(networkReachabilityProvider); _controller = SyncAutoTriggerController( syncNow: () => ref.read(syncCoordinatorProvider).syncNow(), now: DateTime.now, minimumInterval: Duration( seconds: AppConfig.backgroundSyncIntervalSeconds, ), canTrigger: () async { if (AppConfig.useFakeBackend) { return true; } return reachability.isReachable(); }, ); WidgetsBinding.instance.addObserver(this); _configurePeriodicRunner(); _configureReachabilityRunner(reachability); if (AppConfig.enableBackgroundSync) { unawaited(_controller.trigger(ignoreCooldown: true)); } } @override void didChangeAppLifecycleState(AppLifecycleState state) { if (!AppConfig.enableBackgroundSync) { return; } if (state == AppLifecycleState.resumed) { unawaited(_controller.trigger(ignoreCooldown: true)); } } @override void dispose() { WidgetsBinding.instance.removeObserver(this); _periodicTimer?.cancel(); _reachabilitySubscription?.cancel(); super.dispose(); } @override Widget build(BuildContext context) => widget.child; void _configurePeriodicRunner() { if (!AppConfig.enableBackgroundSync) { return; } final int seconds = AppConfig.backgroundSyncIntervalSeconds; if (seconds <= 0) { return; } _periodicTimer = Timer.periodic(Duration(seconds: seconds), (_) { 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 _primeReachabilityState(NetworkReachability reachability) async { _lastReachable = await reachability.isReachable(); } }