Skip to content

feat(messaging, web): migrate web to js_interop to be compatible with WASM #12223

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 1 commit into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';
import 'dart:html';
import 'dart:js_interop';

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_core_web/firebase_core_web.dart';
import 'package:firebase_core_web/firebase_core_web_interop.dart'
as core_interop;
import 'package:firebase_messaging_platform_interface/firebase_messaging_platform_interface.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:web/web.dart' as web;

import 'src/internals.dart';
import 'src/interop/messaging.dart' as messaging_interop;
Expand Down Expand Up @@ -129,7 +130,7 @@ class FirebaseMessagingWeb extends FirebaseMessagingPlatform {

@override
Future<NotificationSettings> getNotificationSettings() async {
return utils.getNotificationSettings(Notification.permission);
return utils.getNotificationSettings(web.Notification.permission);
}

@override
Expand All @@ -143,7 +144,8 @@ class FirebaseMessagingWeb extends FirebaseMessagingPlatform {
bool sound = true,
}) {
return convertWebExceptions(() async {
String status = await Notification.requestPermission();
String status =
(await web.Notification.requestPermission().toDart)! as String;
return utils.getNotificationSettings(status);
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// ignore_for_file: public_member_api_docs

import 'dart:async';
import 'dart:js_interop';

import 'package:firebase_core_web/firebase_core_web_interop.dart';
import 'package:js/js.dart';
Expand All @@ -32,25 +33,26 @@ class Messaging extends JsObjectWrapper<messaging_interop.MessagingJsImpl> {
}

static Future<bool> isSupported() =>
handleThenable(messaging_interop.isSupported());
messaging_interop.isSupported().toDart.then((value) => value! as bool);

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(messaging_interop.deleteToken(jsObject));
Future<void> deleteToken() => messaging_interop.deleteToken(jsObject).toDart;

/// 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}) async {
try {
final token = await handleThenable(messaging_interop.getToken(
jsObject,
vapidKey == null
? null
: messaging_interop.GetTokenOptions(vapidKey: vapidKey)));
final token = (await messaging_interop
.getToken(
jsObject,
vapidKey == null
? null
: messaging_interop.GetTokenOptions(vapidKey: vapidKey.toJS))
.toDart)! as String;
return token;
} catch (err) {
// A race condition can happen in which the service worker get registered
Expand Down Expand Up @@ -78,15 +80,18 @@ class Messaging extends JsObjectWrapper<messaging_interop.MessagingJsImpl> {
StreamController<MessagePayload>? _controller = controller;
if (_controller == null) {
_controller = StreamController.broadcast(sync: true);
final nextWrapper = allowInterop((payload) {
_controller!.add(MessagePayload._fromJsObject(payload));
final nextWrapper = allowInterop((JSAny payload) {
_controller!.add(MessagePayload._fromJsObject(
payload as messaging_interop.MessagePayloadJsImpl));
});
final errorWrapper = allowInterop((e) {
final errorWrapper = allowInterop((JSError e) {
_controller!.addError(e);
});

messaging_interop.onMessage(jsObject,
messaging_interop.Observer(next: nextWrapper, error: errorWrapper));
messaging_interop.onMessage(
jsObject,
messaging_interop.Observer(
next: nextWrapper.toJS, error: errorWrapper.toJS));
}
return _controller.stream;
}
Expand All @@ -98,32 +103,32 @@ class NotificationPayload
messaging_interop.NotificationPayloadJsImpl jsObject)
: super.fromJsObject(jsObject);

String? get title => jsObject.title;
String? get body => jsObject.body;
String? get image => jsObject.image;
String? get title => jsObject.title?.toDart;
String? get body => jsObject.body?.toDart;
String? get image => jsObject.image?.toDart;
}

class MessagePayload
extends JsObjectWrapper<messaging_interop.MessagePayloadJsImpl> {
MessagePayload._fromJsObject(messaging_interop.MessagePayloadJsImpl jsObject)
: super.fromJsObject(jsObject);

String get messageId => jsObject.messageId;
String? get collapseKey => jsObject.collapseKey;
String get messageId => jsObject.messageId.toDart;
String? get collapseKey => jsObject.collapseKey?.toDart;
FcmOptions? get fcmOptions => jsObject.fcmOptions == null
? null
: FcmOptions._fromJsObject(jsObject.fcmOptions!);
NotificationPayload? get notification => jsObject.notification == null
? null
: NotificationPayload._fromJsObject(jsObject.notification!);
Map<String, dynamic>? get data => dartify(jsObject.data);
String? get from => jsObject.from;
String? get from => jsObject.from?.toDart;
}

class FcmOptions extends JsObjectWrapper<messaging_interop.FcmOptionsJsImpl> {
FcmOptions._fromJsObject(messaging_interop.FcmOptionsJsImpl jsObject)
: super.fromJsObject(jsObject);

String? get analyticsLabel => jsObject.analyticsLabel;
String? get link => jsObject.link;
String? get analyticsLabel => jsObject.analyticsLabel?.toDart;
String? get link => jsObject.link?.toDart;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,73 +8,97 @@
@JS('firebase_messaging')
library firebase_interop.messaging;

import 'dart:js_interop';

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

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

@JS()
external PromiseJsImpl<bool> deleteToken(MessagingJsImpl messaging);
@staticInterop
external JSPromise /* bool */ deleteToken(MessagingJsImpl messaging);

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

@JS('isSupported')
external PromiseJsImpl<bool> isSupported();
@staticInterop
external JSPromise /* bool */ isSupported();

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

@JS('Messaging')
@staticInterop
abstract class MessagingJsImpl {}

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

extension ObserverJsImplX on Observer {
external JSAny get next;
external JSAny get error;
}

@JS()
@staticInterop
@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,
JSString? vapidKey,
/*dynamic serviceWorkerRegistration */
});
}

extension GetTokenOptionsJsImplX on GetTokenOptions {
external JSString get vapidKey;
}

@JS()
@staticInterop
@anonymous
abstract class NotificationPayloadJsImpl {
external String? get title;
external String? get body;
external String? get image;
abstract class NotificationPayloadJsImpl {}

extension NotificationPayloadJsImplX on NotificationPayloadJsImpl {
external JSString? get title;
external JSString? get body;
external JSString? get image;
}

@JS()
@staticInterop
@anonymous
abstract class MessagePayloadJsImpl {
external String get messageId;
external String? get collapseKey;
abstract class MessagePayloadJsImpl {}

extension MessagePayloadJsImplX on MessagePayloadJsImpl {
external JSString get messageId;
external JSString? get collapseKey;
external FcmOptionsJsImpl? get fcmOptions;
external NotificationPayloadJsImpl? get notification;
external dynamic /*Map<String, String>*/ get data;
external String? get from;
external JSObject get data;
external JSString? get from;
}

@JS()
@staticInterop
@anonymous
abstract class FcmOptionsJsImpl {
external String? get analyticsLabel;
external String? get link;
abstract class FcmOptionsJsImpl {}

extension FcmOptionsJsImplX on FcmOptionsJsImpl {
external JSString? get analyticsLabel;
external JSString? get link;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ repository: https://github.com/firebase/flutterfire/tree/master/packages/firebas
version: 3.5.18

environment:
sdk: '>=2.18.0 <4.0.0'
sdk: '>=3.2.0 <4.0.0'
flutter: '>=3.3.0'

dependencies:
Expand All @@ -19,6 +19,7 @@ dependencies:
sdk: flutter
js: ^0.6.3
meta: ^1.8.0
web: '>=0.3.0 <0.5.0'

dev_dependencies:
firebase_core_platform_interface: ^5.0.0
Expand Down