diff --git a/lib/features/base/base_controller.dart b/lib/features/base/base_controller.dart index 52ba7259f..9ca854535 100644 --- a/lib/features/base/base_controller.dart +++ b/lib/features/base/base_controller.dart @@ -93,35 +93,34 @@ abstract class BaseController extends GetxController viewState.value = newState; viewState.value.fold( (failure) { - if (_handleCommonException(failure)) { - handleFinallyCommonException(); - return; + if (failure is FeatureFailure) { + final exception = _performFilterExceptionInError(failure.exception); + if (exception != null) { + handleExceptionAction(exception); + } else { + handleFailureViewState(failure); + } + } else { + handleFailureViewState(failure); } - handleFailureViewState(failure); }, handleSuccessViewState); } - bool _handleCommonException(Failure failure) { - if (failure is FeatureFailure) { - return _handleCommonError(failure.exception); - } - return false; - } - void onError(Object error, StackTrace stackTrace) { logError('BaseController::onError():error: $error | stackTrace: $stackTrace'); - if (_handleCommonError(error)) { - handleFinallyCommonException(); - return; + final exception = _performFilterExceptionInError(error); + if (exception != null) { + handleExceptionAction(exception); + } else { + handleErrorViewState(error, stackTrace); } - handleErrorViewState(error, stackTrace); } void onDone() {} - bool _handleCommonError(dynamic error) { - logError('BaseController::_handleCommonError(): $error'); + Exception? _performFilterExceptionInError(dynamic error) { + logError('BaseController::_performFilterExceptionInError(): $error'); if (error is NoNetworkError || error is ConnectError || error is InternalServerError) { if (currentOverlayContext != null && currentContext != null) { _appToast.showToastMessage( @@ -135,46 +134,55 @@ abstract class BaseController extends GetxController infinityToast: true, ); } - return true; + return error; } else if (error is BadCredentialsException) { if (currentOverlayContext != null && currentContext != null) { _appToast.showToastErrorMessage( currentOverlayContext!, AppLocalizations.of(currentContext!).badCredentials); } - checkAuthenticationTypeWhenLogout(); - return true; + performInvokeLogoutAction(); + return error; } - return false; + return null; } void handleErrorViewState(Object error, StackTrace stackTrace) {} - void handleFinallyCommonException() { + void handleExceptionAction(Exception exception) { + log('BaseController::handleExceptionAction():exception: $exception'); clearState(); } void handleFailureViewState(Failure failure) { logError('BaseController::handleFailureViewState(): $failure'); if (failure is LogoutOidcFailure) { - _getSubscriptionLocalAction(); + if (_isFcmEnabled) { + _getSubscriptionLocalAction(); + } else { + _logoutOIDCAction(); + } } else if (failure is GetFCMSubscriptionLocalFailure) { - checkAuthenticationTypeWhenLogout(); + performInvokeLogoutAction(); } else if (failure is DestroySubscriptionFailure) { - checkAuthenticationTypeWhenLogout(); + performInvokeLogoutAction(); } } void handleSuccessViewState(Success success) { log('BaseController::handleSuccessViewState(): $success'); if (success is LogoutOidcSuccess) { - _getSubscriptionLocalAction(); + if (_isFcmEnabled) { + _getSubscriptionLocalAction(); + } else { + _logoutOIDCAction(); + } } else if (success is GetFCMSubscriptionLocalSuccess) { final subscriptionId = success.fcmSubscription.subscriptionId; _destroySubscriptionAction(subscriptionId); } else if (success is DestroySubscriptionSuccess) { - checkAuthenticationTypeWhenLogout(); + performInvokeLogoutAction(); } } @@ -249,6 +257,10 @@ abstract class BaseController extends GetxController } } + AuthenticationType get authenticationType => authorizationInterceptors.authenticationType; + + bool get isAuthenticatedWithOidc => authenticationType == AuthenticationType.oidc; + bool _isFcmActivated(Session session, AccountId accountId) => [FirebaseCapability.fcmIdentifier].isSupported(session, accountId) && AppConfig.fcmAvailable; @@ -258,17 +270,18 @@ abstract class BaseController extends GetxController void logout(Session? session, AccountId? accountId) { if (session == null || accountId == null) { + logError('BaseController::logout(): Session is $session OR AccountId is $accountId'); + performInvokeLogoutAction(); return; } _isFcmEnabled = _isFcmActivated(session, accountId); - final authenticationType = authorizationInterceptors.authenticationType; - if (authenticationType == AuthenticationType.oidc) { + if (isAuthenticatedWithOidc) { consumeState(logoutOidcInteractor.execute()); } else { if (_isFcmEnabled) { _getSubscriptionLocalAction(); } else { - logoutAction(); + _logoutAction(); } } } @@ -278,33 +291,31 @@ abstract class BaseController extends GetxController _destroySubscriptionInteractor = Get.find(); consumeState(_destroySubscriptionInteractor!.execute(subscriptionId)); } catch(e) { - logError('ReloadableController::destroySubscriptionAction(): exception: $e'); - logoutAction(); + logError('BaseController::destroySubscriptionAction(): exception: $e'); + performInvokeLogoutAction(); } } - Future _getSubscriptionLocalAction() { + void _getSubscriptionLocalAction() { try { _getSubscriptionLocalInteractor = Get.find(); consumeState(_getSubscriptionLocalInteractor!.execute()); } catch (e) { - logError( - 'ReloadableController::getSubscriptionLocalAction(): exception: $e'); - logoutAction(); + logError('BaseController::getSubscriptionLocalAction(): exception: $e'); + performInvokeLogoutAction(); } - return Future.value(); } - void checkAuthenticationTypeWhenLogout() { - final authenticationType = authorizationInterceptors.authenticationType; - if (authenticationType == AuthenticationType.oidc) { + void performInvokeLogoutAction() { + if (isAuthenticatedWithOidc) { _logoutOIDCAction(); } else { - logoutAction(); + _logoutAction(); } } - void logoutAction() async { + void _logoutAction() async { + log('BaseController::_logoutAction():'); await Future.wait([ deleteCredentialInteractor.execute(), cachingManager.clearAll(), @@ -321,7 +332,7 @@ abstract class BaseController extends GetxController } void _logoutOIDCAction() async { - log('ReloadableController::_logoutOIDCAction():'); + log('BaseController::_logoutOIDCAction():'); await Future.wait([ deleteAuthorityOidcInteractor.execute(), cachingManager.clearAll(), @@ -334,5 +345,6 @@ abstract class BaseController extends GetxController _fcmReceiver.deleteFcmToken(); } await cachingManager.closeHive(); + goToLogin(arguments: LoginArguments(LoginFormType.ssoForm)); } } diff --git a/lib/features/base/reloadable/reloadable_controller.dart b/lib/features/base/reloadable/reloadable_controller.dart index 0c92eb8b1..35684e353 100644 --- a/lib/features/base/reloadable/reloadable_controller.dart +++ b/lib/features/base/reloadable/reloadable_controller.dart @@ -1,5 +1,4 @@ import 'package:core/data/network/config/dynamic_url_interceptors.dart'; -import 'package:core/presentation/extensions/uri_extension.dart'; import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:core/utils/app_logger.dart'; @@ -8,7 +7,6 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; -import 'package:model/account/authentication_type.dart'; import 'package:model/extensions/session_extension.dart'; import 'package:model/oidc/token_oidc.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; @@ -20,6 +18,7 @@ import 'package:tmail_ui_user/features/login/domain/usecases/update_authenticati import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart'; import 'package:tmail_ui_user/features/login/presentation/model/login_arguments.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/vacation_interactors_bindings.dart'; +import 'package:tmail_ui_user/features/session/domain/extensions/session_extensions.dart'; import 'package:tmail_ui_user/features/session/domain/state/get_session_state.dart'; import 'package:tmail_ui_user/features/session/domain/usecases/get_session_interactor.dart'; import 'package:tmail_ui_user/main/error/capability_validator.dart'; @@ -94,35 +93,22 @@ abstract class ReloadableController extends BaseController { consumeState(_getSessionInteractor.execute()); } - void _handleGetSessionFailure() async { - await Future.wait([ - deleteCredentialInteractor.execute(), - deleteAuthorityOidcInteractor.execute(), - cachingManager.clearAll(), - languageCacheManager.removeLanguage(), - ]); - final authenticationType = authorizationInterceptors.authenticationType; - if (authenticationType == AuthenticationType.oidc) { - goToLogin(arguments: LoginArguments(LoginFormType.ssoForm)); - } else { - goToLogin(arguments: LoginArguments(LoginFormType.credentialForm)); - } + void _handleGetSessionFailure() { + performInvokeLogoutAction(); } void _handleGetSessionSuccess(GetSessionSuccess success) { final session = success.session; final personalAccount = session.personalAccount; - final jmapUrl = _dynamicUrlInterceptors.jmapUrl; - final apiUrl = jmapUrl != null - ? session.apiUrl.toQualifiedUrl(baseUrl: Uri.parse(jmapUrl)).toString() - : session.apiUrl.toString(); + final apiUrl = session.getQualifiedApiUrl(baseUrl: _dynamicUrlInterceptors.jmapUrl); log('ReloadableController::_handleGetSessionSuccess():apiUrl: $apiUrl'); if (apiUrl.isNotEmpty) { _dynamicUrlInterceptors.changeBaseUrl(apiUrl); updateAuthenticationAccount(session, personalAccount.accountId, session.username); handleReloaded(session); } else { - _handleGetSessionFailure(); + logError('ReloadableController::_handleGetSessionSuccess(): apiUrl is NULL'); + performInvokeLogoutAction(); } } @@ -154,10 +140,7 @@ abstract class ReloadableController extends BaseController { } void updateAuthenticationAccount(Session session, AccountId accountId, UserName userName) { - final jmapUrl = _dynamicUrlInterceptors.jmapUrl; - final apiUrl = jmapUrl != null - ? session.apiUrl.toQualifiedUrl(baseUrl: Uri.parse(jmapUrl)).toString() - : session.apiUrl.toString(); + final apiUrl = session.getQualifiedApiUrl(baseUrl: _dynamicUrlInterceptors.jmapUrl);; log('ReloadableController::updateAuthenticationAccount():apiUrl: $apiUrl'); if (apiUrl.isNotEmpty) { consumeState(_updateAuthenticationAccountInteractor.execute(accountId, apiUrl, userName)); diff --git a/lib/features/caching/caching_manager.dart b/lib/features/caching/caching_manager.dart index 2704cfb57..29fbd2db7 100644 --- a/lib/features/caching/caching_manager.dart +++ b/lib/features/caching/caching_manager.dart @@ -81,12 +81,11 @@ class CachingManager { ], eagerError: true); } - Future clearEmailCache() async { - await Future.wait([ + Future clearEmailCache() { + return Future.wait([ _stateCacheClient.deleteItem(StateType.email.value), _emailCacheClient.clearAllData(), ], eagerError: true); - log('CachingManager::clearEmailCache(): success'); } Future onUpgradeCache(int oldVersion, int newVersion) async { diff --git a/lib/features/login/data/extensions/personal_account_extension.dart b/lib/features/login/data/extensions/personal_account_extension.dart index 1f102ce8a..c36dcb93c 100644 --- a/lib/features/login/data/extensions/personal_account_extension.dart +++ b/lib/features/login/data/extensions/personal_account_extension.dart @@ -1,4 +1,3 @@ -import 'package:model/account/authentication_type.dart'; import 'package:model/account/personal_account.dart'; import 'package:tmail_ui_user/features/login/data/model/account_cache.dart'; @@ -6,7 +5,7 @@ extension PersonalAccountExtension on PersonalAccount { AccountCache toCache() { return AccountCache( id, - authenticationType.asString(), + authenticationType.name, isSelected: isSelected, accountId: accountId?.id.value, apiUrl: apiUrl, diff --git a/lib/features/login/presentation/login_controller.dart b/lib/features/login/presentation/login_controller.dart index baf297c57..d8d6c140e 100644 --- a/lib/features/login/presentation/login_controller.dart +++ b/lib/features/login/presentation/login_controller.dart @@ -189,8 +189,8 @@ class LoginController extends ReloadableController { } @override - void handleFinallyCommonException() { - super.handleFinallyCommonException(); + void handleExceptionAction(Exception exception) { + super.handleExceptionAction(exception); loginState.value = LoginState(Right(LoginInitAction())); } diff --git a/lib/features/offline_mode/observer/sending_email_observer.dart b/lib/features/offline_mode/observer/sending_email_observer.dart index 2e7daf9fa..2fcc8d235 100644 --- a/lib/features/offline_mode/observer/sending_email_observer.dart +++ b/lib/features/offline_mode/observer/sending_email_observer.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'package:core/data/network/config/dynamic_url_interceptors.dart'; -import 'package:core/presentation/extensions/uri_extension.dart'; import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:core/utils/app_logger.dart'; @@ -30,6 +29,7 @@ import 'package:tmail_ui_user/features/offline_mode/scheduler/worker_state.dart' import 'package:tmail_ui_user/features/push_notification/presentation/notification/local_notification_config.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/notification/local_notification_manager.dart'; import 'package:tmail_ui_user/features/sending_queue/presentation/utils/sending_queue_isolate_manager.dart'; +import 'package:tmail_ui_user/features/session/domain/extensions/session_extensions.dart'; import 'package:tmail_ui_user/features/session/domain/state/get_session_state.dart'; import 'package:tmail_ui_user/features/session/domain/usecases/get_session_interactor.dart'; import 'package:tmail_ui_user/main/bindings/main_bindings.dart'; @@ -153,11 +153,8 @@ class SendingEmailObserver extends WorkObserver { void _handleGetSessionSuccess(GetSessionSuccess success) { _currentSession = success.session; _userName = success.session.username; - final jmapUrl = _dynamicUrlInterceptors?.jmapUrl; - final apiUrl = jmapUrl != null - ? success.session.apiUrl.toQualifiedUrl(baseUrl: Uri.parse(jmapUrl)).toString() - : success.session.apiUrl.toString(); - log('SendingEmailObserver::_handleGetSessionSuccess():jmapUrl: $jmapUrl | apiUrl: $apiUrl'); + final apiUrl = success.session.getQualifiedApiUrl(baseUrl: _dynamicUrlInterceptors?.jmapUrl); + log('SendingEmailObserver::_handleGetSessionSuccess():apiUrl: $apiUrl'); if (apiUrl.isNotEmpty) { _dynamicUrlInterceptors?.changeBaseUrl(apiUrl); _sendEmailAction(); diff --git a/lib/features/push_notification/presentation/controller/fcm_message_controller.dart b/lib/features/push_notification/presentation/controller/fcm_message_controller.dart index b19f10a66..591c3873a 100644 --- a/lib/features/push_notification/presentation/controller/fcm_message_controller.dart +++ b/lib/features/push_notification/presentation/controller/fcm_message_controller.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:core/data/network/config/dynamic_url_interceptors.dart'; -import 'package:core/presentation/extensions/uri_extension.dart'; import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:core/utils/app_logger.dart'; @@ -34,6 +33,7 @@ import 'package:tmail_ui_user/features/push_notification/presentation/listener/e import 'package:tmail_ui_user/features/push_notification/presentation/listener/mailbox_change_listener.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/services/fcm_service.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/utils/fcm_utils.dart'; +import 'package:tmail_ui_user/features/session/domain/extensions/session_extensions.dart'; import 'package:tmail_ui_user/features/session/domain/state/get_session_state.dart'; import 'package:tmail_ui_user/features/session/domain/usecases/get_session_interactor.dart'; import 'package:tmail_ui_user/main/bindings/main_bindings.dart'; @@ -274,11 +274,8 @@ class FcmMessageController extends FcmBaseController { void _handleGetSessionSuccess(GetSessionSuccess success) { _currentSession = success.session; _userName = success.session.username; - final jmapUrl = _dynamicUrlInterceptors?.jmapUrl; - final apiUrl = jmapUrl != null - ? success.session.apiUrl.toQualifiedUrl(baseUrl: Uri.parse(jmapUrl)).toString() - : success.session.apiUrl.toString(); - log('FcmMessageController::_pushActionFromRemoteMessageBackground():jmapUrl: $jmapUrl | apiUrl: $apiUrl'); + final apiUrl = success.session.getQualifiedApiUrl(baseUrl: _dynamicUrlInterceptors?.jmapUrl); + log('FcmMessageController::_pushActionFromRemoteMessageBackground():apiUrl: $apiUrl'); if (apiUrl.isNotEmpty) { _dynamicUrlInterceptors?.changeBaseUrl(apiUrl); _pushActionFromRemoteMessageBackground(); diff --git a/lib/features/session/domain/extensions/session_extensions.dart b/lib/features/session/domain/extensions/session_extensions.dart index 7ab50a8e9..92ccd311e 100644 --- a/lib/features/session/domain/extensions/session_extensions.dart +++ b/lib/features/session/domain/extensions/session_extensions.dart @@ -1,4 +1,5 @@ +import 'package:core/presentation/extensions/uri_extension.dart'; import 'package:jmap_dart_client/http/converter/state_converter.dart'; import 'package:jmap_dart_client/http/converter/user_name_converter.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; @@ -32,4 +33,12 @@ extension SessionExtensions on Session { } SessionHiveObj toHiveObj() => SessionHiveObj(values: toJson()); + + String getQualifiedApiUrl({String? baseUrl}) { + if (baseUrl != null) { + return apiUrl.toQualifiedUrl(baseUrl: Uri.parse(baseUrl)).toString(); + } else { + return apiUrl.toString(); + } + } } \ No newline at end of file diff --git a/lib/features/session/domain/state/get_session_state.dart b/lib/features/session/domain/state/get_session_state.dart index 548a8f266..0dc643183 100644 --- a/lib/features/session/domain/state/get_session_state.dart +++ b/lib/features/session/domain/state/get_session_state.dart @@ -11,9 +11,8 @@ class GetSessionSuccess extends UIState { } class GetSessionFailure extends FeatureFailure { - final dynamic exception; - GetSessionFailure(this.exception); + GetSessionFailure(dynamic exception) : super(exception: exception); @override List get props => [exception]; diff --git a/lib/features/session/presentation/session_controller.dart b/lib/features/session/presentation/session_controller.dart index 55eceb8cc..f2cc0a415 100644 --- a/lib/features/session/presentation/session_controller.dart +++ b/lib/features/session/presentation/session_controller.dart @@ -1,5 +1,4 @@ import 'package:core/data/network/config/dynamic_url_interceptors.dart'; -import 'package:core/presentation/extensions/uri_extension.dart'; import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:core/presentation/utils/app_toast.dart'; @@ -12,6 +11,7 @@ import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oi import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/update_authentication_account_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart'; +import 'package:tmail_ui_user/features/session/domain/extensions/session_extensions.dart'; import 'package:tmail_ui_user/features/session/domain/state/get_session_state.dart'; import 'package:tmail_ui_user/features/session/domain/usecases/get_session_interactor.dart'; import 'package:tmail_ui_user/main/exceptions/remote_exception.dart'; @@ -55,7 +55,6 @@ class SessionController extends ReloadableController { super.handleFailureViewState(failure); if (failure is GetSessionFailure) { _handleSessionFailure(failure); - _goToLogin(); } } @@ -96,7 +95,7 @@ class SessionController extends ReloadableController { } logError('SessionController::_handleSessionFailure():errorMessage: $errorMessage'); - if (errorMessage.isNotEmpty && currentOverlayContext != null && currentContext != null) { + if (errorMessage.isNotEmpty && currentOverlayContext != null) { _appToast.showToastErrorMessage(currentOverlayContext!, errorMessage); } } @@ -106,21 +105,8 @@ class SessionController extends ReloadableController { return sessionException is ConnectError || sessionException is BadGateway || sessionException is SocketError; } - void _goToLogin() async { - await Future.wait([ - deleteCredentialInteractor.execute(), - deleteAuthorityOidcInteractor.execute(), - cachingManager.clearAll() - ]); - authorizationInterceptors.clear(); - pushAndPopAll(AppRoutes.login); - } - void _goToMailboxDashBoard(GetSessionSuccess success) { - final jmapUrl = _dynamicUrlInterceptors.jmapUrl; - final apiUrl = jmapUrl != null - ? success.session.apiUrl.toQualifiedUrl(baseUrl: Uri.parse(jmapUrl)).toString() - : success.session.apiUrl.toString(); + final apiUrl = success.session.getQualifiedApiUrl(baseUrl: _dynamicUrlInterceptors.jmapUrl); log('SessionController::_goToMailboxDashBoard():apiUrl: $apiUrl'); if (apiUrl.isNotEmpty) { _dynamicUrlInterceptors.changeBaseUrl(apiUrl); @@ -128,7 +114,8 @@ class SessionController extends ReloadableController { RouteUtils.generateNavigationRoute(AppRoutes.dashboard, NavigationRouter()), arguments: success.session); } else { - _goToLogin(); + logError('SessionController::_goToMailboxDashBoard(): apiUrl is NULL'); + performInvokeLogoutAction(); } } } \ No newline at end of file diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index 2c1b5db57..dd74f4350 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -339,8 +339,6 @@ class ThreadController extends BaseController with EmailActionController { if (error is CannotCalculateChangesMethodResponseException) { await cachingManager.clearEmailCache(); _getAllEmailAction(); - } else { - super.onError(error, stackTrace); } } diff --git a/model/lib/account/authentication_type.dart b/model/lib/account/authentication_type.dart index c38842f07..7ec25d7bc 100644 --- a/model/lib/account/authentication_type.dart +++ b/model/lib/account/authentication_type.dart @@ -1,18 +1,5 @@ enum AuthenticationType { basic, oidc, - none -} - -extension AuthenticationTypeExtension on AuthenticationType { - String asString() { - switch (this) { - case AuthenticationType.oidc: - return 'oidc'; - case AuthenticationType.basic: - return 'basic'; - default: - return 'none'; - } - } + none; } \ No newline at end of file