From 631af65a2ff69e9019e6aae33d4c8ed4003901b9 Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Tue, 17 Feb 2026 15:46:52 -0500 Subject: [PATCH] feat: Update session navigation and enhance error handling in data services --- .../lib/src/widgets/session_listener.dart | 6 ++--- .../lib/src/widgets/session_listener.dart | 4 +-- .../src/services/data_connect_service.dart | 22 ++++++++++++--- .../services/mixins/data_error_handler.dart | 27 ++++++++++++++----- .../lib/src/session/staff_session_store.dart | 11 +++----- 5 files changed, 48 insertions(+), 22 deletions(-) diff --git a/apps/mobile/apps/client/lib/src/widgets/session_listener.dart b/apps/mobile/apps/client/lib/src/widgets/session_listener.dart index abb7b559..f481633b 100644 --- a/apps/mobile/apps/client/lib/src/widgets/session_listener.dart +++ b/apps/mobile/apps/client/lib/src/widgets/session_listener.dart @@ -52,7 +52,7 @@ class _SessionListenerState extends State { // Only show dialog if user was previously authenticated (session expired) if (_isInitialState) { _isInitialState = false; - Modular.to.toGetStartedPage(); + Modular.to.toClientGetStartedPage(); } else if (!_sessionExpiredDialogShown) { _sessionExpiredDialogShown = true; _showSessionExpiredDialog(); @@ -77,7 +77,7 @@ class _SessionListenerState extends State { _showSessionErrorDialog(state.errorMessage ?? 'Session error occurred'); } else { _isInitialState = false; - Modular.to.toInitialPage(); + Modular.to.toClientGetStartedPage(); } break; @@ -149,7 +149,7 @@ class _SessionListenerState extends State { DataConnectService.instance.handleSignOut(); // Navigate to authentication - Modular.to.toInitialPage(); + Modular.to.toClientGetStartedPage(); } @override diff --git a/apps/mobile/apps/staff/lib/src/widgets/session_listener.dart b/apps/mobile/apps/staff/lib/src/widgets/session_listener.dart index 160b5fd4..258bd901 100644 --- a/apps/mobile/apps/staff/lib/src/widgets/session_listener.dart +++ b/apps/mobile/apps/staff/lib/src/widgets/session_listener.dart @@ -77,7 +77,7 @@ class _SessionListenerState extends State { _showSessionErrorDialog(state.errorMessage ?? 'Session error occurred'); } else { _isInitialState = false; - Modular.to.toInitialPage(); + Modular.to.toGetStartedPage(); } break; @@ -149,7 +149,7 @@ class _SessionListenerState extends State { DataConnectService.instance.handleSignOut(); // Navigate to authentication - Modular.to.toInitialPage(); + Modular.to.toGetStartedPage(); } @override diff --git a/apps/mobile/packages/data_connect/lib/src/services/data_connect_service.dart b/apps/mobile/packages/data_connect/lib/src/services/data_connect_service.dart index c70f4789..19799467 100644 --- a/apps/mobile/packages/data_connect/lib/src/services/data_connect_service.dart +++ b/apps/mobile/packages/data_connect/lib/src/services/data_connect_service.dart @@ -83,7 +83,7 @@ class DataConnectService with DataErrorHandler, SessionHandlerMixin { // 2. Check Cache if (_cachedBusinessId != null) return _cachedBusinessId!; - // 3. Check Auth Status + // 3. Fetch from Data Connect using Firebase UID final firebase_auth.User? user = _auth.currentUser; if (user == null) { throw const NotAuthenticatedException( @@ -91,8 +91,24 @@ class DataConnectService with DataErrorHandler, SessionHandlerMixin { ); } - // 4. Fallback (should ideally not happen if DB is seeded and session is initialized) - // Ideally we'd have a getBusinessByUserId query here. + try { + final fdc.QueryResult< + dc.GetBusinessesByUserIdData, + dc.GetBusinessesByUserIdVariables + > + response = await executeProtected( + () => connector.getBusinessesByUserId(userId: user.uid).execute(), + ); + + if (response.data.businesses.isNotEmpty) { + _cachedBusinessId = response.data.businesses.first.id; + return _cachedBusinessId!; + } + } catch (e) { + throw Exception('Failed to fetch business ID from Data Connect: $e'); + } + + // 4. Fallback (should ideally not happen if DB is seeded) return user.uid; } diff --git a/apps/mobile/packages/data_connect/lib/src/services/mixins/data_error_handler.dart b/apps/mobile/packages/data_connect/lib/src/services/mixins/data_error_handler.dart index aec89758..49a5cbea 100644 --- a/apps/mobile/packages/data_connect/lib/src/services/mixins/data_error_handler.dart +++ b/apps/mobile/packages/data_connect/lib/src/services/mixins/data_error_handler.dart @@ -20,8 +20,12 @@ mixin DataErrorHandler { try { return await action().timeout(timeout); } on TimeoutException { + debugPrint( + 'DataErrorHandler: Request timed out after ${timeout.inSeconds}s', + ); throw ServiceUnavailableException( - technicalMessage: 'Request timed out after ${timeout.inSeconds}s'); + technicalMessage: 'Request timed out after ${timeout.inSeconds}s', + ); } on SocketException catch (e) { throw NetworkException(technicalMessage: 'SocketException: ${e.message}'); } on FirebaseException catch (e) { @@ -32,16 +36,26 @@ mixin DataErrorHandler { msg.contains('offline') || msg.contains('network') || msg.contains('connection failed')) { + debugPrint( + 'DataErrorHandler: Firebase network error: ${e.code} - ${e.message}', + ); throw NetworkException( - technicalMessage: 'Firebase ${e.code}: ${e.message}'); + technicalMessage: 'Firebase ${e.code}: ${e.message}', + ); } if (code == 'deadline-exceeded') { + debugPrint( + 'DataErrorHandler: Firebase timeout error: ${e.code} - ${e.message}', + ); throw ServiceUnavailableException( - technicalMessage: 'Firebase ${e.code}: ${e.message}'); + technicalMessage: 'Firebase ${e.code}: ${e.message}', + ); } + debugPrint('DataErrorHandler: Firebase error: ${e.code} - ${e.message}'); // Fallback for other Firebase errors throw ServerException( - technicalMessage: 'Firebase ${e.code}: ${e.message}'); + technicalMessage: 'Firebase ${e.code}: ${e.message}', + ); } catch (e) { final String errorStr = e.toString().toLowerCase(); if (errorStr.contains('socketexception') || @@ -56,15 +70,16 @@ mixin DataErrorHandler { errorStr.contains('grpc error') || errorStr.contains('terminated') || errorStr.contains('connectexception')) { + debugPrint('DataErrorHandler: Network-related error: $e'); throw NetworkException(technicalMessage: e.toString()); } - + // If it's already an AppException, rethrow it if (e is AppException) rethrow; // Debugging: Log unexpected errors debugPrint('DataErrorHandler: Unhandled exception caught: $e'); - + throw UnknownException(technicalMessage: e.toString()); } } diff --git a/apps/mobile/packages/data_connect/lib/src/session/staff_session_store.dart b/apps/mobile/packages/data_connect/lib/src/session/staff_session_store.dart index 06be4aef..7c5229c9 100644 --- a/apps/mobile/packages/data_connect/lib/src/session/staff_session_store.dart +++ b/apps/mobile/packages/data_connect/lib/src/session/staff_session_store.dart @@ -1,18 +1,15 @@ import 'package:krow_domain/krow_domain.dart' as domain; class StaffSession { + const StaffSession({required this.user, this.staff, this.ownerId}); + final domain.User user; final domain.Staff? staff; final String? ownerId; - - const StaffSession({ - required this.user, - this.staff, - this.ownerId, - }); } class StaffSessionStore { + StaffSessionStore._(); StaffSession? _session; StaffSession? get session => _session; @@ -26,6 +23,4 @@ class StaffSessionStore { } static final StaffSessionStore instance = StaffSessionStore._(); - - StaffSessionStore._(); }