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 51badac3a..3b8e91f57 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -4,11 +4,13 @@ import 'package:core/core.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; import 'package:jmap_dart_client/jmap/core/capability/mail_capability.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/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; @@ -73,14 +75,18 @@ import 'package:tmail_ui_user/features/push_notification/domain/usecases/delete_ import 'package:tmail_ui_user/features/push_notification/domain/usecases/get_email_state_to_refresh_interactor.dart'; import 'package:tmail_ui_user/features/push_notification/domain/usecases/get_mailbox_state_to_refresh_interactor.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/controller/fcm_controller.dart'; +import 'package:tmail_ui_user/features/push_notification/presentation/notification/local_notification_manager.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/services/fcm_service.dart'; +import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart'; import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart'; import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; import 'package:tmail_ui_user/features/thread/domain/state/empty_trash_folder_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/get_email_by_id_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/mark_as_multiple_email_read_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/mark_as_star_multiple_email_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/move_multiple_email_to_mailbox_state.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/empty_trash_folder_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/get_email_by_id_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_multiple_email_read_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_star_multiple_email_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/move_multiple_email_to_mailbox_interactor.dart'; @@ -116,6 +122,7 @@ class MailboxDashBoardController extends ReloadableController { final MoveMultipleEmailToMailboxInteractor _moveMultipleEmailToMailboxInteractor; final EmptyTrashFolderInteractor _emptyTrashFolderInteractor; final DeleteMultipleEmailsPermanentlyInteractor _deleteMultipleEmailsPermanentlyInteractor; + final GetEmailByIdInteractor _getEmailByIdInteractor; GetAllVacationInteractor? _getAllVacationInteractor; UpdateVacationInteractor? _updateVacationInteractor; @@ -130,7 +137,7 @@ class MailboxDashBoardController extends ReloadableController { final accountId = Rxn(); final userProfile = Rxn(); final dashBoardAction = Rxn(); - final dashboardRoute = DashboardRoutes.thread.obs; + final dashboardRoute = DashboardRoutes.waiting.obs; final appInformation = Rxn(); final currentSelectMode = SelectMode.INACTIVE.obs; final filterMessageOption = FilterMessageOption.all.obs; @@ -173,6 +180,7 @@ class MailboxDashBoardController extends ReloadableController { this._moveMultipleEmailToMailboxInteractor, this._emptyTrashFolderInteractor, this._deleteMultipleEmailsPermanentlyInteractor, + this._getEmailByIdInteractor, ) : super(logoutOidcInteractor, deleteAuthorityOidcInteractor, getAuthenticatedAccountInteractor); @@ -189,6 +197,7 @@ class MailboxDashBoardController extends ReloadableController { _registerPendingFileInfo(); _getSessionCurrent(); _getAppVersion(); + _handleOpenAppByNotification(); super.onReady(); } @@ -259,7 +268,8 @@ class MailboxDashBoardController extends ReloadableController { clearState(); } else if (failure is SaveEmailAsDraftsFailure || failure is RemoveEmailDraftsFailure - || failure is UpdateEmailDraftsFailure) { + || failure is UpdateEmailDraftsFailure + || failure is GetEmailByIdFailure) { clearState(); } else if (failure is MarkAsMailboxReadAllFailure || failure is MarkAsMailboxReadFailure) { @@ -307,6 +317,8 @@ class MailboxDashBoardController extends ReloadableController { _deleteMultipleEmailsPermanentlySuccess(success); } else if (success is GetAppDashboardConfigurationSuccess) { appGridDashboardController.handleShowAppDashboard(success.linagoraApplications); + } else if(success is GetEmailByIdSuccess) { + _moveToEmailDetailedView(success); } } ); @@ -351,6 +363,19 @@ class MailboxDashBoardController extends ReloadableController { _refreshActionEventController.stream .debounceTime(const Duration(milliseconds: FcmService.durationMessageComing)) .listen(_handleRefreshActionWhenBackToApp); + + LocalNotificationManager.instance.localNotificationStream.listen(_handleLocalNotificationResponse); + } + + void _handleOpenAppByNotification() async { + log("_handleOpenAppByNotification(): isOpenAppByNotification: ${LocalNotificationManager.instance.isOpenAppByNotification}"); + await LocalNotificationManager.instance.setUp(); + if(LocalNotificationManager.instance.isOpenAppByNotification) { + final emailId = EmailId(Id(LocalNotificationManager.instance.getEmailIdFromNotification()!)); + _getPresentationEmailFromEmailId(emailId); + } else { + dispatchRoute(DashboardRoutes.thread); + } } void _getUserProfile() async { @@ -1342,6 +1367,36 @@ class MailboxDashBoardController extends ReloadableController { } } + Future haveLocalNotificationPress() async { + return !(await LocalNotificationManager.instance.localNotificationStream.isEmpty); + } + + void _handleLocalNotificationResponse(NotificationResponse response) { + if(response.payload != null) { + final emailId = EmailId(Id(response.payload!)); + _getPresentationEmailFromEmailId(emailId); + } + } + + void _getPresentationEmailFromEmailId(EmailId emailId) { + if(accountId.value != null) { + log('MailboxDashBoardController: _getPresentationEmailFromEmailId: $emailId'); + consumeState(_getEmailByIdInteractor.execute(accountId.value!, emailId, properties: ThreadConstants.propertiesDefault)); + } + } + + void _moveToEmailDetailedView(GetEmailByIdSuccess success) async { + log('MailboxDashBoardController::_moveToEmailDetailedView(): ${success.email}'); + setSelectedEmail(success.email); + dispatchRoute(DashboardRoutes.emailDetailed); + // move to inbox so that user can back to inbox view + dispatchAction(ClearSearchEmailAction()); + } + + void popAllRouteIfHave() { + Get.until((route) => Get.currentRoute == AppRoutes.dashboard); + } + @override void onClose() { _emailReceiveManager.closeEmailReceiveManagerStream(); @@ -1351,6 +1406,7 @@ class MailboxDashBoardController extends ReloadableController { _refreshActionEventController.close(); Get.delete(); FcmController.instance.dispose(); + LocalNotificationManager.instance.dispose(); super.onClose(); } } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view.dart index 471d627cc..8c574f617 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view.dart @@ -1,6 +1,8 @@ +import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:core/presentation/utils/theme_utils.dart'; import 'package:core/presentation/views/responsive/responsive_widget.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:focus_detector/focus_detector.dart'; import 'package:get/get.dart'; @@ -28,11 +30,15 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { ); return FocusDetector( - onFocusGained: () { + onFocusGained: () async { ThemeUtils.setSystemDarkUIStyle(); if (controller.isDrawerOpen) { ThemeUtils.setStatusBarTransparentColor(); } + if(await controller.haveLocalNotificationPress()) { + controller.popAllRouteIfHave(); + controller.dispatchRoute(DashboardRoutes.waiting); + } controller.refreshActionWhenBackToApp(); }, child: Scaffold( @@ -68,6 +74,13 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { mobile: EmailView()); case DashboardRoutes.searchEmail: return SafeArea(child: SearchEmailView()); + case DashboardRoutes.waiting: + return const Padding( + padding: EdgeInsets.all(16), + child: SizedBox( + width: 30, + height: 30, + child: CupertinoActivityIndicator(color: AppColor.colorLoading))); default: return ResponsiveWidget( responsiveUtils: responsiveUtils, diff --git a/lib/features/mailbox_dashboard/presentation/model/dashboard_routes.dart b/lib/features/mailbox_dashboard/presentation/model/dashboard_routes.dart index de89e2f79..82d208a90 100644 --- a/lib/features/mailbox_dashboard/presentation/model/dashboard_routes.dart +++ b/lib/features/mailbox_dashboard/presentation/model/dashboard_routes.dart @@ -2,5 +2,6 @@ enum DashboardRoutes { thread, emailDetailed, - searchEmail + searchEmail, + waiting } \ No newline at end of file diff --git a/lib/features/push_notification/presentation/notification/local_notification_manager.dart b/lib/features/push_notification/presentation/notification/local_notification_manager.dart index e2607decc..ba67de4e4 100644 --- a/lib/features/push_notification/presentation/notification/local_notification_manager.dart +++ b/lib/features/push_notification/presentation/notification/local_notification_manager.dart @@ -1,4 +1,5 @@ +import 'dart:async'; import 'dart:io'; import 'package:core/presentation/extensions/html_extension.dart'; @@ -19,8 +20,15 @@ class LocalNotificationManager { final _localNotificationsPlugin = FlutterLocalNotificationsPlugin(); bool notificationsEnabled = false; + final StreamController localNotificationsController = StreamController.broadcast(); + + Stream get localNotificationStream => localNotificationsController.stream; + + NotificationAppLaunchDetails? _notificationAppLaunchDetails; + Future setUp() async { try { + _notificationAppLaunchDetails = await _localNotificationsPlugin.getNotificationAppLaunchDetails(); await _initLocalNotification(); _checkLocalNotificationPermission(); if (Platform.isAndroid) { @@ -32,6 +40,19 @@ class LocalNotificationManager { } } + bool get isOpenAppByNotification => _notificationAppLaunchDetails?.didNotificationLaunchApp ?? false; + + bool get isLocalNotificationStreamClosed => localNotificationsController.isClosed; + + String? getEmailIdFromNotification() { + if(_notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) { + log('LocalNotificationManager::handleNotificationAppLaunch(): ${_notificationAppLaunchDetails!.notificationResponse}'); + final selectedNotificationPayload = _notificationAppLaunchDetails!.notificationResponse?.payload; + return selectedNotificationPayload; + } + return null; + } + Future _initLocalNotification() async { return await _localNotificationsPlugin.initialize( const InitializationSettings( @@ -43,7 +64,10 @@ class LocalNotificationManager { } void _handleReceiveNotificationResponse(NotificationResponse response) { - log('LocalNotificationManager::handleReceiveNotificationResponse(): $response'); + log('LocalNotificationManager::handleReceiveNotificationResponse(): ${response.payload}'); + if(response.notificationResponseType == NotificationResponseType.selectedNotification) { + localNotificationsController.add(response); + } } void _checkLocalNotificationPermission() async { @@ -139,4 +163,12 @@ class LocalNotificationManager { ); } } + + void _closeStream() { + localNotificationsController.close(); + } + + void dispose() { + _closeStream(); + } } \ No newline at end of file