From 424ab675d5041b8bb2331fe37ac41f2cc6d17fd4 Mon Sep 17 00:00:00 2001 From: dab246 Date: Mon, 23 Oct 2023 23:19:12 +0700 Subject: [PATCH] TF-2177 Fix download attachment not working on web Signed-off-by: dab246 (cherry picked from commit 09cbfe5245c206670aa2191c151fd8f2ea48df3a) --- .../download_attachment_for_web_state.dart | 6 ++- ...ownload_attachment_for_web_interactor.dart | 53 +++++++++---------- .../download_attachments_interactor.dart | 40 ++++++-------- .../export_attachment_interactor.dart | 33 +++++------- .../controller/single_email_controller.dart | 14 +++-- .../hive_account_datasource_impl.dart | 6 +-- .../data/local/account_cache_manager.dart | 30 ++++++----- .../authentication_info_cache_manager.dart | 10 +++- .../config/authorization_interceptors.dart | 17 +++--- .../repository/account_repository_impl.dart | 6 +-- .../credential_repository_impl.dart | 2 +- .../exceptions/authentication_exception.dart | 4 +- .../domain/repository/account_repository.dart | 2 +- .../repository/credential_repository.dart | 2 +- .../hive_session_datasource_impl.dart | 2 +- .../data/exceptions/session_exceptions.dart | 2 +- model/lib/account/account_request.dart | 18 +++++++ 17 files changed, 134 insertions(+), 113 deletions(-) diff --git a/lib/features/email/domain/state/download_attachment_for_web_state.dart b/lib/features/email/domain/state/download_attachment_for_web_state.dart index f6822d887..844ad20f4 100644 --- a/lib/features/email/domain/state/download_attachment_for_web_state.dart +++ b/lib/features/email/domain/state/download_attachment_for_web_state.dart @@ -55,9 +55,11 @@ class DownloadAttachmentForWebSuccess extends UIState { class DownloadAttachmentForWebFailure extends FeatureFailure { - final DownloadTaskId taskId; + final DownloadTaskId? taskId; - DownloadAttachmentForWebFailure(this.taskId, dynamic exception) : super(exception: exception); + DownloadAttachmentForWebFailure({ + this.taskId, dynamic exception + }) : super(exception: exception); @override List get props => [taskId, ...super.props]; diff --git a/lib/features/email/domain/usecases/download_attachment_for_web_interactor.dart b/lib/features/email/domain/usecases/download_attachment_for_web_interactor.dart index eb113fd5f..9ae6b8c50 100644 --- a/lib/features/email/domain/usecases/download_attachment_for_web_interactor.dart +++ b/lib/features/email/domain/usecases/download_attachment_for_web_interactor.dart @@ -45,39 +45,38 @@ class DownloadAttachmentForWebInteractor { if (currentAccount.authenticationType == AuthenticationType.oidc) { final tokenOidc = await _authenticationOIDCRepository.getStoredTokenOIDC(currentAccount.id); - accountRequest = AccountRequest( - token: tokenOidc.toToken(), - authenticationType: AuthenticationType.oidc); + accountRequest = AccountRequest.withOidc(token: tokenOidc.toToken()); } else { final authenticationInfoCache = await credentialRepository.getAuthenticationInfoStored(); - if (authenticationInfoCache != null) { - accountRequest = AccountRequest( - userName: UserName(authenticationInfoCache.username), - password: Password(authenticationInfoCache.password), - authenticationType: AuthenticationType.basic); - } + accountRequest = AccountRequest.withBasic( + userName: UserName(authenticationInfoCache.username), + password: Password(authenticationInfoCache.password), + ); } - if (accountRequest != null) { - final bytesDownloaded = await emailRepository.downloadAttachmentForWeb( - taskId, - attachment, - accountId, - baseDownloadUrl, - accountRequest, - onReceiveController); + final bytesDownloaded = await emailRepository.downloadAttachmentForWeb( + taskId, + attachment, + accountId, + baseDownloadUrl, + accountRequest, + onReceiveController + ); - yield Right(DownloadAttachmentForWebSuccess( - taskId, - attachment, - bytesDownloaded)); - } else { - yield Left(DownloadAttachmentForWebFailure(taskId, null)); - } - } catch (exception) { - yield Left(DownloadAttachmentForWebFailure( + yield Right( + DownloadAttachmentForWebSuccess( taskId, - exception)); + attachment, + bytesDownloaded + ) + ); + } catch (exception) { + yield Left( + DownloadAttachmentForWebFailure( + taskId: taskId, + exception: exception + ) + ); } } } \ No newline at end of file diff --git a/lib/features/email/domain/usecases/download_attachments_interactor.dart b/lib/features/email/domain/usecases/download_attachments_interactor.dart index e20056d7d..955102457 100644 --- a/lib/features/email/domain/usecases/download_attachments_interactor.dart +++ b/lib/features/email/domain/usecases/download_attachments_interactor.dart @@ -46,32 +46,25 @@ class DownloadAttachmentsInteractor { if (currentAccount.authenticationType == AuthenticationType.oidc) { final tokenOidc = await _authenticationOIDCRepository.getStoredTokenOIDC(currentAccount.id); - accountRequest = AccountRequest( - token: tokenOidc.toToken(), - authenticationType: AuthenticationType.oidc); + accountRequest = AccountRequest.withOidc(token: tokenOidc.toToken()); } else { final authenticationInfoCache = await credentialRepository.getAuthenticationInfoStored(); - if (authenticationInfoCache != null) { - accountRequest = AccountRequest( - userName: UserName(authenticationInfoCache.username), - password: Password(authenticationInfoCache.password), - authenticationType: AuthenticationType.basic); - } + accountRequest = AccountRequest.withBasic( + userName: UserName(authenticationInfoCache.username), + password: Password(authenticationInfoCache.password), + ); } - if (accountRequest != null) { - final taskIds = await emailRepository.downloadAttachments( - attachments, - accountId, - baseDownloadUrl, - accountRequest); + final taskIds = await emailRepository.downloadAttachments( + attachments, + accountId, + baseDownloadUrl, + accountRequest + ); - yield Right(DownloadAttachmentsSuccess(taskIds)); - } else { - yield Left(DownloadAttachmentsFailure(null)); - } + yield Right(DownloadAttachmentsSuccess(taskIds)); } catch (exception) { - log('DownloadAttachmentsInteractor::execute(): $exception'); + logError('DownloadAttachmentsInteractor::execute(): $exception'); if (exception is DownloadAttachmentHasTokenExpiredException && exception.refreshToken.isNotEmpty) { yield* _retryDownloadAttachments( @@ -101,9 +94,10 @@ class DownloadAttachmentsInteractor { oidcConfig.scopes, refreshToken); + await _accountRepository.deleteCurrentAccount(accountCurrent.id); + await Future.wait([ _authenticationOIDCRepository.persistTokenOIDC(newTokenOIDC), - _accountRepository.deleteCurrentAccount(accountCurrent.id), _accountRepository.setCurrentAccount(PersonalAccount( newTokenOIDC.tokenIdHash, AuthenticationType.oidc, @@ -117,9 +111,7 @@ class DownloadAttachmentsInteractor { newToken: newTokenOIDC.toToken(), newConfig: oidcConfig); - final accountRequest = AccountRequest( - token: newTokenOIDC.toToken(), - authenticationType: AuthenticationType.oidc); + final accountRequest = AccountRequest.withOidc(token: newTokenOIDC.toToken()); final taskIds = await emailRepository.downloadAttachments( attachments, diff --git a/lib/features/email/domain/usecases/export_attachment_interactor.dart b/lib/features/email/domain/usecases/export_attachment_interactor.dart index 2696d30d7..20192f8f9 100644 --- a/lib/features/email/domain/usecases/export_attachment_interactor.dart +++ b/lib/features/email/domain/usecases/export_attachment_interactor.dart @@ -38,31 +38,24 @@ class ExportAttachmentInteractor { if (currentAccount.authenticationType == AuthenticationType.oidc) { final tokenOidc = await _authenticationOIDCRepository.getStoredTokenOIDC(currentAccount.id); - accountRequest = AccountRequest( - token: tokenOidc.toToken(), - authenticationType: AuthenticationType.oidc); + accountRequest = AccountRequest.withOidc(token: tokenOidc.toToken()); } else { final authenticationInfoCache = await credentialRepository.getAuthenticationInfoStored(); - if (authenticationInfoCache != null) { - accountRequest = AccountRequest( - userName: UserName(authenticationInfoCache.username), - password: Password(authenticationInfoCache.password), - authenticationType: AuthenticationType.basic); - } + accountRequest = AccountRequest.withBasic( + userName: UserName(authenticationInfoCache.username), + password: Password(authenticationInfoCache.password), + ); } - if (accountRequest != null) { - final downloadedResponse = await emailRepository.exportAttachment( - attachment, - accountId, - baseDownloadUrl, - accountRequest, - cancelToken); + final downloadedResponse = await emailRepository.exportAttachment( + attachment, + accountId, + baseDownloadUrl, + accountRequest, + cancelToken + ); - yield Right(ExportAttachmentSuccess(downloadedResponse)); - } else { - yield Left(ExportAttachmentFailure(null)); - } + yield Right(ExportAttachmentSuccess(downloadedResponse)); } catch (exception) { log('ExportAttachmentInteractor::execute(): exception: $exception'); yield Left(ExportAttachmentFailure(exception)); diff --git a/lib/features/email/presentation/controller/single_email_controller.dart b/lib/features/email/presentation/controller/single_email_controller.dart index cec64f641..30e5332a3 100644 --- a/lib/features/email/presentation/controller/single_email_controller.dart +++ b/lib/features/email/presentation/controller/single_email_controller.dart @@ -74,6 +74,7 @@ import 'package:tmail_ui_user/features/manage_account/domain/usecases/create_new import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/extensions/datetime_extension.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rules_filter_creator_arguments.dart'; +import 'package:tmail_ui_user/features/session/data/exceptions/session_exceptions.dart'; import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart'; import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_type.dart'; import 'package:tmail_ui_user/main/error/capability_validator.dart'; @@ -698,8 +699,9 @@ class SingleEmailController extends BaseController with AppLoaderMixin { void _downloadAttachmentForWebAction(BuildContext context, Attachment attachment) async { final accountId = mailboxDashBoardController.accountId.value; - if (accountId != null && mailboxDashBoardController.sessionCurrent != null) { - final baseDownloadUrl = mailboxDashBoardController.sessionCurrent!.getDownloadUrl(jmapUrl: _dynamicUrlInterceptors.jmapUrl); + final session = mailboxDashBoardController.sessionCurrent; + if (accountId != null && session != null) { + final baseDownloadUrl = session.getDownloadUrl(jmapUrl: _dynamicUrlInterceptors.jmapUrl); final generateTaskId = DownloadTaskId(_uuid.v4()); consumeState(_downloadAttachmentForWebInteractor.execute( generateTaskId, @@ -707,6 +709,10 @@ class SingleEmailController extends BaseController with AppLoaderMixin { accountId, baseDownloadUrl, _downloadProgressStateController)); + } else { + consumeState(Stream.value( + Left(DownloadAttachmentForWebFailure(exception: NotFoundSessionException())) + )); } } @@ -721,7 +727,9 @@ class SingleEmailController extends BaseController with AppLoaderMixin { void _downloadAttachmentForWebFailureAction(DownloadAttachmentForWebFailure failure) { log('SingleEmailController::_downloadAttachmentForWebFailureAction(): $failure'); - mailboxDashBoardController.deleteDownloadTask(failure.taskId); + if (failure.taskId != null) { + mailboxDashBoardController.deleteDownloadTask(failure.taskId!); + } if (currentOverlayContext != null && currentContext != null) { _appToast.showToastErrorMessage( 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 e5cc4ef61..5350bc49b 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 @@ -13,21 +13,21 @@ class HiveAccountDatasourceImpl extends AccountDatasource { @override Future getCurrentAccount() { return Future.sync(() async { - return await _accountCacheManager.getSelectedAccount(); + return await _accountCacheManager.getCurrentAccount(); }).catchError(_exceptionThrower.throwException); } @override Future setCurrentAccount(PersonalAccount newCurrentAccount) { return Future.sync(() async { - return await _accountCacheManager.setSelectedAccount(newCurrentAccount); + return await _accountCacheManager.setCurrentAccount(newCurrentAccount); }).catchError(_exceptionThrower.throwException); } @override Future deleteCurrentAccount(String accountId) { return Future.sync(() async { - return await _accountCacheManager.deleteSelectedAccount(accountId); + return await _accountCacheManager.deleteCurrentAccount(accountId); }).catchError(_exceptionThrower.throwException); } } \ No newline at end of file diff --git a/lib/features/login/data/local/account_cache_manager.dart b/lib/features/login/data/local/account_cache_manager.dart index a68830980..e6515644d 100644 --- a/lib/features/login/data/local/account_cache_manager.dart +++ b/lib/features/login/data/local/account_cache_manager.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:core/utils/app_logger.dart'; import 'package:model/account/personal_account.dart'; import 'package:tmail_ui_user/features/caching/clients/account_cache_client.dart'; @@ -10,24 +11,29 @@ class AccountCacheManager { AccountCacheManager(this._accountCacheClient); - Future getSelectedAccount() async { - try { - final allAccounts = await _accountCacheClient.getAll(); - return allAccounts.firstWhere((account) => account.isSelected) - .toAccount(); - } catch (e) { - logError('AccountCacheManager::getSelectedAccount(): $e'); + Future getCurrentAccount() async { + final allAccounts = await _accountCacheClient.getAll(); + log('AccountCacheManager::getCurrentAccount::allAccounts(): $allAccounts'); + final accountCache = allAccounts.firstWhereOrNull((account) => account.isSelected); + log('AccountCacheManager::getCurrentAccount::accountCache(): $accountCache'); + if (accountCache != null) { + return accountCache.toAccount(); + } else { throw NotFoundAuthenticatedAccountException(); } } - Future setSelectedAccount(PersonalAccount account) { - log('AccountCacheManager::setSelectedAccount(): $_accountCacheClient'); + Future setCurrentAccount(PersonalAccount account) async { + log('AccountCacheManager::setCurrentAccount(): $account'); + final accountCacheExist = await _accountCacheClient.isExistTable(); + if (accountCacheExist) { + await _accountCacheClient.clearAllData(); + } return _accountCacheClient.insertItem(account.id, account.toCache()); } - Future deleteSelectedAccount(String accountId) { - log('AccountCacheManager::deleteSelectedAccount(): $accountId'); - return _accountCacheClient.deleteItem(accountId); + Future deleteCurrentAccount(String hashId) { + log('AccountCacheManager::deleteCurrentAccount(): $hashId'); + return _accountCacheClient.deleteItem(hashId); } } \ No newline at end of file diff --git a/lib/features/login/data/local/authentication_info_cache_manager.dart b/lib/features/login/data/local/authentication_info_cache_manager.dart index e200a361a..a9089267f 100644 --- a/lib/features/login/data/local/authentication_info_cache_manager.dart +++ b/lib/features/login/data/local/authentication_info_cache_manager.dart @@ -1,5 +1,6 @@ import 'package:tmail_ui_user/features/caching/clients/authentication_info_cache_client.dart'; import 'package:tmail_ui_user/features/login/data/model/authentication_info_cache.dart'; +import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; class AuthenticationInfoCacheManager { final AuthenticationInfoCacheClient _authenticationInfoCacheClient; @@ -12,8 +13,13 @@ class AuthenticationInfoCacheManager { authenticationInfoCache); } - Future getAuthenticationInfoStored() { - return _authenticationInfoCacheClient.getItem(AuthenticationInfoCache.keyCacheValue); + Future getAuthenticationInfoStored() async { + final authenticationInfoCache = await _authenticationInfoCacheClient.getItem(AuthenticationInfoCache.keyCacheValue); + if (authenticationInfoCache != null) { + return authenticationInfoCache; + } else { + throw NotFoundAuthenticationInfoCache(); + } } Future removeAuthenticationInfo() { diff --git a/lib/features/login/data/network/config/authorization_interceptors.dart b/lib/features/login/data/network/config/authorization_interceptors.dart index 9a7bdfac6..e555c397d 100644 --- a/lib/features/login/data/network/config/authorization_interceptors.dart +++ b/lib/features/login/data/network/config/authorization_interceptors.dart @@ -56,8 +56,7 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper { @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) { - log('AuthorizationInterceptors::onRequest():DATA: ${options.data}'); - log('AuthorizationInterceptors::onRequest():TOKEN_HASHCODE_CURRENT: ${_token?.token.hashCode}'); + log('AuthorizationInterceptors::onRequest():data: ${options.data} | header: ${options.headers}'); switch(_authenticationType) { case AuthenticationType.basic: if (_authorization != null) { @@ -78,8 +77,6 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper { @override void onError(DioError err, ErrorInterceptorHandler handler) async { logError('AuthorizationInterceptors::onError(): $err'); - logError('AuthorizationInterceptors::onError():TOKEN_HASHCODE_CURRENT: ${_token?.token.hashCode}'); - final requestOptions = err.requestOptions; final extraInRequest = requestOptions.extra; var retries = extraInRequest[RETRY_KEY] ?? 0; @@ -94,20 +91,20 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper { _token!.refreshToken ); - final accountCurrent = await _accountCacheManager.getSelectedAccount(); + final currentAccount = await _accountCacheManager.getCurrentAccount(); - await _accountCacheManager.deleteSelectedAccount(_token!.tokenIdHash); + await _accountCacheManager.deleteCurrentAccount(currentAccount.id); await Future.wait([ _tokenOidcCacheManager.persistOneTokenOidc(newToken), - _accountCacheManager.setSelectedAccount( + _accountCacheManager.setCurrentAccount( PersonalAccount( newToken.tokenIdHash, AuthenticationType.oidc, isSelected: true, - accountId: accountCurrent.accountId, - apiUrl: accountCurrent.apiUrl, - userName: accountCurrent.userName + accountId: currentAccount.accountId, + apiUrl: currentAccount.apiUrl, + userName: currentAccount.userName ) ) ]); diff --git a/lib/features/login/data/repository/account_repository_impl.dart b/lib/features/login/data/repository/account_repository_impl.dart index 062d34994..0df1f5ded 100644 --- a/lib/features/login/data/repository/account_repository_impl.dart +++ b/lib/features/login/data/repository/account_repository_impl.dart @@ -1,4 +1,3 @@ -import 'package:core/utils/app_logger.dart'; import 'package:model/account/personal_account.dart'; import 'package:tmail_ui_user/features/login/data/datasource/account_datasource.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; @@ -16,12 +15,11 @@ class AccountRepositoryImpl extends AccountRepository { @override Future setCurrentAccount(PersonalAccount newCurrentAccount) { - log('AccountRepositoryImpl::setCurrentAccount(): $newCurrentAccount'); return _accountDatasource.setCurrentAccount(newCurrentAccount); } @override - Future deleteCurrentAccount(String accountId) { - return _accountDatasource.deleteCurrentAccount(accountId); + Future deleteCurrentAccount(String hashId) { + return _accountDatasource.deleteCurrentAccount(hashId); } } \ No newline at end of file diff --git a/lib/features/login/data/repository/credential_repository_impl.dart b/lib/features/login/data/repository/credential_repository_impl.dart index 13973ee97..283d54206 100644 --- a/lib/features/login/data/repository/credential_repository_impl.dart +++ b/lib/features/login/data/repository/credential_repository_impl.dart @@ -36,7 +36,7 @@ class CredentialRepositoryImpl extends CredentialRepository { } @override - Future getAuthenticationInfoStored() { + Future getAuthenticationInfoStored() { return _authenticationInfoCacheManager.getAuthenticationInfoStored(); } diff --git a/lib/features/login/domain/exceptions/authentication_exception.dart b/lib/features/login/domain/exceptions/authentication_exception.dart index 35604d7aa..a03da51e1 100644 --- a/lib/features/login/domain/exceptions/authentication_exception.dart +++ b/lib/features/login/domain/exceptions/authentication_exception.dart @@ -51,4 +51,6 @@ class CanNotFoundUserName implements Exception {} class CanNotFoundPassword implements Exception {} -class CanNotAuthenticationInfoOnWeb implements Exception {} \ No newline at end of file +class CanNotAuthenticationInfoOnWeb implements Exception {} + +class NotFoundAuthenticationInfoCache implements Exception {} \ 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 f164c18b4..d1d656e72 100644 --- a/lib/features/login/domain/repository/account_repository.dart +++ b/lib/features/login/domain/repository/account_repository.dart @@ -6,5 +6,5 @@ abstract class AccountRepository { Future setCurrentAccount(PersonalAccount newCurrentAccount); - Future deleteCurrentAccount(String accountId); + Future deleteCurrentAccount(String hashId); } \ No newline at end of file diff --git a/lib/features/login/domain/repository/credential_repository.dart b/lib/features/login/domain/repository/credential_repository.dart index 805e70379..bf5deb603 100644 --- a/lib/features/login/domain/repository/credential_repository.dart +++ b/lib/features/login/domain/repository/credential_repository.dart @@ -9,7 +9,7 @@ abstract class CredentialRepository { Future storeAuthenticationInfo(AuthenticationInfoCache authenticationInfoCache); - Future getAuthenticationInfoStored(); + Future getAuthenticationInfoStored(); Future removeAuthenticationInfo(); } \ No newline at end of file diff --git a/lib/features/session/data/datasource_impl/hive_session_datasource_impl.dart b/lib/features/session/data/datasource_impl/hive_session_datasource_impl.dart index 07275b624..1d3037819 100644 --- a/lib/features/session/data/datasource_impl/hive_session_datasource_impl.dart +++ b/lib/features/session/data/datasource_impl/hive_session_datasource_impl.dart @@ -33,7 +33,7 @@ class HiveSessionDataSourceImpl extends SessionDataSource { if (sessionHiveObj != null) { return sessionHiveObj.toSession(); } else { - throw NotFoundSessionHiveObject(); + throw NotFoundSessionException(); } }).catchError(_exceptionThrower.throwException); } diff --git a/lib/features/session/data/exceptions/session_exceptions.dart b/lib/features/session/data/exceptions/session_exceptions.dart index 705ee3cee..d9a028bb4 100644 --- a/lib/features/session/data/exceptions/session_exceptions.dart +++ b/lib/features/session/data/exceptions/session_exceptions.dart @@ -1,2 +1,2 @@ -class NotFoundSessionHiveObject implements Exception {} \ No newline at end of file +class NotFoundSessionException implements Exception {} \ No newline at end of file diff --git a/model/lib/account/account_request.dart b/model/lib/account/account_request.dart index b285c0f05..86c9bb558 100644 --- a/model/lib/account/account_request.dart +++ b/model/lib/account/account_request.dart @@ -19,6 +19,24 @@ class AccountRequest with EquatableMixin { this.authenticationType = AuthenticationType.none, }); + factory AccountRequest.withOidc({required Token token}) { + return AccountRequest( + token: token, + authenticationType: AuthenticationType.oidc + ); + } + + factory AccountRequest.withBasic({ + required UserName userName, + required Password password + }) { + return AccountRequest( + userName: userName, + password: password, + authenticationType: AuthenticationType.basic + ); + } + String get basicAuth => 'Basic ${base64Encode(utf8.encode('${userName?.value}:${password?.value}'))}'; String get bearerToken => 'Bearer ${token?.token}';