diff --git a/lib/features/base/base_controller.dart b/lib/features/base/base_controller.dart index fb6d72f6e..52b02c346 100644 --- a/lib/features/base/base_controller.dart +++ b/lib/features/base/base_controller.dart @@ -243,9 +243,6 @@ abstract class BaseController extends GetxController FcmConfiguration.initialize(); FcmInteractorBindings().dependencies(); FcmMessageController.instance.initializeFromAccountId(accountId, session); - if (!BuildUtils.isWeb) { - LocalNotificationManager.instance.setUp(); - } } else { throw NotSupportFCMException(); } diff --git a/lib/features/composer/domain/model/sending_email.dart b/lib/features/composer/domain/model/sending_email.dart index a1338900b..dbefc2da0 100644 --- a/lib/features/composer/domain/model/sending_email.dart +++ b/lib/features/composer/domain/model/sending_email.dart @@ -1,10 +1,12 @@ import 'dart:convert'; +import 'package:core/domain/extensions/datetime_extension.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'; 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/http/converter/mailbox_name_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'; @@ -73,18 +75,19 @@ class SendingEmail with EquatableMixin { sendingId: json['sendingId'] as String, email: Email.fromJson(jsonDecode(json['email'])), emailActionType: _getEmailActionType(json['emailActionType'] as String), - sentMailboxId: json['sentMailboxId'] != null ? const MailboxIdNullableConverter().fromJson(json['sentMailboxId'] as String?) : null, - emailIdDestroyed: json['emailIdDestroyed'] != null ? const EmailIdNullableConverter().fromJson(json['emailIdDestroyed'] as String?) : null, - emailIdAnsweredOrForwarded: json['emailIdAnsweredOrForwarded'] != null ? const EmailIdNullableConverter().fromJson(json['emailIdAnsweredOrForwarded'] as String?) : null, - identityId: json['identityId'] != null ? const IdentityIdNullableConverter().fromJson(json['identityId'] as String?) : null, - mailboxNameRequest: json['mailboxNameRequest'] != null ? MailboxName(json['mailboxNameRequest'] as String) : null, - creationIdRequest: json['creationIdRequest'] != null ? const IdNullableConverter().fromJson(json['creationIdRequest'] as String) : null, + createTime: DateTime.parse(json['createTime'] as String), + sentMailboxId: const MailboxIdNullableConverter().fromJson(json['sentMailboxId'] as String?), + emailIdDestroyed: const EmailIdNullableConverter().fromJson(json['emailIdDestroyed'] as String?), + emailIdAnsweredOrForwarded: const EmailIdNullableConverter().fromJson(json['emailIdAnsweredOrForwarded'] as String?), + identityId: const IdentityIdNullableConverter().fromJson(json['identityId'] as String?), + mailboxNameRequest: const MailboxNameConverter().fromJson(json['mailboxNameRequest'] as String?), + creationIdRequest: const IdNullableConverter().fromJson(json['creationIdRequest'] as String?), ); } static EmailActionType _getEmailActionType(String value) { return EmailActionType.values.firstWhere( - (type) => type.toString().split('.').last == value, + (type) => type.name == value, orElse: () => throw ArgumentError('Invalid email action type: $value'), ); } diff --git a/lib/features/email/data/datasource/email_datasource.dart b/lib/features/email/data/datasource/email_datasource.dart index 245f30e86..ca6f9ab1d 100644 --- a/lib/features/email/data/datasource/email_datasource.dart +++ b/lib/features/email/data/datasource/email_datasource.dart @@ -94,5 +94,5 @@ abstract class EmailDataSource { Future> getAllSendingEmails(AccountId accountId, UserName userName); - Future deleteSendingEmail(AccountId accountId, UserName userName, EmailId emailId); + Future deleteSendingEmail(AccountId accountId, UserName userName, String sendingId); } \ No newline at end of file diff --git a/lib/features/email/data/datasource_impl/email_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_datasource_impl.dart index 63e87eaa3..198062d8d 100644 --- a/lib/features/email/data/datasource_impl/email_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_datasource_impl.dart @@ -200,7 +200,7 @@ class EmailDataSourceImpl extends EmailDataSource { } @override - Future deleteSendingEmail(AccountId accountId, UserName userName, EmailId emailId) { + Future deleteSendingEmail(AccountId accountId, UserName userName, String sendingId) { throw UnimplementedError(); } } \ No newline at end of file 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 2b55a783b..57c3639f5 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 @@ -261,11 +261,10 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource { }).catchError(_exceptionThrower.throwException); } - @override - Future deleteSendingEmail(AccountId accountId, UserName userName, EmailId emailId) { + Future deleteSendingEmail(AccountId accountId, UserName userName, String sendingId) { return Future.sync(() async { - return await _sendingEmailCacheManager.deleteSendingEmail(accountId, userName, emailId.id.value); + return await _sendingEmailCacheManager.deleteSendingEmail(accountId, userName, sendingId); }).catchError(_exceptionThrower.throwException); } } \ No newline at end of file diff --git a/lib/features/email/data/repository/email_repository_impl.dart b/lib/features/email/data/repository/email_repository_impl.dart index fb0c938d3..9301b08d6 100644 --- a/lib/features/email/data/repository/email_repository_impl.dart +++ b/lib/features/email/data/repository/email_repository_impl.dart @@ -228,7 +228,7 @@ class EmailRepositoryImpl extends EmailRepository { } @override - Future deleteSendingEmail(AccountId accountId, UserName userName, EmailId emailId) { - return emailDataSource[DataSourceType.hiveCache]!.deleteSendingEmail(accountId, userName, emailId); + Future deleteSendingEmail(AccountId accountId, UserName userName, String sendingId) { + return emailDataSource[DataSourceType.hiveCache]!.deleteSendingEmail(accountId, userName, sendingId); } } \ No newline at end of file diff --git a/lib/features/email/domain/repository/email_repository.dart b/lib/features/email/domain/repository/email_repository.dart index 64789f2b9..49cbc4d50 100644 --- a/lib/features/email/domain/repository/email_repository.dart +++ b/lib/features/email/domain/repository/email_repository.dart @@ -104,5 +104,5 @@ abstract class EmailRepository { Future storeSendingEmail(AccountId accountId, UserName userName, SendingEmail sendingEmail); - Future deleteSendingEmail(AccountId accountId, UserName userName, EmailId emailId); + Future deleteSendingEmail(AccountId accountId, UserName userName, String sendingId); } \ No newline at end of file diff --git a/lib/features/email/domain/state/delete_sending_email_state.dart b/lib/features/email/domain/state/delete_sending_email_state.dart index 4d9d5c6b1..bffb50b8d 100644 --- a/lib/features/email/domain/state/delete_sending_email_state.dart +++ b/lib/features/email/domain/state/delete_sending_email_state.dart @@ -3,11 +3,7 @@ import 'package:core/presentation/state/success.dart'; class DeleteSendingEmailLoading extends UIState {} -class DeleteSendingEmailSuccess extends UIState { - - @override - List get props => []; -} +class DeleteSendingEmailSuccess extends UIState {} class DeleteSendingEmailFailure extends FeatureFailure { DeleteSendingEmailFailure(dynamic exception) : super(exception: exception); diff --git a/lib/features/email/domain/usecases/delete_sending_email_interactor.dart b/lib/features/email/domain/usecases/delete_sending_email_interactor.dart index 21b758d04..a3376e111 100644 --- a/lib/features/email/domain/usecases/delete_sending_email_interactor.dart +++ b/lib/features/email/domain/usecases/delete_sending_email_interactor.dart @@ -12,10 +12,10 @@ class DeleteSendingEmailInteractor { DeleteSendingEmailInteractor(this._emailRepository); - Stream> execute(AccountId accountId, UserName userName, EmailId emailId) async* { + Stream> execute(AccountId accountId, UserName userName, String sendingId) async* { try { yield Right(DeleteSendingEmailLoading()); - await _emailRepository.deleteSendingEmail(accountId, userName, emailId); + await _emailRepository.deleteSendingEmail(accountId, userName, sendingId); yield Right(DeleteSendingEmailSuccess()); } catch (e) { yield Left(DeleteSendingEmailFailure(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 39dde3f7f..b986fca5e 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -1533,7 +1533,7 @@ class MailboxDashBoardController extends ReloadableController { void _handleMessageFromNotification(String? payload, {bool onForeground = true}) { log('MailboxDashBoardController::_handleMessageFromNotification():payload: $payload'); - if (payload == null) { + if (payload == null || payload.isEmpty) { dispatchRoute(DashboardRoutes.thread); return; } diff --git a/lib/features/offline_mode/controller/work_scheduler_controller.dart b/lib/features/offline_mode/controller/work_scheduler_controller.dart index 0193e3f10..71ba1f3cd 100644 --- a/lib/features/offline_mode/controller/work_scheduler_controller.dart +++ b/lib/features/offline_mode/controller/work_scheduler_controller.dart @@ -64,7 +64,7 @@ class WorkSchedulerController { final matchedType = WorkerType.values.firstWhereOrNull((type) => type.name == workerType); if (matchedType != null) { - await matchedType.usingObserver().bindDI(completer); + await matchedType.usingObserver().bindDI(); await matchedType.usingObserver().observe(taskName, dataObject, completer); } } diff --git a/lib/features/offline_mode/exceptions/workmanager_exception.dart b/lib/features/offline_mode/exceptions/workmanager_exception.dart new file mode 100644 index 000000000..b765fad07 --- /dev/null +++ b/lib/features/offline_mode/exceptions/workmanager_exception.dart @@ -0,0 +1 @@ +class SendingEmailFromWorkmanagerException implements Exception {} \ No newline at end of file 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 e593d0916..a7087b133 100644 --- a/lib/features/offline_mode/manager/sending_email_cache_manager.dart +++ b/lib/features/offline_mode/manager/sending_email_cache_manager.dart @@ -34,9 +34,9 @@ class SendingEmailCacheManager { Future deleteSendingEmail( AccountId accountId, UserName userName, - String sendingEmailId + String sendingId ) { - final keyCache = TupleKey(sendingEmailId, accountId.asString, userName.value).encodeKey; + final keyCache = TupleKey(sendingId, accountId.asString, userName.value).encodeKey; log('SendingEmailCacheManager::deleteSendingEmail():keyCache: $keyCache'); return _hiveCacheClient.deleteItem(keyCache); } diff --git a/lib/features/offline_mode/observer/sending_email_observer.dart b/lib/features/offline_mode/observer/sending_email_observer.dart index 449101c59..ca459cabe 100644 --- a/lib/features/offline_mode/observer/sending_email_observer.dart +++ b/lib/features/offline_mode/observer/sending_email_observer.dart @@ -1,16 +1,12 @@ import 'dart:async'; import 'package:core/data/network/config/dynamic_url_interceptors.dart'; import 'package:core/presentation/extensions/uri_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:core/utils/app_logger.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; -import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; -import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:model/oidc/token_oidc.dart'; import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart'; import 'package:tmail_ui_user/features/composer/domain/extensions/sending_email_extension.dart'; @@ -28,11 +24,13 @@ import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_a import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart'; import 'package:tmail_ui_user/features/offline_mode/biding/sending_email_biding.dart'; +import 'package:tmail_ui_user/features/offline_mode/exceptions/workmanager_exception.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'; +import 'package:tmail_ui_user/features/push_notification/presentation/notification/local_notification_manager.dart'; import 'package:tmail_ui_user/features/session/domain/state/get_session_state.dart'; import 'package:tmail_ui_user/features/session/domain/usecases/get_session_interactor.dart'; import 'package:tmail_ui_user/main/bindings/main_bindings.dart'; -import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; class SendingEmailObserver extends WorkObserver { @@ -41,10 +39,7 @@ class SendingEmailObserver extends WorkObserver { Session? _currentSession; UserName? _userName; SendingEmail? _sendingEmail; - EmailId? _emailId; - AppToast? _appToast; - ImagePaths? _imagePaths; SendEmailInteractor? _sendEmailInteractor; GetAuthenticatedAccountInteractor? _getAuthenticatedAccountInteractor; DynamicUrlInterceptors? _dynamicUrlInterceptors; @@ -65,17 +60,15 @@ class SendingEmailObserver extends WorkObserver { log('SendingEmailObserver::observe():taskId: $taskId | inputData: $inputData'); _completer = completer; _sendingEmail = SendingEmail.fromJson(inputData); - _emailId = EmailId(Id(taskId)); - log('SendingEmailObserver::observe():_sendingEmail: $_sendingEmail | EmailId: $_emailId'); + log('SendingEmailObserver::observe():_sendingEmail: $_sendingEmail'); _getAuthenticatedAccount(); } @override - Future bindDI(Completer completer) async { + Future bindDI() async { return await _initialAppConfig(); } - Future _initialAppConfig() async { await Future.wait([ MainBindings().dependencies(), @@ -97,7 +90,6 @@ class SendingEmailObserver extends WorkObserver { log('SendingEmailObserver::_handleFailureViewState(): $failure'); } - @override void handleSuccessViewState(Success success) { if (success is GetAuthenticatedAccountSuccess) { @@ -121,15 +113,12 @@ class SendingEmailObserver extends WorkObserver { _dynamicUrlInterceptors = getBinding(); _authorizationInterceptors = getBinding(); _getSessionInteractor = getBinding(); - _appToast = getBinding(); - _imagePaths = getBinding(); _sendEmailInteractor = getBinding(); _deleteSendingEmailInteractor = getBinding(); } catch (e) { logError('SendingEmailObserver::_getInteractorBindings(): ${e.toString()}'); return _completer?.completeError(false); } - return _completer?.complete(true); } void _handleGetAuthenticatedAccountSuccess(GetAuthenticatedAccountSuccess success) { @@ -200,6 +189,7 @@ class SendingEmailObserver extends WorkObserver { : null )); } else { + _clearDataQueue(); logError('SendingEmailObserver::_sendEmailAction():_sendEmailInteractor is null'); } } @@ -219,33 +209,37 @@ class SendingEmailObserver extends WorkObserver { void _handleSendEmailSuccess(SendEmailSuccess success) async { log("SendingEmailObserver::_handleSendEmailSuccess()"); _deleteSendingEmailAction(); - if (currentOverlayContext != null && currentContext != null) { - _appToast?.showToastSuccessMessage( - currentOverlayContext!, - AppLocalizations.of(currentContext!).messageHasBeenSentSuccessfully, - leadingSVGIcon: _imagePaths?.icSendSuccessToast); - } } void _deleteSendingEmailAction() { - if (_deleteSendingEmailInteractor != null && _currentSession != null && _currentAccountId != null && _emailId != null) { + if (_deleteSendingEmailInteractor != null && _currentSession != null && _currentAccountId != null && _sendingEmail != null) { consumeState(_deleteSendingEmailInteractor!.execute( _currentAccountId!, _currentSession!.username, - _emailId!, + _sendingEmail!.sendingId, )); } else { + _clearDataQueue(); logError('SendingEmailObserver::_deleteSendingEmailAction():_deleteSendingEmailInteractor is null'); } } void _handleDeleteSendingEmailSuccess() async { log('SendingEmailObserver::_handleDeleteSendingEmailSuccess(): Success'); + _showLocalNotification(); return _completer?.complete(true); } void _clearDataQueue() async { _sendingEmail = null; - return _completer?.completeError(false); + _completer?.completeError(SendingEmailFromWorkmanagerException()); + } + + void _showLocalNotification() { + log('SendingEmailObserver::_showLocalNotification():SendingId: ${_sendingEmail?.sendingId}'); + LocalNotificationManager.instance.showPushNotification( + id: _sendingEmail?.sendingId ?? '', + title: LocalNotificationConfig.messageHasBeenSentSuccessfully, + ); } } \ No newline at end of file diff --git a/lib/features/offline_mode/observer/work_observer.dart b/lib/features/offline_mode/observer/work_observer.dart index 3c1265745..286828b21 100644 --- a/lib/features/offline_mode/observer/work_observer.dart +++ b/lib/features/offline_mode/observer/work_observer.dart @@ -1,13 +1,11 @@ - 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:dartz/dartz.dart'; abstract class WorkObserver { - Future bindDI(Completer completer); + Future bindDI(); Future observe(String taskId, Map inputData, Completer completer); diff --git a/lib/features/push_notification/presentation/notification/local_notification_config.dart b/lib/features/push_notification/presentation/notification/local_notification_config.dart index 65612ca88..8af71cd80 100644 --- a/lib/features/push_notification/presentation/notification/local_notification_config.dart +++ b/lib/features/push_notification/presentation/notification/local_notification_config.dart @@ -9,6 +9,7 @@ class LocalNotificationConfig { static const _channelDescription = 'Team Mail notifications'; static const notificationTitle = 'Team Mail'; static const notificationMessage = 'You have new messages'; + static const messageHasBeenSentSuccessfully = 'Message has been sent successfully.'; static const int groupNotificationId = 1995; static const iosInitializationSettings = DarwinInitializationSettings(); diff --git a/lib/main.dart b/lib/main.dart index 670efe229..b7194e1a6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,6 +6,7 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:get/get.dart'; import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart'; import 'package:tmail_ui_user/features/offline_mode/config/work_manager_config.dart'; +import 'package:tmail_ui_user/features/push_notification/presentation/notification/local_notification_manager.dart'; import 'package:tmail_ui_user/main/bindings/main_bindings.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations_delegate.dart'; @@ -24,8 +25,10 @@ void main() async { MainBindings().dependencies(), HiveCacheConfig().setUp(), Executor().warmUp(), - if (PlatformInfo.isMobile) - WorkManagerConfig().initialize(), + if (PlatformInfo.isMobile) ... [ + WorkManagerConfig().initialize(), + LocalNotificationManager.instance.setUp(), + ], AppUtils.loadEnvFile() ]); await HiveCacheConfig.initializeEncryptionKey();