diff --git a/lib/features/offline_mode/manager/sending_email_cache_manager.dart b/lib/features/offline_mode/manager/sending_email_cache_manager.dart index bbac57def..173e58016 100644 --- a/lib/features/offline_mode/manager/sending_email_cache_manager.dart +++ b/lib/features/offline_mode/manager/sending_email_cache_manager.dart @@ -21,7 +21,6 @@ class SendingEmailCacheManager { SendingEmailHiveCache sendingEmailHiveCache ) async { final keyCache = TupleKey(sendingEmailHiveCache.sendingId, accountId.asString, userName.value).encodeKey; - log('SendingEmailCacheManager::storeSendingEmail():keyCache: $keyCache | sendingEmailHiveCache: $sendingEmailHiveCache'); await _hiveCacheClient.insertItem(keyCache, sendingEmailHiveCache); final newSendingEmailHiveCache = await _hiveCacheClient.getItem(keyCache); if (newSendingEmailHiveCache != null) { @@ -32,29 +31,15 @@ class SendingEmailCacheManager { } } - Future> getAllSendingEmailsByTupleKey( - AccountId accountId, - UserName userName, - {bool needToReopen = false} - ) async { - final sendingEmailsCache = await _hiveCacheClient.getListByTupleKey( - accountId.asString, - userName.value, - needToReopen: needToReopen); - log('SendingEmailCacheManager::getAllSendingEmailsByTupleKey():COUNT: ${sendingEmailsCache.length}'); + Future> getAllSendingEmailsByTupleKey(AccountId accountId, UserName userName) async { + final sendingEmailsCache = await _hiveCacheClient.getListByTupleKey(accountId.asString, userName.value); sendingEmailsCache.sortByLatestTime(); return sendingEmailsCache; } - Future deleteSendingEmail( - AccountId accountId, - UserName userName, - String sendingId, - {bool needToReopen = false} - ) async { + Future deleteSendingEmail(AccountId accountId, UserName userName, String sendingId) async { final keyCache = TupleKey(sendingId, accountId.asString, userName.value).encodeKey; - log('SendingEmailCacheManager::deleteSendingEmail():keyCache: $keyCache'); - await _hiveCacheClient.deleteItem(keyCache, needToReopen: needToReopen); + await _hiveCacheClient.deleteItem(keyCache); final storedSendingEmail = await _hiveCacheClient.getItem(keyCache); if (storedSendingEmail != null) { log('SendingEmailCacheManager::deleteSendingEmail():sendingId: ${storedSendingEmail.sendingId}'); @@ -62,9 +47,8 @@ class SendingEmailCacheManager { } } - Future> getAllSendingEmails({bool needToReopen = false}) async { - final sendingEmailsCache = await _hiveCacheClient.getAll(needToReopen: needToReopen); - log('SendingEmailCacheManager::getAllSendingEmails():COUNT: ${sendingEmailsCache.length}'); + Future> getAllSendingEmails() async { + final sendingEmailsCache = await _hiveCacheClient.getAll(); sendingEmailsCache.sortByLatestTime(); return sendingEmailsCache; } @@ -74,12 +58,10 @@ class SendingEmailCacheManager { Future updateSendingEmail( AccountId accountId, UserName userName, - SendingEmailHiveCache sendingEmailHiveCache, - {bool needToReopen = false} + SendingEmailHiveCache sendingEmailHiveCache ) async { final keyCache = TupleKey(sendingEmailHiveCache.sendingId, accountId.asString, userName.value).encodeKey; - log('SendingEmailCacheManager::updateSendingEmail():keyCache: $keyCache | sendingEmailHiveCache: $sendingEmailHiveCache'); - await _hiveCacheClient.updateItem(keyCache, sendingEmailHiveCache, needToReopen: needToReopen); + await _hiveCacheClient.updateItem(keyCache, sendingEmailHiveCache); final newSendingEmailHiveCache = await _hiveCacheClient.getItem(keyCache); if (newSendingEmailHiveCache != null) { log('SendingEmailCacheManager::updateSendingEmail():sendingId: ${newSendingEmailHiveCache.sendingId} | sendingState: ${newSendingEmailHiveCache.sendingState}'); @@ -88,4 +70,31 @@ class SendingEmailCacheManager { throw NotFoundSendingEmailHiveObject(); } } + + Future> updateMultipleSendingEmail( + AccountId accountId, + UserName userName, + List listSendingEmailHiveCache + ) async { + final mapSendingEmailCache = { + for (var sendingEmailCache in listSendingEmailHiveCache) + TupleKey(sendingEmailCache.sendingId, accountId.asString, userName.value).encodeKey: sendingEmailCache + }; + await _hiveCacheClient.updateMultipleItem(mapSendingEmailCache); + final newListSendingEmailCache = await _hiveCacheClient.getValuesByListKey(mapSendingEmailCache.keys.toList()); + log('SendingEmailCacheManager::updateMultipleSendingEmail():newListSendingEmailCache: ${newListSendingEmailCache.map((sendingEmail) => '${sendingEmail.sendingId} | ${sendingEmail.sendingState}').toList()}'); + return newListSendingEmailCache; + } + + Future deleteMultipleSendingEmail(AccountId accountId, UserName userName, List sendingIds) async { + final listTupleKey = sendingIds.map((sendingId) => TupleKey(sendingId, accountId.asString, userName.value).encodeKey).toList(); + await _hiveCacheClient.deleteMultipleItem(listTupleKey); + final newListSendingEmailCache = await _hiveCacheClient.getValuesByListKey(listTupleKey); + log('SendingEmailCacheManager::deleteMultipleSendingEmail():newListSendingEmailCache: ${newListSendingEmailCache.map((sendingEmail) => '${sendingEmail.sendingId} | ${sendingEmail.sendingState}').toList()}'); + if (newListSendingEmailCache.isNotEmpty) { + throw ExistSendingEmailHiveObject(); + } + } + + Future closeSendingEmailHiveCacheBox() => _hiveCacheClient.closeBox(); } \ No newline at end of file diff --git a/lib/features/offline_mode/observer/sending_email_observer.dart b/lib/features/offline_mode/observer/sending_email_observer.dart index 73f7d91a5..9696457a3 100644 --- a/lib/features/offline_mode/observer/sending_email_observer.dart +++ b/lib/features/offline_mode/observer/sending_email_observer.dart @@ -23,6 +23,7 @@ import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_r import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart'; import 'package:tmail_ui_user/features/offline_mode/bindings/sending_email_interactor_bindings.dart'; import 'package:tmail_ui_user/features/offline_mode/exceptions/workmanager_exception.dart'; +import 'package:tmail_ui_user/features/offline_mode/manager/sending_email_cache_manager.dart'; import 'package:tmail_ui_user/features/offline_mode/model/sending_state.dart'; import 'package:tmail_ui_user/features/offline_mode/observer/work_observer.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/notification/local_notification_config.dart'; @@ -53,6 +54,7 @@ class SendingEmailObserver extends WorkObserver { DeleteSendingEmailInteractor? _deleteSendingEmailInteractor; SendingQueueIsolateManager? _sendingQueueIsolateManager; UpdateSendingEmailInteractor? _updateSendingEmailInteractor; + SendingEmailCacheManager? _sendingEmailCacheManager; Completer? _completer; @@ -64,7 +66,6 @@ class SendingEmailObserver extends WorkObserver { @override Future observe(String taskId, Map inputData, Completer completer) async { - log('SendingEmailObserver::observe():taskId: $taskId | inputData: $inputData'); _completer = completer; _sendingEmail = SendingEmail.fromJson(inputData); log('SendingEmailObserver::observe():_sendingEmail: $_sendingEmail'); @@ -85,14 +86,20 @@ class SendingEmailObserver extends WorkObserver { SendEmailInteractorBindings().dependencies(); }); - return await _getInteractorBindings(); + await _getInteractorBindings(); + + await _sendingEmailCacheManager?.closeSendingEmailHiveCacheBox(); + + return Future.value(); } @override void handleFailureViewState(Failure failure) { log('SendingEmailObserver::_handleFailureViewState(): $failure'); - if (failure is SendEmailFailure || failure is DeleteSendingEmailFailure) { - _updateStoredSendingEmail(SendingState.error); + if (failure is SendEmailFailure) { + _handleSendEmailFailure(failure); + } else if (failure is DeleteSendingEmailFailure) { + _handleDeleteSendingEmailFailure(failure); } else if (failure is GetAuthenticatedAccountFailure || failure is NoAuthenticatedAccountFailure || failure is GetSessionFailure || @@ -115,7 +122,7 @@ class SendingEmailObserver extends WorkObserver { } else if (success is SendEmailSuccess) { _handleSendEmailSuccess(success); } else if (success is DeleteSendingEmailSuccess) { - _handleDeleteSendingEmailSuccess(); + _handleDeleteSendingEmailSuccess(success); } else if (success is UpdateSendingEmailSuccess) { _handleUpdateStoredSendingEmailSuccess(success); } @@ -137,6 +144,7 @@ class SendingEmailObserver extends WorkObserver { _deleteSendingEmailInteractor = getBinding(); _sendingQueueIsolateManager = getBinding(); _updateSendingEmailInteractor = getBinding(); + _sendingEmailCacheManager = getBinding(); } catch (e) { logError('SendingEmailObserver::_getInteractorBindings(): ${e.toString()}'); _handleTaskFailureInWorkManager(); @@ -153,19 +161,11 @@ class SendingEmailObserver extends WorkObserver { } void _getAuthenticatedAccount() { - if (_getAuthenticatedAccountInteractor != null) { - consumeState(_getAuthenticatedAccountInteractor!.execute()); - } else { - _handleTaskFailureInWorkManager(); - } + consumeState(_getAuthenticatedAccountInteractor!.execute()); } void _getSessionAction() { - if (_getSessionInteractor != null) { - consumeState(_getSessionInteractor!.execute()); - } else { - _handleTaskFailureInWorkManager(); - } + consumeState(_getSessionInteractor!.execute()); } void _handleGetSessionSuccess(GetSessionSuccess success) async { @@ -174,13 +174,7 @@ class SendingEmailObserver extends WorkObserver { final apiUrl = success.session.getQualifiedApiUrl(baseUrl: _dynamicUrlInterceptors?.jmapUrl); if (apiUrl.isNotEmpty) { _dynamicUrlInterceptors?.changeBaseUrl(apiUrl); - - _updateStoredSendingEmail(SendingState.running); - - await Future.delayed( - const Duration(milliseconds: 2000), - _sendEmailAction - ); + _sendEmailAction(); } else { _handleTaskFailureInWorkManager(); } @@ -197,26 +191,32 @@ class SendingEmailObserver extends WorkObserver { } void _sendEmailAction() { - if (_sendEmailInteractor != null && _isDataParameterNotNull) { - final mailboxRequest = _sendingEmail!.mailboxNameRequest != null && _sendingEmail!.creationIdRequest != null - ? CreateNewMailboxRequest(_sendingEmail!.creationIdRequest!, _sendingEmail!.mailboxNameRequest!) - : null; - consumeState(_sendEmailInteractor!.execute( + consumeState( + _sendEmailInteractor!.execute( _currentSession!, _currentAccountId!, _sendingEmail!.toEmailRequest(), - mailboxRequest: mailboxRequest - )); + mailboxRequest: _getMailboxRequest() + ) + ); + } + + CreateNewMailboxRequest? _getMailboxRequest() { + if (_sendingEmail!.mailboxNameRequest != null && + _sendingEmail!.creationIdRequest != null) { + return CreateNewMailboxRequest( + _sendingEmail!.creationIdRequest!, + _sendingEmail!.mailboxNameRequest!); } else { - _updateStoredSendingEmail(SendingState.error); + return null; } } void _handleGetAccountByOidcSuccess(GetStoredTokenOidcSuccess storedTokenOidcSuccess) { _dynamicUrlInterceptors?.setJmapUrl(storedTokenOidcSuccess.baseUrl.toString()); _authorizationInterceptors?.setTokenAndAuthorityOidc( - newToken: storedTokenOidcSuccess.tokenOidc.toToken(), - newConfig: storedTokenOidcSuccess.oidcConfiguration + newToken: storedTokenOidcSuccess.tokenOidc.toToken(), + newConfig: storedTokenOidcSuccess.oidcConfiguration ); _dynamicUrlInterceptors?.changeBaseUrl(storedTokenOidcSuccess.baseUrl.toString()); _getSessionAction(); @@ -224,28 +224,31 @@ class SendingEmailObserver extends WorkObserver { void _handleSendEmailSuccess(SendEmailSuccess success) { _showLocalNotification(); - _deleteSendingEmailAction(); + _updateStoredSendingEmail(SendingState.success); + } + + void _handleSendEmailFailure(SendEmailFailure failure) { + _updateStoredSendingEmail(SendingState.error); } void _deleteSendingEmailAction() { - log('SendingEmailObserver::_deleteSendingEmailAction():'); - if (_deleteSendingEmailInteractor != null && _isDataParameterNotNull) { - consumeState(_deleteSendingEmailInteractor!.execute( + consumeState( + _deleteSendingEmailInteractor!.execute( _currentAccountId!, _currentSession!.username, - _sendingEmail!.sendingId, - needToReopen: true - )); - } else { - log('SendingEmailObserver::_deleteSendingEmailAction(): NOT CALLED'); - _updateStoredSendingEmail(SendingState.error); - } + _sendingEmail!.sendingId + ) + ); } - void _handleDeleteSendingEmailSuccess() { + void _handleDeleteSendingEmailSuccess(DeleteSendingEmailSuccess success) { _handleTaskSuccessInWorkManager(); } + void _handleDeleteSendingEmailFailure(DeleteSendingEmailFailure failure) { + _handleTaskFailureInWorkManager(); + } + void _showLocalNotification() { LocalNotificationManager.instance.showPushNotification( id: _sendingEmail?.sendingId ?? '', @@ -255,26 +258,26 @@ class SendingEmailObserver extends WorkObserver { ); } - bool get _isDataParameterNotNull => _currentSession != null && _currentAccountId != null && _sendingEmail != null; - void _updateStoredSendingEmail(SendingState newState) { log('SendingEmailObserver::_updateStoredSendingEmail():newState: $newState'); - if (_updateSendingEmailInteractor != null && _isDataParameterNotNull) { - consumeState(_updateSendingEmailInteractor!.execute( + consumeState( + _updateSendingEmailInteractor!.execute( _currentAccountId!, _currentSession!.username, - _sendingEmail!.updatingSendingState(newState), - needToReopen: true - )); - } else { - _handleTaskFailureInWorkManager(); - } + _sendingEmail!.updatingSendingState(newState) + ) + ); } void _handleUpdateStoredSendingEmailSuccess(UpdateSendingEmailSuccess success) { - _updatingSendingStateToMainUI( - sendingState: success.newSendingEmail.sendingState, - sendingId: success.newSendingEmail.sendingId); + log('SendingEmailObserver::_handleUpdateStoredSendingEmailSuccess(): $success'); + if (success.newSendingEmail.isSuccess) { + _deleteSendingEmailAction(); + } else { + _updatingSendingStateToMainUI( + sendingState: success.newSendingEmail.sendingState, + sendingId: success.newSendingEmail.sendingId); + } } void _handleTaskFailureInWorkManager() async { diff --git a/lib/features/sending_queue/domain/extensions/list_sending_email_extension.dart b/lib/features/sending_queue/domain/extensions/list_sending_email_extension.dart new file mode 100644 index 000000000..950cf71c9 --- /dev/null +++ b/lib/features/sending_queue/domain/extensions/list_sending_email_extension.dart @@ -0,0 +1,9 @@ +import 'package:tmail_ui_user/features/offline_mode/model/sending_email_hive_cache.dart'; +import 'package:tmail_ui_user/features/sending_queue/domain/extensions/sending_email_extension.dart'; +import 'package:tmail_ui_user/features/sending_queue/domain/model/sending_email.dart'; + +extension ListSendingEmailExtension on List { + List toHiveCache() => map((sendingEmail) => sendingEmail.toHiveCache()).toList(); + + List get sendingIds => map((sendingEmail) => sendingEmail.sendingId).toSet().toList(); +} \ No newline at end of file diff --git a/lib/features/sending_queue/domain/model/sending_email.dart b/lib/features/sending_queue/domain/model/sending_email.dart index cd0684f25..0b3f548ae 100644 --- a/lib/features/sending_queue/domain/model/sending_email.dart +++ b/lib/features/sending_queue/domain/model/sending_email.dart @@ -101,6 +101,10 @@ class SendingEmail with EquatableMixin { bool get isWaiting => sendingState == SendingState.waiting; + bool get isError => sendingState == SendingState.error; + + bool get isSuccess => sendingState == SendingState.success; + @override List get props => [ sendingId, diff --git a/lib/features/sending_queue/domain/state/update_multiple_sending_email_state.dart b/lib/features/sending_queue/domain/state/update_multiple_sending_email_state.dart new file mode 100644 index 000000000..d4d383917 --- /dev/null +++ b/lib/features/sending_queue/domain/state/update_multiple_sending_email_state.dart @@ -0,0 +1,40 @@ + +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:tmail_ui_user/features/sending_queue/domain/model/sending_email.dart'; + +class UpdateMultipleSendingEmailLoading extends UIState {} + +class UpdateMultipleSendingEmailAllSuccess extends UIState { + final List newSendingEmails; + + UpdateMultipleSendingEmailAllSuccess(this.newSendingEmails); + + @override + List get props => [newSendingEmails]; +} + +class UpdateMultipleSendingEmailHasSomeSuccess extends UIState { + final List newSendingEmails; + + UpdateMultipleSendingEmailHasSomeSuccess(this.newSendingEmails); + + @override + List get props => [newSendingEmails]; +} + +class UpdateMultipleSendingEmailAllFailure extends FeatureFailure { + + UpdateMultipleSendingEmailAllFailure(dynamic exception) : super(exception: exception); + + @override + List get props => [exception]; +} + +class UpdateMultipleSendingEmailFailure extends FeatureFailure { + + UpdateMultipleSendingEmailFailure(dynamic exception) : super(exception: exception); + + @override + List get props => [exception]; +} \ No newline at end of file diff --git a/lib/features/sending_queue/domain/usecases/update_multiple_sending_email_interactor.dart b/lib/features/sending_queue/domain/usecases/update_multiple_sending_email_interactor.dart new file mode 100644 index 000000000..95b114309 --- /dev/null +++ b/lib/features/sending_queue/domain/usecases/update_multiple_sending_email_interactor.dart @@ -0,0 +1,38 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +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/sending_queue/data/exceptions/sending_queue_exceptions.dart'; +import 'package:tmail_ui_user/features/sending_queue/domain/model/sending_email.dart'; +import 'package:tmail_ui_user/features/sending_queue/domain/repository/sending_queue_repository.dart'; +import 'package:tmail_ui_user/features/sending_queue/domain/state/update_multiple_sending_email_state.dart'; + +class UpdateMultipleSendingEmailInteractor { + final SendingQueueRepository _sendingQueueRepository; + + UpdateMultipleSendingEmailInteractor(this._sendingQueueRepository); + + Stream> execute( + AccountId accountId, + UserName userName, + List newSendingEmails + ) async* { + try { + yield Right(UpdateMultipleSendingEmailLoading()); + final storedSendingEmails = await _sendingQueueRepository.updateMultipleSendingEmail( + accountId, + userName, + newSendingEmails); + if (storedSendingEmails.length == newSendingEmails.length) { + yield Right(UpdateMultipleSendingEmailAllSuccess(storedSendingEmails)); + } else if (storedSendingEmails.isEmpty) { + yield Left(UpdateMultipleSendingEmailAllFailure(NotFoundSendingEmailHiveObject())); + } else { + yield Right(UpdateMultipleSendingEmailHasSomeSuccess(storedSendingEmails)); + } + } catch (e) { + yield Left(UpdateMultipleSendingEmailFailure(e)); + } + } +} \ No newline at end of file diff --git a/lib/features/sending_queue/domain/usecases/update_sending_email_interactor.dart b/lib/features/sending_queue/domain/usecases/update_sending_email_interactor.dart index c20459342..f8b1dd5db 100644 --- a/lib/features/sending_queue/domain/usecases/update_sending_email_interactor.dart +++ b/lib/features/sending_queue/domain/usecases/update_sending_email_interactor.dart @@ -15,12 +15,11 @@ class UpdateSendingEmailInteractor { Stream> execute( AccountId accountId, UserName userName, - SendingEmail newSendingEmail, - {bool needToReopen = false} + SendingEmail newSendingEmail ) async* { try { yield Right(UpdateSendingEmailLoading()); - final storedSendingEmail = await _sendingQueueRepository.updateSendingEmail(accountId, userName, newSendingEmail, needToReopen: needToReopen); + final storedSendingEmail = await _sendingQueueRepository.updateSendingEmail(accountId, userName, newSendingEmail); yield Right(UpdateSendingEmailSuccess(storedSendingEmail)); } catch (e) { yield Left(UpdateSendingEmailFailure(e)); diff --git a/lib/features/sending_queue/presentation/bindings/sending_queue_bindings.dart b/lib/features/sending_queue/presentation/bindings/sending_queue_bindings.dart index bb612e64d..37d8bb028 100644 --- a/lib/features/sending_queue/presentation/bindings/sending_queue_bindings.dart +++ b/lib/features/sending_queue/presentation/bindings/sending_queue_bindings.dart @@ -1,6 +1,7 @@ import 'package:get/get.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/usecases/delete_multiple_sending_email_interactor.dart'; +import 'package:tmail_ui_user/features/sending_queue/domain/usecases/update_multiple_sending_email_interactor.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/usecases/update_sending_email_interactor.dart'; import 'package:tmail_ui_user/features/sending_queue/presentation/sending_queue_controller.dart'; @@ -15,6 +16,7 @@ class SendingQueueBindings extends Bindings { Get.put(SendingQueueController( Get.find(), Get.find(), + Get.find(), )); } } \ No newline at end of file diff --git a/lib/features/sending_queue/presentation/bindings/sending_queue_interactor_bindings.dart b/lib/features/sending_queue/presentation/bindings/sending_queue_interactor_bindings.dart index b91a0201b..67df1e1c3 100644 --- a/lib/features/sending_queue/presentation/bindings/sending_queue_interactor_bindings.dart +++ b/lib/features/sending_queue/presentation/bindings/sending_queue_interactor_bindings.dart @@ -7,7 +7,6 @@ import 'package:tmail_ui_user/features/sending_queue/data/repository/sending_que import 'package:tmail_ui_user/features/sending_queue/domain/repository/sending_queue_repository.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/usecases/delete_multiple_sending_email_interactor.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/usecases/delete_sending_email_interactor.dart'; -import 'package:tmail_ui_user/features/sending_queue/domain/usecases/delete_single_sending_email_interactor.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/usecases/get_all_sending_email_interactor.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/detailed_email_cache_manager.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/detailed_email_cache_worker_queue.dart'; @@ -15,6 +14,7 @@ import 'package:tmail_ui_user/features/offline_mode/manager/opened_email_cache_m import 'package:tmail_ui_user/features/offline_mode/manager/opened_email_cache_worker_queue.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/sending_email_cache_manager.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/usecases/store_sending_email_interactor.dart'; +import 'package:tmail_ui_user/features/sending_queue/domain/usecases/update_multiple_sending_email_interactor.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/usecases/update_sending_email_interactor.dart'; import 'package:tmail_ui_user/features/thread/data/local/email_cache_manager.dart'; import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart'; @@ -41,10 +41,10 @@ class SendingQueueInteractorBindings extends InteractorsBindings { void bindingsInteractor() { Get.lazyPut(() => StoreSendingEmailInteractor(Get.find())); Get.lazyPut(() => GetAllSendingEmailInteractor(Get.find())); - Get.lazyPut(() => DeleteSingleSendingEmailInteractor(Get.find())); - Get.lazyPut(() => DeleteMultipleSendingEmailInteractor(Get.find())); + Get.lazyPut(() => DeleteMultipleSendingEmailInteractor(Get.find())); Get.lazyPut(() => DeleteSendingEmailInteractor(Get.find())); Get.lazyPut(() => UpdateSendingEmailInteractor(Get.find())); + Get.lazyPut(() => UpdateMultipleSendingEmailInteractor(Get.find())); } @override diff --git a/lib/features/sending_queue/presentation/extensions/list_sending_email_extension.dart b/lib/features/sending_queue/presentation/extensions/list_sending_email_extension.dart index 953880613..e3d77db63 100644 --- a/lib/features/sending_queue/presentation/extensions/list_sending_email_extension.dart +++ b/lib/features/sending_queue/presentation/extensions/list_sending_email_extension.dart @@ -1,4 +1,5 @@ +import 'package:tmail_ui_user/features/offline_mode/model/sending_state.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/extensions/sending_email_extension.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/model/sending_email.dart'; @@ -16,4 +17,8 @@ extension ListSendingEmailExtension on List { bool isAllUnSelected() => every((sendingEmail) => !sendingEmail.isSelected); List listSelected() => where((sendingEmail) => sendingEmail.isSelected).toList(); + + bool isAllSendingStateError() => every((sendingEmail) => sendingEmail.isError); + + List toSendingStateWaiting() => map((sendingEmail) => sendingEmail.updatingSendingState(SendingState.waiting)).toList(); } \ No newline at end of file diff --git a/lib/features/sending_queue/presentation/model/sending_email_action_type.dart b/lib/features/sending_queue/presentation/model/sending_email_action_type.dart index f57181039..d3bcf417e 100644 --- a/lib/features/sending_queue/presentation/model/sending_email_action_type.dart +++ b/lib/features/sending_queue/presentation/model/sending_email_action_type.dart @@ -7,6 +7,7 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; enum SendingEmailActionType { create, edit, + resend, delete; String getButtonTitle(BuildContext context) { @@ -17,6 +18,8 @@ enum SendingEmailActionType { return AppLocalizations.of(context).edit; case SendingEmailActionType.create: return ''; + case SendingEmailActionType.resend: + return AppLocalizations.of(context).resend; } } @@ -28,6 +31,8 @@ enum SendingEmailActionType { return 'button_edit_sending_email'; case SendingEmailActionType.create: return ''; + case SendingEmailActionType.resend: + return 'button_resend_sending_email'; } } @@ -36,6 +41,7 @@ enum SendingEmailActionType { case SendingEmailActionType.delete: return AppColor.colorDeletePermanentlyButton.withOpacity(buttonState.opacity); case SendingEmailActionType.edit: + case SendingEmailActionType.resend: return AppColor.primaryColor.withOpacity(buttonState.opacity); case SendingEmailActionType.create: return Colors.transparent.withOpacity(buttonState.opacity); @@ -47,6 +53,7 @@ enum SendingEmailActionType { case SendingEmailActionType.delete: return AppColor.colorDeletePermanentlyButton.withOpacity(buttonState.opacity); case SendingEmailActionType.edit: + case SendingEmailActionType.resend: return AppColor.primaryColor.withOpacity(buttonState.opacity); case SendingEmailActionType.create: return Colors.transparent.withOpacity(buttonState.opacity); diff --git a/lib/features/sending_queue/presentation/sending_queue_controller.dart b/lib/features/sending_queue/presentation/sending_queue_controller.dart index 78fb563ea..413adb8c7 100644 --- a/lib/features/sending_queue/presentation/sending_queue_controller.dart +++ b/lib/features/sending_queue/presentation/sending_queue_controller.dart @@ -1,8 +1,8 @@ +import 'package:core/presentation/state/failure.dart'; import 'package:core/utils/app_logger.dart'; import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/resources/image_paths.dart'; -import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:core/presentation/utils/app_toast.dart'; import 'package:flutter/material.dart'; @@ -17,9 +17,14 @@ import 'package:tmail_ui_user/features/caching/utils/cache_utils.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/network_status_handle/presentation/network_connnection_controller.dart'; +import 'package:tmail_ui_user/features/offline_mode/manager/sending_email_cache_manager.dart'; import 'package:tmail_ui_user/features/offline_mode/model/sending_state.dart'; +import 'package:tmail_ui_user/features/sending_queue/domain/extensions/list_sending_email_extension.dart'; +import 'package:tmail_ui_user/features/sending_queue/domain/extensions/sending_email_extension.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/model/sending_email.dart'; +import 'package:tmail_ui_user/features/sending_queue/domain/state/update_multiple_sending_email_state.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/state/update_sending_email_state.dart'; +import 'package:tmail_ui_user/features/sending_queue/domain/usecases/update_multiple_sending_email_interactor.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/usecases/update_sending_email_interactor.dart'; import 'package:tmail_ui_user/features/sending_queue/presentation/utils/sending_queue_isolate_manager.dart'; import 'package:tmail_ui_user/features/offline_mode/controller/work_scheduler_controller.dart'; @@ -34,10 +39,12 @@ class SendingQueueController extends BaseController with MessageDialogActionMixi final DeleteMultipleSendingEmailInteractor _deleteMultipleSendingEmailInteractor; final UpdateSendingEmailInteractor _updateSendingEmailInteractor; + final UpdateMultipleSendingEmailInteractor _updateMultipleSendingEmailInteractor; final dashboardController = getBinding(); final _networkConnectionController = getBinding(); final _sendingQueueIsolateManager = getBinding(); + final _sendingEmailCacheManager = getBinding(); final _imagePaths = getBinding(); final _appToast = getBinding(); @@ -48,6 +55,7 @@ class SendingQueueController extends BaseController with MessageDialogActionMixi SendingQueueController( this._deleteMultipleSendingEmailInteractor, this._updateSendingEmailInteractor, + this._updateMultipleSendingEmailInteractor, ); @override @@ -61,7 +69,7 @@ class SendingQueueController extends BaseController with MessageDialogActionMixi ); } - void _handleSendingQueueEvent(Object? event) { + void _handleSendingQueueEvent(Object? event) async { log('SendingQueueController::_handleSendingQueueEvent():event: $event'); try { if (event is String) { @@ -69,16 +77,20 @@ class SendingQueueController extends BaseController with MessageDialogActionMixi final sendingId = tupleKey.parts[0]; final sendingState = SendingState.values.firstWhereOrNull((state) => state.name == tupleKey.parts[1]); log('SendingQueueController::_handleSendingQueueEvent():sendingId: $sendingId | sendingState: $sendingState'); - if (sendingState != null) { + await _sendingEmailCacheManager?.closeSendingEmailHiveCacheBox(); + switch(sendingState) { case SendingState.waiting: - _handleSendingStateRunningEvent(sendingId); + _updatingSendingStateToRunningAction(sendingId); break; case SendingState.running: case SendingState.success: + refreshSendingQueue(); + break; case SendingState.error: - refreshSendingQueue(needToReopen: true); + await WorkSchedulerController().cancelByUniqueId(sendingId); + _updatingSendingStateToErrorAction(sendingId); break; } } @@ -88,11 +100,21 @@ class SendingQueueController extends BaseController with MessageDialogActionMixi } } - void _handleSendingStateRunningEvent(String sendingId) { - log('SendingQueueController::_handleSendingStateRunningEvent():sendingId: $sendingId'); + void _updatingSendingStateToRunningAction(String sendingId) { + log('SendingQueueController::_updatingSendingStateToRunningAction():sendingId: $sendingId'); final matchedSendingEmail = dashboardController!.listSendingEmails.firstWhereOrNull((sendingEmail) => sendingEmail.sendingId == sendingId); if (matchedSendingEmail != null) { - _updateSendingEmailAction(matchedSendingEmail); + final newSendingEmail = matchedSendingEmail.updatingSendingState(SendingState.running); + _updateSendingEmailAction(newSendingEmail); + } + } + + void _updatingSendingStateToErrorAction(String sendingId) { + log('SendingQueueController::_updatingSendingStateToErrorAction():sendingId: $sendingId'); + final matchedSendingEmail = dashboardController!.listSendingEmails.firstWhereOrNull((sendingEmail) => sendingEmail.sendingId == sendingId); + if (matchedSendingEmail != null) { + final newSendingEmail = matchedSendingEmail.updatingSendingState(SendingState.error); + _updateSendingEmailAction(newSendingEmail); } } @@ -118,8 +140,8 @@ class SendingQueueController extends BaseController with MessageDialogActionMixi bool get isConnectedNetwork => _networkConnectionController?.isNetworkConnectionAvailable() == true; - void refreshSendingQueue({bool needToReopen = false}) { - dashboardController!.getAllSendingEmails(needToReopen: needToReopen); + void refreshSendingQueue() { + dashboardController!.getAllSendingEmails(); } void openMailboxMenu() { @@ -148,6 +170,9 @@ class SendingQueueController extends BaseController with MessageDialogActionMixi break; case SendingEmailActionType.create: break; + case SendingEmailActionType.resend: + _resendSendingEmailAction(listSendingEmails); + break; } } @@ -185,32 +210,31 @@ class SendingQueueController extends BaseController with MessageDialogActionMixi } void _handleDeleteSendingEmail(List listSendingEmails) async { + disableSelectionMode(); + final accountId = dashboardController!.accountId.value; final session = dashboardController!.sessionCurrent; - if (accountId != null && session != null) { - final sendingIds = listSendingEmails.map((sendingEmail) => sendingEmail.sendingId).toSet().toList(); - - await Future.wait( - sendingIds.map((sendingId) => WorkSchedulerController().cancelByUniqueId(sendingId)), - eagerError: true + consumeState( + _deleteMultipleSendingEmailInteractor.execute( + accountId, + session.username, + listSendingEmails.sendingIds + ) ); - - consumeState(_deleteMultipleSendingEmailInteractor.execute(accountId, session.username, sendingIds)); } } - void _handleDeleteSendingEmailSuccess() { - if (currentContext != null && currentOverlayContext != null) { - selectionState.value = SelectMode.INACTIVE; + void _handleDeleteSendingEmailSuccess(DeleteMultipleSendingEmailSuccess success) async { + await Future.wait(success.sendingIds.map(WorkSchedulerController().cancelByUniqueId)); + if (currentContext != null && currentOverlayContext != null) { _appToast!.showToastSuccessMessage( currentOverlayContext!, - AppLocalizations.of(currentContext!).messageHaveBeenDeletedSuccessfully - ); - - refreshSendingQueue(); + AppLocalizations.of(currentContext!).messageHaveBeenDeletedSuccessfully); } + + refreshSendingQueue(); } void _editSendingEmailAction(SendingEmail sendingEmail) { @@ -227,10 +251,6 @@ class SendingQueueController extends BaseController with MessageDialogActionMixi dashboardController?.goToComposer(arguments); } - void _handleDeleteSendingEmailFailure() { - selectionState.value = SelectMode.INACTIVE; - } - void _updateSendingEmailAction(SendingEmail newSendingEmail) { final accountId = dashboardController!.accountId.value; final session = dashboardController!.sessionCurrent; @@ -238,29 +258,62 @@ class SendingQueueController extends BaseController with MessageDialogActionMixi consumeState(_updateSendingEmailInteractor.execute( accountId, session.username, - newSendingEmail, - needToReopen: true + newSendingEmail )); } } + void _resendSendingEmailAction(List listSendingEmails) async { + disableSelectionMode(); + + final accountId = dashboardController!.accountId.value; + final session = dashboardController!.sessionCurrent; + + if (accountId != null && session != null) { + consumeState( + _updateMultipleSendingEmailInteractor.execute( + accountId, + session.username, + listSendingEmails.toSendingStateWaiting() + ) + ); + } + } + + void _handleResendSendingEmailSuccess(List newListSendingEmails) async { + await Future.forEach(newListSendingEmails, (sendingEmail) async { + await WorkSchedulerController().cancelByUniqueId(sendingEmail.sendingId); + dashboardController!.addSendingEmailToSendingQueue(sendingEmail); + }); + + if (currentContext != null && currentOverlayContext != null) { + _appToast!.showToastSuccessMessage( + currentOverlayContext!, + AppLocalizations.of(currentContext!).messagesHaveBeenResent); + } + refreshSendingQueue(); + } + + @override void handleSuccessViewState(Success success) { super.handleSuccessViewState(success); - if (success is DeleteMultipleSendingEmailAllSuccess || - success is DeleteMultipleSendingEmailHasSomeSuccess) { - _handleDeleteSendingEmailSuccess(); + if (success is DeleteMultipleSendingEmailSuccess) { + _handleDeleteSendingEmailSuccess(success); } else if (success is UpdateSendingEmailSuccess) { - refreshSendingQueue(needToReopen: true); + refreshSendingQueue(); + } else if (success is UpdateMultipleSendingEmailAllSuccess) { + _handleResendSendingEmailSuccess(success.newSendingEmails); + } else if (success is UpdateMultipleSendingEmailHasSomeSuccess) { + _handleResendSendingEmailSuccess(success.newSendingEmails); } } @override void handleFailureViewState(Failure failure) { super.handleFailureViewState(failure); - if (failure is DeleteMultipleSendingEmailAllFailure || - failure is DeleteMultipleSendingEmailFailure) { - _handleDeleteSendingEmailFailure(); + if (failure is UpdateSendingEmailFailure) { + refreshSendingQueue(); } } diff --git a/lib/features/sending_queue/presentation/sending_queue_view.dart b/lib/features/sending_queue/presentation/sending_queue_view.dart index d72a556c5..efd861057 100644 --- a/lib/features/sending_queue/presentation/sending_queue_view.dart +++ b/lib/features/sending_queue/presentation/sending_queue_view.dart @@ -64,7 +64,7 @@ class SendingQueueView extends GetWidget with AppLoaderM if (controller.selectionState.value == SelectMode.INACTIVE) { return RefreshIndicator( color: AppColor.primaryColor, - onRefresh: () async => controller.refreshSendingQueue(needToReopen: true), + onRefresh: () async => controller.refreshSendingQueue(), child: _buildListViewItemSendingEmails()); } else { return _buildListViewItemSendingEmails(); diff --git a/lib/features/sending_queue/presentation/widgets/bottom_bar_sending_queue_widget.dart b/lib/features/sending_queue/presentation/widgets/bottom_bar_sending_queue_widget.dart index 6a7ae06d1..362e88687 100644 --- a/lib/features/sending_queue/presentation/widgets/bottom_bar_sending_queue_widget.dart +++ b/lib/features/sending_queue/presentation/widgets/bottom_bar_sending_queue_widget.dart @@ -7,6 +7,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:tmail_ui_user/features/base/state/button_state.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/model/sending_email.dart'; +import 'package:tmail_ui_user/features/sending_queue/presentation/extensions/list_sending_email_extension.dart'; import 'package:tmail_ui_user/features/sending_queue/presentation/model/sending_email_action_type.dart'; typedef OnHandleSendingEmailActionType = void Function(SendingEmailActionType, List); @@ -53,6 +54,19 @@ class BottomBarSendingQueueWidget extends StatelessWidget { }) ..text(SendingEmailActionType.edit.getButtonTitle(context), isVertical: responsiveUtils.isPortraitMobile(context)) ).build()), + Expanded(child: (ButtonBuilder(imagePaths.icRefresh) + ..key(Key(SendingEmailActionType.resend.getButtonKey())) + ..iconColor(SendingEmailActionType.resend.getButtonIconColor(_isCanResend ? ButtonState.enabled : ButtonState.disabled)) + ..padding(const EdgeInsets.all(8)) + ..radiusSplash(8) + ..textStyle(TextStyle(fontSize: 12, color: SendingEmailActionType.resend.getButtonTitleColor(_isCanResend ? ButtonState.enabled : ButtonState.disabled))) + ..onPressActionClick(() { + if (_isCanResend) { + onHandleSendingEmailActionType?.call(SendingEmailActionType.resend, listSendingEmailSelected); + } + }) + ..text(SendingEmailActionType.resend.getButtonTitle(context), isVertical: responsiveUtils.isPortraitMobile(context)) + ).build()), Expanded(child: (ButtonBuilder(imagePaths.icDeleteComposer) ..key(Key(SendingEmailActionType.delete.getButtonKey())) ..iconColor(SendingEmailActionType.delete.getButtonIconColor(ButtonState.enabled)) @@ -70,4 +84,6 @@ class BottomBarSendingQueueWidget extends StatelessWidget { bool get _isEditable => !isConnectedNetwork && listSendingEmailSelected.length == 1 && listSendingEmailSelected.first.isWaiting; + + bool get _isCanResend => listSendingEmailSelected.isAllSendingStateError(); } \ No newline at end of file diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index d494e2461..9758dcdcb 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-06-09T10:30:29.843203", + "@@last_modified": "2023-06-09T22:51:52.101985", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -2905,5 +2905,17 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "resend": "Resend", + "@resend": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "messagesHaveBeenResent": "Messages have been resent", + "@messagesHaveBeenResent": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index ab91b5d1f..905501138 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -2995,4 +2995,16 @@ class AppLocalizations { name: 'connectedToTheInternet' ); } + + String get resend { + return Intl.message( + 'Resend', + name: 'resend'); + } + + String get messagesHaveBeenResent { + return Intl.message( + 'Messages have been resent', + name: 'messagesHaveBeenResent'); + } } \ No newline at end of file