From b66c12ff50e0c7446a0ec04602dedcdd780a410a Mon Sep 17 00:00:00 2001 From: dab246 Date: Mon, 23 Oct 2023 23:19:12 +0700 Subject: [PATCH 1/5] TF-2177 Fix download attachment not working on web Signed-off-by: dab246 --- .../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 f6822d8873..844ad20f4d 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 eb113fd5f4..9ae6b8c50d 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 e20056d7d2..955102457b 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 2696d30d74..20192f8f99 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 cec64f6415..30e5332a35 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 e5cc4ef615..5350bc49bc 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 a688309808..e6515644da 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 e200a361ab..a9089267f9 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 9a7bdfac67..e555c397d1 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 062d349945..0df1f5dedc 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 13973ee97f..283d542068 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 35604d7aaa..a03da51e18 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 f164c18b4c..d1d656e72f 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 805e703798..bf5deb6036 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 07275b624e..1d3037819a 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 705ee3ceea..d9a028bb44 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 b285c0f05c..86c9bb558d 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}'; From d5bf1626f07b141a54616e947c7edcd1e8046c09 Mon Sep 17 00:00:00 2001 From: dab246 Date: Mon, 23 Oct 2023 23:21:01 +0700 Subject: [PATCH 2/5] TF-2177 Add loading bar progress when download attachment Signed-off-by: dab246 --- lib/features/base/mixin/app_loader_mixin.dart | 23 +------------------ .../email/data/network/email_api.dart | 6 ++++- .../model/download/download_task_state.dart | 11 ++++++++- .../download/download_task_item_widget.dart | 17 +++++++++++--- 4 files changed, 30 insertions(+), 27 deletions(-) diff --git a/lib/features/base/mixin/app_loader_mixin.dart b/lib/features/base/mixin/app_loader_mixin.dart index a9395e619e..f1bab3c9b0 100644 --- a/lib/features/base/mixin/app_loader_mixin.dart +++ b/lib/features/base/mixin/app_loader_mixin.dart @@ -1,8 +1,7 @@ -import 'package:core/core.dart'; +import 'package:core/presentation/extensions/color_extension.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:percent_indicator/circular_percent_indicator.dart'; import 'package:percent_indicator/linear_percent_indicator.dart'; mixin AppLoaderMixin { @@ -22,18 +21,6 @@ mixin AppLoaderMixin { backgroundColor: AppColor.colorBgMailboxSelected)); } - Widget circularPercentLoadingWidget(double percent) { - return Center( - child: CircularPercentIndicator( - percent: percent > 1.0 ? 1.0 : percent, - backgroundColor: AppColor.colorBgMailboxSelected, - progressColor: AppColor.primaryColor, - lineWidth: 3, - radius: 14, - ) - ); - } - Widget horizontalPercentLoadingWidget(double percent) { return Center( child: LinearPercentIndicator( @@ -44,12 +31,4 @@ mixin AppLoaderMixin { progressColor: AppColor.primaryColor, )); } - - Widget loadingWidgetWithSizeColor({double? size, Color? color}) { - return Center(child: SizedBox( - width: size ?? 24, - height: size ?? 24, - child: CircularProgressIndicator( - color: color ?? AppColor.colorLoading))); - } } \ No newline at end of file diff --git a/lib/features/email/data/network/email_api.dart b/lib/features/email/data/network/email_api.dart index c4e20c8e02..54912f730a 100644 --- a/lib/features/email/data/network/email_api.dart +++ b/lib/features/email/data/network/email_api.dart @@ -366,7 +366,11 @@ class EmailAPI with HandleSetErrorMixin { headers: headerParam, responseType: ResponseType.bytes), onReceiveProgress: (downloaded, total) { - final progress = (downloaded / total) * 100; + log('DownloadClient::downloadFileForWeb(): downloaded = $downloaded | total: $total'); + double progress = 0; + if (downloaded > 0 && total > downloaded) { + progress = (downloaded / total) * 100; + } log('DownloadClient::downloadFileForWeb(): progress = ${progress.round()}%'); onReceiveController.add(Right(DownloadingAttachmentForWeb( taskId, diff --git a/lib/features/mailbox_dashboard/presentation/model/download/download_task_state.dart b/lib/features/mailbox_dashboard/presentation/model/download/download_task_state.dart index 19ca745a27..4ab2331c51 100644 --- a/lib/features/mailbox_dashboard/presentation/model/download/download_task_state.dart +++ b/lib/features/mailbox_dashboard/presentation/model/download/download_task_state.dart @@ -34,7 +34,16 @@ class DownloadTaskState with EquatableMixin { ); } - double get percentDownloading => progress / 100; + double get percentDownloading { + final percent = progress / 100; + if (percent < 0) { + return 0; + } else if (percent > 1) { + return 1; + } else { + return percent; + } + } @override List get props => [taskId, attachment, progress, downloaded, total]; diff --git a/lib/features/mailbox_dashboard/presentation/widgets/download/download_task_item_widget.dart b/lib/features/mailbox_dashboard/presentation/widgets/download/download_task_item_widget.dart index af6eeca6c8..e3d3b77ae2 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/download/download_task_item_widget.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/download/download_task_item_widget.dart @@ -1,15 +1,16 @@ import 'package:byte_converter/byte_converter.dart'; +import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/utils/style_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; -import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; +import 'package:percent_indicator/circular_percent_indicator.dart'; import 'package:tmail_ui_user/features/email/presentation/extensions/attachment_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/download/download_task_state.dart'; -class DownloadTaskItemWidget extends StatelessWidget with AppLoaderMixin { +class DownloadTaskItemWidget extends StatelessWidget { final DownloadTaskState taskState; @@ -40,7 +41,17 @@ class DownloadTaskItemWidget extends StatelessWidget with AppLoaderMixin { width: 16, height: 16, fit: BoxFit.fill), - circularPercentLoadingWidget(taskState.percentDownloading) + Center( + child: taskState.percentDownloading == 0 + ? const CircularProgressIndicator(color: AppColor.primaryColor, strokeWidth: 3) + : CircularPercentIndicator( + percent: taskState.percentDownloading, + backgroundColor: AppColor.colorBgMailboxSelected, + progressColor: AppColor.primaryColor, + lineWidth: 3, + radius: 14, + ) + ) ]), ), const SizedBox(width: 12), From 850eab5ffdaf5bc7f89999440dccdda35ed1734a Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 24 Oct 2023 13:01:31 +0700 Subject: [PATCH 3/5] TF-2177 Upgrade hive database version and clear all account data Signed-off-by: dab246 --- lib/features/caching/caching_manager.dart | 3 +++ lib/features/caching/config/cache_version.dart | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/features/caching/caching_manager.dart b/lib/features/caching/caching_manager.dart index 9f9350809e..e730d83ee6 100644 --- a/lib/features/caching/caching_manager.dart +++ b/lib/features/caching/caching_manager.dart @@ -104,6 +104,9 @@ class CachingManager { Future onUpgradeCache(int oldVersion, int newVersion) async { log('CachingManager::onUpgradeCache():oldVersion $oldVersion | newVersion: $newVersion'); await clearData(); + if (oldVersion > 0 && oldVersion < newVersion && newVersion == 7) { + await clearAll(); + } await storeCacheVersion(newVersion); } diff --git a/lib/features/caching/config/cache_version.dart b/lib/features/caching/config/cache_version.dart index a79acd0b2e..4209327c0a 100644 --- a/lib/features/caching/config/cache_version.dart +++ b/lib/features/caching/config/cache_version.dart @@ -1,4 +1,4 @@ class CacheVersion { - static const int hiveDBVersion = 6; + static const int hiveDBVersion = 7; } \ No newline at end of file From 3d626b10abfe6ccb6f81fe3f3be38a161d97a747 Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 24 Oct 2023 14:08:54 +0700 Subject: [PATCH 4/5] TF-2177 Update account cache when store new account Signed-off-by: dab246 --- .../extensions/account_cache_extensions.dart | 22 +++++++++ .../list_account_cache_extensions.dart | 23 ++++++++++ .../data/local/account_cache_manager.dart | 24 +++++++--- .../features/caching/accountl_cache_test.dart | 46 +++++++++++++++++++ 4 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 lib/features/login/data/extensions/list_account_cache_extensions.dart create mode 100644 test/features/caching/accountl_cache_test.dart diff --git a/lib/features/login/data/extensions/account_cache_extensions.dart b/lib/features/login/data/extensions/account_cache_extensions.dart index 20c61ed719..1217c03b02 100644 --- a/lib/features/login/data/extensions/account_cache_extensions.dart +++ b/lib/features/login/data/extensions/account_cache_extensions.dart @@ -26,4 +26,26 @@ extension AccountCacheExtension on AccountCache { apiUrl: apiUrl, userName: userName != null ? UserName(userName!) : null); } + + AccountCache unselected() { + return AccountCache( + id, + authenticationType, + isSelected: false, + accountId: accountId, + apiUrl: apiUrl, + userName: userName + ); + } + + AccountCache emptyId() { + return AccountCache( + '', + authenticationType, + isSelected: false, + accountId: accountId, + apiUrl: apiUrl, + userName: userName + ); + } } \ No newline at end of file diff --git a/lib/features/login/data/extensions/list_account_cache_extensions.dart b/lib/features/login/data/extensions/list_account_cache_extensions.dart new file mode 100644 index 0000000000..fa31cc2f4f --- /dev/null +++ b/lib/features/login/data/extensions/list_account_cache_extensions.dart @@ -0,0 +1,23 @@ +import 'package:collection/collection.dart'; +import 'package:core/utils/app_logger.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/account_cache_extensions.dart'; +import 'package:tmail_ui_user/features/login/data/model/account_cache.dart'; + +extension ListAccountCacheExtension on List { + List unselected() => map((account) => account.unselected()).toList(); + + List removeDuplicated() { + final listAccountId = map((account) => account.accountId).whereNotNull().toSet(); + log('ListAccountCacheExtension::removeDuplicated:listAccountId: $listAccountId'); + retainWhere((account) => listAccountId.remove(account.accountId)); + log('ListAccountCacheExtension::removeDuplicated:listAccount: $this'); + return this; + } + + Map toMap() { + return { + for (var account in this) + account.id: account + }; + } +} \ 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 e6515644da..3448272f1c 100644 --- a/lib/features/login/data/local/account_cache_manager.dart +++ b/lib/features/login/data/local/account_cache_manager.dart @@ -3,6 +3,7 @@ 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'; import 'package:tmail_ui_user/features/login/data/extensions/account_cache_extensions.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/list_account_cache_extensions.dart'; import 'package:tmail_ui_user/features/login/data/extensions/personal_account_extension.dart'; import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; @@ -23,15 +24,26 @@ class AccountCacheManager { } } - Future setCurrentAccount(PersonalAccount account) async { - log('AccountCacheManager::setCurrentAccount(): $account'); - final accountCacheExist = await _accountCacheClient.isExistTable(); - if (accountCacheExist) { - await _accountCacheClient.clearAllData(); + Future setCurrentAccount(PersonalAccount newAccount) async { + log('AccountCacheManager::setCurrentAccount(): $newAccount'); + final newAccountCache = newAccount.toCache(); + final allAccounts = await _accountCacheClient.getAll(); + log('AccountCacheManager::setCurrentAccount::allAccounts(): $allAccounts'); + if (allAccounts.isNotEmpty) { + final newAllAccounts = allAccounts + .unselected() + .removeDuplicated() + .whereNot((account) => account.accountId == newAccountCache.accountId) + .toList(); + if (newAllAccounts.isNotEmpty) { + await _accountCacheClient.clearAllData(); + await _accountCacheClient.updateMultipleItem(newAllAccounts.toMap()); + } } - return _accountCacheClient.insertItem(account.id, account.toCache()); + return _accountCacheClient.insertItem(newAccountCache.id, newAccountCache); } + Future deleteCurrentAccount(String hashId) { log('AccountCacheManager::deleteCurrentAccount(): $hashId'); return _accountCacheClient.deleteItem(hashId); diff --git a/test/features/caching/accountl_cache_test.dart b/test/features/caching/accountl_cache_test.dart new file mode 100644 index 0000000000..7e75f126aa --- /dev/null +++ b/test/features/caching/accountl_cache_test.dart @@ -0,0 +1,46 @@ + +import 'package:flutter_test/flutter_test.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/list_account_cache_extensions.dart'; +import 'package:tmail_ui_user/features/login/data/model/account_cache.dart'; + +void main() { + group('AccountCache test', () { + test('removeDuplicated should remove duplicate accountId', () async { + final account1 = AccountCache( + '1', + 'oidc', + isSelected: true, + accountId: '1', + userName: '1', + apiUrl: 'https://example.com/jmap' + ); + final account2 = AccountCache( + '2', + 'oidc', + isSelected: true, + accountId: '1', + userName: '1', + apiUrl: 'https://example.com/jmap' + ); + final account3 = AccountCache( + '3', + 'basic', + isSelected: true, + accountId: '2', + userName: '2', + apiUrl: 'https://example.com/jmap' + ); + final account4 = AccountCache( + '4', + 'basic', + isSelected: true, + accountId: '2', + userName: '2', + apiUrl: 'https://example.com/jmap' + ); + + final result = [account1, account2, account3, account4].removeDuplicated(); + expect(result, equals([account1, account3])); + }); + }); +} \ No newline at end of file From d59e2167ca1a69989ffc07bd435c0725b271ed8f Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 24 Oct 2023 14:40:56 +0700 Subject: [PATCH 5/5] TF-2177 Remove import unnecessary avoid build failed on CI --- .../login/domain/usecases/get_credential_interactor.dart | 2 +- lib/features/sending_queue/domain/model/sending_email.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/features/login/domain/usecases/get_credential_interactor.dart b/lib/features/login/domain/usecases/get_credential_interactor.dart index b2dd3f903b..621c260497 100644 --- a/lib/features/login/domain/usecases/get_credential_interactor.dart +++ b/lib/features/login/domain/usecases/get_credential_interactor.dart @@ -19,7 +19,7 @@ class GetCredentialInteractor { try { final baseUrl = await credentialRepository.getBaseUrl(); final authenticationInfo = await credentialRepository.getAuthenticationInfoStored(); - if (isCredentialValid(baseUrl) && authenticationInfo != null) { + if (isCredentialValid(baseUrl)) { return Right(GetCredentialViewState( baseUrl, UserName(authenticationInfo.username), diff --git a/lib/features/sending_queue/domain/model/sending_email.dart b/lib/features/sending_queue/domain/model/sending_email.dart index ff5ee87e7c..d3009735a5 100644 --- a/lib/features/sending_queue/domain/model/sending_email.dart +++ b/lib/features/sending_queue/domain/model/sending_email.dart @@ -1,6 +1,6 @@ import 'dart:convert'; -import 'package:core/core.dart'; import 'package:core/domain/extensions/datetime_extension.dart'; +import 'package:core/utils/platform_info.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_date_range_picker/flutter_date_range_picker.dart'; import 'package:jmap_dart_client/http/converter/email_id_nullable_converter.dart';