diff --git a/lib/core/auth/auth_notifier.dart b/lib/core/auth/auth_notifier.dart index 40cd99a..5e0936d 100644 --- a/lib/core/auth/auth_notifier.dart +++ b/lib/core/auth/auth_notifier.dart @@ -1,7 +1,9 @@ -// Version: 1.1.0 | Created: 2026-04-01 +// Version: 1.1.1 | Created: 2026-04-01 // Riverpod notifier that owns the auth state machine. // All login/logout/session-restore transitions go through here. +import 'dart:async'; + import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../features/auth/data/auth_repository.dart'; @@ -39,25 +41,40 @@ class AuthNotifier extends _$AuthNotifier { try { final repo = ref.read(authRepositoryProvider); - await repo.restoreSession( - accessToken: credentials.accessToken, - userId: credentials.userId, - deviceId: credentials.deviceId, - ); + + // Timeout: client.init() starts the Matrix sync loop and can hang + // indefinitely if the token is expired or the server is unreachable. + // After 12 seconds we give up and send the user to the login screen. + await repo + .restoreSession( + accessToken: credentials.accessToken, + userId: credentials.userId, + deviceId: credentials.deviceId, + ) + .timeout( + const Duration(seconds: 12), + onTimeout: () => throw Exception('Session restore timed out'), + ); + state = AuthState.authenticated( userId: credentials.userId, accessToken: credentials.accessToken, deviceId: credentials.deviceId, ); - // Resume background persistence for the restored session. - ref.read(syncPersistenceServiceProvider).start(); + // Resume background persistence — fire-and-forget so it never blocks auth. + try { + ref.read(syncPersistenceServiceProvider).start(); + } catch (_) { + // Persistence failure is non-fatal; the app works without it. + } } on AuthFailure { // Stored credentials are invalid; force re-login. await storage.clearCredentials(); state = const AuthState.unauthenticated(); } on Exception { - // Network offline at startup; still land on login rather than crashing. + // Covers: timeout, network offline, Olm init failure. + // Clear stale credentials so the login screen appears cleanly. await storage.clearCredentials(); state = const AuthState.unauthenticated(); }