From ea58e3297b72e6e20548712105f31f44c72eb509 Mon Sep 17 00:00:00 2001 From: DatDang Date: Tue, 17 Dec 2024 14:59:00 +0700 Subject: [PATCH] TF-3341 Enable web socket for mobile --- .../mailbox_dashboard_controller.dart | 7 +-- .../controller/fcm_message_controller.dart | 23 ---------- .../controller/push_base_controller.dart | 2 + .../controller/web_socket_controller.dart | 43 ++++++++++++++++--- .../presentation/services/fcm_receiver.dart | 5 --- .../presentation/services/fcm_service.dart | 27 ------------ 6 files changed, 43 insertions(+), 64 deletions(-) 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 9a532c123..1387e4fc4 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -125,6 +125,7 @@ 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/web_socket_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/push_notification/presentation/utils/fcm_utils.dart'; @@ -627,9 +628,8 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo injectAutoCompleteBindings(session, currentAccountId); injectRuleFilterBindings(session, currentAccountId); injectVacationBindings(session, currentAccountId); - if (PlatformInfo.isWeb) { - injectWebSocket(session, currentAccountId); - } else { + injectWebSocket(session, currentAccountId); + if (PlatformInfo.isMobile) { injectFCMBindings(session, currentAccountId); } @@ -2948,6 +2948,7 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo sessionCurrent = null; mapMailboxById = {}; mapDefaultMailboxIdByRole = {}; + WebSocketController.instance.onClose(); super.onClose(); } } \ No newline at end of file diff --git a/lib/features/push_notification/presentation/controller/fcm_message_controller.dart b/lib/features/push_notification/presentation/controller/fcm_message_controller.dart index a290e4dbe..5782f3ed5 100644 --- a/lib/features/push_notification/presentation/controller/fcm_message_controller.dart +++ b/lib/features/push_notification/presentation/controller/fcm_message_controller.dart @@ -59,17 +59,9 @@ class FcmMessageController extends PushBaseController { super.initialize(accountId: accountId, session: session); _listenTokenStream(); - _listenForegroundMessageStream(); _listenBackgroundMessageStream(); } - void _listenForegroundMessageStream() { - FcmService.instance.foregroundMessageStreamController - ?.stream - .debounceTime(const Duration(milliseconds: FcmUtils.durationMessageComing)) - .listen(_handleForegroundMessageAction); - } - void _listenBackgroundMessageStream() { FcmService.instance.backgroundMessageStreamController ?.stream @@ -84,21 +76,6 @@ class FcmMessageController extends PushBaseController { .listen(FcmTokenController.instance.onFcmTokenChanged); } - void _handleForegroundMessageAction(Map payloadData) { - log('FcmMessageController::_handleForegroundMessageAction():payloadData: $payloadData | accountId: $accountId'); - if (accountId != null && session?.username != null) { - final stateChange = FcmUtils.instance.convertFirebaseDataMessageToStateChange(payloadData); - final mapTypeState = stateChange.getMapTypeState(accountId!); - mappingTypeStateToAction( - mapTypeState, - accountId!, - emailChangeListener: EmailChangeListener.instance, - mailboxChangeListener: MailboxChangeListener.instance, - session!.username, - session: session); - } - } - void _handleBackgroundMessageAction(Map payloadData) async { log('FcmMessageController::_handleBackgroundMessageAction():payloadData: $payloadData'); final stateChange = FcmUtils.instance.convertFirebaseDataMessageToStateChange(payloadData); diff --git a/lib/features/push_notification/presentation/controller/push_base_controller.dart b/lib/features/push_notification/presentation/controller/push_base_controller.dart index a9c25f16f..d555c33a9 100644 --- a/lib/features/push_notification/presentation/controller/push_base_controller.dart +++ b/lib/features/push_notification/presentation/controller/push_base_controller.dart @@ -43,6 +43,8 @@ abstract class PushBaseController { this.session = session; } + void onClose() {} + void mappingTypeStateToAction( Map mapTypeState, AccountId accountId, diff --git a/lib/features/push_notification/presentation/controller/web_socket_controller.dart b/lib/features/push_notification/presentation/controller/web_socket_controller.dart index f0dbeedc3..821a873cb 100644 --- a/lib/features/push_notification/presentation/controller/web_socket_controller.dart +++ b/lib/features/push_notification/presentation/controller/web_socket_controller.dart @@ -4,7 +4,9 @@ import 'dart:convert'; import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:core/utils/app_logger.dart'; +import 'package:core/utils/platform_info.dart'; import 'package:fcm/model/type_name.dart'; +import 'package:flutter/material.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/push/state_change.dart'; @@ -33,13 +35,12 @@ class WebSocketController extends PushBaseController { WebSocketChannel? _webSocketChannel; Timer? _webSocketPingTimer; StreamSubscription? _webSocketSubscription; + AppLifecycleListener? _appLifecycleListener; @override void handleFailureViewState(Failure failure) { logError('WebSocketController::handleFailureViewState():Failure $failure'); - _webSocketSubscription?.cancel(); - _webSocketChannel = null; - _webSocketPingTimer?.cancel(); + _cleanUpWebSocketResources(); if (failure is WebSocketConnectionFailed) { _handleWebSocketConnectionRetry(); } @@ -64,6 +65,31 @@ class WebSocketController extends PushBaseController { super.initialize(accountId: accountId, session: session); _connectWebSocket(accountId, session); + if (PlatformInfo.isMobile) { + _listenToAppLifeCycle(accountId, session); + } + } + + @override + void onClose() { + _cleanUpWebSocketResources(); + _appLifecycleListener?.dispose(); + _appLifecycleListener = null; + super.onClose(); + } + + void _listenToAppLifeCycle(AccountId? accountId, Session? session) { + _appLifecycleListener ??= AppLifecycleListener( + onStateChange: (appLifecycleState) { + switch (appLifecycleState) { + case AppLifecycleState.resumed: + _connectWebSocket(accountId, session); + break; + default: + _cleanUpWebSocketResources(); + } + }, + ); } void _connectWebSocket(AccountId? accountId, Session? session) { @@ -74,9 +100,16 @@ class WebSocketController extends PushBaseController { consumeState(_connectWebSocketInteractor!.execute(session, accountId)); } + + void _cleanUpWebSocketResources() { + _webSocketSubscription?.cancel(); + _webSocketChannel = null; + _webSocketPingTimer?.cancel(); + } void _handleWebSocketConnectionSuccess(WebSocketConnectionSuccess success) { log('WebSocketController::_handleWebSocketConnectionSuccess(): $success'); + _cleanUpWebSocketResources(); _retryRemained = 3; _webSocketChannel = success.webSocketChannel; _enableWebSocketPush(); @@ -87,9 +120,7 @@ class WebSocketController extends PushBaseController { } void _handleWebSocketConnectionRetry() { - _webSocketSubscription?.cancel(); - _webSocketChannel = null; - _webSocketPingTimer?.cancel(); + _cleanUpWebSocketResources(); if (_retryRemained > 0) { _retryRemained--; _connectWebSocket(accountId, session); diff --git a/lib/features/push_notification/presentation/services/fcm_receiver.dart b/lib/features/push_notification/presentation/services/fcm_receiver.dart index 0cb6efd14..a56ee2164 100644 --- a/lib/features/push_notification/presentation/services/fcm_receiver.dart +++ b/lib/features/push_notification/presentation/services/fcm_receiver.dart @@ -20,16 +20,11 @@ class FcmReceiver { static const int MAX_COUNT_RETRY_TO_GET_FCM_TOKEN = 3; Future onInitialFcmListener() async { - _onForegroundMessage(); _onBackgroundMessage(); await _onHandleFcmToken(); } - void _onForegroundMessage() { - FirebaseMessaging.onMessage.listen(FcmService.instance.handleFirebaseForegroundMessage); - } - void _onBackgroundMessage() { FirebaseMessaging.onBackgroundMessage(handleFirebaseBackgroundMessage); } diff --git a/lib/features/push_notification/presentation/services/fcm_service.dart b/lib/features/push_notification/presentation/services/fcm_service.dart index 147bded91..2906bf981 100644 --- a/lib/features/push_notification/presentation/services/fcm_service.dart +++ b/lib/features/push_notification/presentation/services/fcm_service.dart @@ -1,15 +1,11 @@ import 'dart:async'; -import 'dart:convert'; import 'package:core/utils/app_logger.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:tmail_ui_user/features/push_notification/presentation/model/broadcast_message_event_data.dart'; -import 'package:universal_html/html.dart' as html; class FcmService { - StreamController>? foregroundMessageStreamController; StreamController>? backgroundMessageStreamController; StreamController? fcmTokenStreamController; @@ -19,13 +15,6 @@ class FcmService { static FcmService get instance => _instance; - void handleFirebaseForegroundMessage(RemoteMessage newRemoteMessage) { - log('FcmService::handleFirebaseForegroundMessage():data: ${newRemoteMessage.data}'); - if (newRemoteMessage.data.isNotEmpty) { - foregroundMessageStreamController?.add(newRemoteMessage.data); - } - } - void handleFirebaseBackgroundMessage(RemoteMessage newRemoteMessage) { log('FcmService::handleFirebaseBackgroundMessage():data: ${newRemoteMessage.data}'); if (newRemoteMessage.data.isNotEmpty) { @@ -33,19 +22,6 @@ class FcmService { } } - void handleMessageEventBroadcastChannel(html.MessageEvent messageEvent) { - log('FcmService::handleMessageEventBroadcastChannel():TYPE: ${messageEvent.data.runtimeType} | DATA: ${messageEvent.data}'); - try { - final jsonEventData = jsonDecode(jsonEncode(messageEvent.data)) as Map; - final eventData = BroadcastMessageEventData.fromJson(jsonEventData); - if (eventData.data?.isNotEmpty == true) { - foregroundMessageStreamController?.add(eventData.data!); - } - } catch (e) { - logError('FcmService::handleMessageEventBroadcastChannel: Exception = $e'); - } - } - void handleToken(String? token) { log('FcmService::handleToken():token: $token'); fcmTokenStreamController?.add(token); @@ -53,17 +29,14 @@ class FcmService { void initialStreamController() { log('FcmService::initialStreamController:'); - foregroundMessageStreamController = StreamController>.broadcast(); backgroundMessageStreamController = StreamController>.broadcast(); fcmTokenStreamController = StreamController.broadcast(); } void closeStream() { - foregroundMessageStreamController?.close(); backgroundMessageStreamController?.close(); fcmTokenStreamController?.close(); - foregroundMessageStreamController = null; backgroundMessageStreamController = null; fcmTokenStreamController = null; }