Implemented per stream subscription recovery with polling fallback

This commit is contained in:
2026-03-01 17:24:04 +08:00
parent e91e7b43d2
commit c9479f01f0
19 changed files with 894 additions and 494 deletions
+27 -4
View File
@@ -161,13 +161,17 @@ final appRouterProvider = Provider<GoRouter>((ref) {
class RouterNotifier extends ChangeNotifier {
RouterNotifier(this.ref) {
_authSub = ref.listen(authStateChangesProvider, (previous, next) {
// Enforce auth-level ban when a session becomes available.
// Only enforce lock on successful sign-in events, not on every auth state change
if (next is AsyncData) {
final authState = next.value;
final session = authState?.session;
if (session != null) {
// Fire-and-forget enforcement (best-effort client-side sign-out)
enforceLockForCurrentUser(ref.read(supabaseClientProvider));
// Only check for bans when we have a session and the previous state didn't
final previousSession = previous is AsyncData
? previous.value?.session
: null;
if (session != null && previousSession == null) {
// User just signed in; enforce lock check
_enforceLockAsync();
}
}
notifyListeners();
@@ -180,6 +184,25 @@ class RouterNotifier extends ChangeNotifier {
final Ref ref;
late final ProviderSubscription _authSub;
late final ProviderSubscription _profileSub;
bool _lockEnforcementInProgress = false;
/// Safely enforce lock in the background, preventing concurrent calls
void _enforceLockAsync() {
// Prevent concurrent enforcement calls
if (_lockEnforcementInProgress) return;
_lockEnforcementInProgress = true;
// Use Future.microtask to defer execution and avoid blocking
Future.microtask(() async {
try {
await enforceLockForCurrentUser(ref.read(supabaseClientProvider));
} catch (e) {
debugPrint('RouterNotifier: lock enforcement error: $e');
} finally {
_lockEnforcementInProgress = false;
}
});
}
@override
void dispose() {