diff --git a/packages/firebase_remote_config/firebase_remote_config/lib/src/firebase_remote_config.dart b/packages/firebase_remote_config/firebase_remote_config/lib/src/firebase_remote_config.dart index 785d84a3e8d9..f7c504730f04 100644 --- a/packages/firebase_remote_config/firebase_remote_config/lib/src/firebase_remote_config.dart +++ b/packages/firebase_remote_config/firebase_remote_config/lib/src/firebase_remote_config.dart @@ -160,7 +160,8 @@ class FirebaseRemoteConfig extends FirebasePluginPlatform { /// Starts listening for real-time config updates from the Remote Config backend and automatically /// fetches updates from the RC backend when they are available. /// - /// This feature is not supported on Web. + /// On web, you must call [fetchAndActivate] before listening to this stream. Events will only be + /// received after an initial call to [fetchAndActivate]. /// /// If a connection to the Remote Config backend is not already open, calling this method will /// open it. Multiple listeners can be added by calling this method again, but subsequent calls diff --git a/packages/firebase_remote_config/firebase_remote_config_web/lib/firebase_remote_config_web.dart b/packages/firebase_remote_config/firebase_remote_config_web/lib/firebase_remote_config_web.dart index 702000ce92ea..0bca9f38c1bd 100644 --- a/packages/firebase_remote_config/firebase_remote_config_web/lib/firebase_remote_config_web.dart +++ b/packages/firebase_remote_config/firebase_remote_config_web/lib/firebase_remote_config_web.dart @@ -184,7 +184,8 @@ class FirebaseRemoteConfigWeb extends FirebaseRemoteConfigPlatform { @override Stream get onConfigUpdated { - throw UnsupportedError('onConfigUpdated is not supported for web'); + return _delegate.onConfigUpdated + .map((event) => RemoteConfigUpdate(event.updatedKeys)); } @override diff --git a/packages/firebase_remote_config/firebase_remote_config_web/lib/src/interop/firebase_remote_config.dart b/packages/firebase_remote_config/firebase_remote_config_web/lib/src/interop/firebase_remote_config.dart index 355bab7bd436..045409fd398a 100644 --- a/packages/firebase_remote_config/firebase_remote_config_web/lib/src/interop/firebase_remote_config.dart +++ b/packages/firebase_remote_config/firebase_remote_config_web/lib/src/interop/firebase_remote_config.dart @@ -2,6 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'dart:async'; import 'dart:convert' show utf8; import 'dart:js_interop'; @@ -158,6 +159,32 @@ class RemoteConfig .setCustomSignals(jsObject, customSignals.jsify()! as JSObject) .toDart; } + + StreamController? _onConfigUpdatedController; + + Stream get onConfigUpdated { + if (_onConfigUpdatedController == null) { + _onConfigUpdatedController = + StreamController.broadcast(sync: true); + final errorWrapper = (JSObject error) { + _onConfigUpdatedController?.addError(error); + }; + final nextWrapper = + (remote_config_interop.ConfigUpdateJsImpl configUpdate) { + _onConfigUpdatedController + ?.add(RemoteConfigUpdatePayload._fromJsObject(configUpdate)); + }; + remote_config_interop.ConfigUpdateObserver observer = + remote_config_interop.ConfigUpdateObserver( + error: errorWrapper.toJS, + next: nextWrapper.toJS, + ); + + remote_config_interop.onConfigUpdate(jsObject, observer); + } + + return _onConfigUpdatedController!.stream; + } } ValueSource getSource(String source) { @@ -220,3 +247,19 @@ enum RemoteConfigLogLevel { error, silent, } + +class RemoteConfigUpdatePayload + extends JsObjectWrapper { + RemoteConfigUpdatePayload._fromJsObject( + remote_config_interop.ConfigUpdateJsImpl jsObject, + ) : super.fromJsObject(jsObject); + + Set get updatedKeys { + final updatedKeysSet = {}; + final callback = (JSAny key, JSString value, JSAny set) { + updatedKeysSet.add(value.toDart); + }; + jsObject.getUpdatedKeys().forEach(callback.toJS); + return updatedKeysSet; + } +} diff --git a/packages/firebase_remote_config/firebase_remote_config_web/lib/src/interop/firebase_remote_config_interop.dart b/packages/firebase_remote_config/firebase_remote_config_web/lib/src/interop/firebase_remote_config_interop.dart index 71348f47b2b3..445913d55442 100644 --- a/packages/firebase_remote_config/firebase_remote_config_web/lib/src/interop/firebase_remote_config_interop.dart +++ b/packages/firebase_remote_config/firebase_remote_config_web/lib/src/interop/firebase_remote_config_interop.dart @@ -103,3 +103,41 @@ extension SettingsJsImplExtension on SettingsJsImpl { external JSNumber get fetchTimeoutMillis; external set fetchTimeoutMillis(JSNumber value); } + +@JS() +@staticInterop +@anonymous +abstract class ConfigUpdateObserver { + external factory ConfigUpdateObserver({ + JSAny complete, + JSAny error, + JSAny next, + }); +} + +extension ConfigUpdateObserverJsImpl on ConfigUpdateObserver { + external JSAny get next; + external JSAny get error; + external JSAny get complete; +} + +@JS() +@staticInterop +@anonymous +abstract class ConfigUpdateJsImpl {} + +extension ConfigUpdateJsImplExtension on ConfigUpdateJsImpl { + external JSSet getUpdatedKeys(); +} + +@JS() +@staticInterop +external JSFunction onConfigUpdate( + RemoteConfigJsImpl remoteConfig, + ConfigUpdateObserver observer, +); + +@JS('Set') +extension type JSSet._(JSObject _) implements JSObject { + external void forEach(JSAny callback); +} diff --git a/tests/integration_test/firebase_remote_config/firebase_remote_config_e2e_test.dart b/tests/integration_test/firebase_remote_config/firebase_remote_config_e2e_test.dart index 14a16840e89b..3ffc3f0490c9 100644 --- a/tests/integration_test/firebase_remote_config/firebase_remote_config_e2e_test.dart +++ b/tests/integration_test/firebase_remote_config/firebase_remote_config_e2e_test.dart @@ -138,8 +138,6 @@ void main() { await configSubscription.cancel(); }, - // This feature is not supported on Web - skip: kIsWeb, ); test('default values', () async {