From 2dbf5aad49b3ce4d87a897b228dc2ea726bc92ec Mon Sep 17 00:00:00 2001 From: dab246 Date: Mon, 8 Jan 2024 19:41:34 +0700 Subject: [PATCH] TF-2431 Handle switch active account Signed-off-by: dab246 --- lib/features/base/base_controller.dart | 37 ++++++ .../reloadable/reloadable_controller.dart | 62 +++------- .../home/presentation/home_controller.dart | 108 ++++++++++++++++-- .../data/datasource/account_datasource.dart | 2 + .../hive_account_datasource_impl.dart | 7 ++ .../personal_account_extension.dart | 6 +- .../data/local/account_cache_manager.dart | 14 +++ .../repository/account_repository_impl.dart | 5 + .../domain/repository/account_repository.dart | 2 + ...dd_account_id_to_active_account_state.dart | 11 ++ .../set_current_account_active_state.dart | 11 ++ .../update_authentication_account_state.dart | 11 -- ...ount_id_to_active_account_interactor.dart} | 14 +-- ...set_current_account_active_interactor.dart | 22 ++++ .../login/presentation/login_controller.dart | 6 +- .../model/login_navigate_arguments.dart | 17 ++- .../model/login_navigate_type.dart | 3 +- .../mailbox_dashboard_controller.dart | 59 ++++++++-- .../restore_active_account_arguments.dart | 21 ++++ .../switch_active_account_arguments.dart | 24 ++++ .../twake_id/twake_id_controller.dart | 4 + .../presentation/twake_id/twake_id_view.dart | 3 +- .../presentation/thread_controller.dart | 24 +++- .../thread/presentation/thread_view.dart | 3 +- lib/l10n/intl_messages.arb | 8 +- .../credential/credential_bindings.dart | 6 +- lib/main/localizations/app_localizations.dart | 7 ++ .../utils/authenticated_account_manager.dart | 16 ++- 28 files changed, 416 insertions(+), 97 deletions(-) create mode 100644 lib/features/login/domain/state/add_account_id_to_active_account_state.dart create mode 100644 lib/features/login/domain/state/set_current_account_active_state.dart delete mode 100644 lib/features/login/domain/state/update_authentication_account_state.dart rename lib/features/login/domain/usecases/{update_authentication_account_interactor.dart => add_account_id_to_active_account_interactor.dart} (67%) create mode 100644 lib/features/login/domain/usecases/set_current_account_active_interactor.dart create mode 100644 lib/features/mailbox_dashboard/presentation/model/restore_active_account_arguments.dart create mode 100644 lib/features/mailbox_dashboard/presentation/model/switch_active_account_arguments.dart diff --git a/lib/features/base/base_controller.dart b/lib/features/base/base_controller.dart index 4750def6b1..ca74e347dc 100644 --- a/lib/features/base/base_controller.dart +++ b/lib/features/base/base_controller.dart @@ -25,11 +25,13 @@ import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_mixin.da import 'package:tmail_ui_user/features/base/mixin/popup_context_menu_action_mixin.dart'; import 'package:tmail_ui_user/features/caching/caching_manager.dart'; import 'package:tmail_ui_user/features/email/presentation/bindings/mdn_interactor_bindings.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extension.dart'; import 'package:tmail_ui_user/features/login/data/network/config/authorization_interceptors.dart'; import 'package:tmail_ui_user/features/login/domain/state/logout_basic_auth_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/logout_oidc_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/logout_state.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/logout_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/set_current_account_active_interactor.dart'; 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/mailbox_dashboard/presentation/bindings/contact_autocomplete_bindings.dart'; @@ -75,6 +77,7 @@ abstract class BaseController extends GetxController final ResponsiveUtils responsiveUtils = Get.find(); final Uuid uuid = Get.find(); final AppStore appStore = Get.find(); + final SetCurrentAccountActiveInteractor _setCurrentAccountActiveInteractor = Get.find(); final _fcmReceiver = FcmReceiver.instance; bool _isFcmEnabled = false; @@ -383,4 +386,38 @@ abstract class BaseController extends GetxController logError('BaseController::clearAllData: Exception: $e | Stack: $s'); } } + + void setUpInterceptors(PersonalAccount personalAccount) { + dynamicUrlInterceptors.setJmapUrl(personalAccount.baseUrl); + dynamicUrlInterceptors.changeBaseUrl(personalAccount.baseUrl); + + switch(personalAccount.authType) { + case AuthenticationType.oidc: + authorizationInterceptors.setTokenAndAuthorityOidc( + newToken: personalAccount.tokenOidc, + newConfig: personalAccount.tokenOidc!.oidcConfiguration + ); + authorizationIsolateInterceptors.setTokenAndAuthorityOidc( + newToken: personalAccount.tokenOidc, + newConfig: personalAccount.tokenOidc!.oidcConfiguration + ); + break; + case AuthenticationType.basic: + authorizationInterceptors.setBasicAuthorization( + personalAccount.basicAuth!.userName, + personalAccount.basicAuth!.password, + ); + authorizationIsolateInterceptors.setBasicAuthorization( + personalAccount.basicAuth!.userName, + personalAccount.basicAuth!.password, + ); + break; + default: + break; + } + } + + void setCurrentAccountActive(PersonalAccount activeAccount) { + consumeState(_setCurrentAccountActiveInteractor.execute(activeAccount)); + } } diff --git a/lib/features/base/reloadable/reloadable_controller.dart b/lib/features/base/reloadable/reloadable_controller.dart index fc00649c3c..21bb7d8909 100644 --- a/lib/features/base/reloadable/reloadable_controller.dart +++ b/lib/features/base/reloadable/reloadable_controller.dart @@ -5,17 +5,14 @@ import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/account_id.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/account/personal_account.dart'; import 'package:model/extensions/session_extension.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart'; import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart'; import 'package:tmail_ui_user/features/home/domain/usecases/get_session_interactor.dart'; -import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extension.dart'; import 'package:tmail_ui_user/features/login/domain/state/get_authenticated_account_state.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/add_account_id_to_active_account_interactor.dart'; 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/login/presentation/login_form_type.dart'; import 'package:tmail_ui_user/features/login/presentation/model/login_arguments.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; @@ -23,9 +20,9 @@ import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:tmail_ui_user/main/utils/message_toast_utils.dart'; abstract class ReloadableController extends BaseController { - final GetSessionInteractor _getSessionInteractor = Get.find(); + final GetSessionInteractor getSessionInteractor = Get.find(); final GetAuthenticatedAccountInteractor _getAuthenticatedAccountInteractor = Get.find(); - final UpdateAuthenticationAccountInteractor _updateAuthenticationAccountInteractor = Get.find(); + final AddAccountIdToActiveAccountInteractor _addAccountIdToActiveAccountInteractor = Get.find(); @override void handleFailureViewState(Failure failure) { @@ -68,38 +65,8 @@ abstract class ReloadableController extends BaseController { consumeState(_getAuthenticatedAccountInteractor.execute()); } - void setUpInterceptors(PersonalAccount personalAccount) { - dynamicUrlInterceptors.setJmapUrl(personalAccount.baseUrl); - dynamicUrlInterceptors.changeBaseUrl(personalAccount.baseUrl); - - switch(personalAccount.authType) { - case AuthenticationType.oidc: - authorizationInterceptors.setTokenAndAuthorityOidc( - newToken: personalAccount.tokenOidc, - newConfig: personalAccount.tokenOidc!.oidcConfiguration - ); - authorizationIsolateInterceptors.setTokenAndAuthorityOidc( - newToken: personalAccount.tokenOidc, - newConfig: personalAccount.tokenOidc!.oidcConfiguration - ); - break; - case AuthenticationType.basic: - authorizationInterceptors.setBasicAuthorization( - personalAccount.basicAuth!.userName, - personalAccount.basicAuth!.password, - ); - authorizationIsolateInterceptors.setBasicAuthorization( - personalAccount.basicAuth!.userName, - personalAccount.basicAuth!.password, - ); - break; - default: - break; - } - } - void getSessionAction({AccountId? accountId, UserName? userName}) { - consumeState(_getSessionInteractor.execute( + consumeState(getSessionInteractor.execute( accountId: accountId, userName: userName )); @@ -121,7 +88,11 @@ abstract class ReloadableController extends BaseController { final apiUrl = session.getQualifiedApiUrl(baseUrl: dynamicUrlInterceptors.jmapUrl); if (apiUrl.isNotEmpty) { dynamicUrlInterceptors.changeBaseUrl(apiUrl); - updateAuthenticationAccount(session, personalAccount.accountId, session.username); + _addAccountIdToActiveAccount( + personalAccount.accountId, + session.username, + apiUrl + ); handleReloaded(session); } else { clearDataAndGoToLoginPage(); @@ -130,10 +101,15 @@ abstract class ReloadableController extends BaseController { void handleReloaded(Session session) {} - void updateAuthenticationAccount(Session session, AccountId accountId, UserName userName) { - final apiUrl = session.getQualifiedApiUrl(baseUrl: dynamicUrlInterceptors.jmapUrl); - if (apiUrl.isNotEmpty) { - consumeState(_updateAuthenticationAccountInteractor.execute(accountId, apiUrl, userName)); - } + void _addAccountIdToActiveAccount( + AccountId accountId, + UserName userName, + String apiUrl, + ) { + consumeState(_addAccountIdToActiveAccountInteractor.execute( + accountId, + apiUrl, + userName + )); } } \ No newline at end of file diff --git a/lib/features/home/presentation/home_controller.dart b/lib/features/home/presentation/home_controller.dart index 149a6611c7..4cba794668 100644 --- a/lib/features/home/presentation/home_controller.dart +++ b/lib/features/home/presentation/home_controller.dart @@ -1,3 +1,6 @@ +import 'dart:async'; + +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'; @@ -21,7 +24,12 @@ import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_email_cac 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/get_session_state.dart'; +import 'package:tmail_ui_user/features/login/presentation/model/login_navigate_arguments.dart'; +import 'package:tmail_ui_user/features/login/presentation/model/login_navigate_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/preview_email_arguments.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/restore_active_account_arguments.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/switch_active_account_arguments.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/services/fcm_receiver.dart'; import 'package:tmail_ui_user/main/routes/app_routes.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; @@ -44,8 +52,8 @@ class HomeController extends ReloadableController { this._cleanupRecentLoginUsernameCacheInteractor, ); - PersonalAccount? currentAccount; EmailId? _emailIdPreview; + StreamSubscription? _sessionStreamSubscription; @override void onInit() { @@ -65,6 +73,12 @@ class HomeController extends ReloadableController { super.onReady(); } + @override + void onClose() { + _sessionStreamSubscription?.cancel(); + super.onClose(); + } + @override void handleReloaded(Session session) { if (_emailIdPreview != null) { @@ -84,20 +98,26 @@ class HomeController extends ReloadableController { } void _initFlutterDownloader() { - FlutterDownloader - .initialize(debug: kDebugMode) - .then((_) => FlutterDownloader.registerCallback(downloadCallback)); + if (!FlutterDownloader.initialized) { + FlutterDownloader + .initialize(debug: kDebugMode) + .then((_) => FlutterDownloader.registerCallback(downloadCallback)); + } } static void downloadCallback(String id, DownloadTaskStatus status, int progress) {} void _handleNavigateToScreen() async { if (PlatformInfo.isMobile) { - final firstTimeAppLaunch = await appStore.getItemBoolean(AppConfig.firstTimeAppLaunchKey); - if (firstTimeAppLaunch) { - await _cleanupCache(); + if (Get.arguments is LoginNavigateArguments) { + _handleLoginNavigateArguments(Get.arguments); } else { - _navigateToTwakeWelcomePage(); + final firstTimeAppLaunch = await appStore.getItemBoolean(AppConfig.firstTimeAppLaunchKey); + if (firstTimeAppLaunch) { + await _cleanupCache(); + } else { + _navigateToTwakeWelcomePage(); + } } } else { await _cleanupCache(); @@ -148,4 +168,76 @@ class HomeController extends ReloadableController { } } } + + void _handleLoginNavigateArguments(LoginNavigateArguments navigateArguments) async { + if (navigateArguments.navigateType == LoginNavigateType.switchActiveAccount) { + _switchActiveAccount( + navigateArguments.currentAccount!, + navigateArguments.sessionCurrentAccount!, + navigateArguments.nextActiveAccount!); + } else { + await _cleanupCache(); + } + } + + void _switchActiveAccount( + PersonalAccount currentActiveAccount, + Session sessionCurrentAccount, + PersonalAccount nextActiveAccount + ) { + setUpInterceptors(nextActiveAccount); + + _sessionStreamSubscription = getSessionInteractor.execute( + accountId: nextActiveAccount.accountId, + userName: nextActiveAccount.userName + ).listen( + (viewState) { + viewState.fold( + (failure) => _handleGetSessionFailureWhenSwitchActiveAccount( + currentActiveAccount: currentActiveAccount, + session: sessionCurrentAccount, + exception: failure), + (success) => success is GetSessionSuccess + ? _handleGetSessionSuccessWhenSwitchActiveAccount(nextActiveAccount, success.session) + : null, + ); + }, + onError: (error, stack) { + logError('HomeController::_switchActiveAccount:Exception: $error | Stack: $stack'); + _handleGetSessionFailureWhenSwitchActiveAccount( + currentActiveAccount: currentActiveAccount, + session: sessionCurrentAccount, + exception: error); + } + ); + } + + void _handleGetSessionSuccessWhenSwitchActiveAccount( + PersonalAccount nextActiveAccount, + Session sessionActiveAccount + ) async { + log('HomeController::_handleGetSessionSuccessWhenSwitchActiveAccount:sessionActiveAccount: $sessionActiveAccount'); + await popAndPush( + RouteUtils.generateNavigationRoute(AppRoutes.dashboard), + arguments: SwitchActiveAccountArguments( + session: sessionActiveAccount, + nextActiveAccount: nextActiveAccount, + ) + ); + } + + void _handleGetSessionFailureWhenSwitchActiveAccount({ + required PersonalAccount currentActiveAccount, + required Session session, + dynamic exception + }) async { + logError('HomeController::_handleGetSessionFailureWhenSwitchActiveAccount:exception: $exception'); + await popAndPush( + RouteUtils.generateNavigationRoute(AppRoutes.dashboard), + arguments: RestoreActiveAccountArguments( + currentAccount: currentActiveAccount, + session: session + ) + ); + } } \ No newline at end of file diff --git a/lib/features/login/data/datasource/account_datasource.dart b/lib/features/login/data/datasource/account_datasource.dart index 3a1870d4cc..0d704835bd 100644 --- a/lib/features/login/data/datasource/account_datasource.dart +++ b/lib/features/login/data/datasource/account_datasource.dart @@ -8,4 +8,6 @@ abstract class AccountDatasource { Future deleteCurrentAccount(String accountId); Future> getAllAccount(); + + Future setCurrentAccountActive(PersonalAccount activeAccount); } \ No newline at end of file diff --git a/lib/features/login/data/datasource_impl/hive_account_datasource_impl.dart b/lib/features/login/data/datasource_impl/hive_account_datasource_impl.dart index 162f0ebd00..135df9527a 100644 --- a/lib/features/login/data/datasource_impl/hive_account_datasource_impl.dart +++ b/lib/features/login/data/datasource_impl/hive_account_datasource_impl.dart @@ -48,4 +48,11 @@ class HiveAccountDatasourceImpl extends AccountDatasource { return await _accountCacheManager.getAllAccount(); }).catchError(_exceptionThrower.throwException); } + + @override + Future setCurrentAccountActive(PersonalAccount activeAccount) { + return Future.sync(() async { + return await _accountCacheManager.setCurrentAccountActive(activeAccount); + }).catchError(_exceptionThrower.throwException); + } } \ No newline at end of file diff --git a/lib/features/login/data/extensions/personal_account_extension.dart b/lib/features/login/data/extensions/personal_account_extension.dart index 0106ca2aa8..76fbd6fb4c 100644 --- a/lib/features/login/data/extensions/personal_account_extension.dart +++ b/lib/features/login/data/extensions/personal_account_extension.dart @@ -8,11 +8,11 @@ import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extensio import 'package:tmail_ui_user/features/login/data/model/account_cache.dart'; extension PersonalAccountExtension on PersonalAccount { - AccountCache toCache() { + AccountCache toCache({bool? isSelected}) { return AccountCache( id: id, authType: authType.name, - isSelected: isSelected, + isSelected: isSelected ?? this.isSelected, baseUrl: baseUrl, accountId: accountId?.id.value, apiUrl: apiUrl, @@ -36,7 +36,7 @@ extension PersonalAccountExtension on PersonalAccount { ); } - PersonalAccount updateAccountId({ + PersonalAccount addAccountId({ required AccountId accountId, required String apiUrl, required UserName userName, diff --git a/lib/features/login/data/local/account_cache_manager.dart b/lib/features/login/data/local/account_cache_manager.dart index 69b37baa48..8eb31b7990 100644 --- a/lib/features/login/data/local/account_cache_manager.dart +++ b/lib/features/login/data/local/account_cache_manager.dart @@ -57,4 +57,18 @@ class AccountCacheManager { throw NotFoundAuthenticatedAccountException(); } } + + Future setCurrentAccountActive(PersonalAccount activeAccount) async { + log('AccountCacheManager::setCurrentAccountActive(): $activeAccount'); + final newAccountCache = activeAccount.toCache(isSelected: true); + final allAccounts = await _accountCacheClient.getAll(); + log('AccountCacheManager::setCurrentAccountActive::allAccounts(): $allAccounts'); + if (allAccounts.isNotEmpty) { + final newAllAccounts = allAccounts.unselected().toList(); + if (newAllAccounts.isNotEmpty) { + await _accountCacheClient.updateMultipleItem(newAllAccounts.toMap()); + } + } + return _accountCacheClient.insertItem(newAccountCache.id, newAccountCache); + } } \ No newline at end of file diff --git a/lib/features/login/data/repository/account_repository_impl.dart b/lib/features/login/data/repository/account_repository_impl.dart index 4b6aceb8b1..6c496419ff 100644 --- a/lib/features/login/data/repository/account_repository_impl.dart +++ b/lib/features/login/data/repository/account_repository_impl.dart @@ -27,4 +27,9 @@ class AccountRepositoryImpl extends AccountRepository { Future> getAllAccount() { return _accountDatasource.getAllAccount(); } + + @override + Future setCurrentAccountActive(PersonalAccount activeAccount) { + return _accountDatasource.setCurrentAccountActive(activeAccount); + } } \ No newline at end of file diff --git a/lib/features/login/domain/repository/account_repository.dart b/lib/features/login/domain/repository/account_repository.dart index d5d9827f51..63bd3145d4 100644 --- a/lib/features/login/domain/repository/account_repository.dart +++ b/lib/features/login/domain/repository/account_repository.dart @@ -9,4 +9,6 @@ abstract class AccountRepository { Future deleteCurrentAccount(String hashId); Future> getAllAccount(); + + Future setCurrentAccountActive(PersonalAccount activeAccount); } \ No newline at end of file diff --git a/lib/features/login/domain/state/add_account_id_to_active_account_state.dart b/lib/features/login/domain/state/add_account_id_to_active_account_state.dart new file mode 100644 index 0000000000..1dfde78274 --- /dev/null +++ b/lib/features/login/domain/state/add_account_id_to_active_account_state.dart @@ -0,0 +1,11 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; + +class AddAccountIdToActiveAccountLoading extends LoadingState {} + +class AddAccountIdToActiveAccountSuccess extends UIState {} + +class AddAccountIdToActiveAccountFailure extends FeatureFailure { + + AddAccountIdToActiveAccountFailure(dynamic exception) : super(exception: exception); +} \ No newline at end of file diff --git a/lib/features/login/domain/state/set_current_account_active_state.dart b/lib/features/login/domain/state/set_current_account_active_state.dart new file mode 100644 index 0000000000..dbdc1d7aad --- /dev/null +++ b/lib/features/login/domain/state/set_current_account_active_state.dart @@ -0,0 +1,11 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; + +class SetCurrentAccountActiveLoading extends LoadingState {} + +class SetCurrentAccountActiveSuccess extends UIState {} + +class SetCurrentAccountActiveFailure extends FeatureFailure { + + SetCurrentAccountActiveFailure(dynamic exception) : super(exception: exception); +} \ No newline at end of file diff --git a/lib/features/login/domain/state/update_authentication_account_state.dart b/lib/features/login/domain/state/update_authentication_account_state.dart deleted file mode 100644 index c5d1931edf..0000000000 --- a/lib/features/login/domain/state/update_authentication_account_state.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; - -class UpdateAuthenticationAccountLoading extends LoadingState {} - -class UpdateAuthenticationAccountSuccess extends UIState {} - -class UpdateAuthenticationAccountFailure extends FeatureFailure { - - UpdateAuthenticationAccountFailure(dynamic exception) : super(exception: exception); -} \ No newline at end of file diff --git a/lib/features/login/domain/usecases/update_authentication_account_interactor.dart b/lib/features/login/domain/usecases/add_account_id_to_active_account_interactor.dart similarity index 67% rename from lib/features/login/domain/usecases/update_authentication_account_interactor.dart rename to lib/features/login/domain/usecases/add_account_id_to_active_account_interactor.dart index b7e0df51d4..bdcf085ed7 100644 --- a/lib/features/login/domain/usecases/update_authentication_account_interactor.dart +++ b/lib/features/login/domain/usecases/add_account_id_to_active_account_interactor.dart @@ -5,27 +5,27 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:tmail_ui_user/features/login/data/extensions/personal_account_extension.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/state/update_authentication_account_state.dart'; +import 'package:tmail_ui_user/features/login/domain/state/add_account_id_to_active_account_state.dart'; -class UpdateAuthenticationAccountInteractor { +class AddAccountIdToActiveAccountInteractor { final AccountRepository _accountRepository; - UpdateAuthenticationAccountInteractor(this._accountRepository); + AddAccountIdToActiveAccountInteractor(this._accountRepository); Stream> execute(AccountId accountId, String apiUrl, UserName userName) async* { try{ - yield Right(UpdateAuthenticationAccountLoading()); + yield Right(AddAccountIdToActiveAccountLoading()); final currentAccount = await _accountRepository.getCurrentAccount(); await _accountRepository.setCurrentAccount( - currentAccount.updateAccountId( + currentAccount.addAccountId( accountId: accountId, apiUrl: apiUrl, userName: userName ) ); - yield Right(UpdateAuthenticationAccountSuccess()); + yield Right(AddAccountIdToActiveAccountSuccess()); } catch(e) { - yield Left(UpdateAuthenticationAccountFailure(e)); + yield Left(AddAccountIdToActiveAccountFailure(e)); } } } \ No newline at end of file diff --git a/lib/features/login/domain/usecases/set_current_account_active_interactor.dart b/lib/features/login/domain/usecases/set_current_account_active_interactor.dart new file mode 100644 index 0000000000..3c64f0b39b --- /dev/null +++ b/lib/features/login/domain/usecases/set_current_account_active_interactor.dart @@ -0,0 +1,22 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:model/account/personal_account.dart'; +import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; +import 'package:tmail_ui_user/features/login/domain/state/set_current_account_active_state.dart'; + +class SetCurrentAccountActiveInteractor { + final AccountRepository _accountRepository; + + SetCurrentAccountActiveInteractor(this._accountRepository); + + Stream> execute(PersonalAccount activeAccount) async* { + try{ + yield Right(SetCurrentAccountActiveLoading()); + await _accountRepository.setCurrentAccountActive(activeAccount); + yield Right(SetCurrentAccountActiveSuccess()); + } catch(e) { + yield Left(SetCurrentAccountActiveFailure(e)); + } + } +} \ No newline at end of file diff --git a/lib/features/login/presentation/login_controller.dart b/lib/features/login/presentation/login_controller.dart index eeb0d05915..cd39af3cce 100644 --- a/lib/features/login/presentation/login_controller.dart +++ b/lib/features/login/presentation/login_controller.dart @@ -230,7 +230,11 @@ class LoginController extends ReloadableController { switch(loginFormType.value) { case LoginFormType.dnsLookupForm: case LoginFormType.baseUrlForm: - navigateToTwakeIdPage(); + if (PlatformInfo.isMobile && _isAddAnotherAccount) { + popBack(); + } else { + navigateToTwakeIdPage(); + } break; case LoginFormType.passwordForm: _password = null; diff --git a/lib/features/login/presentation/model/login_navigate_arguments.dart b/lib/features/login/presentation/model/login_navigate_arguments.dart index 0c7026155e..5d2509d601 100644 --- a/lib/features/login/presentation/model/login_navigate_arguments.dart +++ b/lib/features/login/presentation/model/login_navigate_arguments.dart @@ -1,4 +1,5 @@ +import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:model/account/personal_account.dart'; import 'package:tmail_ui_user/features/login/presentation/model/login_navigate_type.dart'; import 'package:tmail_ui_user/main/routes/router_arguments.dart'; @@ -7,9 +8,21 @@ class LoginNavigateArguments extends RouterArguments { final LoginNavigateType navigateType; final PersonalAccount? currentAccount; + final Session? sessionCurrentAccount; + final PersonalAccount? nextActiveAccount; - LoginNavigateArguments(this.navigateType, this.currentAccount); + LoginNavigateArguments({ + required this.navigateType, + this.currentAccount, + this.sessionCurrentAccount, + this.nextActiveAccount, + }); @override - List get props => [navigateType, currentAccount]; + List get props => [ + navigateType, + currentAccount, + sessionCurrentAccount, + nextActiveAccount, + ]; } \ No newline at end of file diff --git a/lib/features/login/presentation/model/login_navigate_type.dart b/lib/features/login/presentation/model/login_navigate_type.dart index b29a26df15..e110545742 100644 --- a/lib/features/login/presentation/model/login_navigate_type.dart +++ b/lib/features/login/presentation/model/login_navigate_type.dart @@ -1,5 +1,6 @@ enum LoginNavigateType { signIn, - addAnotherAccount; + addAnotherAccount, + switchActiveAccount; } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart index 5335598cc9..944d7aa863 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -87,9 +87,11 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/down import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/draggable_app_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/preview_email_arguments.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/refresh_action_view_event.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/restore_active_account_arguments.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/switch_active_account_arguments.dart'; import 'package:tmail_ui_user/features/mailto/presentation/model/mailto_arguments.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_vacation_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/update_vacation_state.dart'; @@ -467,6 +469,10 @@ class MailboxDashBoardController extends ReloadableController { _handleMailtoURL(arguments); } else if (arguments is PreviewEmailArguments) { _handleOpenEmailAction(arguments); + } else if (arguments is SwitchActiveAccountArguments) { + _handleSwitchActiveAccountAction(arguments); + } else if (arguments is RestoreActiveAccountArguments) { + _handleRestorePreviousActiveAccountAction(arguments); } else { dispatchRoute(DashboardRoutes.thread); reload(); @@ -497,7 +503,7 @@ class MailboxDashBoardController extends ReloadableController { void _handleSession(Session session) { log('MailboxDashBoardController::_handleSession:'); - _setUpComponentsFromSession(session); + _setUpComponentsFromSession(session, saveSession: true); if (PlatformInfo.isMobile && !_notificationManager.isNotificationClickedOnTerminate) { _handleClickLocalNotificationOnTerminated(); @@ -506,7 +512,7 @@ class MailboxDashBoardController extends ReloadableController { } } - void _setUpComponentsFromSession(Session session) { + void _setUpComponentsFromSession(Session session, {bool saveSession = false}) { sessionCurrent = session; accountId.value = sessionCurrent!.personalAccount.accountId; userProfile.value = UserProfile(sessionCurrent!.username.value); @@ -521,11 +527,14 @@ class MailboxDashBoardController extends ReloadableController { if (PlatformInfo.isMobile) { getAllSendingEmails(); - _storeSessionAction( - sessionCurrent!, - accountId.value!, - sessionCurrent!.username - ); + + if (saveSession) { + _storeSessionAction( + sessionCurrent!, + accountId.value!, + sessionCurrent!.username + ); + } } } @@ -1287,7 +1296,7 @@ class MailboxDashBoardController extends ReloadableController { void handleReloaded(Session session) { log('MailboxDashBoardController::handleReloaded():'); _getRouteParameters(); - _setUpComponentsFromSession(session); + _setUpComponentsFromSession(session, saveSession: true); if (PlatformInfo.isWeb) { _handleComposerCache(); } @@ -2425,6 +2434,40 @@ class MailboxDashBoardController extends ReloadableController { } } + void _handleSwitchActiveAccountAction(SwitchActiveAccountArguments arguments) { + log('MailboxDashBoardController::_handleSwitchActiveAccountAction:arguments: $arguments'); + dispatchRoute(DashboardRoutes.waiting); + + setCurrentAccountActive(arguments.nextActiveAccount!); + + dynamicUrlInterceptors.changeBaseUrl(arguments.nextActiveAccount!.apiUrl); + + _setUpComponentsFromSession(arguments.session); + + dispatchRoute(DashboardRoutes.thread); + } + + void _handleRestorePreviousActiveAccountAction(RestoreActiveAccountArguments arguments) { + log('MailboxDashBoardController::_handleRestorePreviousActiveAccountAction:arguments: $arguments'); + + if (currentContext != null && currentOverlayContext != null) { + appToast.showToastErrorMessage( + currentOverlayContext!, + AppLocalizations.of(currentContext!).toastMessageFailureWhenSwitchActiveAccount); + } + + dispatchRoute(DashboardRoutes.waiting); + + setCurrentAccountActive(arguments.currentAccount); + + dynamicUrlInterceptors.changeBaseUrl(arguments.currentAccount.apiUrl); + + _setUpComponentsFromSession(arguments.session); + + dispatchRoute(DashboardRoutes.thread); + + } + @override void onClose() { _emailReceiveManager.closeEmailReceiveManagerStream(); diff --git a/lib/features/mailbox_dashboard/presentation/model/restore_active_account_arguments.dart b/lib/features/mailbox_dashboard/presentation/model/restore_active_account_arguments.dart new file mode 100644 index 0000000000..0ea75a2aa4 --- /dev/null +++ b/lib/features/mailbox_dashboard/presentation/model/restore_active_account_arguments.dart @@ -0,0 +1,21 @@ + +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:model/account/personal_account.dart'; +import 'package:tmail_ui_user/main/routes/router_arguments.dart'; + +class RestoreActiveAccountArguments extends RouterArguments { + + final PersonalAccount currentAccount; + final Session session; + + RestoreActiveAccountArguments({ + required this.currentAccount, + required this.session, + }); + + @override + List get props => [ + currentAccount, + session, + ]; +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/model/switch_active_account_arguments.dart b/lib/features/mailbox_dashboard/presentation/model/switch_active_account_arguments.dart new file mode 100644 index 0000000000..94e18673f5 --- /dev/null +++ b/lib/features/mailbox_dashboard/presentation/model/switch_active_account_arguments.dart @@ -0,0 +1,24 @@ + +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:model/account/personal_account.dart'; +import 'package:tmail_ui_user/main/routes/router_arguments.dart'; + +class SwitchActiveAccountArguments extends RouterArguments { + + final Session session; + final PersonalAccount? currentAccount; + final PersonalAccount? nextActiveAccount; + + SwitchActiveAccountArguments({ + required this.session, + this.currentAccount, + this.nextActiveAccount, + }); + + @override + List get props => [ + session, + currentAccount, + nextActiveAccount, + ]; +} \ No newline at end of file diff --git a/lib/features/starting_page/presentation/twake_id/twake_id_controller.dart b/lib/features/starting_page/presentation/twake_id/twake_id_controller.dart index f2779f10ad..8df1df2cbc 100644 --- a/lib/features/starting_page/presentation/twake_id/twake_id_controller.dart +++ b/lib/features/starting_page/presentation/twake_id/twake_id_controller.dart @@ -32,6 +32,10 @@ class TwakeIdController extends GetxController { } } + void backToHomeView() { + popAndPush(AppRoutes.home); + } + bool get isAddAnotherAccount => navigateArguments.value != null && navigateArguments.value?.navigateType == LoginNavigateType.addAnotherAccount; } \ No newline at end of file diff --git a/lib/features/starting_page/presentation/twake_id/twake_id_view.dart b/lib/features/starting_page/presentation/twake_id/twake_id_view.dart index cd67e54bcc..46bfe81cfa 100644 --- a/lib/features/starting_page/presentation/twake_id/twake_id_view.dart +++ b/lib/features/starting_page/presentation/twake_id/twake_id_view.dart @@ -5,7 +5,6 @@ import 'package:get/get.dart'; import 'package:linagora_design_flutter/linagora_design_flutter.dart'; import 'package:tmail_ui_user/features/starting_page/presentation/twake_id/twake_id_controller.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -import 'package:tmail_ui_user/main/routes/route_navigation.dart'; class TwakeIdView extends GetWidget { @@ -35,7 +34,7 @@ class TwakeIdView extends GetWidget { backgroundColor: Colors.transparent, margin: const EdgeInsetsDirectional.only(start: 8), iconColor: Colors.black, - onTapActionCallback: popBack) + onTapActionCallback: controller.backToHomeView) : null, ); }); diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index b2ccd6f931..cf93a8a53a 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -1246,12 +1246,28 @@ class ThreadController extends BaseController with EmailActionController { mailboxDashBoardController.clearDashBoardAction(); } - void addAnotherAccount(PersonalAccount? currentAccount) { - push( + void addAnotherAccount(PersonalAccount? currentAccount) async { + final result = await popAndPush( AppRoutes.twakeId, arguments: LoginNavigateArguments( - LoginNavigateType.addAnotherAccount, - currentAccount + navigateType: LoginNavigateType.addAnotherAccount, + currentAccount: currentAccount + ) + ); + log('ThreadController::addAnotherAccount:result: $result'); + } + + void switchActiveAccount( + PersonalAccount currentActiveAccount, + PersonalAccount nextActiveAccount + ) async { + await popAndPush( + AppRoutes.home, + arguments: LoginNavigateArguments( + navigateType: LoginNavigateType.switchActiveAccount, + currentAccount: currentActiveAccount, + sessionCurrentAccount: mailboxDashBoardController.sessionCurrent, + nextActiveAccount: nextActiveAccount, ) ); } diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index 7d4c78719c..c82d6f7e17 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -105,7 +105,8 @@ class ThreadView extends GetWidget await controller.authenticatedAccountManager.showAccountsBottomSheetModal( context: context, onGoToManageAccount: controller.mailboxDashBoardController.goToSettings, - onAddAnotherAccountAction: controller.addAnotherAccount + onAddAnotherAccountAction: controller.addAnotherAccount, + onSwitchActiveAccountAction: controller.switchActiveAccount ); }, ); diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 138830b6eb..103998a4b8 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2024-01-05T19:34:13.578280", + "@@last_modified": "2024-01-08T14:23:35.609263", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -3593,5 +3593,11 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "toastMessageFailureWhenSwitchActiveAccount": "Switch active account failure", + "@toastMessageFailureWhenSwitchActiveAccount": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/lib/main/bindings/credential/credential_bindings.dart b/lib/main/bindings/credential/credential_bindings.dart index 9438b71c1e..55aa972c9c 100644 --- a/lib/main/bindings/credential/credential_bindings.dart +++ b/lib/main/bindings/credential/credential_bindings.dart @@ -15,13 +15,14 @@ import 'package:tmail_ui_user/features/login/data/repository/authentication_repo import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_repository.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/add_account_id_to_active_account_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/authentication_user_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_all_authenticated_account_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/logout_basic_auth_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/logout_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/logout_oidc_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/update_authentication_account_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/set_current_account_active_interactor.dart'; import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart'; import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart'; import 'package:tmail_ui_user/main/utils/ios_sharing_manager.dart'; @@ -43,7 +44,8 @@ class CredentialBindings extends InteractorsBindings { Get.find(), Get.find() )); - Get.put(UpdateAuthenticationAccountInteractor(Get.find())); + Get.put(AddAccountIdToActiveAccountInteractor(Get.find())); + Get.put(SetCurrentAccountActiveInteractor(Get.find())); } @override diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index 85724048dd..deb5e00b32 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -3737,4 +3737,11 @@ class AppLocalizations { name: 'addAnotherAccount', ); } + + String get toastMessageFailureWhenSwitchActiveAccount { + return Intl.message( + 'Switch active account failure', + name: 'toastMessageFailureWhenSwitchActiveAccount', + ); + } } \ No newline at end of file diff --git a/lib/main/utils/authenticated_account_manager.dart b/lib/main/utils/authenticated_account_manager.dart index c8bd6f1a17..bd5d32af2a 100644 --- a/lib/main/utils/authenticated_account_manager.dart +++ b/lib/main/utils/authenticated_account_manager.dart @@ -18,6 +18,9 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; typedef OnAddAnotherAccountAction = Function(PersonalAccount? currentAccount); +typedef OnSwitchActiveAccountAction = Function( + PersonalAccount currentActiveAccount, + PersonalAccount nextActiveAccount); class AuthenticatedAccountManager { @@ -78,9 +81,10 @@ class AuthenticatedAccountManager { required BuildContext context, VoidCallback? onGoToManageAccount, OnAddAnotherAccountAction? onAddAnotherAccountAction, + OnSwitchActiveAccountAction? onSwitchActiveAccountAction, }) async { final listPresentationAccount = await _getAllTwakeMailPresentationAccount(); - final activeAccount = listPresentationAccount + final currentActiveAccount = listPresentationAccount .firstWhereOrNull((presentationAccount) => presentationAccount.isActive); if (context.mounted) { @@ -119,9 +123,15 @@ class AuthenticatedAccountManager { titleAccountSettingsStyle: Theme.of(context).textTheme.labelLarge!.copyWith( color: LinagoraSysColors.material().primary, ), - onAddAnotherAccount: () => onAddAnotherAccountAction?.call(activeAccount?.personalAccount), + onAddAnotherAccount: () => onAddAnotherAccountAction?.call(currentActiveAccount?.personalAccount), onGoToAccountSettings: () => onGoToManageAccount?.call(), - onSetAccountAsActive: (presentationAccount) {}, + onSetAccountAsActive: (presentationAccount) { + if (presentationAccount is TwakeMailPresentationAccount) { + onSwitchActiveAccountAction?.call( + currentActiveAccount!.personalAccount, + presentationAccount.personalAccount); + } + }, ); } }