Skip to content

Commit

Permalink
feat(firebase_messaging)!: upgrade messaging web to Firebase v9 JS SD…
Browse files Browse the repository at this point in the history
…K. (#8860)

Co-authored-by: Elliot Hesp <elliot.hesp@gmail.com>
  • Loading branch information
russellwheatley and Ehesp committed Jul 7, 2022
1 parent 8e95a51 commit f3a6bdc
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 56 deletions.
Expand Up @@ -124,7 +124,7 @@ class FirebaseMessaging extends FirebasePluginPlatform {
return _delegate.onTokenRefresh;
}

bool isSupported() {
Future<bool> isSupported() {
return _delegate.isSupported();
}

Expand Down
Expand Up @@ -144,8 +144,8 @@ class MethodChannelFirebaseMessaging extends FirebaseMessagingPlatform {

/// Returns "true" as this API is used to inform users of web browser support
@override
bool isSupported() {
return true;
Future<bool> isSupported() {
return Future.value(true);
}

@override
Expand Down
Expand Up @@ -183,7 +183,7 @@ abstract class FirebaseMessagingPlatform extends PlatformInterface {

/// isSupported() informs web users whether
/// the browser supports Firebase.Messaging
bool isSupported() {
Future<bool> isSupported() {
throw UnimplementedError('isSupported() is not implemented');
}

Expand Down
Expand Up @@ -27,7 +27,7 @@ class FirebaseMessagingWeb extends FirebaseMessagingPlatform {
_webMessaging ??=
messaging_interop.getMessagingInstance(core_interop.app(app.name));

if (!_initialized && messaging_interop.isSupported()) {
if (!_initialized) {
_webMessaging!.onMessage
.listen((messaging_interop.MessagePayload webMessagePayload) {
RemoteMessage remoteMessage =
Expand Down Expand Up @@ -57,8 +57,8 @@ class FirebaseMessagingWeb extends FirebaseMessagingPlatform {

/// Updates user on browser support for Firebase.Messaging
@override
bool isSupported() {
return messaging_interop.isSupported();
Future<bool> isSupported() {
return messaging_interop.Messaging.isSupported();
}

@override
Expand Down

This file was deleted.

Expand Up @@ -11,15 +11,14 @@ import 'package:js/js.dart';

import 'package:firebase_core_web/firebase_core_web_interop.dart';
import 'messaging_interop.dart' as messaging_interop;
import 'firebase_interop.dart' as firebase_interop;

export 'messaging_interop.dart';

/// Given an AppJSImp, return the Messaging instance.
Messaging getMessagingInstance([App? app]) {
return Messaging.getInstance(app != null
? firebase_interop.messaging(app.jsObject)
: firebase_interop.messaging());
? messaging_interop.getMessaging(app.jsObject)
: messaging_interop.getMessaging());
}

class Messaging extends JsObjectWrapper<messaging_interop.MessagingJsImpl> {
Expand All @@ -29,23 +28,25 @@ class Messaging extends JsObjectWrapper<messaging_interop.MessagingJsImpl> {
return _expando[jsObject] ??= Messaging._fromJsObject(jsObject);
}

static bool isSupported() => messaging_interop.isSupported();
static Future<bool> isSupported() =>
handleThenable(messaging_interop.isSupported());

Messaging._fromJsObject(messaging_interop.MessagingJsImpl jsObject)
: super.fromJsObject(jsObject);

/// To forcibly stop a registration token from being used, delete it by calling this method.
/// Calling this method will stop the periodic data transmission to the FCM backend.
Future<void> deleteToken() => handleThenable(jsObject.deleteToken());
Future<void> deleteToken() =>
handleThenable(messaging_interop.deleteToken(jsObject));

/// After calling [requestPermission] you can call this method to get an FCM registration token
/// that can be used to send push messages to this user.
Future<String> getToken({String? vapidKey}) =>
handleThenable(jsObject.getToken(vapidKey == null
? null
: {
'vapidKey': vapidKey,
}));
handleThenable(messaging_interop.getToken(
jsObject,
vapidKey == null
? null
: messaging_interop.GetTokenOptions(vapidKey: vapidKey)));

// ignore: close_sinks
StreamController<MessagePayload>? _onMessageController;
Expand All @@ -67,7 +68,8 @@ class Messaging extends JsObjectWrapper<messaging_interop.MessagingJsImpl> {
_controller!.addError(e);
});

jsObject.onMessage(nextWrapper, errorWrapper);
messaging_interop.onMessage(jsObject,
messaging_interop.Observer(next: nextWrapper, error: errorWrapper));
}
return _controller.stream;
}
Expand Down
Expand Up @@ -5,23 +5,52 @@

// ignore_for_file: public_member_api_docs

@JS('firebase.messaging')
@JS('firebase_messaging')
library firebase_interop.messaging;

import 'package:js/js.dart';
import 'package:firebase_core_web/firebase_core_web_interop.dart';

@JS()
external MessagingJsImpl getMessaging([AppJsImpl? app]);

@JS()
external PromiseJsImpl<bool> deleteToken(MessagingJsImpl messaging);

@JS()
external PromiseJsImpl<String> getToken(
MessagingJsImpl messaging, GetTokenOptions? getTokenOptions);

@JS('isSupported')
external bool isSupported();
external PromiseJsImpl<bool> isSupported();

@JS()
external void Function() onMessage(
MessagingJsImpl messaging,
Observer observer,
);

@JS('Messaging')
abstract class MessagingJsImpl {
external PromiseJsImpl<void> deleteToken();
external PromiseJsImpl<String> getToken(dynamic getTokenOptions);
external void Function() onMessage(
dynamic optionsOrObserverOrOnNext,
dynamic observerOrOnNextOrOnError,
);
abstract class MessagingJsImpl {}

@JS()
@anonymous
class Observer {
external dynamic get next;
external dynamic get error;
external factory Observer({dynamic next, dynamic error});
}

@JS()
@anonymous
class GetTokenOptions {
external String get vapidKey;
// TODO - I imagine we won't be implementing serviceWorkerRegistration type as it extends EventTarget class
// external String get serviceWorkerRegistration
external factory GetTokenOptions({
String? vapidKey,
/*dynamic serviceWorkerRegistration */
});
}

@JS()
Expand Down
52 changes: 40 additions & 12 deletions tests/test_driver/firebase_messaging/firebase_messaging_e2e.dart
Expand Up @@ -75,8 +75,10 @@ void setupTests() {
});

group('isSupported()', () {
test('returns "true" value', () {
expect(messaging.isSupported(), isTrue);
test('returns "true" value', () async {

This comment has been minimized.

Copy link
@mishkov

mishkov Dec 9, 2022

I think better rename this test to "returns bool value" and add some tests that specifically check true/false returned value;

final result = await messaging.isSupported();

expect(result, isA<bool>());
});
});

Expand All @@ -97,6 +99,7 @@ void setupTests() {
'authorizationStatus returns AuthorizationStatus.notDetermined on Web',
() async {
final result = await messaging.requestPermission();

expect(result, isA<NotificationSettings>());
expect(
result.authorizationStatus,
Expand Down Expand Up @@ -136,8 +139,21 @@ void setupTests() {
'getToken()',
() {
test('returns a token', () async {
final result = await messaging.getToken();
expect(result, isA<String>());
final result = await messaging.requestPermission();

if (result.authorizationStatus == AuthorizationStatus.authorized) {
final result = await messaging.getToken();

expect(result, isA<String>());
} else {
await expectLater(
messaging.getToken(),
throwsA(
isA<FirebaseException>()
.having((e) => e.code, 'code', 'permission-blocked'),
),
);
}
});
},
skip: skipManualTests,
Expand All @@ -147,14 +163,26 @@ void setupTests() {
test(
'generate a new token after deleting',
() async {
final token1 = await messaging.getToken();
await Future.delayed(const Duration(seconds: 3));
await messaging.deleteToken();
await Future.delayed(const Duration(seconds: 3));
final token2 = await messaging.getToken();
expect(token1, isA<String>());
expect(token2, isA<String>());
expect(token1, isNot(token2));
final result = await messaging.requestPermission();

if (result.authorizationStatus == AuthorizationStatus.authorized) {
final token1 = await messaging.getToken();
await Future.delayed(const Duration(seconds: 3));
await messaging.deleteToken();
await Future.delayed(const Duration(seconds: 3));
final token2 = await messaging.getToken();
expect(token1, isA<String>());
expect(token2, isA<String>());
expect(token1, isNot(token2));
} else {
await expectLater(
messaging.getToken(),
throwsA(
isA<FirebaseException>()
.having((e) => e.code, 'code', 'permission-blocked'),
),
);
}
},
skip: skipManualTests,
); // only run for manual testing
Expand Down

0 comments on commit f3a6bdc

Please sign in to comment.