import 'dart:async'; import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:core/utils/app_logger.dart'; import 'package:core/utils/platform_info.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:model/account/personal_account.dart'; import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart'; import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart'; import 'package:tmail_ui_user/features/cleanup/domain/model/cleanup_rule.dart'; import 'package:tmail_ui_user/features/cleanup/domain/model/email_cleanup_rule.dart'; import 'package:tmail_ui_user/features/cleanup/domain/model/recent_login_url_cleanup_rule.dart'; import 'package:tmail_ui_user/features/cleanup/domain/model/recent_login_username_cleanup_rule.dart'; import 'package:tmail_ui_user/features/cleanup/domain/model/recent_search_cleanup_rule.dart'; import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_email_cache_interactor.dart'; import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_login_url_cache_interactor.dart'; import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_login_username_interactor.dart'; import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_search_cache_interactor.dart'; import 'package:tmail_ui_user/features/home/domain/state/auto_sign_in_via_deep_link_state.dart'; import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/get_credential_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/get_stored_token_oidc_state.dart'; import 'package:tmail_ui_user/main/deep_links/deep_link_data.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/app_routes.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:tmail_ui_user/main/routes/route_utils.dart'; import 'package:tmail_ui_user/main/deep_links/deep_links_manager.dart'; import 'package:tmail_ui_user/main/utils/email_receive_manager.dart'; import 'package:tmail_ui_user/main/utils/ios_notification_manager.dart'; class HomeController extends ReloadableController { final CleanupEmailCacheInteractor _cleanupEmailCacheInteractor; final EmailReceiveManager _emailReceiveManager; final CleanupRecentSearchCacheInteractor _cleanupRecentSearchCacheInteractor; final CleanupRecentLoginUrlCacheInteractor _cleanupRecentLoginUrlCacheInteractor; final CleanupRecentLoginUsernameCacheInteractor _cleanupRecentLoginUsernameCacheInteractor; IOSNotificationManager? _iosNotificationManager; DeepLinksManager? _deepLinksManager; HomeController( this._cleanupEmailCacheInteractor, this._emailReceiveManager, this._cleanupRecentSearchCacheInteractor, this._cleanupRecentLoginUrlCacheInteractor, this._cleanupRecentLoginUsernameCacheInteractor, ); @override void onInit() { if (PlatformInfo.isMobile) { _initFlutterDownloader(); _registerReceivingFileSharing(); _registerDeepLinks(); } if (PlatformInfo.isIOS) { _registerNotificationClickOnIOS(); } super.onInit(); } @override void onReady() { _cleanupCache(); super.onReady(); } @override void handleReloaded(Session session) { pushAndPopAll( RouteUtils.generateNavigationRoute(AppRoutes.dashboard), arguments: session); } void _initFlutterDownloader() { FlutterDownloader .initialize(debug: kDebugMode) .then((_) => FlutterDownloader.registerCallback(downloadCallback)); } static void downloadCallback(String id, DownloadTaskStatus status, int progress) {} Future _cleanupCache() async { await HiveCacheConfig.instance.onUpgradeDatabase(cachingManager); await Future.wait([ _cleanupEmailCacheInteractor.execute(EmailCleanupRule(Duration.defaultCacheInternal)), _cleanupRecentSearchCacheInteractor.execute(RecentSearchCleanupRule()), _cleanupRecentLoginUrlCacheInteractor.execute(RecentLoginUrlCleanupRule()), _cleanupRecentLoginUsernameCacheInteractor.execute(RecentLoginUsernameCleanupRule()), ], eagerError: true).then((_) => getAuthenticatedAccountAction()); } void _registerReceivingFileSharing() { _emailReceiveManager.registerReceivingFileSharingStreamWhileAppClosed(); } void _registerNotificationClickOnIOS() { _iosNotificationManager = getBinding(); _iosNotificationManager?.listenClickNotification(); } void _registerDeepLinks() { _deepLinksManager = getBinding(); } void _continueUsingTheApp(Success authenticationViewStateSuccess) { super.handleSuccessViewState(authenticationViewStateSuccess); } PersonalAccount? _getPersonalAccountFromViewStateSuccess(Success success) { if (success is GetCredentialViewState) { return success.personalAccount; } else if (success is GetStoredTokenOidcSuccess) { return success.personalAccount; } return null; } void _handleLogOutAndSignInNewAccount({ required Success authenticationViewStateSuccess, required PersonalAccount personalAccount, required DeepLinkData deepLinkData, }) { if (authenticationViewStateSuccess is GetCredentialViewState) { setDataToInterceptors( baseUrl: authenticationViewStateSuccess.baseUrl.toString(), userName: authenticationViewStateSuccess.userName, password: authenticationViewStateSuccess.password, ); } else if (authenticationViewStateSuccess is GetStoredTokenOidcSuccess) { setDataToInterceptors( baseUrl: authenticationViewStateSuccess.baseUrl.toString(), tokenOIDC: authenticationViewStateSuccess.tokenOidc, oidcConfiguration: authenticationViewStateSuccess.oidcConfiguration, ); } _getSessionActionToLogOut( authenticationViewStateSuccess: authenticationViewStateSuccess, personalAccount: personalAccount, deepLinkData: deepLinkData, ); } Future _getSessionActionToLogOut({ required Success authenticationViewStateSuccess, required PersonalAccount personalAccount, required DeepLinkData deepLinkData, }) async { try { final sessionViewState = await getSessionInteractor.execute().last; sessionViewState.fold( (failure) => _handleGetSessionFailureToLogOut(authenticationViewStateSuccess), (success) => _handleGetSessionSuccessToLogOut( sessionViewStateSuccess: success, authenticationViewStateSuccess: authenticationViewStateSuccess, personalAccount: personalAccount, deepLinkData: deepLinkData, ), ); } catch (e) { logError('HomeController::_getSessionActionToLogOut:Exception = $e'); _handleGetSessionFailureToLogOut(authenticationViewStateSuccess); } } void _handleGetSessionSuccessToLogOut({ required Success sessionViewStateSuccess, required Success authenticationViewStateSuccess, required PersonalAccount personalAccount, required DeepLinkData deepLinkData, }) { if (sessionViewStateSuccess is GetSessionSuccess) { logoutToSignInNewAccount( session: sessionViewStateSuccess.session, accountId: personalAccount.accountId!, onFailureCallback: () => _continueUsingTheApp(authenticationViewStateSuccess), onSuccessCallback: () => _deepLinksManager?.autoSignInViaDeepLink( deepLinkData: deepLinkData, onFailureCallback: () => _continueUsingTheApp(authenticationViewStateSuccess), onSuccessCallback: _handleAutoSignInViaDeepLinkSuccess, ), ); } else { _continueUsingTheApp(authenticationViewStateSuccess); } } void _handleGetSessionFailureToLogOut(Success authenticationViewStateSuccess) { _continueUsingTheApp(authenticationViewStateSuccess); if (currentContext == null || currentOverlayContext == null) { return; } appToast.showToastErrorMessage( currentOverlayContext!, AppLocalizations.of(currentContext!).notFoundSession, ); } void _handleAutoSignInViaDeepLinkSuccess(AutoSignInViaDeepLinkSuccess success) { setDataToInterceptors( baseUrl: success.baseUri.toString(), tokenOIDC: success.tokenOIDC, oidcConfiguration: success.oidcConfiguration, ); getSessionAction(); } bool _validateToHandleDeepLinksNotSignedIn(Failure failure) { return PlatformInfo.isMobile && isNotSignedIn(failure) && _deepLinksManager != null; } bool _validateToHandleDeepLinksSignedIn(Success success) { final personalAccount = _getPersonalAccountFromViewStateSuccess(success); return PlatformInfo.isMobile && (success is GetCredentialViewState || success is GetStoredTokenOidcSuccess) && personalAccount != null && _deepLinksManager != null; } @override void handleFailureViewState(Failure failure) { if (_validateToHandleDeepLinksNotSignedIn(failure)) { _deepLinksManager!.handleDeepLinksWhenAppTerminatedNotSignedIn( onSuccessCallback: _handleAutoSignInViaDeepLinkSuccess, onFailureCallback: goToLogin, ); } else { super.handleFailureViewState(failure); } } @override void handleSuccessViewState(Success success) { if (_validateToHandleDeepLinksSignedIn(success)) { final personalAccount = _getPersonalAccountFromViewStateSuccess(success); _deepLinksManager!.handleDeepLinksWhenAppTerminatedSignedIn( username: personalAccount?.userName?.value, onFailureCallback: () => _continueUsingTheApp(success), onConfirmCallback: (deepLinkData) => _handleLogOutAndSignInNewAccount( authenticationViewStateSuccess: success, personalAccount: personalAccount!, deepLinkData: deepLinkData, ), ); } else { super.handleSuccessViewState(success); } } }