diff --git a/lib/features/destination_picker/presentation/destination_picker_bindings.dart b/lib/features/destination_picker/presentation/destination_picker_bindings.dart index 6a97aa19f..227ff9b82 100644 --- a/lib/features/destination_picker/presentation/destination_picker_bindings.dart +++ b/lib/features/destination_picker/presentation/destination_picker_bindings.dart @@ -22,6 +22,7 @@ import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree_b import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart'; import 'package:tmail_ui_user/features/thread/data/datasource_impl/thread_datasource_impl.dart'; import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart'; +import 'package:tmail_ui_user/features/thread/data/network/thread_isolate_worker.dart'; class DestinationPickerBindings extends BaseBindings { @@ -58,7 +59,7 @@ class DestinationPickerBindings extends BaseBindings { Get.lazyPut(() => MailboxCacheDataSourceImpl(Get.find())); Get.lazyPut(() => StateDataSourceImpl(Get.find())); Get.lazyPut(() => EmailDataSourceImpl(Get.find())); - Get.lazyPut(() => ThreadDataSourceImpl(Get.find())); + Get.lazyPut(() => ThreadDataSourceImpl(Get.find(), Get.find())); } @override diff --git a/lib/features/mailbox/presentation/mailbox_bindings.dart b/lib/features/mailbox/presentation/mailbox_bindings.dart index f237a043d..a1428bae5 100644 --- a/lib/features/mailbox/presentation/mailbox_bindings.dart +++ b/lib/features/mailbox/presentation/mailbox_bindings.dart @@ -31,6 +31,7 @@ import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_na import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart'; import 'package:tmail_ui_user/features/thread/data/datasource_impl/thread_datasource_impl.dart'; import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart'; +import 'package:tmail_ui_user/features/thread/data/network/thread_isolate_worker.dart'; class MailboxBindings extends BaseBindings { @@ -73,7 +74,7 @@ class MailboxBindings extends BaseBindings { Get.lazyPut(() => MailboxCacheDataSourceImpl(Get.find())); Get.lazyPut(() => StateDataSourceImpl(Get.find())); Get.lazyPut(() => EmailDataSourceImpl(Get.find())); - Get.lazyPut(() => ThreadDataSourceImpl(Get.find())); + Get.lazyPut(() => ThreadDataSourceImpl(Get.find(), Get.find())); } @override diff --git a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart index b4327d8a3..11e5f155d 100644 --- a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart +++ b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart @@ -80,6 +80,7 @@ import 'package:tmail_ui_user/features/thread/data/datasource_impl/local_thread_ import 'package:tmail_ui_user/features/thread/data/datasource_impl/thread_datasource_impl.dart'; import 'package:tmail_ui_user/features/thread/data/local/email_cache_manager.dart'; import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart'; +import 'package:tmail_ui_user/features/thread/data/network/thread_isolate_worker.dart'; import 'package:tmail_ui_user/features/thread/data/repository/thread_repository_impl.dart'; import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart'; import 'package:tmail_ui_user/features/thread/presentation/thread_bindings.dart'; @@ -138,7 +139,7 @@ class MailboxDashBoardBindings extends BaseBindings { Get.find() )); Get.lazyPut(() => SearchDataSourceImpl(Get.find())); - Get.lazyPut(() => ThreadDataSourceImpl(Get.find())); + Get.lazyPut(() => ThreadDataSourceImpl(Get.find(), Get.find())); Get.lazyPut(() => LocalThreadDataSourceImpl(Get.find())); Get.lazyPut(() => StateDataSourceImpl(Get.find())); Get.lazyPut(() => MailboxDataSourceImpl(Get.find(), Get.find())); diff --git a/lib/features/thread/data/datasource/thread_datasource.dart b/lib/features/thread/data/datasource/thread_datasource.dart index a235df0af..0a466f6b2 100644 --- a/lib/features/thread/data/datasource/thread_datasource.dart +++ b/lib/features/thread/data/datasource/thread_datasource.dart @@ -1,3 +1,7 @@ +import 'dart:async'; + +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/filter/filter.dart'; import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; @@ -9,6 +13,7 @@ import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart'; import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart'; import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart'; +import 'package:dartz/dartz.dart' as dartz; abstract class ThreadDataSource { Future getAllEmail( @@ -33,4 +38,11 @@ abstract class ThreadDataSource { Future> getAllEmailCache({MailboxId? inMailboxId, Set? sort, FilterMessageOption? filterOption}); Future update({List? updated, List? created, List? destroyed}); + + Future> emptyTrashFolder( + AccountId accountId, + MailboxId mailboxId, + Future Function(State state) updateState, + Future Function(List? newDestroyed) updateDestroyedEmailCache, + ); } \ No newline at end of file diff --git a/lib/features/thread/data/datasource_impl/local_thread_datasource_impl.dart b/lib/features/thread/data/datasource_impl/local_thread_datasource_impl.dart index 1b765dcc4..46d0c075f 100644 --- a/lib/features/thread/data/datasource_impl/local_thread_datasource_impl.dart +++ b/lib/features/thread/data/datasource_impl/local_thread_datasource_impl.dart @@ -1,3 +1,8 @@ +import 'dart:async'; + +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart' as dartz; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/filter/filter.dart'; import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; @@ -60,4 +65,9 @@ class LocalThreadDataSourceImpl extends ThreadDataSource { throw error; }); } + + @override + Future> emptyTrashFolder(AccountId accountId, MailboxId mailboxId, Future Function(State state) updateState, Future Function(List? newDestroyed) updateDestroyedEmailCache) { + throw UnimplementedError(); + } } \ No newline at end of file diff --git a/lib/features/thread/data/datasource_impl/thread_datasource_impl.dart b/lib/features/thread/data/datasource_impl/thread_datasource_impl.dart index 9bd2a56ff..edcbc9976 100644 --- a/lib/features/thread/data/datasource_impl/thread_datasource_impl.dart +++ b/lib/features/thread/data/datasource_impl/thread_datasource_impl.dart @@ -1,3 +1,8 @@ +import 'dart:async'; + +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart' as dartz; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/filter/filter.dart'; import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; @@ -8,6 +13,7 @@ import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart'; import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart'; +import 'package:tmail_ui_user/features/thread/data/network/thread_isolate_worker.dart'; import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart'; import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart'; import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart'; @@ -15,8 +21,9 @@ import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option class ThreadDataSourceImpl extends ThreadDataSource { final ThreadAPI threadAPI; + final ThreadIsolateWorker _threadIsolateWorker; - ThreadDataSourceImpl(this.threadAPI); + ThreadDataSourceImpl(this.threadAPI, this._threadIsolateWorker); @override Future getAllEmail( @@ -69,4 +76,18 @@ class ThreadDataSourceImpl extends ThreadDataSource { Future update({List? updated, List? created, List? destroyed}) { throw UnimplementedError(); } + + @override + Future> emptyTrashFolder(AccountId accountId, MailboxId mailboxId, Future Function(State state) updateState, Future Function(List? newDestroyed) updateDestroyedEmailCache) { + return Future.sync(() async { + return await _threadIsolateWorker.emptyTrashFolder( + accountId, + mailboxId, + updateState, + updateDestroyedEmailCache, + ); + }).catchError((error) { + throw error; + }); + } } \ No newline at end of file diff --git a/lib/features/thread/data/model/empty_trash_folder_arguments.dart b/lib/features/thread/data/model/empty_trash_folder_arguments.dart index 9dbd04a7e..ed6021a72 100644 --- a/lib/features/thread/data/model/empty_trash_folder_arguments.dart +++ b/lib/features/thread/data/model/empty_trash_folder_arguments.dart @@ -9,17 +9,10 @@ import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart'; class EmptyTrashFolderArguments with EquatableMixin { AccountId accountId; MailboxId trashMailboxId; - Future Function(State state) updateState; - Future Function({ - List? newUpdated, - List? newCreated, - List? newDestroyed, - }) updateEmailCache; final ThreadAPI threadAPI; final EmailAPI emailAPI; - EmptyTrashFolderArguments(this.threadAPI, this.emailAPI, this.accountId, - this.trashMailboxId, this.updateEmailCache, this.updateState); + EmptyTrashFolderArguments(this.threadAPI, this.emailAPI, this.accountId, this.trashMailboxId); @override List get props => [accountId, trashMailboxId]; diff --git a/lib/features/thread/data/network/thread_isolate_worker.dart b/lib/features/thread/data/network/thread_isolate_worker.dart index 7255849e7..f059307f0 100644 --- a/lib/features/thread/data/network/thread_isolate_worker.dart +++ b/lib/features/thread/data/network/thread_isolate_worker.dart @@ -1,10 +1,7 @@ import 'dart:async'; -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; import 'package:core/utils/app_logger.dart'; import 'package:core/utils/build_utils.dart'; -import 'package:dartz/dartz.dart' as dartz; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; import 'package:jmap_dart_client/jmap/core/state.dart'; @@ -18,7 +15,6 @@ import 'package:model/email/email_property.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; import 'package:tmail_ui_user/features/thread/data/model/empty_trash_folder_arguments.dart'; import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart'; -import 'package:tmail_ui_user/features/thread/domain/state/empty_trash_folder_state.dart'; import 'package:worker_manager/worker_manager.dart'; class ThreadIsolateWorker { @@ -31,28 +27,22 @@ class ThreadIsolateWorker { Future> emptyTrashFolder( AccountId accountId, MailboxId mailboxId, - int totalEmailUnread, Future Function(State state) updateState, - Future Function({ - List? newUpdated, - List? newCreated, - List? newDestroyed, - }) updateEmailCache, - StreamController> onProgressController, + Future Function(List? newDestroyed) updateDestroyedEmailCache, ) async { if (BuildUtils.isWeb) { - return _emptyTrashFolderOnWeb(accountId, mailboxId, updateState, updateEmailCache, onProgressController); + return _emptyTrashFolderOnWeb(accountId, mailboxId, updateState, updateDestroyedEmailCache); } else { final result = await _isolateExecutor.execute( - arg1: EmptyTrashFolderArguments(_threadAPI, _emailAPI, accountId, mailboxId, updateEmailCache, updateState), + arg1: EmptyTrashFolderArguments(_threadAPI, _emailAPI, accountId, mailboxId), fun1: _emptyTrashFolderAction, notification: (value) { - if (value is List) { - log('ThreadIsolateWorker::markAsThreadRead(): onUpdateProgress: PERCENT ${value.length / totalEmailUnread}'); - onProgressController.add(dartz.Right(UpdatingEmptyTrashFolderState( - mailboxId: mailboxId, - countRemove: value.length, - ))); + if(value is State) { + updateState.call(value); + } + if (value is List) { + updateDestroyedEmailCache.call(value); + log('ThreadIsolateWorker::emptyTrashFolder(): onUpdateProgress: PERCENT ${value.length}'); } }); return result; @@ -75,7 +65,7 @@ class ThreadIsolateWorker { properties: Properties({EmailProperty.id})); if (emailsResponse.state != null) { - await args.updateState.call(emailsResponse.state!); + sendPort.send(emailsResponse.state!); } var newEmailList = emailsResponse.emailList ?? []; @@ -83,7 +73,7 @@ class ThreadIsolateWorker { newEmailList = newEmailList.where((email) => email.id != lastEmail!.id).toList(); } - log('ThreadRepositoryImpl::emptyTrashFolder(): ${newEmailList.length}'); + log('ThreadIsolateWorker::_emptyTrashFolderAction(): ${newEmailList.length}'); if (newEmailList.isNotEmpty == true) { lastEmail = newEmailList.last; @@ -93,7 +83,7 @@ class ThreadIsolateWorker { final listEmailIdDeleted = await args.emailAPI.deleteMultipleEmailsPermanently(args.accountId, emailIds); if (listEmailIdDeleted.isNotEmpty && listEmailIdDeleted.length == emailIds.length) { - await args.updateEmailCache(newDestroyed: listEmailIdDeleted); + sendPort.send(listEmailIdDeleted); } emailListCompleted.addAll(listEmailIdDeleted); @@ -103,9 +93,9 @@ class ThreadIsolateWorker { } } } catch (e) { - log('ThreadIsolateWorker::_emptyTrashFolderOnWeb(): ERROR: $e'); + log('ThreadIsolateWorker::_emptyTrashFolderAction(): ERROR: $e'); } - log('ThreadIsolateWorker::_emptyTrashFolderOnWeb(): TOTAL_REMOVE: ${emailListCompleted.length}'); + log('ThreadIsolateWorker::_emptyTrashFolderAction(): TOTAL_REMOVE: ${emailListCompleted.length}'); return emailListCompleted; } @@ -113,12 +103,7 @@ class ThreadIsolateWorker { AccountId accountId, MailboxId trashMailboxId, Future Function(State state) updateState, - Future Function({ - List? newUpdated, - List? newCreated, - List? newDestroyed, - }) updateEmailCache, - StreamController> onProgressController, + Future Function(List newDestroyed) updateDestroyedEmailCache, ) async { List emailListCompleted = List.empty(growable: true); try { @@ -143,7 +128,7 @@ class ThreadIsolateWorker { newEmailList = newEmailList.where((email) => email.id != lastEmail!.id).toList(); } - log('ThreadRepositoryImpl::emptyTrashFolder(): ${newEmailList.length}'); + log('ThreadIsolateWorker::_emptyTrashFolderOnWeb(): ${newEmailList.length}'); if (newEmailList.isNotEmpty == true) { lastEmail = newEmailList.last; @@ -153,13 +138,10 @@ class ThreadIsolateWorker { final listEmailIdDeleted = await _emailAPI.deleteMultipleEmailsPermanently(accountId, emailIds); if (listEmailIdDeleted.isNotEmpty && listEmailIdDeleted.length == emailIds.length) { - await updateEmailCache(newDestroyed: listEmailIdDeleted); + await updateDestroyedEmailCache(listEmailIdDeleted); } emailListCompleted.addAll(listEmailIdDeleted); - onProgressController.add(dartz.Right(UpdatingEmptyTrashFolderState( - mailboxId: trashMailboxId, - countRemove: emailListCompleted.length))); } else { hasEmails = false; } diff --git a/lib/features/thread/domain/repository/thread_repository.dart b/lib/features/thread/domain/repository/thread_repository.dart index 43641327f..365a2a3d6 100644 --- a/lib/features/thread/domain/repository/thread_repository.dart +++ b/lib/features/thread/domain/repository/thread_repository.dart @@ -1,3 +1,7 @@ +import 'dart:async'; + +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/filter/filter.dart'; import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; @@ -10,6 +14,7 @@ import 'package:tmail_ui_user/features/thread/domain/model/email_filter.dart'; import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart'; import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart'; import 'package:tmail_ui_user/features/thread/domain/model/get_email_request.dart'; +import 'package:dartz/dartz.dart' as dartz; abstract class ThreadRepository { Stream getAllEmail( @@ -47,5 +52,8 @@ abstract class ThreadRepository { } ); - Future emptyTrashFolder(AccountId accountId, MailboxId trashMailboxId); + Future> emptyTrashFolder( + AccountId accountId, + MailboxId trashMailboxId, + ); } \ No newline at end of file diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index 9e15adce4..cb8d19141 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -71,6 +71,8 @@ import 'package:tmail_ui_user/features/thread/presentation/model/search_status.d import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/app_routes.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; +import 'package:tmail_ui_user/main/utils/binding_tag.dart'; +import 'package:worker_manager/worker_manager.dart'; class ThreadController extends BaseController { @@ -78,6 +80,7 @@ class ThreadController extends BaseController { final _imagePaths = Get.find(); final _responsiveUtils = Get.find(); final _appToast = Get.find(); + final Executor _isolateExecutor = Get.find(tag: BindingTag.threadExecutor); final GetEmailsInMailboxInteractor _getEmailsInMailboxInteractor; final MarkAsMultipleEmailReadInteractor _markAsMultipleEmailReadInteractor; @@ -105,7 +108,6 @@ class ThreadController extends BaseController { final ScrollController listEmailController = ScrollController(); late Worker mailboxWorker, searchWorker, dashboardActionWorker, viewStateWorker, advancedSearchFilterWorker; - Set? get _sortOrder => {} ..add(EmailComparator(EmailComparatorProperty.receivedAt) ..setIsAscending(false)); @@ -141,6 +143,7 @@ class ThreadController extends BaseController { @override void onInit() { _initWorker(); + _initializeIsolateExecutor(); super.onInit(); } @@ -153,6 +156,7 @@ class ThreadController extends BaseController { @override void onClose() { listEmailController.dispose(); + _isolateExecutor.dispose(); _clearWorker(); super.onClose(); } @@ -315,6 +319,10 @@ class ThreadController extends BaseController { }); } + void _initializeIsolateExecutor() async { + await _isolateExecutor.warmUp(log: BuildUtils.isDebugMode); + } + void _clearWorker() { mailboxWorker.call(); dashboardActionWorker.call(); diff --git a/lib/main/bindings/network/network_bindings.dart b/lib/main/bindings/network/network_bindings.dart index a25fe8450..e6fc61eb4 100644 --- a/lib/main/bindings/network/network_bindings.dart +++ b/lib/main/bindings/network/network_bindings.dart @@ -20,6 +20,8 @@ import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_isolate_work import 'package:tmail_ui_user/features/manage_account/data/network/manage_account_api.dart'; import 'package:tmail_ui_user/features/session/data/network/session_api.dart'; import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart'; +import 'package:tmail_ui_user/features/thread/data/network/thread_isolate_worker.dart'; +import 'package:tmail_ui_user/main/utils/binding_tag.dart'; import 'package:worker_manager/worker_manager.dart'; class NetworkBindings extends Bindings { @@ -91,6 +93,11 @@ class NetworkBindings extends Bindings { } void _bindingIsolateWorker() { + Get.put(Executor(), tag: BindingTag.threadExecutor); + Get.put(ThreadIsolateWorker( + Get.find(), + Get.find(), + Get.find(tag: BindingTag.threadExecutor))); Get.put(Executor()); Get.put(MailboxIsolateWorker( Get.find(), diff --git a/lib/main/utils/binding_tag.dart b/lib/main/utils/binding_tag.dart new file mode 100644 index 000000000..1c35ce841 --- /dev/null +++ b/lib/main/utils/binding_tag.dart @@ -0,0 +1,4 @@ + +class BindingTag { + static const String threadExecutor = '#ThreadExecutor' ; +} \ No newline at end of file