88 lines
3.1 KiB
Dart
88 lines
3.1 KiB
Dart
import 'dart:async';
|
|
import 'package:flutter/foundation.dart' show debugPrint;
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:http/http.dart' as http;
|
|
|
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
|
|
|
import '../brick/cache_warmer.dart';
|
|
import '../brick/repository.dart';
|
|
import 'stream_recovery.dart';
|
|
|
|
/// Current online/offline status.
|
|
/// Defaults to true so the UI doesn't flash an offline banner on startup.
|
|
final isOnlineProvider = StateProvider<bool>((ref) => true);
|
|
|
|
/// Activating this provider starts a periodic connectivity poll.
|
|
/// Watch it from the root widget to keep it alive.
|
|
final connectivityMonitorProvider = Provider.autoDispose<void>((ref) {
|
|
// Wire up the global online check for StreamRecoveryWrapper so all stream
|
|
// wrappers can suppress recovery while offline without per-instance setup.
|
|
StreamRecoveryWrapper.setIsOnlineCallback(() => ref.read(isOnlineProvider));
|
|
|
|
bool previouslyOnline = true;
|
|
|
|
Future<void> check() async {
|
|
final online = await ConnectivityMonitor.check();
|
|
final wasOnline = ref.read(isOnlineProvider);
|
|
if (online != wasOnline) {
|
|
ref.read(isOnlineProvider.notifier).state = online;
|
|
}
|
|
if (previouslyOnline && !online) {
|
|
_onDisconnected();
|
|
}
|
|
if (!previouslyOnline && online) {
|
|
_onReconnected();
|
|
}
|
|
previouslyOnline = online;
|
|
}
|
|
|
|
// Run immediately, then every 5 seconds.
|
|
check();
|
|
final timer = Timer.periodic(const Duration(seconds: 5), (_) => check());
|
|
ref.onDispose(timer.cancel);
|
|
});
|
|
|
|
/// Pings the app's own backend to determine online status.
|
|
class ConnectivityMonitor {
|
|
static Future<bool> check() async {
|
|
try {
|
|
final res = await http
|
|
.head(Uri.parse('https://tasq.crmc.ph'))
|
|
.timeout(const Duration(seconds: 5));
|
|
return res.statusCode < 500;
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void _onDisconnected() {
|
|
debugPrint('[Connectivity] Went offline — pausing Brick request queue');
|
|
// Stop the retry loop so the queue doesn't spam network errors while offline.
|
|
// Queued writes are preserved in SQLite and will sync when _onReconnected fires.
|
|
AppRepository.offlineQueue?.stop();
|
|
}
|
|
|
|
void _onReconnected() {
|
|
debugPrint('[Connectivity] Back online — queued writes will sync via Brick');
|
|
if (!AppRepository.isConfigured) return;
|
|
|
|
// Resume the Brick HTTP queue so queued writes (e.g. offline task creation)
|
|
// are replayed against Supabase now that the network is available again.
|
|
AppRepository.offlineQueue?.start();
|
|
|
|
// Restart any Supabase realtime streams that were suppressed while offline.
|
|
// Streams that disconnected due to no network are NOT in an error-retry loop
|
|
// (recovery was suppressed); this kick-starts them back to live updates.
|
|
StreamRecoveryWrapper.notifyAllOnlineRestored();
|
|
|
|
// Re-warm the Brick cache so the local SQLite mirror reflects any changes
|
|
// that happened on the server while we were offline. Fire-and-forget — it
|
|
// runs in the background and never throws.
|
|
final user = Supabase.instance.client.auth.currentUser;
|
|
if (user != null) {
|
|
CacheWarmer.warmAll(Supabase.instance.client);
|
|
}
|
|
}
|