From eadc0c4ddc9dcb2e63f31a9d349fe3f24542d8bd Mon Sep 17 00:00:00 2001 From: dab246 Date: Mon, 9 Jan 2023 14:11:19 +0700 Subject: [PATCH] TF-1360 Fix can not add data to stream controller when re-login --- .../presentation/action/email_ui_action.dart | 21 ++++++ .../controller/single_email_controller.dart | 19 ++---- .../action/mailbox_ui_action.dart | 28 ++++++++ .../presentation/mailbox_controller.dart | 35 ++++------ .../presentation/action/dashboard_action.dart | 28 -------- .../bindings/mailbox_dashboard_bindings.dart | 12 ++++ .../mailbox_dashboard_controller.dart | 55 ++++++++++++--- .../presentation/mailbox_dashboard_view.dart | 2 +- .../config/fcm_configuration.dart | 5 +- .../controller/fcm_controller.dart | 31 ++++++--- .../controller/fcm_token_handler.dart | 10 +-- .../listener/email_change_listener.dart | 4 +- .../listener/mailbox_change_listener.dart | 4 +- .../local_notification_manager.dart | 23 ++++--- .../presentation/services/fcm_receiver.dart | 15 ++-- .../presentation/services/fcm_service.dart | 68 +++++++++++++++---- .../presentation/search_email_controller.dart | 4 +- .../presentation/thread_controller.dart | 39 +++++------ lib/main/utils/app_config.dart | 2 + lib/main/utils/app_utils.dart | 3 + 20 files changed, 260 insertions(+), 148 deletions(-) create mode 100644 lib/features/email/presentation/action/email_ui_action.dart create mode 100644 lib/features/mailbox/presentation/action/mailbox_ui_action.dart diff --git a/lib/features/email/presentation/action/email_ui_action.dart b/lib/features/email/presentation/action/email_ui_action.dart new file mode 100644 index 000000000..eeb63d8ae --- /dev/null +++ b/lib/features/email/presentation/action/email_ui_action.dart @@ -0,0 +1,21 @@ + +import 'package:tmail_ui_user/features/base/action/ui_action.dart'; +import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; + +class EmailUIAction extends UIAction { + static final idle = EmailUIAction(); + + EmailUIAction() : super(); + + @override + List get props => []; +} + +class RefreshChangeEmailAction extends EmailUIAction { + final jmap.State? newState; + + RefreshChangeEmailAction(this.newState); + + @override + List get props => [newState]; +} \ No newline at end of file diff --git a/lib/features/email/presentation/controller/single_email_controller.dart b/lib/features/email/presentation/controller/single_email_controller.dart index 94461cde3..a28be304d 100644 --- a/lib/features/email/presentation/controller/single_email_controller.dart +++ b/lib/features/email/presentation/controller/single_email_controller.dart @@ -50,8 +50,8 @@ import 'package:tmail_ui_user/features/email/domain/usecases/send_receipt_to_sen import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/email_address_bottom_sheet_builder.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/email_address_dialog_builder.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/action/mailbox_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/download/download_task_state.dart'; @@ -101,7 +101,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin { EmailId? _currentEmailId; Identity? _identitySelected; List? initialEmailContents; - late Worker selectedEmailWorker, accountIdWorker; final StreamController> _downloadProgressStateController = StreamController>.broadcast(); @@ -127,7 +126,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { @override void onInit() { - _registerListenerWorker(); + _registerObxStreamListener(); _listenDownloadAttachmentProgressState(); super.onInit(); } @@ -135,12 +134,11 @@ class SingleEmailController extends BaseController with AppLoaderMixin { @override void onClose() { _downloadProgressStateController.close(); - _unregisterListenerWorker(); super.onClose(); } - void _registerListenerWorker() { - accountIdWorker = ever(mailboxDashBoardController.accountId, (accountId) { + void _registerObxStreamListener() { + ever(mailboxDashBoardController.accountId, (accountId) { if (accountId is AccountId) { _injectAndGetInteractorBindings( mailboxDashBoardController.sessionCurrent, @@ -149,7 +147,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { } }); - selectedEmailWorker = ever( + ever( mailboxDashBoardController.selectedEmail, _handleOpenEmailDetailedView ); @@ -160,11 +158,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin { && emailSupervisorController.currentListEmail.listEmailIds.contains(selectedEmail.id); } - void _unregisterListenerWorker() { - accountIdWorker.dispose(); - selectedEmailWorker.dispose(); - } - void _handleOpenEmailDetailedView(PresentationEmail? selectedEmail) { if (selectedEmail == null || _currentEmailId == selectedEmail.id) { log('SingleEmailController::_handleOpenEmailDetailedView(): email unselected'); @@ -1030,7 +1023,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { } else { mailboxDashBoardController.dispatchRoute(DashboardRoutes.thread); if (isOpenEmailNotMailboxFromRoute) { - mailboxDashBoardController.dispatchAction(SelectMailboxDefaultAction()); + mailboxDashBoardController.dispatchMailboxUIAction(SelectMailboxDefaultAction()); } } } diff --git a/lib/features/mailbox/presentation/action/mailbox_ui_action.dart b/lib/features/mailbox/presentation/action/mailbox_ui_action.dart new file mode 100644 index 000000000..2d522840d --- /dev/null +++ b/lib/features/mailbox/presentation/action/mailbox_ui_action.dart @@ -0,0 +1,28 @@ + +import 'package:tmail_ui_user/features/base/action/ui_action.dart'; +import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; + +class MailboxUIAction extends UIAction { + static final idle = MailboxUIAction(); + + MailboxUIAction() : super(); + + @override + List get props => []; +} + +class SelectMailboxDefaultAction extends MailboxUIAction { + SelectMailboxDefaultAction(); + + @override + List get props => []; +} + +class RefreshChangeMailboxAction extends MailboxUIAction { + final jmap.State? newState; + + RefreshChangeMailboxAction(this.newState); + + @override + List get props => [newState]; +} \ No newline at end of file diff --git a/lib/features/mailbox/presentation/mailbox_controller.dart b/lib/features/mailbox/presentation/mailbox_controller.dart index bd0fb0f2a..9a9ff6502 100644 --- a/lib/features/mailbox/presentation/mailbox_controller.dart +++ b/lib/features/mailbox/presentation/mailbox_controller.dart @@ -43,6 +43,7 @@ import 'package:tmail_ui_user/features/mailbox/domain/usecases/move_mailbox_inte import 'package:tmail_ui_user/features/mailbox/domain/usecases/refresh_all_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/rename_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/search_mailbox_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/action/mailbox_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_categories.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_categories_expand_mode.dart'; @@ -105,11 +106,6 @@ class MailboxController extends BaseMailboxController { jmap.State? _currentMailboxState; List listMailboxNameAsStringExist = []; - late Worker accountIdWorker, - viewStateWorker, - dashboardActionWorker, - routerParametersWorker; - PresentationMailbox? get selectedMailbox => mailboxDashBoardController.selectedMailbox.value; MailboxController( @@ -126,7 +122,7 @@ class MailboxController extends BaseMailboxController { @override void onInit() { - _registerListenerWorker(); + _registerObxStreamListener(); _registerSearchFocusListener(); super.onInit(); } @@ -147,7 +143,6 @@ class MailboxController extends BaseMailboxController { searchFocus.dispose(); _openMailboxEventController.close(); mailboxListScrollController.dispose(); - _unregisterListenerWorker(); super.onClose(); } @@ -205,19 +200,19 @@ class MailboxController extends BaseMailboxController { isMailboxListScrollable.value = mailboxListScrollController.hasClients && mailboxListScrollController.position.maxScrollExtent > 0; } - void _registerListenerWorker() { - accountIdWorker = ever(mailboxDashBoardController.accountId, (accountId) { + void _registerObxStreamListener() { + ever(mailboxDashBoardController.accountId, (accountId) { if (accountId is AccountId) { getAllMailboxAction(accountId); } }); - routerParametersWorker = ever?>( + ever?>( mailboxDashBoardController.routerParameters, _handleNavigationRouteParameters ); - viewStateWorker = ever(mailboxDashBoardController.viewState, (state) { + ever(mailboxDashBoardController.viewState, (state) { if (state is Either) { state.fold((failure) => null, (success) { if (success is MarkAsMultipleEmailReadAllSuccess) { @@ -257,18 +252,23 @@ class MailboxController extends BaseMailboxController { } }); - dashboardActionWorker = ever(mailboxDashBoardController.dashBoardAction, (action) { - log('MailboxController::_registerListenerWorker():action: $action'); + ever(mailboxDashBoardController.dashBoardAction, (action) { if (action is ClearSearchEmailAction) { _switchBackToMailboxDefault(); - } else if (action is SelectMailboxDefaultAction) { + } + }); + + ever(mailboxDashBoardController.mailboxUIAction, (action) { + if (action is SelectMailboxDefaultAction) { if (mailboxDashBoardController.selectedMailbox.value == null) { _switchBackToMailboxDefault(); } + mailboxDashBoardController.clearMailboxUIAction(); } else if (action is RefreshChangeMailboxAction) { if (action.newState != _currentMailboxState) { refreshMailboxChanges(); } + mailboxDashBoardController.clearMailboxUIAction(); } }); } @@ -284,13 +284,6 @@ class MailboxController extends BaseMailboxController { }); } - void _unregisterListenerWorker() { - accountIdWorker.dispose(); - viewStateWorker.dispose(); - dashboardActionWorker.dispose(); - routerParametersWorker.dispose(); - } - void _initCollapseMailboxCategories() { if (kIsWeb && currentContext != null && (_responsiveUtils.isMobile(currentContext!) || _responsiveUtils.isTablet(currentContext!))) { diff --git a/lib/features/mailbox_dashboard/presentation/action/dashboard_action.dart b/lib/features/mailbox_dashboard/presentation/action/dashboard_action.dart index cf889f988..000d9017a 100644 --- a/lib/features/mailbox_dashboard/presentation/action/dashboard_action.dart +++ b/lib/features/mailbox_dashboard/presentation/action/dashboard_action.dart @@ -5,7 +5,6 @@ import 'package:model/email/presentation_email.dart'; import 'package:tmail_ui_user/features/base/action/ui_action.dart'; import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart'; import 'package:tmail_ui_user/main/routes/navigation_router.dart'; -import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; class DashBoardAction extends UIAction { static final idle = DashBoardAction(); @@ -106,13 +105,6 @@ class ClearAllFieldOfAdvancedSearchAction extends DashBoardAction { List get props => []; } -class SelectMailboxDefaultAction extends DashBoardAction { - SelectMailboxDefaultAction(); - - @override - List get props => []; -} - class SelectEmailByIdAction extends DashBoardAction { final NavigationRouter navigationRouter; @@ -131,24 +123,4 @@ class SearchEmailByQueryAction extends DashBoardAction { @override List get props => [navigationRouter]; -} - -class RefreshChangeEmailAction extends DashBoardAction { - - final jmap.State? newState; - - RefreshChangeEmailAction(this.newState); - - @override - List get props => [newState]; -} - -class RefreshChangeMailboxAction extends DashBoardAction { - - final jmap.State? newState; - - RefreshChangeMailboxAction(this.newState); - - @override - List get props => [newState]; } \ No newline at end of file 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 408f7857f..7b50d4dcf 100644 --- a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart +++ b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart @@ -20,6 +20,8 @@ import 'package:tmail_ui_user/features/email/domain/usecases/delete_multiple_ema import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_star_email_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/move_to_mailbox_interactor.dart'; +import 'package:tmail_ui_user/features/email/presentation/controller/email_supervisor_controller.dart'; +import 'package:tmail_ui_user/features/email/presentation/controller/single_email_controller.dart'; import 'package:tmail_ui_user/features/email/presentation/email_bindings.dart'; import 'package:tmail_ui_user/features/login/data/datasource/account_datasource.dart'; import 'package:tmail_ui_user/features/login/data/datasource/authentication_oidc_datasource.dart'; @@ -51,6 +53,7 @@ import 'package:tmail_ui_user/features/mailbox/data/repository/mailbox_repositor import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/mark_as_mailbox_read_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_bindings.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/search_datasource.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/session_storage_composer_datasource.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource_impl/search_datasource_impl.dart'; @@ -92,6 +95,7 @@ import 'package:tmail_ui_user/features/thread/domain/usecases/move_multiple_emai import 'package:tmail_ui_user/features/thread/domain/usecases/search_email_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/search_more_email_interactor.dart'; import 'package:tmail_ui_user/features/thread/presentation/thread_bindings.dart'; +import 'package:tmail_ui_user/features/thread/presentation/thread_controller.dart'; import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart'; import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart'; @@ -292,4 +296,12 @@ class MailboxDashBoardBindings extends BaseBindings { Get.lazyPut(() => AuthenticationOIDCRepositoryImpl(Get.find())); Get.lazyPut(() => ComposerCacheRepositoryImpl(Get.find())); } + + void deleteController() { + Get.delete(); + Get.delete(); + Get.delete(); + Get.delete(); + Get.delete(); + } } \ No newline at end of file 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 14e6a2cf4..be5ad3281 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -38,11 +38,13 @@ import 'package:tmail_ui_user/features/email/domain/usecases/delete_multiple_ema import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_star_email_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/move_to_mailbox_interactor.dart'; +import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/mark_as_mailbox_read_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/action/mailbox_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_app_dashboard_configuration_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_composer_cache_state.dart'; @@ -50,6 +52,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/remove_ema import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_composer_cache_on_web_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_email_drafts_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/download/download_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart'; @@ -74,7 +77,6 @@ import 'package:tmail_ui_user/features/push_notification/domain/usecases/delete_ import 'package:tmail_ui_user/features/push_notification/domain/usecases/delete_mailbox_state_to_refresh_interactor.dart'; 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'; @@ -137,6 +139,8 @@ class MailboxDashBoardController extends ReloadableController { final accountId = Rxn(); final userProfile = Rxn(); final dashBoardAction = Rxn(); + final mailboxUIAction = Rxn(); + final emailUIAction = Rxn(); final dashboardRoute = DashboardRoutes.waiting.obs; final appInformation = Rxn(); final currentSelectMode = SelectMode.INACTIVE.obs; @@ -166,7 +170,7 @@ class MailboxDashBoardController extends ReloadableController { StreamController.broadcast(); final _notificationManager = LocalNotificationManager.instance; - final _fcmController = FcmController.instance; + final _fcmService = FcmService.instance; MailboxDashBoardController( LogoutOidcInteractor logoutOidcInteractor, @@ -245,10 +249,10 @@ class MailboxDashBoardController extends ReloadableController { icon: _imagePaths.icSendToast); } } else if (success is GetEmailStateToRefreshSuccess) { - dispatchAction(RefreshChangeEmailAction(success.storedState)); + dispatchEmailUIAction(RefreshChangeEmailAction(success.storedState)); _deleteEmailStateToRefreshAction(); } else if (success is GetMailboxStateToRefreshSuccess) { - dispatchAction(RefreshChangeMailboxAction(success.storedState)); + dispatchMailboxUIAction(RefreshChangeMailboxAction(success.storedState)); _deleteMailboxStateToRefreshAction(); } } @@ -367,6 +371,10 @@ class MailboxDashBoardController extends ReloadableController { .debounceTime(const Duration(milliseconds: FcmService.durationMessageComing)) .listen(_handleRefreshActionWhenBackToApp); + _registerLocalNotificationStreamListener(); + } + + void _registerLocalNotificationStreamListener() { _notificationManager.localNotificationStream.listen(_handleClickLocalNotificationOnForeground); } @@ -425,6 +433,17 @@ class MailboxDashBoardController extends ReloadableController { } } + @override + void injectFCMBindings(Session? session, AccountId? accountId) async { + try { + super.injectFCMBindings(session, accountId); + await LocalNotificationManager.instance.recreateStreamController(); + _registerLocalNotificationStreamListener(); + } catch (e) { + logError('MailboxDashBoardController::injectFCMBindings(): $e'); + } + } + Future _getAppVersion() async { final info = await PackageInfo.fromPlatform(); log('MailboxDashBoardController::_getAppVersion(): ${info.version}'); @@ -1086,6 +1105,16 @@ class MailboxDashBoardController extends ReloadableController { dashBoardAction.value = action; } + void dispatchMailboxUIAction(MailboxUIAction newAction) { + log('MailboxDashBoardController::dispatchMailboxUIAction():newAction: ${newAction.runtimeType}'); + mailboxUIAction.value = newAction; + } + + void dispatchEmailUIAction(EmailUIAction newAction) { + log('MailboxDashBoardController::dispatchEmailUIAction():newAction: ${newAction.runtimeType}'); + emailUIAction.value = newAction; + } + void openComposerOverlay(RouterArguments? arguments) { routerArguments = arguments; ComposerBindings().dependencies(); @@ -1213,6 +1242,14 @@ class MailboxDashBoardController extends ReloadableController { dashBoardAction.value = DashBoardAction.idle; } + void clearMailboxUIAction() { + mailboxUIAction.value = MailboxUIAction.idle; + } + + void clearEmailUIAction() { + emailUIAction.value = EmailUIAction.idle; + } + void goToSettings() async { if (isDrawerOpen) { closeMailboxMenuDrawer(); @@ -1424,8 +1461,8 @@ class MailboxDashBoardController extends ReloadableController { Get.until((route) => Get.currentRoute == AppRoutes.dashboard); } - void handleOnFocusGained() { - log('MailboxDashBoardController::handleOnFocusGained():'); + void handleOnForegroundGained() { + log('MailboxDashBoardController::handleOnForegroundGained():'); if (!BuildUtils.isWeb) { _updateTheme(); } @@ -1446,9 +1483,9 @@ class MailboxDashBoardController extends ReloadableController { _fileReceiveManagerStreamSubscription.cancel(); _progressStateController.close(); _refreshActionEventController.close(); - Get.delete(); - _notificationManager.dispose(); - _fcmController.dispose(); + _notificationManager.closeStream(); + _fcmService.closeStream(); + MailboxDashBoardBindings().deleteController(); 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 f495a178d..78305c1b6 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view.dart @@ -29,7 +29,7 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { ); return FocusDetector( - onFocusGained: controller.handleOnFocusGained, + onForegroundGained: controller.handleOnForegroundGained, child: Scaffold( drawerEnableOpenDragGesture: responsiveUtils.hasLeftMenuDrawerActive(context), body: Obx(() { diff --git a/lib/features/push_notification/presentation/config/fcm_configuration.dart b/lib/features/push_notification/presentation/config/fcm_configuration.dart index eeefb236e..fa5ecb863 100644 --- a/lib/features/push_notification/presentation/config/fcm_configuration.dart +++ b/lib/features/push_notification/presentation/config/fcm_configuration.dart @@ -1,20 +1,21 @@ -import 'package:core/utils/build_utils.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/config/firebase_options.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/services/fcm_receiver.dart'; +import 'package:tmail_ui_user/features/push_notification/presentation/services/fcm_service.dart'; class FcmConfiguration { static void initialize() async { await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); + await FcmService.instance.recreateStreamController(); _initMessageListener(); } static void _initMessageListener() { FcmReceiver.instance.onForegroundMessage(); FcmReceiver.instance.onBackgroundMessage(); - BuildUtils.isWeb ? FcmReceiver.instance.getFcmToken() : null; + FcmReceiver.instance.getFcmToken(); FcmReceiver.instance.onRefreshFcmToken(); } } \ No newline at end of file diff --git a/lib/features/push_notification/presentation/controller/fcm_controller.dart b/lib/features/push_notification/presentation/controller/fcm_controller.dart index 82e9850d2..ebf0bf363 100644 --- a/lib/features/push_notification/presentation/controller/fcm_controller.dart +++ b/lib/features/push_notification/presentation/controller/fcm_controller.dart @@ -47,7 +47,7 @@ class FcmController extends BaseController { GetSessionInteractor? _getSessionInteractor; FcmController._internal() { - _listenFcmMessageStream(); + _listenFcmStream(); } static final FcmController _instance = FcmController._internal(); @@ -59,17 +59,33 @@ class FcmController extends BaseController { FcmTokenHandler.instance.initialize(); } - void _listenFcmMessageStream() { - log('FcmController::_listenFcmMessageStream():'); + void _listenFcmStream() async { + await Future.wait([ + listenForegroundMessageStream(), + listenBackgroundMessageStream(), + listenTokenStream() + ]); + } + + Future listenForegroundMessageStream() { FcmService.instance.foregroundMessageStream .throttleTime(const Duration(milliseconds: FcmService.durationMessageComing)) .listen(_handleForegroundMessageAction); + return Future.value(); + } + Future listenBackgroundMessageStream() { FcmService.instance.backgroundMessageStream .throttleTime(const Duration(milliseconds: FcmService.durationMessageComing)) .listen(_handleBackgroundMessageAction); + return Future.value(); + } - FcmService.instance.fcmTokenStream.listen(FcmTokenHandler.instance.handle); + Future listenTokenStream() { + FcmService.instance.fcmTokenStream + .debounceTime(const Duration(milliseconds: FcmService.durationRefreshToken)) + .listen(FcmTokenHandler.instance.handleTokenAction); + return Future.value(); } void _handleForegroundMessageAction(RemoteMessage newRemoteMessage) { @@ -253,13 +269,6 @@ class FcmController extends BaseController { _remoteMessageBackground = null; } - @override - void dispose() { - _clearRemoteMessageBackground(); - FcmService.instance.dispose(); - super.dispose(); - } - @override void onData(Either newState) { super.onData(newState); diff --git a/lib/features/push_notification/presentation/controller/fcm_token_handler.dart b/lib/features/push_notification/presentation/controller/fcm_token_handler.dart index 5cd9784a6..2822ae1d6 100644 --- a/lib/features/push_notification/presentation/controller/fcm_token_handler.dart +++ b/lib/features/push_notification/presentation/controller/fcm_token_handler.dart @@ -54,14 +54,14 @@ class FcmTokenHandler { } } - void handle(String? token) { - log('FcmTokenHandler::handle():token: $token'); + void handleTokenAction(String? token) { + log('FcmTokenHandler::handleTokenAction():token: $token'); if (token != null) { _fcmToken = FirebaseToken(token); final deviceId = FcmUtils.instance.hashTokenToDeviceId(token); _deviceClientId = DeviceClientId(deviceId); - log('FcmTokenHandler::handle(): fcmToken: $_fcmToken'); - log('FcmTokenHandler::handle(): deviceId: $deviceId'); + log('FcmTokenHandler::handleTokenAction(): fcmToken: $_fcmToken'); + log('FcmTokenHandler::handleTokenAction(): deviceId: $deviceId'); _getFcmTokenFromBackend(deviceId); } else { _getFCMSubscriptionLocalAction(); @@ -171,7 +171,7 @@ class FcmTokenHandler { deviceClientId: deviceClientId, types: [TypeName.emailType, TypeName.mailboxType, TypeName.emailDelivery] ); - log('FcmTokenHandler::_handleSuccessViewState():firebaseSubscription: $firebaseSubscription'); + log('FcmTokenHandler::_handleRegisterNewToken():firebaseSubscription: $firebaseSubscription'); _invokeRegisterNewTokenAction(RegisterNewTokenRequest( generateCreationId, firebaseSubscription diff --git a/lib/features/push_notification/presentation/listener/email_change_listener.dart b/lib/features/push_notification/presentation/listener/email_change_listener.dart index f9fc997fe..6e2557a6f 100644 --- a/lib/features/push_notification/presentation/listener/email_change_listener.dart +++ b/lib/features/push_notification/presentation/listener/email_change_listener.dart @@ -9,7 +9,7 @@ import 'package:model/email/presentation_email.dart'; import 'package:tmail_ui_user/features/base/action/ui_action.dart'; import 'package:tmail_ui_user/features/email/domain/state/get_stored_state_email_state.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_stored_email_state_interactor.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart'; +import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/push_notification/domain/exceptions/fcm_exception.dart'; import 'package:tmail_ui_user/features/push_notification/domain/state/get_email_changes_state.dart'; @@ -70,7 +70,7 @@ class EmailChangeListener extends ChangeListener { void _synchronizeEmailOnForegroundAction(jmap.State newState) { log('EmailChangeListener::_synchronizeEmailAction():newState: $newState'); if (_dashBoardController != null) { - _dashBoardController!.dispatchAction(RefreshChangeEmailAction(newState)); + _dashBoardController!.dispatchEmailUIAction(RefreshChangeEmailAction(newState)); } } diff --git a/lib/features/push_notification/presentation/listener/mailbox_change_listener.dart b/lib/features/push_notification/presentation/listener/mailbox_change_listener.dart index 2ed18fd6d..b0339906d 100644 --- a/lib/features/push_notification/presentation/listener/mailbox_change_listener.dart +++ b/lib/features/push_notification/presentation/listener/mailbox_change_listener.dart @@ -4,7 +4,7 @@ import 'package:core/presentation/state/success.dart'; import 'package:core/utils/app_logger.dart'; import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; import 'package:tmail_ui_user/features/base/action/ui_action.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/action/mailbox_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/push_notification/domain/usecases/store_mailbox_state_to_refresh_interactor.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/action/fcm_action.dart'; @@ -54,7 +54,7 @@ class MailboxChangeListener extends ChangeListener { void _synchronizeMailboxOnForegroundAction(jmap.State newState) { log('MailboxChangeListener::_synchronizeMailboxOnForegroundAction():newState: $newState'); if (_dashBoardController != null) { - _dashBoardController!.dispatchAction(RefreshChangeMailboxAction(newState)); + _dashBoardController!.dispatchMailboxUIAction(RefreshChangeMailboxAction(newState)); } } 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 614cb9667..794079997 100644 --- a/lib/features/push_notification/presentation/notification/local_notification_manager.dart +++ b/lib/features/push_notification/presentation/notification/local_notification_manager.dart @@ -11,7 +11,11 @@ import 'package:tmail_ui_user/features/push_notification/presentation/notificati class LocalNotificationManager { - LocalNotificationManager._internal(); + late StreamController localNotificationsController; + + LocalNotificationManager._internal() { + localNotificationsController = StreamController.broadcast(); + } static final LocalNotificationManager _instance = LocalNotificationManager._internal(); @@ -22,8 +26,6 @@ class LocalNotificationManager { bool _notificationsEnabled = false; bool _isNotificationClickedOnTerminate = false; - final StreamController localNotificationsController = StreamController.broadcast(); - Stream get localNotificationStream => localNotificationsController.stream; NotificationAppLaunchDetails? _notificationAppLaunchDetails; @@ -69,7 +71,9 @@ class LocalNotificationManager { void _handleReceiveNotificationResponse(NotificationResponse response) { log('LocalNotificationManager::handleReceiveNotificationResponse():payload: ${response.payload}'); if (response.notificationResponseType == NotificationResponseType.selectedNotification) { - localNotificationsController.add(response); + if (!localNotificationsController.isClosed) { + localNotificationsController.add(response); + } } } @@ -171,11 +175,14 @@ class LocalNotificationManager { } } - void _closeStream() { - localNotificationsController.close(); + Future recreateStreamController() { + if (localNotificationsController.isClosed) { + localNotificationsController = StreamController.broadcast(); + } + return Future.value(); } - void dispose() { - _closeStream(); + void closeStream() { + localNotificationsController.close(); } } \ No newline at end of file diff --git a/lib/features/push_notification/presentation/services/fcm_receiver.dart b/lib/features/push_notification/presentation/services/fcm_receiver.dart index 067438ad1..5103cdfec 100644 --- a/lib/features/push_notification/presentation/services/fcm_receiver.dart +++ b/lib/features/push_notification/presentation/services/fcm_receiver.dart @@ -1,14 +1,12 @@ import 'package:core/utils/app_logger.dart'; +import 'package:core/utils/build_utils.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter_dotenv/flutter_dotenv.dart'; -import 'package:tmail_ui_user/features/push_notification/domain/exceptions/fcm_exception.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/services/fcm_service.dart'; +import 'package:tmail_ui_user/main/utils/app_utils.dart'; @pragma('vm:entry-point') Future handleFirebaseBackgroundMessage(RemoteMessage message) async { - log('FcmReceiver::handleFirebaseBackgroundMessage():messageId: ${message.messageId}'); log('FcmReceiver::handleFirebaseBackgroundMessage(): ${message.data}'); FcmService.instance.handleFirebaseBackgroundMessage(message); } @@ -30,12 +28,13 @@ class FcmReceiver { void getFcmToken() async { try { - final token = await FirebaseMessaging.instance.getToken(vapidKey: kIsWeb ? dotenv.get('FIREBASE_WEB_VAPID_PUBLIC_KEY', fallback: '') : null); - log('FcmReceiver::onFcmToken():token: $token'); - FcmService.instance.handleRefreshToken(token); + final currentToken = await FirebaseMessaging.instance.getToken(vapidKey: AppUtils.fcmVapidPublicKey); + log('FcmReceiver::onFcmToken():currentToken: $currentToken'); + if (BuildUtils.isWeb) { + FcmService.instance.handleGetToken(currentToken); + } } catch(e) { log('FcmReceiver::onFcmToken():exception: $e'); - throw NotLoadedFCMTokenException(); } } diff --git a/lib/features/push_notification/presentation/services/fcm_service.dart b/lib/features/push_notification/presentation/services/fcm_service.dart index b0baa4034..1c565f208 100644 --- a/lib/features/push_notification/presentation/services/fcm_service.dart +++ b/lib/features/push_notification/presentation/services/fcm_service.dart @@ -8,44 +8,84 @@ import 'package:tmail_ui_user/features/push_notification/presentation/controller class FcmService { static const int durationMessageComing = 2000; + static const int durationRefreshToken = 2000; - final StreamController foregroundMessageStreamController = StreamController.broadcast(); + late StreamController foregroundMessageStreamController; Stream get foregroundMessageStream => foregroundMessageStreamController.stream; - final StreamController backgroundMessageStreamController = StreamController.broadcast(); + late StreamController backgroundMessageStreamController; Stream get backgroundMessageStream => backgroundMessageStreamController.stream; - final StreamController fcmTokenStreamController = StreamController.broadcast(); + late StreamController fcmTokenStreamController; Stream get fcmTokenStream => fcmTokenStreamController.stream; - FcmService._internal(); + FcmService._internal() { + foregroundMessageStreamController = StreamController.broadcast(); + backgroundMessageStreamController = StreamController.broadcast(); + fcmTokenStreamController = StreamController.broadcast(); + } static final FcmService _instance = FcmService._internal(); static FcmService get instance => _instance; void handleFirebaseForegroundMessage(RemoteMessage newRemoteMessage) { - log('FcmService::handleFirebaseForegroundMessage():messageId: ${newRemoteMessage.messageId}'); log('FcmService::handleFirebaseForegroundMessage():message: ${newRemoteMessage.data}'); - foregroundMessageStreamController.add(newRemoteMessage); + if (!foregroundMessageStreamController.isClosed) { + foregroundMessageStreamController.add(newRemoteMessage); + } } void handleFirebaseBackgroundMessage(RemoteMessage newRemoteMessage) { FcmController.instance.initialize(); - backgroundMessageStreamController.add(newRemoteMessage); + if (!backgroundMessageStreamController.isClosed) { + backgroundMessageStreamController.add(newRemoteMessage); + } } - void handleRefreshToken(String? newToken) { - fcmTokenStreamController.add(newToken); + void handleGetToken(String? currentToken) async { + log('FcmService::handleGetToken():currentToken: $currentToken'); + if (fcmTokenStreamController.isClosed) { + log('FcmService::handleGetToken():fcmTokenStreamController: isClosed'); + fcmTokenStreamController = StreamController.broadcast(); + await FcmController.instance.listenTokenStream(); + } + if (!fcmTokenStreamController.isClosed) { + fcmTokenStreamController.add(currentToken); + } } - void _closeStream() { + void handleRefreshToken(String? newToken) async { + log('FcmService::handleRefreshToken():newToken: $newToken'); + if (fcmTokenStreamController.isClosed) { + log('FcmService::handleRefreshToken():fcmTokenStreamController: isClosed'); + fcmTokenStreamController = StreamController.broadcast(); + await FcmController.instance.listenTokenStream(); + } + if (!fcmTokenStreamController.isClosed) { + fcmTokenStreamController.add(newToken); + } + } + + Future recreateStreamController() async { + if (foregroundMessageStreamController.isClosed) { + foregroundMessageStreamController = StreamController.broadcast(); + await FcmController.instance.listenForegroundMessageStream(); + } + if (backgroundMessageStreamController.isClosed) { + backgroundMessageStreamController = StreamController.broadcast(); + await FcmController.instance.listenBackgroundMessageStream(); + } + if (fcmTokenStreamController.isClosed) { + fcmTokenStreamController = StreamController.broadcast(); + await FcmController.instance.listenTokenStream(); + } + return Future.value(); + } + + void closeStream() { foregroundMessageStreamController.close(); backgroundMessageStreamController.close(); fcmTokenStreamController.close(); } - - void dispose() { - _closeStream(); - } } \ No newline at end of file diff --git a/lib/features/search/presentation/search_email_controller.dart b/lib/features/search/presentation/search_email_controller.dart index 400faee64..8aad31675 100644 --- a/lib/features/search/presentation/search_email_controller.dart +++ b/lib/features/search/presentation/search_email_controller.dart @@ -24,6 +24,7 @@ import 'package:tmail_ui_user/features/email/domain/state/delete_multiple_emails import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_star_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/move_to_mailbox_state.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/action/mailbox_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/recent_search.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_all_recent_search_latest_state.dart'; @@ -31,7 +32,6 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/quick_sear import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_all_recent_search_latest_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/quick_search_email_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_recent_search_interactor.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart'; @@ -642,7 +642,7 @@ class SearchEmailController extends BaseController clearAllResultSearch(); FocusScope.of(context).unfocus(); mailboxDashBoardController.searchController.disableAllSearchEmail(); - mailboxDashBoardController.dispatchAction(SelectMailboxDefaultAction()); + mailboxDashBoardController.dispatchMailboxUIAction(SelectMailboxDefaultAction()); mailboxDashBoardController.dispatchRoute(DashboardRoutes.thread); SearchEmailBindings().disposeBindings(); } diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index f1a92ee6e..61f578bc7 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -29,6 +29,7 @@ import 'package:tmail_ui_user/features/email/domain/state/delete_multiple_emails import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_star_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/move_to_mailbox_state.dart'; +import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/remove_email_drafts_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart'; @@ -97,7 +98,6 @@ class ThreadController extends BaseController with EmailActionController { final ScrollController listEmailController = ScrollController(); final FocusNode focusNodeKeyBoard = FocusNode(); final latestEmailSelectedOrUnselected = Rxn(); - late Worker mailboxWorker, searchWorker, dashboardActionWorker, viewStateWorker, advancedSearchFilterWorker; Set? get _sortOrder => {} ..add(EmailComparator(EmailComparatorProperty.receivedAt) @@ -131,7 +131,7 @@ class ThreadController extends BaseController with EmailActionController { @override void onInit() { - _registerListenerWorker(); + _registerObxStreamListener(); super.onInit(); } @@ -145,7 +145,6 @@ class ThreadController extends BaseController with EmailActionController { void onClose() { listEmailController.dispose(); focusNodeKeyBoard.dispose(); - _unregisterListenerWorker(); super.onClose(); } @@ -195,8 +194,8 @@ class ThreadController extends BaseController with EmailActionController { _handleErrorGetAllOrRefreshChangesEmail(error); } - void _registerListenerWorker() { - mailboxWorker = ever(mailboxDashBoardController.selectedMailbox, (mailbox) { + void _registerObxStreamListener() { + ever(mailboxDashBoardController.selectedMailbox, (mailbox) { if (mailbox is PresentationMailbox) { if (_currentMailboxId != mailbox.id) { _currentMailboxId = mailbox.id; @@ -209,7 +208,7 @@ class ThreadController extends BaseController with EmailActionController { } }); - searchWorker = ever(searchController.searchState, (searchState) { + ever(searchController.searchState, (searchState) { if (searchState is SearchState) { if (searchState.searchStatus == SearchStatus.ACTIVE) { cancelSelectEmail(); @@ -217,13 +216,13 @@ class ThreadController extends BaseController with EmailActionController { } }); - advancedSearchFilterWorker = ever(searchController.isAdvancedSearchViewOpen, (hasOpen) { + ever(searchController.isAdvancedSearchViewOpen, (hasOpen) { if (hasOpen == true) { mailboxDashBoardController.filterMessageOption.value = FilterMessageOption.all; } }); - dashboardActionWorker = ever(mailboxDashBoardController.dashBoardAction, (action) { + ever(mailboxDashBoardController.dashBoardAction, (action) { if (action is RefreshAllEmailAction) { refreshAllEmail(); mailboxDashBoardController.clearDashBoardAction(); @@ -268,15 +267,19 @@ class ThreadController extends BaseController with EmailActionController { _navigationRouter = action.navigationRouter; _activateSearchFromRouter(); mailboxDashBoardController.clearDashBoardAction(); - } else if (action is RefreshChangeEmailAction) { - if (action.newState != _currentEmailState) { - _refreshEmailChanges(); - } - mailboxDashBoardController.clearDashBoardAction(); } }); - viewStateWorker = ever(mailboxDashBoardController.viewState, (viewState) { + ever(mailboxDashBoardController.emailUIAction, (action) { + if (action is RefreshChangeEmailAction) { + if (action.newState != _currentEmailState) { + _refreshEmailChanges(); + } + mailboxDashBoardController.clearEmailUIAction(); + } + }); + + ever(mailboxDashBoardController.viewState, (viewState) { if (viewState is Either) { viewState.map((success) { if (success is MarkAsEmailReadSuccess) { @@ -323,14 +326,6 @@ class ThreadController extends BaseController with EmailActionController { }); } - void _unregisterListenerWorker() { - mailboxWorker.dispose(); - dashboardActionWorker.dispose(); - searchWorker.dispose(); - advancedSearchFilterWorker.dispose(); - viewStateWorker.dispose(); - } - void _activateSearchFromRouter() { searchController.autoFocus.value = false; searchController.enableSearch(); diff --git a/lib/main/utils/app_config.dart b/lib/main/utils/app_config.dart index 81221e4c4..ab6620885 100644 --- a/lib/main/utils/app_config.dart +++ b/lib/main/utils/app_config.dart @@ -26,4 +26,6 @@ class AppConfig { } } static String appFCMConfigurationPath = "configurations/env.fcm"; + + static String get fcmVapidPublicKeyWeb => dotenv.get('FIREBASE_WEB_VAPID_PUBLIC_KEY', fallback: ''); } \ No newline at end of file diff --git a/lib/main/utils/app_utils.dart b/lib/main/utils/app_utils.dart index f7e6888dc..cfa6ebbb0 100644 --- a/lib/main/utils/app_utils.dart +++ b/lib/main/utils/app_utils.dart @@ -1,3 +1,4 @@ +import 'package:core/core.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:tmail_ui_user/main/utils/app_config.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -23,4 +24,6 @@ class AppUtils { webOnlyWindowName: isNewTab ? '_blank' : '_self', ); } + + static String? get fcmVapidPublicKey => BuildUtils.isWeb ? AppConfig.fcmVapidPublicKeyWeb : null; } \ No newline at end of file