From fc94e0aa047f715b821b3ec9bd7b6932b01abd32 Mon Sep 17 00:00:00 2001 From: dab246 Date: Fri, 26 May 2023 08:51:16 +0700 Subject: [PATCH] TF-1812 Handle add sending email to queue in work manager (cherry picked from commit c5d89665f525550949bf4e694133ce5ea2777846) --- ios/Podfile.lock | 6 ++ ios/Runner/AppDelegate.swift | 7 +++ ...sion.dart => email_request_extension.dart} | 0 .../composer/domain/model/sending_email.dart | 28 +++++++++ .../email_hive_cache_datasource_impl.dart | 1 - .../state/store_sending_email_state.dart | 11 +++- .../store_sending_email_interactor.dart | 2 +- .../mailbox_dashboard_controller.dart | 25 +++++++- .../config/work_manager_constants.dart | 1 + .../controller/work_scheduler_controller.dart | 58 ++++++++++--------- .../model/sending_email_hive_cache.dart | 6 +- .../observer/sending_email_observer.dart | 5 ++ .../offline_mode/scheduler/worker.dart | 2 +- model/lib/extensions/email_extension.dart | 4 ++ 14 files changed, 122 insertions(+), 34 deletions(-) rename lib/features/composer/domain/extensions/{email_requestl_extension.dart => email_request_extension.dart} (100%) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 390896e64..cada0c7e5 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -172,6 +172,8 @@ PODS: - SwiftyGif (5.4.4) - url_launcher_ios (0.0.1): - Flutter + - workmanager (0.0.1): + - Flutter DEPENDENCIES: - better_open_file (from `.symlinks/plugins/better_open_file/ios`) @@ -198,6 +200,7 @@ DEPENDENCIES: - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + - workmanager (from `.symlinks/plugins/workmanager/ios`) SPEC REPOS: trunk: @@ -270,6 +273,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/shared_preferences_foundation/ios" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" + workmanager: + :path: ".symlinks/plugins/workmanager/ios" SPEC CHECKSUMS: AppAuth: 8fca6b5563a5baef2c04bee27538025e4ceb2add @@ -315,6 +320,7 @@ SPEC CHECKSUMS: shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4 + workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6 PODFILE CHECKSUM: e209bf81fb4facf3f68c43dbffa0b2d05ff63db0 diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 732d22e1f..f293d82ca 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -20,6 +20,8 @@ import workmanager /// Register the app's plugins in the context of a normal run AppDelegate.registerPlugins(with: self) + UNUserNotificationCenter.current().delegate = self + if #available(iOS 10.0, *) { UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate } @@ -91,4 +93,9 @@ import workmanager return URL(string: "ShareMedia-\(appDomain)://dataUrl=\(sharedKey)#text") } + override func userNotificationCenter(_ center: UNUserNotificationCenter, + willPresent notification: UNNotification, + withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { + completionHandler(.alert) // shows banner even if app is in foreground + } } diff --git a/lib/features/composer/domain/extensions/email_requestl_extension.dart b/lib/features/composer/domain/extensions/email_request_extension.dart similarity index 100% rename from lib/features/composer/domain/extensions/email_requestl_extension.dart rename to lib/features/composer/domain/extensions/email_request_extension.dart diff --git a/lib/features/composer/domain/model/sending_email.dart b/lib/features/composer/domain/model/sending_email.dart index ee09c48a6..de30f07a1 100644 --- a/lib/features/composer/domain/model/sending_email.dart +++ b/lib/features/composer/domain/model/sending_email.dart @@ -1,9 +1,15 @@ + import 'package:equatable/equatable.dart'; +import 'package:jmap_dart_client/http/converter/email_id_nullable_converter.dart'; +import 'package:jmap_dart_client/http/converter/id_nullable_converter.dart'; +import 'package:jmap_dart_client/http/converter/identities/identity_id_nullable_converter.dart'; +import 'package:jmap_dart_client/http/converter/mailbox_id_nullable_converter.dart'; import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/email/email_action_type.dart'; +import 'package:model/extensions/email_extension.dart'; class SendingEmail with EquatableMixin { final String sendingId; @@ -28,6 +34,28 @@ class SendingEmail with EquatableMixin { this.creationIdRequest }); + Map toJson() { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('sendingId', sendingId); + writeNotNull('email', email.asString()); + writeNotNull('emailActionType', emailActionType.name); + writeNotNull('sentMailboxId', const MailboxIdNullableConverter().toJson(sentMailboxId)); + writeNotNull('emailIdDestroyed', const EmailIdNullableConverter().toJson(emailIdDestroyed)); + writeNotNull('emailIdAnsweredOrForwarded', const EmailIdNullableConverter().toJson(emailIdAnsweredOrForwarded)); + writeNotNull('identityId', const IdentityIdNullableConverter().toJson(identityId)); + writeNotNull('mailboxNameRequest', mailboxNameRequest?.name); + writeNotNull('creationIdRequest', const IdNullableConverter().toJson(creationIdRequest)); + + return val; + } + @override List get props => [ sendingId, diff --git a/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart index 63349472c..51aa46e3f 100644 --- a/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart @@ -35,7 +35,6 @@ import 'package:tmail_ui_user/features/offline_mode/manager/detailed_email_cache import 'package:tmail_ui_user/features/offline_mode/manager/detailed_email_cache_worker_queue.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/opened_email_cache_manager.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/opened_email_cache_worker_queue.dart'; -import 'package:tmail_ui_user/features/offline_mode/worker/hive_task.dart'; import 'package:tmail_ui_user/features/thread/data/extensions/email_cache_extension.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/sending_email_cache_manager.dart'; import 'package:tmail_ui_user/features/thread/data/extensions/email_extension.dart'; diff --git a/lib/features/email/domain/state/store_sending_email_state.dart b/lib/features/email/domain/state/store_sending_email_state.dart index 2ef3e341e..9a3599907 100644 --- a/lib/features/email/domain/state/store_sending_email_state.dart +++ b/lib/features/email/domain/state/store_sending_email_state.dart @@ -1,10 +1,19 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; +import 'package:tmail_ui_user/features/composer/domain/model/sending_email.dart'; class StoreSendingEmailLoading extends UIState {} -class StoreSendingEmailSuccess extends UIState {} +class StoreSendingEmailSuccess extends UIState { + + final SendingEmail sendingEmail; + + StoreSendingEmailSuccess(this.sendingEmail); + + @override + List get props => [sendingEmail]; +} class StoreSendingEmailFailure extends FeatureFailure { StoreSendingEmailFailure(dynamic exception) : super(exception: exception); diff --git a/lib/features/email/domain/usecases/store_sending_email_interactor.dart b/lib/features/email/domain/usecases/store_sending_email_interactor.dart index 88c639e70..67bc46962 100644 --- a/lib/features/email/domain/usecases/store_sending_email_interactor.dart +++ b/lib/features/email/domain/usecases/store_sending_email_interactor.dart @@ -16,7 +16,7 @@ class StoreSendingEmailInteractor { try { yield Right(StoreSendingEmailLoading()); await _emailRepository.storeSendingEmail(accountId, userName, sendingEmail); - yield Right(StoreSendingEmailSuccess()); + yield Right(StoreSendingEmailSuccess(sendingEmail)); } catch (e) { yield Left(StoreSendingEmailFailure(e)); } diff --git a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart index 95a2c0288..065981227 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -25,7 +25,7 @@ import 'package:rxdart/transformers.dart'; import 'package:tmail_ui_user/features/base/action/ui_action.dart'; import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart'; import 'package:tmail_ui_user/features/composer/domain/exceptions/set_email_method_exception.dart'; -import 'package:tmail_ui_user/features/composer/domain/extensions/email_requestl_extension.dart'; +import 'package:tmail_ui_user/features/composer/domain/extensions/email_request_extension.dart'; import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'; import 'package:tmail_ui_user/features/composer/domain/model/sending_email.dart'; import 'package:tmail_ui_user/features/composer/domain/state/save_email_as_drafts_state.dart'; @@ -86,6 +86,10 @@ import 'package:tmail_ui_user/features/manage_account/presentation/extensions/va import 'package:tmail_ui_user/features/manage_account/presentation/model/account_menu_item.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/model/manage_account_arguments.dart'; import 'package:tmail_ui_user/features/network_status_handle/presentation/network_connnection_controller.dart'; +import 'package:tmail_ui_user/features/offline_mode/config/work_manager_constants.dart'; +import 'package:tmail_ui_user/features/offline_mode/controller/work_scheduler_controller.dart'; +import 'package:tmail_ui_user/features/offline_mode/scheduler/one_time_work_request.dart'; +import 'package:tmail_ui_user/features/offline_mode/scheduler/worker_type.dart'; import 'package:tmail_ui_user/features/push_notification/domain/state/get_email_state_to_refresh_state.dart'; import 'package:tmail_ui_user/features/push_notification/domain/state/get_mailbox_state_to_refresh_state.dart'; import 'package:tmail_ui_user/features/push_notification/domain/usecases/delete_email_state_to_refresh_interactor.dart'; @@ -117,6 +121,8 @@ import 'package:tmail_ui_user/main/routes/router_arguments.dart'; import 'package:tmail_ui_user/main/utils/email_receive_manager.dart'; import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; import 'package:uuid/uuid.dart'; +import 'package:tmail_ui_user/features/offline_mode/scheduler/worker.dart' as worker_scheduler; +import 'package:workmanager/workmanager.dart' as work_manager; class MailboxDashBoardController extends ReloadableController { @@ -1751,6 +1757,23 @@ class MailboxDashBoardController extends ReloadableController { leadingSVGIconColor: Colors.white, leadingSVGIcon: _imagePaths.icEmail); } + + _addSendingEmailToSendingQueue(success.sendingEmail); + } + + void _addSendingEmailToSendingQueue(SendingEmail sendingEmail) async { + log('MailboxDashBoardController::_addSendingEmailToSendingQueue():sendingEmail: $sendingEmail'); + final worker = worker_scheduler.Worker( + sendingEmail.sendingId, + WorkerType.sendingEmail, + sendingEmail.toJson() + ); + final workRequest = OneTimeWorkRequest( + worker, + initialDelay: const Duration(milliseconds: WorkManagerConstants.initialDelayTime), + constraints: work_manager.Constraints(networkType: work_manager.NetworkType.connected) + ); + await WorkSchedulerController().enqueue(workRequest); } @override diff --git a/lib/features/offline_mode/config/work_manager_constants.dart b/lib/features/offline_mode/config/work_manager_constants.dart index 4dc9a09c5..d00ec2db7 100644 --- a/lib/features/offline_mode/config/work_manager_constants.dart +++ b/lib/features/offline_mode/config/work_manager_constants.dart @@ -2,4 +2,5 @@ class WorkManagerConstants { static const String sendingEmailUniqueId = 'com.linagora.ios.teammail.sendingQueue'; static const String workerTypeKey = 'worker_type'; + static const int initialDelayTime = 2000; } \ No newline at end of file diff --git a/lib/features/offline_mode/controller/work_scheduler_controller.dart b/lib/features/offline_mode/controller/work_scheduler_controller.dart index 639bb9c88..e110e32f3 100644 --- a/lib/features/offline_mode/controller/work_scheduler_controller.dart +++ b/lib/features/offline_mode/controller/work_scheduler_controller.dart @@ -17,32 +17,38 @@ class WorkSchedulerController { factory WorkSchedulerController() => _instance ??= WorkSchedulerController._(); Future enqueue(WorkRequest workRequest) { - if (workRequest is OneTimeWorkRequest) { - return Workmanager().registerOneOffTask( - workRequest.worker.uniqueId, - workRequest.worker.id, - tag: workRequest.worker.type.name, - initialDelay: workRequest.initialDelay, - constraints: workRequest.constraints, - backoffPolicy: workRequest.backoffPolicy, - backoffPolicyDelay: workRequest.backoffPolicyDelay, - outOfQuotaPolicy: workRequest.outOfQuotaPolicy, - inputData: workRequest.worker.inputData - ); - } if (workRequest is PeriodicWorkRequest) { - return Workmanager().registerPeriodicTask( - workRequest.worker.uniqueId, - workRequest.worker.id, - frequency: workRequest.frequency, - tag: workRequest.worker.type.name, - initialDelay: workRequest.initialDelay, - constraints: workRequest.constraints, - backoffPolicy: workRequest.backoffPolicy, - backoffPolicyDelay: workRequest.backoffPolicyDelay, - outOfQuotaPolicy: workRequest.outOfQuotaPolicy, - inputData: workRequest.worker.inputData - ); - } else { + try { + log('WorkSchedulerController::enqueue():workRequest: $workRequest'); + if (workRequest is OneTimeWorkRequest) { + return Workmanager().registerOneOffTask( + workRequest.worker.uniqueId, + workRequest.worker.id, + tag: workRequest.worker.type.name, + initialDelay: workRequest.initialDelay, + constraints: workRequest.constraints, + backoffPolicy: workRequest.backoffPolicy, + backoffPolicyDelay: workRequest.backoffPolicyDelay, + outOfQuotaPolicy: workRequest.outOfQuotaPolicy, + inputData: workRequest.worker.inputData + ); + } if (workRequest is PeriodicWorkRequest) { + return Workmanager().registerPeriodicTask( + workRequest.worker.uniqueId, + workRequest.worker.id, + frequency: workRequest.frequency, + tag: workRequest.worker.type.name, + initialDelay: workRequest.initialDelay, + constraints: workRequest.constraints, + backoffPolicy: workRequest.backoffPolicy, + backoffPolicyDelay: workRequest.backoffPolicyDelay, + outOfQuotaPolicy: workRequest.outOfQuotaPolicy, + inputData: workRequest.worker.inputData + ); + } else { + return Future.value(); + } + } catch (e) { + logError('WorkSchedulerController::enqueue(): EXCEPTION: $e'); return Future.value(); } } diff --git a/lib/features/offline_mode/model/sending_email_hive_cache.dart b/lib/features/offline_mode/model/sending_email_hive_cache.dart index cd32a56ca..6a45156cc 100644 --- a/lib/features/offline_mode/model/sending_email_hive_cache.dart +++ b/lib/features/offline_mode/model/sending_email_hive_cache.dart @@ -12,7 +12,7 @@ class SendingEmailHiveCache extends HiveObject with EquatableMixin { final String sendingId; @HiveField(1) - final Map emailData; + final Map email; @HiveField(2) final String emailActionType; @@ -37,7 +37,7 @@ class SendingEmailHiveCache extends HiveObject with EquatableMixin { SendingEmailHiveCache( this.sendingId, - this.emailData, + this.email, this.emailActionType, this.sentMailboxId, this.emailIdDestroyed, @@ -50,7 +50,7 @@ class SendingEmailHiveCache extends HiveObject with EquatableMixin { @override List get props => [ sendingId, - emailData, + email, emailActionType, sentMailboxId, emailIdDestroyed, diff --git a/lib/features/offline_mode/observer/sending_email_observer.dart b/lib/features/offline_mode/observer/sending_email_observer.dart index f60e9c5c3..d0bb7ac9e 100644 --- a/lib/features/offline_mode/observer/sending_email_observer.dart +++ b/lib/features/offline_mode/observer/sending_email_observer.dart @@ -1,4 +1,5 @@ +import 'package:core/utils/app_logger.dart'; import 'package:tmail_ui_user/features/offline_mode/observer/work_observer.dart'; class SendingEmailObserver extends WorkObserver { @@ -11,9 +12,13 @@ class SendingEmailObserver extends WorkObserver { @override Future observe(String taskId, Map inputData) async { + log('SendingEmailObserver::observe():taskId: $taskId | inputData: $inputData'); + return Future.value(); } @override Future bindDI() async { + log('SendingEmailObserver::bindDI(): '); + return Future.value(); } } \ No newline at end of file diff --git a/lib/features/offline_mode/scheduler/worker.dart b/lib/features/offline_mode/scheduler/worker.dart index 19dd7c2e5..a8480efaf 100644 --- a/lib/features/offline_mode/scheduler/worker.dart +++ b/lib/features/offline_mode/scheduler/worker.dart @@ -21,7 +21,7 @@ class Worker with EquatableMixin { } Map get inputData { - data[WorkManagerConstants.workerTypeKey] = type; + data[WorkManagerConstants.workerTypeKey] = type.name; return data; } diff --git a/model/lib/extensions/email_extension.dart b/model/lib/extensions/email_extension.dart index 75ba2d0ec..48c6c576a 100644 --- a/model/lib/extensions/email_extension.dart +++ b/model/lib/extensions/email_extension.dart @@ -1,4 +1,6 @@ +import 'dart:convert'; + import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_body_part.dart'; @@ -8,6 +10,8 @@ import 'package:model/model.dart'; extension EmailExtension on Email { + String asString() => jsonEncode(toJson()); + bool get hasRead => keywords?.containsKey(KeyWordIdentifier.emailSeen) == true; bool get hasStarred => keywords?.containsKey(KeyWordIdentifier.emailFlagged) == true;