import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'supabase_provider.dart'; final realtimeControllerProvider = ChangeNotifierProvider(( ref, ) { final client = ref.watch(supabaseClientProvider); final controller = RealtimeController(client); ref.onDispose(controller.dispose); return controller; }); /// Simplified realtime controller for app-lifecycle awareness. /// Individual streams now handle their own recovery via [StreamRecoveryWrapper]. /// This controller only coordinates: /// - App lifecycle transitions (background/foreground) /// - Auth token refreshes /// - Global connection state notification (for UI indicators) class RealtimeController extends ChangeNotifier { final SupabaseClient _client; bool _disposed = false; /// Global flag: true if any stream is recovering; used for subtle UI indicator. bool isAnyStreamRecovering = false; RealtimeController(this._client) { _init(); } void _init() { try { // Listen for auth changes; ensure tokens are fresh for realtime. // Individual streams will handle their own reconnection. _client.auth.onAuthStateChange.listen((data) { final event = data.event; // Only refresh token on existing session refreshes, not immediately after sign-in // (sign-in already provides a fresh token) if (event == AuthChangeEvent.tokenRefreshed) { _ensureTokenFresh(); } }); } catch (e) { debugPrint('RealtimeController._init error: $e'); } } /// Ensure auth token is fresh for upcoming realtime operations. /// This is called after token refresh events, not immediately after sign-in. Future _ensureTokenFresh() async { if (_disposed) return; try { // Defensive: only refresh if the method exists (SDK version compatibility) final authDynamic = _client.auth as dynamic; if (authDynamic.refreshSession != null) { await authDynamic.refreshSession?.call(); } } catch (e) { debugPrint('RealtimeController: token refresh failed: $e'); } } /// Notify that a stream is starting recovery. Used for global UI indicator. void markStreamRecovering() { if (!isAnyStreamRecovering) { isAnyStreamRecovering = true; notifyListeners(); } } /// Notify that stream recovery completed. If all streams recovered, update state. void markStreamRecovered() { // In practice, individual streams notify their own status via statusChanges. // This is kept for potential future global coordination. if (isAnyStreamRecovering) { isAnyStreamRecovering = false; notifyListeners(); } } @override void dispose() { _disposed = true; super.dispose(); } }