Skip to content

Commit f3a6bdc

Browse files
feat(firebase_messaging)!: upgrade messaging web to Firebase v9 JS SDK. (#8860)
Co-authored-by: Elliot Hesp <[email protected]>
1 parent 8e95a51 commit f3a6bdc

File tree

8 files changed

+98
-56
lines changed

8 files changed

+98
-56
lines changed

packages/firebase_messaging/firebase_messaging/lib/src/messaging.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ class FirebaseMessaging extends FirebasePluginPlatform {
124124
return _delegate.onTokenRefresh;
125125
}
126126

127-
bool isSupported() {
127+
Future<bool> isSupported() {
128128
return _delegate.isSupported();
129129
}
130130

packages/firebase_messaging/firebase_messaging_platform_interface/lib/src/method_channel/method_channel_messaging.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,8 @@ class MethodChannelFirebaseMessaging extends FirebaseMessagingPlatform {
144144

145145
/// Returns "true" as this API is used to inform users of web browser support
146146
@override
147-
bool isSupported() {
148-
return true;
147+
Future<bool> isSupported() {
148+
return Future.value(true);
149149
}
150150

151151
@override

packages/firebase_messaging/firebase_messaging_platform_interface/lib/src/platform_interface/platform_interface_messaging.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ abstract class FirebaseMessagingPlatform extends PlatformInterface {
183183

184184
/// isSupported() informs web users whether
185185
/// the browser supports Firebase.Messaging
186-
bool isSupported() {
186+
Future<bool> isSupported() {
187187
throw UnimplementedError('isSupported() is not implemented');
188188
}
189189

packages/firebase_messaging/firebase_messaging_web/lib/firebase_messaging_web.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class FirebaseMessagingWeb extends FirebaseMessagingPlatform {
2727
_webMessaging ??=
2828
messaging_interop.getMessagingInstance(core_interop.app(app.name));
2929

30-
if (!_initialized && messaging_interop.isSupported()) {
30+
if (!_initialized) {
3131
_webMessaging!.onMessage
3232
.listen((messaging_interop.MessagePayload webMessagePayload) {
3333
RemoteMessage remoteMessage =
@@ -57,8 +57,8 @@ class FirebaseMessagingWeb extends FirebaseMessagingPlatform {
5757

5858
/// Updates user on browser support for Firebase.Messaging
5959
@override
60-
bool isSupported() {
61-
return messaging_interop.isSupported();
60+
Future<bool> isSupported() {
61+
return messaging_interop.Messaging.isSupported();
6262
}
6363

6464
@override

packages/firebase_messaging/firebase_messaging_web/lib/src/interop/firebase_interop.dart

Lines changed: 0 additions & 17 deletions
This file was deleted.

packages/firebase_messaging/firebase_messaging_web/lib/src/interop/messaging.dart

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,14 @@ import 'package:js/js.dart';
1111

1212
import 'package:firebase_core_web/firebase_core_web_interop.dart';
1313
import 'messaging_interop.dart' as messaging_interop;
14-
import 'firebase_interop.dart' as firebase_interop;
1514

1615
export 'messaging_interop.dart';
1716

1817
/// Given an AppJSImp, return the Messaging instance.
1918
Messaging getMessagingInstance([App? app]) {
2019
return Messaging.getInstance(app != null
21-
? firebase_interop.messaging(app.jsObject)
22-
: firebase_interop.messaging());
20+
? messaging_interop.getMessaging(app.jsObject)
21+
: messaging_interop.getMessaging());
2322
}
2423

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

32-
static bool isSupported() => messaging_interop.isSupported();
31+
static Future<bool> isSupported() =>
32+
handleThenable(messaging_interop.isSupported());
3333

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

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

4142
/// After calling [requestPermission] you can call this method to get an FCM registration token
4243
/// that can be used to send push messages to this user.
4344
Future<String> getToken({String? vapidKey}) =>
44-
handleThenable(jsObject.getToken(vapidKey == null
45-
? null
46-
: {
47-
'vapidKey': vapidKey,
48-
}));
45+
handleThenable(messaging_interop.getToken(
46+
jsObject,
47+
vapidKey == null
48+
? null
49+
: messaging_interop.GetTokenOptions(vapidKey: vapidKey)));
4950

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

70-
jsObject.onMessage(nextWrapper, errorWrapper);
71+
messaging_interop.onMessage(jsObject,
72+
messaging_interop.Observer(next: nextWrapper, error: errorWrapper));
7173
}
7274
return _controller.stream;
7375
}

packages/firebase_messaging/firebase_messaging_web/lib/src/interop/messaging_interop.dart

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,52 @@
55

66
// ignore_for_file: public_member_api_docs
77

8-
@JS('firebase.messaging')
8+
@JS('firebase_messaging')
99
library firebase_interop.messaging;
1010

1111
import 'package:js/js.dart';
1212
import 'package:firebase_core_web/firebase_core_web_interop.dart';
1313

14+
@JS()
15+
external MessagingJsImpl getMessaging([AppJsImpl? app]);
16+
17+
@JS()
18+
external PromiseJsImpl<bool> deleteToken(MessagingJsImpl messaging);
19+
20+
@JS()
21+
external PromiseJsImpl<String> getToken(
22+
MessagingJsImpl messaging, GetTokenOptions? getTokenOptions);
23+
1424
@JS('isSupported')
15-
external bool isSupported();
25+
external PromiseJsImpl<bool> isSupported();
26+
27+
@JS()
28+
external void Function() onMessage(
29+
MessagingJsImpl messaging,
30+
Observer observer,
31+
);
1632

1733
@JS('Messaging')
18-
abstract class MessagingJsImpl {
19-
external PromiseJsImpl<void> deleteToken();
20-
external PromiseJsImpl<String> getToken(dynamic getTokenOptions);
21-
external void Function() onMessage(
22-
dynamic optionsOrObserverOrOnNext,
23-
dynamic observerOrOnNextOrOnError,
24-
);
34+
abstract class MessagingJsImpl {}
35+
36+
@JS()
37+
@anonymous
38+
class Observer {
39+
external dynamic get next;
40+
external dynamic get error;
41+
external factory Observer({dynamic next, dynamic error});
42+
}
43+
44+
@JS()
45+
@anonymous
46+
class GetTokenOptions {
47+
external String get vapidKey;
48+
// TODO - I imagine we won't be implementing serviceWorkerRegistration type as it extends EventTarget class
49+
// external String get serviceWorkerRegistration
50+
external factory GetTokenOptions({
51+
String? vapidKey,
52+
/*dynamic serviceWorkerRegistration */
53+
});
2554
}
2655

2756
@JS()

tests/test_driver/firebase_messaging/firebase_messaging_e2e.dart

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,10 @@ void setupTests() {
7575
});
7676

7777
group('isSupported()', () {
78-
test('returns "true" value', () {
79-
expect(messaging.isSupported(), isTrue);
78+
test('returns "true" value', () async {
79+
final result = await messaging.isSupported();
80+
81+
expect(result, isA<bool>());
8082
});
8183
});
8284

@@ -97,6 +99,7 @@ void setupTests() {
9799
'authorizationStatus returns AuthorizationStatus.notDetermined on Web',
98100
() async {
99101
final result = await messaging.requestPermission();
102+
100103
expect(result, isA<NotificationSettings>());
101104
expect(
102105
result.authorizationStatus,
@@ -136,8 +139,21 @@ void setupTests() {
136139
'getToken()',
137140
() {
138141
test('returns a token', () async {
139-
final result = await messaging.getToken();
140-
expect(result, isA<String>());
142+
final result = await messaging.requestPermission();
143+
144+
if (result.authorizationStatus == AuthorizationStatus.authorized) {
145+
final result = await messaging.getToken();
146+
147+
expect(result, isA<String>());
148+
} else {
149+
await expectLater(
150+
messaging.getToken(),
151+
throwsA(
152+
isA<FirebaseException>()
153+
.having((e) => e.code, 'code', 'permission-blocked'),
154+
),
155+
);
156+
}
141157
});
142158
},
143159
skip: skipManualTests,
@@ -147,14 +163,26 @@ void setupTests() {
147163
test(
148164
'generate a new token after deleting',
149165
() async {
150-
final token1 = await messaging.getToken();
151-
await Future.delayed(const Duration(seconds: 3));
152-
await messaging.deleteToken();
153-
await Future.delayed(const Duration(seconds: 3));
154-
final token2 = await messaging.getToken();
155-
expect(token1, isA<String>());
156-
expect(token2, isA<String>());
157-
expect(token1, isNot(token2));
166+
final result = await messaging.requestPermission();
167+
168+
if (result.authorizationStatus == AuthorizationStatus.authorized) {
169+
final token1 = await messaging.getToken();
170+
await Future.delayed(const Duration(seconds: 3));
171+
await messaging.deleteToken();
172+
await Future.delayed(const Duration(seconds: 3));
173+
final token2 = await messaging.getToken();
174+
expect(token1, isA<String>());
175+
expect(token2, isA<String>());
176+
expect(token1, isNot(token2));
177+
} else {
178+
await expectLater(
179+
messaging.getToken(),
180+
throwsA(
181+
isA<FirebaseException>()
182+
.having((e) => e.code, 'code', 'permission-blocked'),
183+
),
184+
);
185+
}
158186
},
159187
skip: skipManualTests,
160188
); // only run for manual testing

0 commit comments

Comments
 (0)