From b76e8bb7253708a98dddb5246f1816a04388e63a Mon Sep 17 00:00:00 2001 From: DatDang Date: Mon, 4 Nov 2024 16:28:13 +0700 Subject: [PATCH] TF-3157 Remove unnecessary background service worker --- core/lib/data/constants/constant.dart | 1 - .../datasource/web_socket_datasource.dart | 5 +- .../web_socket_datasource_impl.dart | 56 ++------ .../data/model/web_socket_echo.dart | 31 ----- .../data/model/web_socket_echo_request.dart | 19 +++ .../model/web_socket_push_enable_request.dart | 14 ++ .../data/model/web_socket_request.dart | 5 + .../web_socket_repository_impl.dart | 7 +- .../repository/web_socket_repository.dart | 5 +- .../domain/state/web_socket_push_state.dart | 10 +- .../connect_web_socket_interactor.dart | 30 +---- .../controller/push_base_controller.dart | 9 +- .../controller/web_socket_controller.dart | 94 +++++++++++--- .../presentation/services/fcm_receiver.dart | 50 +------- lib/main/utils/app_config.dart | 1 - .../data/model/web_socket_echo_test.dart | 44 ------- .../connect_web_socket_interactor_test.dart | 121 ------------------ web/firebase-messaging-sw.js | 20 --- web/web-sockets-worker.js | 74 ----------- web/worker_service/worker_service.js | 2 +- 20 files changed, 151 insertions(+), 447 deletions(-) delete mode 100644 lib/features/push_notification/data/model/web_socket_echo.dart create mode 100644 lib/features/push_notification/data/model/web_socket_echo_request.dart create mode 100644 lib/features/push_notification/data/model/web_socket_push_enable_request.dart create mode 100644 lib/features/push_notification/data/model/web_socket_request.dart delete mode 100644 test/features/push_notification/data/model/web_socket_echo_test.dart delete mode 100644 test/features/push_notification/domain/usecases/connect_web_socket_interactor_test.dart delete mode 100644 web/firebase-messaging-sw.js delete mode 100644 web/web-sockets-worker.js diff --git a/core/lib/data/constants/constant.dart b/core/lib/data/constants/constant.dart index 088fed2aa8..5540b0ac1c 100644 --- a/core/lib/data/constants/constant.dart +++ b/core/lib/data/constants/constant.dart @@ -7,5 +7,4 @@ class Constant { static const octetStreamMimeType = 'application/octet-stream'; static const pdfExtension = '.pdf'; static const imageType = 'image'; - static const wsServiceWorkerBroadcastChannel = 'background-message'; } \ No newline at end of file diff --git a/lib/features/push_notification/data/datasource/web_socket_datasource.dart b/lib/features/push_notification/data/datasource/web_socket_datasource.dart index 6dbfde4218..9e15463114 100644 --- a/lib/features/push_notification/data/datasource/web_socket_datasource.dart +++ b/lib/features/push_notification/data/datasource/web_socket_datasource.dart @@ -1,6 +1,9 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; abstract class WebSocketDatasource { - Stream getWebSocketChannel(Session session, AccountId accountId); + Future getWebSocketChannel( + Session session, + AccountId accountId); } \ No newline at end of file diff --git a/lib/features/push_notification/data/datasource_impl/web_socket_datasource_impl.dart b/lib/features/push_notification/data/datasource_impl/web_socket_datasource_impl.dart index 1c88936e6b..7ec0c650d1 100644 --- a/lib/features/push_notification/data/datasource_impl/web_socket_datasource_impl.dart +++ b/lib/features/push_notification/data/datasource_impl/web_socket_datasource_impl.dart @@ -1,21 +1,17 @@ -import 'package:core/data/constants/constant.dart'; -import 'package:core/utils/app_logger.dart'; -import 'package:core/utils/broadcast_channel/broadcast_channel.dart'; +import 'dart:async'; + 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/websocket_capability.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:model/extensions/session_extension.dart'; -import 'package:rxdart/transformers.dart'; import 'package:tmail_ui_user/features/push_notification/data/datasource/web_socket_datasource.dart'; -import 'package:tmail_ui_user/features/push_notification/data/model/connect_web_socket_message.dart'; import 'package:tmail_ui_user/features/push_notification/data/network/web_socket_api.dart'; import 'package:tmail_ui_user/features/push_notification/domain/exceptions/web_socket_exceptions.dart'; -import 'package:tmail_ui_user/features/push_notification/domain/model/web_socket_action.dart'; import 'package:tmail_ui_user/main/error/capability_validator.dart'; import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart'; -import 'package:universal_html/html.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; class WebSocketDatasourceImpl implements WebSocketDatasource { final WebSocketApi _webSocketApi; @@ -23,35 +19,21 @@ class WebSocketDatasourceImpl implements WebSocketDatasource { const WebSocketDatasourceImpl(this._webSocketApi, this._exceptionThrower); - static const String _webSocketClosed = 'webSocketClosed'; - @override - Stream getWebSocketChannel(Session session, AccountId accountId) { - return Stream - .castFrom(_getWebSocketChannel(session, accountId)) - .doOnError(_exceptionThrower.throwException); - } - - Stream _getWebSocketChannel( - Session session, - AccountId accountId, - ) async* { - final broadcastChannel = BroadcastChannel(Constant.wsServiceWorkerBroadcastChannel); - try { + Future getWebSocketChannel(Session session, AccountId accountId) { + return Future.sync(() async { _verifyWebSocketCapabilities(session, accountId); final webSocketTicket = await _webSocketApi.getWebSocketTicket(session, accountId); final webSocketUri = _getWebSocketUri(session, accountId); - window.navigator.serviceWorker?.controller?.postMessage(ConnectWebSocketMessage( - webSocketAction: WebSocketAction.connect, - webSocketUrl: webSocketUri.toString(), - webSocketTicket: webSocketTicket - ).toJson()); - - yield* _webSocketListener(broadcastChannel); - } catch (e) { - logError('RemoteWebSocketDatasourceImpl::getWebSocketChannel():error: $e'); - rethrow; - } + final webSocketChannel = WebSocketChannel.connect( + Uri.parse('$webSocketUri?ticket=$webSocketTicket'), + protocols: ["jmap"], + ); + + await webSocketChannel.ready; + + return webSocketChannel; + }).catchError(_exceptionThrower.throwException); } void _verifyWebSocketCapabilities(Session session, AccountId accountId) { @@ -77,14 +59,4 @@ class WebSocketDatasourceImpl implements WebSocketDatasource { return webSocketUri; } - - Stream _webSocketListener(BroadcastChannel broadcastChannel) { - return broadcastChannel.onMessage.map((event) { - if (event.data == _webSocketClosed) { - throw WebSocketClosedException(); - } - - return event.data; - }); - } } \ No newline at end of file diff --git a/lib/features/push_notification/data/model/web_socket_echo.dart b/lib/features/push_notification/data/model/web_socket_echo.dart deleted file mode 100644 index 624167ddf6..0000000000 --- a/lib/features/push_notification/data/model/web_socket_echo.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:json_annotation/json_annotation.dart'; - -part 'web_socket_echo.g.dart'; - -@JsonSerializable(includeIfNull: false) -class WebSocketEcho { - @JsonKey(name: '@type') - final String? type; - final String? requestId; - final List>? methodResponses; - - WebSocketEcho({ - this.type, - this.requestId, - this.methodResponses, - }); - - factory WebSocketEcho.fromJson(Map json) => _$WebSocketEchoFromJson(json); - - Map toJson() => _$WebSocketEchoToJson(this); - - static bool isValid(Map json) { - try { - final webSocketEcho = WebSocketEcho.fromJson(json); - final listResponses = webSocketEcho.methodResponses?.firstOrNull; - return listResponses?.contains('Core/echo') ?? false; - } catch (_) { - return false; - } - } -} diff --git a/lib/features/push_notification/data/model/web_socket_echo_request.dart b/lib/features/push_notification/data/model/web_socket_echo_request.dart new file mode 100644 index 0000000000..1d55c9d861 --- /dev/null +++ b/lib/features/push_notification/data/model/web_socket_echo_request.dart @@ -0,0 +1,19 @@ +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; +import 'package:tmail_ui_user/features/push_notification/data/model/web_socket_request.dart'; + +class WebSocketEchoRequest extends WebSocketRequest { + static const String type = 'Request'; + static const String id = 'R1'; + static final CapabilityIdentifier usingCapability = CapabilityIdentifier.jmapCore; + static const String method = 'Core/echo'; + + @override + Map toJson() { + return { + '@type': type, + 'id': id, + 'using': [usingCapability.value.toString()], + 'methodCalls': [[method, {}, 'c0']], + }; + } +} \ No newline at end of file diff --git a/lib/features/push_notification/data/model/web_socket_push_enable_request.dart b/lib/features/push_notification/data/model/web_socket_push_enable_request.dart new file mode 100644 index 0000000000..6d4b289b3a --- /dev/null +++ b/lib/features/push_notification/data/model/web_socket_push_enable_request.dart @@ -0,0 +1,14 @@ +import 'package:fcm/model/type_name.dart'; + +class WebSocketPushEnableRequest { + static const String type = 'WebSocketPushEnable'; + + static Map toJson({ + required List dataTypes, + }) { + return { + '@type': type, + 'dataTypes': dataTypes.map((typeName) => typeName.value).toList(), + }; + } +} \ No newline at end of file diff --git a/lib/features/push_notification/data/model/web_socket_request.dart b/lib/features/push_notification/data/model/web_socket_request.dart new file mode 100644 index 0000000000..729c9eec56 --- /dev/null +++ b/lib/features/push_notification/data/model/web_socket_request.dart @@ -0,0 +1,5 @@ +abstract class WebSocketRequest { + const WebSocketRequest(); + + Map toJson(); +} \ No newline at end of file diff --git a/lib/features/push_notification/data/repository/web_socket_repository_impl.dart b/lib/features/push_notification/data/repository/web_socket_repository_impl.dart index 0f7ecd93cb..9d6cc1b403 100644 --- a/lib/features/push_notification/data/repository/web_socket_repository_impl.dart +++ b/lib/features/push_notification/data/repository/web_socket_repository_impl.dart @@ -2,6 +2,7 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:tmail_ui_user/features/push_notification/data/datasource/web_socket_datasource.dart'; import 'package:tmail_ui_user/features/push_notification/domain/repository/web_socket_repository.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; class WebSocketRepositoryImpl implements WebSocketRepository { final WebSocketDatasource _webSocketDatasource; @@ -9,6 +10,8 @@ class WebSocketRepositoryImpl implements WebSocketRepository { WebSocketRepositoryImpl(this._webSocketDatasource); @override - Stream getWebSocketChannel(Session session, AccountId accountId) - => _webSocketDatasource.getWebSocketChannel(session, accountId); + Future getWebSocketChannel( + Session session, + AccountId accountId + ) => _webSocketDatasource.getWebSocketChannel(session, accountId); } \ No newline at end of file diff --git a/lib/features/push_notification/domain/repository/web_socket_repository.dart b/lib/features/push_notification/domain/repository/web_socket_repository.dart index 158272d393..b524c4e930 100644 --- a/lib/features/push_notification/domain/repository/web_socket_repository.dart +++ b/lib/features/push_notification/domain/repository/web_socket_repository.dart @@ -1,6 +1,9 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; abstract class WebSocketRepository { - Stream getWebSocketChannel(Session session, AccountId accountId); + Future getWebSocketChannel( + Session session, + AccountId accountId); } \ No newline at end of file diff --git a/lib/features/push_notification/domain/state/web_socket_push_state.dart b/lib/features/push_notification/domain/state/web_socket_push_state.dart index f6bdba4c94..d51a1890f9 100644 --- a/lib/features/push_notification/domain/state/web_socket_push_state.dart +++ b/lib/features/push_notification/domain/state/web_socket_push_state.dart @@ -1,16 +1,16 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; -import 'package:jmap_dart_client/jmap/push/state_change.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; class InitializingWebSocketPushChannel extends LoadingState {} -class WebSocketPushStateReceived extends UIState { - final StateChange? stateChange; +class WebSocketConnectionSuccess extends UIState { + final WebSocketChannel webSocketChannel; - WebSocketPushStateReceived(this.stateChange); + WebSocketConnectionSuccess(this.webSocketChannel); @override - List get props => [stateChange]; + List get props => [webSocketChannel]; } class WebSocketConnectionFailed extends FeatureFailure { diff --git a/lib/features/push_notification/domain/usecases/connect_web_socket_interactor.dart b/lib/features/push_notification/domain/usecases/connect_web_socket_interactor.dart index 48cf8b725d..5e6dbc02bd 100644 --- a/lib/features/push_notification/domain/usecases/connect_web_socket_interactor.dart +++ b/lib/features/push_notification/domain/usecases/connect_web_socket_interactor.dart @@ -1,13 +1,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:dartz/dartz.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'; -import 'package:tmail_ui_user/features/push_notification/data/model/web_socket_echo.dart'; import 'package:tmail_ui_user/features/push_notification/domain/repository/web_socket_repository.dart'; import 'package:tmail_ui_user/features/push_notification/domain/state/web_socket_push_state.dart'; @@ -22,31 +18,13 @@ class ConnectWebSocketInteractor { ) async* { try { yield Right(InitializingWebSocketPushChannel()); - yield* _webSocketRepository - .getWebSocketChannel(session, accountId) - .map(_toStateChange); + final webSocketChannel = await _webSocketRepository.getWebSocketChannel( + session, + accountId); + yield Right(WebSocketConnectionSuccess(webSocketChannel)); } catch (e) { logError('ConnectWebSocketInteractor::execute: $e'); yield Left(WebSocketConnectionFailed(exception: e)); } } - - Either _toStateChange(dynamic data) { - StateChange? possibleStateChange; - try { - if (data is String) { - data = jsonDecode(data); - } - possibleStateChange = StateChange.fromJson(data); - return Right(WebSocketPushStateReceived(possibleStateChange)); - } catch (e) { - logError('ConnectWebSocketInteractor::_toStateChange: ' - 'websocket message is not StateChange: $data'); - final dataIsWebSocketEcho = WebSocketEcho.isValid(data); - if (dataIsWebSocketEcho) { - return Right(WebSocketPushStateReceived(null)); - } - return Left(WebSocketConnectionFailed(exception: e)); - } - } } \ No newline at end of file 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 14ccdc7355..a9c25f16ff 100644 --- a/lib/features/push_notification/presentation/controller/push_base_controller.dart +++ b/lib/features/push_notification/presentation/controller/push_base_controller.dart @@ -19,10 +19,8 @@ abstract class PushBaseController { Session? session; AccountId? accountId; - StreamSubscription>? _stateStreamSubscription; - void consumeState(Stream> newStateStream) { - _stateStreamSubscription = newStateStream.listen( + newStateStream.listen( _handleStateStream, onError: handleErrorViewState, ); @@ -40,11 +38,6 @@ abstract class PushBaseController { logError('PushBaseController::handleErrorViewState():error: $error | stackTrace: $stackTrace'); } - void cancelStateStreamSubscription() { - _stateStreamSubscription?.cancel(); - _stateStreamSubscription = null; - } - void initialize({AccountId? accountId, Session? session}) { this.accountId = accountId; this.session = session; 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 6632c78a52..a262e7508f 100644 --- a/lib/features/push_notification/presentation/controller/web_socket_controller.dart +++ b/lib/features/push_notification/presentation/controller/web_socket_controller.dart @@ -1,9 +1,15 @@ +import 'dart:async'; +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:fcm/model/type_name.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; -import 'package:tmail_ui_user/features/push_notification/domain/exceptions/web_socket_exceptions.dart'; +import 'package:jmap_dart_client/jmap/push/state_change.dart'; +import 'package:tmail_ui_user/features/push_notification/data/model/web_socket_echo_request.dart'; +import 'package:tmail_ui_user/features/push_notification/data/model/web_socket_push_enable_request.dart'; import 'package:tmail_ui_user/features/push_notification/domain/state/web_socket_push_state.dart'; import 'package:tmail_ui_user/features/push_notification/domain/usecases/connect_web_socket_interactor.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/controller/push_base_controller.dart'; @@ -11,6 +17,7 @@ import 'package:tmail_ui_user/features/push_notification/presentation/extensions import 'package:tmail_ui_user/features/push_notification/presentation/listener/email_change_listener.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/listener/mailbox_change_listener.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; class WebSocketController extends PushBaseController { WebSocketController._internal(); @@ -22,11 +29,16 @@ class WebSocketController extends PushBaseController { ConnectWebSocketInteractor? _connectWebSocketInteractor; int _retryRemained = 3; + WebSocketChannel? _webSocketChannel; + Timer? _webSocketPingTimer; + StreamSubscription? _webSocketSubscription; @override void handleFailureViewState(Failure failure) { logError('WebSocketController::handleFailureViewState():Failure $failure'); - cancelStateStreamSubscription(); + _webSocketSubscription?.cancel(); + _webSocketChannel = null; + _webSocketPingTimer?.cancel(); if (failure is WebSocketConnectionFailed) { _handleWebSocketConnectionRetry(); } @@ -35,18 +47,15 @@ class WebSocketController extends PushBaseController { @override void handleSuccessViewState(Success success) { log('WebSocketController::handleSuccessViewState():Success $success'); - if (success is WebSocketPushStateReceived) { - _handleWebSocketPushStateReceived(success); + if (success is WebSocketConnectionSuccess) { + _handleWebSocketConnectionSuccess(success); } } @override void handleErrorViewState(Object error, StackTrace stackTrace) { super.handleErrorViewState(error, stackTrace); - cancelStateStreamSubscription(); - if (error is WebSocketClosedException) { - _handleWebSocketConnectionRetry(); - } + handleFailureViewState(WebSocketConnectionFailed()); } @override @@ -65,26 +74,69 @@ class WebSocketController extends PushBaseController { consumeState(_connectWebSocketInteractor!.execute(session, accountId)); } - void _handleWebSocketPushStateReceived(WebSocketPushStateReceived success) { - log('WebSocketController::_handleWebSocketPushStateReceived(): $success'); + void _handleWebSocketConnectionSuccess(WebSocketConnectionSuccess success) { + log('WebSocketController::_handleWebSocketConnectionSuccess(): $success'); _retryRemained = 3; - if (accountId == null || session == null) return; - final stateChange = success.stateChange; - if (stateChange == null) return; - final mapTypeState = stateChange.getMapTypeState(accountId!); - mappingTypeStateToAction( - mapTypeState, - accountId!, - emailChangeListener: EmailChangeListener.instance, - mailboxChangeListener: MailboxChangeListener.instance, - session!.username, - session: session); + _webSocketChannel = success.webSocketChannel; + _enableWebSocketPush(); + _pingWebSocket(); + _listenToWebSocket(); } void _handleWebSocketConnectionRetry() { + _webSocketSubscription?.cancel(); + _webSocketChannel = null; + _webSocketPingTimer?.cancel(); if (_retryRemained > 0) { _retryRemained--; _connectWebSocket(accountId, session); } } + + void _enableWebSocketPush() { + _webSocketChannel?.sink.add(jsonEncode(WebSocketPushEnableRequest.toJson( + dataTypes: [TypeName.emailType, TypeName.mailboxType] + ))); + } + + void _pingWebSocket() { + _webSocketPingTimer = Timer.periodic(const Duration(seconds: 10), (_) { + _webSocketChannel?.sink.add(jsonEncode(WebSocketEchoRequest().toJson())); + }); + } + + void _listenToWebSocket() { + _webSocketSubscription = _webSocketChannel?.stream.listen( + (data) { + log('WebSocketController::_listenToWebSocket(): $data'); + if (session == null || accountId == null) return; + if (data is String) { + data = jsonDecode(data); + } + + try { + final stateChange = StateChange.fromJson(data); + final mapTypeState = stateChange.getMapTypeState(accountId!); + mappingTypeStateToAction( + mapTypeState, + accountId!, + emailChangeListener: EmailChangeListener.instance, + mailboxChangeListener: MailboxChangeListener.instance, + session!.username, + session: session); + } catch (e) { + logError('WebSocketController::_listenToWebSocket(): Data is not StateChange'); + } + }, + cancelOnError: true, + onError: (error) { + logError('WebSocketController::_listenToWebSocket():Error: $error'); + handleFailureViewState(WebSocketConnectionFailed(exception: error)); + }, + onDone: () { + log('WebSocketController::_listenToWebSocket():onDone'); + _handleWebSocketConnectionRetry(); + }, + ); + } } \ 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 51fa43e66f..0cb6efd145 100644 --- a/lib/features/push_notification/presentation/services/fcm_receiver.dart +++ b/lib/features/push_notification/presentation/services/fcm_receiver.dart @@ -1,11 +1,7 @@ import 'package:core/utils/app_logger.dart'; -import 'package:core/utils/broadcast_channel/broadcast_channel.dart'; -import 'package:core/utils/platform_info.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/controller/fcm_message_controller.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/services/fcm_service.dart'; -import 'package:tmail_ui_user/main/utils/app_config.dart'; -import 'package:universal_html/html.dart' as html show MessageEvent, DomException; @pragma('vm:entry-point') Future handleFirebaseBackgroundMessage(RemoteMessage message) async { @@ -23,32 +19,11 @@ class FcmReceiver { static const int MAX_COUNT_RETRY_TO_GET_FCM_TOKEN = 3; - int _countRetryToGetFcmToken = 0; - Future onInitialFcmListener() async { - _countRetryToGetFcmToken = 0; _onForegroundMessage(); _onBackgroundMessage(); - if (PlatformInfo.isWeb) { - _onMessageBroadcastChannel(); - await _requestNotificationPermissionOnWeb(); - } else { - await _onHandleFcmToken(); - } - } - - Future _requestNotificationPermissionOnWeb() async { - NotificationSettings notificationSetting = await FirebaseMessaging.instance.getNotificationSettings(); - log('FcmReceiver::_requestNotificationPermissionOnWeb: authorizationStatus = ${notificationSetting.authorizationStatus}'); - if (notificationSetting.authorizationStatus != AuthorizationStatus.authorized) { - notificationSetting = await FirebaseMessaging.instance.requestPermission(); - if (notificationSetting.authorizationStatus == AuthorizationStatus.authorized) { - await _onHandleFcmToken(); - } - } else { - await _onHandleFcmToken(); - } + await _onHandleFcmToken(); } void _onForegroundMessage() { @@ -59,38 +34,17 @@ class FcmReceiver { FirebaseMessaging.onBackgroundMessage(handleFirebaseBackgroundMessage); } - void _onMessageBroadcastChannel() { - final broadcast = BroadcastChannel('background-message'); - broadcast.onMessage.listen((event) { - if (event is html.MessageEvent) { - FcmService.instance.handleMessageEventBroadcastChannel(event); - } - }); - } - Future _getInitialToken() async { try { - final vapidKey = PlatformInfo.isWeb ? AppConfig.fcmVapidPublicKeyWeb : null; - final token = await FirebaseMessaging.instance.getToken(vapidKey: vapidKey); + final token = await FirebaseMessaging.instance.getToken(); log('FcmReceiver::_getInitialToken:token: $token'); return token; } catch (e) { logError('FcmReceiver::_getInitialToken: TYPE = ${e.runtimeType} | Exception = $e'); - if (PlatformInfo.isWeb - && e is html.DomException - && _countRetryToGetFcmToken < MAX_COUNT_RETRY_TO_GET_FCM_TOKEN) { - return await _retryGetToken(); - } return null; } } - Future _retryGetToken() async { - _countRetryToGetFcmToken++; - log('FcmReceiver::_retryGetToken: CountRetry = $_countRetryToGetFcmToken'); - return await _getInitialToken(); - } - Future _onHandleFcmToken() async { final token = await _getInitialToken(); FcmService.instance.handleToken(token); diff --git a/lib/main/utils/app_config.dart b/lib/main/utils/app_config.dart index c03faff1f0..2abac88f85 100644 --- a/lib/main/utils/app_config.dart +++ b/lib/main/utils/app_config.dart @@ -36,7 +36,6 @@ class AppConfig { return supportedOtherPlatform == 'supported'; } } - static String get fcmVapidPublicKeyWeb => dotenv.get('FIREBASE_WEB_VAPID_PUBLIC_KEY', fallback: ''); static List get oidcScopes { try { final envScopes = dotenv.get('OIDC_SCOPES', fallback: ''); diff --git a/test/features/push_notification/data/model/web_socket_echo_test.dart b/test/features/push_notification/data/model/web_socket_echo_test.dart deleted file mode 100644 index cd91450448..0000000000 --- a/test/features/push_notification/data/model/web_socket_echo_test.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:tmail_ui_user/features/push_notification/data/model/web_socket_echo.dart'; - -void main() { - group('web socket echo test:', () { - group('isValid():', () { - test( - 'should return true ' - 'when json is web socket echo', - () { - // arrange - final json = { - "@type": "Response", - "requestId": "R1", - "methodResponses": [["Core/echo", {}, "c0"]] - }; - - // act - final result = WebSocketEcho.isValid(json); - - // assert - expect(result, true); - }); - - test( - 'should return false ' - 'when json is not web socket echo', - () { - // arrange - final json = { - "@type": "Response", - "requestId": "R1", - "methodResponses": [["Core/not-echo", {}, "c0"]] - }; - - // act - final result = WebSocketEcho.isValid(json); - - // assert - expect(result, false); - }); - }); - }); -} \ No newline at end of file diff --git a/test/features/push_notification/domain/usecases/connect_web_socket_interactor_test.dart b/test/features/push_notification/domain/usecases/connect_web_socket_interactor_test.dart deleted file mode 100644 index 06f1a609aa..0000000000 --- a/test/features/push_notification/domain/usecases/connect_web_socket_interactor_test.dart +++ /dev/null @@ -1,121 +0,0 @@ -import 'package:dartz/dartz.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:jmap_dart_client/jmap/push/state_change.dart'; -import 'package:mockito/annotations.dart'; -import 'package:mockito/mockito.dart'; -import 'package:tmail_ui_user/features/push_notification/domain/repository/web_socket_repository.dart'; -import 'package:tmail_ui_user/features/push_notification/domain/state/web_socket_push_state.dart'; -import 'package:tmail_ui_user/features/push_notification/domain/usecases/connect_web_socket_interactor.dart'; - -import '../../../../fixtures/account_fixtures.dart'; -import '../../../../fixtures/session_fixtures.dart'; -import 'connect_web_socket_interactor_test.mocks.dart'; - -@GenerateNiceMocks([MockSpec()]) -void main() { - late MockWebSocketRepository webSocketRepository; - late ConnectWebSocketInteractor connectWebSocketInteractor; - - setUp(() { - webSocketRepository = MockWebSocketRepository(); - connectWebSocketInteractor = ConnectWebSocketInteractor(webSocketRepository); - }); - - group('connect web socket interactor test:', () { - test( - 'should yield WebSocketPushStateReceived with StateChange ' - 'when web socket repository yield StateChange', - () { - // arrange - final stateChangeJson = { - "@type": "StateChange", - "changed": {} - }; - when(webSocketRepository.getWebSocketChannel(any, any)) - .thenAnswer((_) => Stream.value(stateChangeJson)); - - // assert - expect( - connectWebSocketInteractor.execute( - SessionFixtures.aliceSession, - AccountFixtures.aliceAccountId), - emitsInOrder([ - Right(InitializingWebSocketPushChannel()), - Right(WebSocketPushStateReceived(StateChange.fromJson(stateChangeJson))), - ])); - }); - - test( - 'should yield WebSocketPushStateReceived with null ' - 'when web socket repository yield WebSocketEcho', - () { - // arrange - final webSocketEchoJson = { - "@type": "Response", - "requestId": "R1", - "methodResponses": [["Core/echo", {}, "c0"]] - }; - when(webSocketRepository.getWebSocketChannel(any, any)) - .thenAnswer((_) => Stream.value(webSocketEchoJson)); - - // assert - expect( - connectWebSocketInteractor.execute( - SessionFixtures.aliceSession, - AccountFixtures.aliceAccountId), - emitsInOrder([ - Right(InitializingWebSocketPushChannel()), - Right(WebSocketPushStateReceived(null)), - ])); - }); - - test( - 'should yield WebSocketConnectionFailed ' - 'when web socket repository throws exception', - () { - // arrange - final exception = Exception(); - when(webSocketRepository.getWebSocketChannel(any, any)) - .thenThrow(exception); - - // assert - expect( - connectWebSocketInteractor.execute( - SessionFixtures.aliceSession, - AccountFixtures.aliceAccountId), - emitsInOrder([ - Right(InitializingWebSocketPushChannel()), - Left(WebSocketConnectionFailed(exception: exception)), - ])); - }); - - test( - 'should yield WebSocketConnectionFailed ' - 'when web socket repository yield data ' - 'and data is not web socket echo', - () async { - // arrange - final notWebSocketEchoJson = { - "@type": "Response", - "requestId": "R1", - "methodResponses": [["Core/not-echo", {}, "c0"]] - }; - when(webSocketRepository.getWebSocketChannel(any, any)) - .thenAnswer((_) => Stream.value(notWebSocketEchoJson)); - - // act - final lastState = await connectWebSocketInteractor.execute( - SessionFixtures.aliceSession, - AccountFixtures.aliceAccountId).last; - - // assert - expect( - lastState.fold( - (failure) => failure is WebSocketConnectionFailed, - (success) => false, - ), - true - ); - }); - }); -} \ No newline at end of file diff --git a/web/firebase-messaging-sw.js b/web/firebase-messaging-sw.js deleted file mode 100644 index 5b27455fd2..0000000000 --- a/web/firebase-messaging-sw.js +++ /dev/null @@ -1,20 +0,0 @@ -importScripts("https://www.gstatic.com/firebasejs/9.10.0/firebase-app-compat.js"); -importScripts("https://www.gstatic.com/firebasejs/9.10.0/firebase-messaging-compat.js"); -// Initialize the Firebase app in the service worker by passing in the messagingSenderId. -firebase.initializeApp({ - apiKey: "...", - authDomain: "...", - databaseURL: "...", - projectId: "...", - storageBucket: "...", - messagingSenderId: "...", - appId: "...", -}); -// Retrieve an instance of Firebase Messaging so that it can handle background messages. -const messaging = firebase.messaging(); - -const broadcast = new BroadcastChannel('background-message'); - -messaging.onBackgroundMessage((message) => { - broadcast.postMessage(message); -}); \ No newline at end of file diff --git a/web/web-sockets-worker.js b/web/web-sockets-worker.js deleted file mode 100644 index 9205342bc6..0000000000 --- a/web/web-sockets-worker.js +++ /dev/null @@ -1,74 +0,0 @@ -importScripts('flutter_service_worker.js?v={{flutter_service_worker_version}}'); - -var webSocket; -const broadcast = new BroadcastChannel("background-message"); -var intervalId; -const pingIntervalInMs = 10000; - -function connect(url, ticket) { - webSocket = new WebSocket(`${url}?ticket=${ticket}`, "jmap"); - - webSocket.onopen = () => { - console.log("websocket open"); - webSocket.send( - JSON.stringify({ - "@type": "WebSocketPushEnable", - dataTypes: ["Mailbox", "Email"], - }) - ); - intervalId = setInterval(() => { - webSocket.send( - JSON.stringify({ - "@type": "Request", - id: "R1", - using: ["urn:ietf:params:jmap:core"], - methodCalls: [["Core/echo", {}, "c0"]], - }) - ); - }, pingIntervalInMs); - }; - - webSocket.onmessage = (event) => { - console.log(`websocket received message: ${event.data}`); - broadcast.postMessage(event.data); - }; - - webSocket.onclose = (event) => { - console.log( - `websocket connection closed with code: "${event.code}" reason: "${event.reason}" and cleanly: "${event.wasClean}"` - ); - broadcast.postMessage("webSocketClosed"); - webSocket = null; - clearInterval(intervalId); - }; -} - -function disconnect() { - if (webSocket == null) { - return; - } - webSocket.close(); -} - -self.addEventListener("install", (event) => { - self.skipWaiting().then(() => { - console.log("Service worker installed"); - }); -}); - -self.addEventListener("activate", (event) => { - event.waitUntil( - self.clients.claim().then(() => { - console.log("Service worker activated"); - }) - ); -}); - -self.addEventListener("message", (event) => { - console.log(`web socket worker received message: ${event.data}`); - if (event.data.action === "connect") { - connect(event.data.url, event.data.ticket); - } else if (event.data.action === "disconnect") { - disconnect(); - } -}); diff --git a/web/worker_service/worker_service.js b/web/worker_service/worker_service.js index 2fac331b61..ec6a1a7ee9 100644 --- a/web/worker_service/worker_service.js +++ b/web/worker_service/worker_service.js @@ -23,7 +23,7 @@ function fetchServiceWorker() { // Wait for registration to finish before dropping the