Skip to content

Commit 799b12e

Browse files
authored
feat(remote_config, web): add web support for onConfigUpdated (#17750)
* feat(remote_config, web): add web support for `onConfigUpdated` * docs(remote_config): update web support documentation for `onConfigUpdated`
1 parent 51ed3fb commit 799b12e

File tree

5 files changed

+85
-4
lines changed

5 files changed

+85
-4
lines changed

packages/firebase_remote_config/firebase_remote_config/lib/src/firebase_remote_config.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,8 @@ class FirebaseRemoteConfig extends FirebasePluginPlatform {
160160
/// Starts listening for real-time config updates from the Remote Config backend and automatically
161161
/// fetches updates from the RC backend when they are available.
162162
///
163-
/// This feature is not supported on Web.
163+
/// On web, you must call [fetchAndActivate] before listening to this stream. Events will only be
164+
/// received after an initial call to [fetchAndActivate].
164165
///
165166
/// If a connection to the Remote Config backend is not already open, calling this method will
166167
/// open it. Multiple listeners can be added by calling this method again, but subsequent calls

packages/firebase_remote_config/firebase_remote_config_web/lib/firebase_remote_config_web.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,8 @@ class FirebaseRemoteConfigWeb extends FirebaseRemoteConfigPlatform {
184184

185185
@override
186186
Stream<RemoteConfigUpdate> get onConfigUpdated {
187-
throw UnsupportedError('onConfigUpdated is not supported for web');
187+
return _delegate.onConfigUpdated
188+
.map((event) => RemoteConfigUpdate(event.updatedKeys));
188189
}
189190

190191
@override

packages/firebase_remote_config/firebase_remote_config_web/lib/src/interop/firebase_remote_config.dart

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'dart:async';
56
import 'dart:convert' show utf8;
67
import 'dart:js_interop';
78

@@ -158,6 +159,32 @@ class RemoteConfig
158159
.setCustomSignals(jsObject, customSignals.jsify()! as JSObject)
159160
.toDart;
160161
}
162+
163+
StreamController<RemoteConfigUpdatePayload>? _onConfigUpdatedController;
164+
165+
Stream<RemoteConfigUpdatePayload> get onConfigUpdated {
166+
if (_onConfigUpdatedController == null) {
167+
_onConfigUpdatedController =
168+
StreamController<RemoteConfigUpdatePayload>.broadcast(sync: true);
169+
final errorWrapper = (JSObject error) {
170+
_onConfigUpdatedController?.addError(error);
171+
};
172+
final nextWrapper =
173+
(remote_config_interop.ConfigUpdateJsImpl configUpdate) {
174+
_onConfigUpdatedController
175+
?.add(RemoteConfigUpdatePayload._fromJsObject(configUpdate));
176+
};
177+
remote_config_interop.ConfigUpdateObserver observer =
178+
remote_config_interop.ConfigUpdateObserver(
179+
error: errorWrapper.toJS,
180+
next: nextWrapper.toJS,
181+
);
182+
183+
remote_config_interop.onConfigUpdate(jsObject, observer);
184+
}
185+
186+
return _onConfigUpdatedController!.stream;
187+
}
161188
}
162189

163190
ValueSource getSource(String source) {
@@ -220,3 +247,19 @@ enum RemoteConfigLogLevel {
220247
error,
221248
silent,
222249
}
250+
251+
class RemoteConfigUpdatePayload
252+
extends JsObjectWrapper<remote_config_interop.ConfigUpdateJsImpl> {
253+
RemoteConfigUpdatePayload._fromJsObject(
254+
remote_config_interop.ConfigUpdateJsImpl jsObject,
255+
) : super.fromJsObject(jsObject);
256+
257+
Set<String> get updatedKeys {
258+
final updatedKeysSet = <String>{};
259+
final callback = (JSAny key, JSString value, JSAny set) {
260+
updatedKeysSet.add(value.toDart);
261+
};
262+
jsObject.getUpdatedKeys().forEach(callback.toJS);
263+
return updatedKeysSet;
264+
}
265+
}

packages/firebase_remote_config/firebase_remote_config_web/lib/src/interop/firebase_remote_config_interop.dart

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,41 @@ extension SettingsJsImplExtension on SettingsJsImpl {
103103
external JSNumber get fetchTimeoutMillis;
104104
external set fetchTimeoutMillis(JSNumber value);
105105
}
106+
107+
@JS()
108+
@staticInterop
109+
@anonymous
110+
abstract class ConfigUpdateObserver {
111+
external factory ConfigUpdateObserver({
112+
JSAny complete,
113+
JSAny error,
114+
JSAny next,
115+
});
116+
}
117+
118+
extension ConfigUpdateObserverJsImpl on ConfigUpdateObserver {
119+
external JSAny get next;
120+
external JSAny get error;
121+
external JSAny get complete;
122+
}
123+
124+
@JS()
125+
@staticInterop
126+
@anonymous
127+
abstract class ConfigUpdateJsImpl {}
128+
129+
extension ConfigUpdateJsImplExtension on ConfigUpdateJsImpl {
130+
external JSSet getUpdatedKeys();
131+
}
132+
133+
@JS()
134+
@staticInterop
135+
external JSFunction onConfigUpdate(
136+
RemoteConfigJsImpl remoteConfig,
137+
ConfigUpdateObserver observer,
138+
);
139+
140+
@JS('Set')
141+
extension type JSSet._(JSObject _) implements JSObject {
142+
external void forEach(JSAny callback);
143+
}

tests/integration_test/firebase_remote_config/firebase_remote_config_e2e_test.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,6 @@ void main() {
138138

139139
await configSubscription.cancel();
140140
},
141-
// This feature is not supported on Web
142-
skip: kIsWeb,
143141
);
144142

145143
test('default values', () async {

0 commit comments

Comments
 (0)