Skip to content

Commit f122380

Browse files
fix(database, web): ensure exact same streams are not unsubscribed in debug mode (#13029)
1 parent 015dbff commit f122380

File tree

2 files changed

+45
-23
lines changed

2 files changed

+45
-23
lines changed

packages/firebase_database/firebase_database_web/lib/src/interop/database.dart

+12-12
Original file line numberDiff line numberDiff line change
@@ -250,18 +250,18 @@ class Query<T extends database_interop.QueryJsImpl> extends JsObjectWrapper<T> {
250250
/// DatabaseReference to the Query's location.
251251
DatabaseReference get ref => DatabaseReference.getInstance(jsObject.ref);
252252

253-
Stream<QueryEvent> _onValue(String appName, int hashCode) => _createStream(
253+
Stream<QueryEvent> _onValue(String appName, String hashCode) => _createStream(
254254
'value',
255255
appName,
256256
hashCode,
257257
);
258258

259259
/// Stream for a value event. Event is triggered once with the initial
260260
/// data stored at location, and then again each time the data changes.
261-
Stream<QueryEvent> onValue(String appName, int hashCode) =>
261+
Stream<QueryEvent> onValue(String appName, String hashCode) =>
262262
_onValue(appName, hashCode);
263263

264-
Stream<QueryEvent> _onChildAdded(String appName, int hashCode) =>
264+
Stream<QueryEvent> _onChildAdded(String appName, String hashCode) =>
265265
_createStream(
266266
'child_added',
267267
appName,
@@ -270,10 +270,10 @@ class Query<T extends database_interop.QueryJsImpl> extends JsObjectWrapper<T> {
270270

271271
/// Stream for a child_added event. Event is triggered once for each
272272
/// initial child at location, and then again every time a new child is added.
273-
Stream<QueryEvent> onChildAdded(String appName, int hashCode) =>
273+
Stream<QueryEvent> onChildAdded(String appName, String hashCode) =>
274274
_onChildAdded(appName, hashCode);
275275

276-
Stream<QueryEvent> _onChildRemoved(String appName, int hashCode) =>
276+
Stream<QueryEvent> _onChildRemoved(String appName, String hashCode) =>
277277
_createStream(
278278
'child_removed',
279279
appName,
@@ -282,10 +282,10 @@ class Query<T extends database_interop.QueryJsImpl> extends JsObjectWrapper<T> {
282282

283283
/// Stream for a child_removed event. Event is triggered once every time
284284
/// a child is removed.
285-
Stream<QueryEvent> onChildRemoved(String appName, int hashCode) =>
285+
Stream<QueryEvent> onChildRemoved(String appName, String hashCode) =>
286286
_onChildRemoved(appName, hashCode);
287287

288-
Stream<QueryEvent> _onChildChanged(String appName, int hashCode) =>
288+
Stream<QueryEvent> _onChildChanged(String appName, String hashCode) =>
289289
_createStream(
290290
'child_changed',
291291
appName,
@@ -295,9 +295,9 @@ class Query<T extends database_interop.QueryJsImpl> extends JsObjectWrapper<T> {
295295
/// Stream for a child_changed event. Event is triggered when the data
296296
/// stored in a child (or any of its descendants) changes.
297297
/// Single child_changed event may represent multiple changes to the child.
298-
Stream<QueryEvent> onChildChanged(String appName, int hashCode) =>
298+
Stream<QueryEvent> onChildChanged(String appName, String hashCode) =>
299299
_onChildChanged(appName, hashCode);
300-
Stream<QueryEvent> _onChildMoved(String appName, int hashCode) =>
300+
Stream<QueryEvent> _onChildMoved(String appName, String hashCode) =>
301301
_createStream(
302302
'child_moved',
303303
appName,
@@ -306,7 +306,7 @@ class Query<T extends database_interop.QueryJsImpl> extends JsObjectWrapper<T> {
306306

307307
/// Stream for a child_moved event. Event is triggered when a child's priority
308308
/// changes such that its position relative to its siblings changes.
309-
Stream<QueryEvent> onChildMoved(String appName, int hashCode) =>
309+
Stream<QueryEvent> onChildMoved(String appName, String hashCode) =>
310310
_onChildMoved(appName, hashCode);
311311

312312
/// Creates a new Query from a [jsObject].
@@ -403,13 +403,13 @@ class Query<T extends database_interop.QueryJsImpl> extends JsObjectWrapper<T> {
403403
);
404404
}
405405

406-
String _streamWindowsKey(String appName, String eventType, int hashCode) =>
406+
String _streamWindowsKey(String appName, String eventType, String hashCode) =>
407407
'flutterfire-${appName}_${eventType}_${hashCode}_snapshot';
408408

409409
Stream<QueryEvent> _createStream(
410410
String eventType,
411411
String appName,
412-
int hashCode,
412+
String hashCode,
413413
) {
414414
late StreamController<QueryEvent> streamController;
415415
unsubscribeWindowsListener(_streamWindowsKey(appName, eventType, hashCode));

packages/firebase_database/firebase_database_web/lib/src/query_web.dart

+33-11
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ class QueryWeb extends QueryPlatform {
6464
return instance;
6565
}
6666

67+
final Map<String, int> _streamHashCodeMap = {};
68+
6769
@override
6870
String get path {
6971
final refPath = Uri.parse(_queryDelegate.ref.toString()).path;
@@ -89,26 +91,46 @@ class QueryWeb extends QueryPlatform {
8991
throw UnsupportedError('keepSynced() is not supported on web');
9092
}
9193

92-
@override
93-
Stream<DatabaseEventPlatform> observe(
94-
QueryModifiers modifiers, DatabaseEventType eventType) {
95-
database_interop.Query instance = _getQueryDelegateInstance(modifiers);
96-
97-
int hashCode = 0;
98-
final appName =
99-
_database.app != null ? _database.app!.name : Firebase.app().name;
94+
String _createHashCode(
95+
QueryModifiers modifiers,
96+
DatabaseEventType eventType,
97+
String appName,
98+
) {
99+
String hashCode = '0';
100100
if (kDebugMode) {
101-
// Purely for unsubscribing purposes in debug mode on "hot restart"
102-
// if not running in debug mode, hashCode won't be used
103101
hashCode = Object.hashAll([
104102
appName,
105103
path,
106104
...modifiers
107105
.toList()
108106
.map((e) => const DeepCollectionEquality().hash(e)),
109107
eventType.index,
110-
]);
108+
]).toString();
109+
// Need to track as the same properties to create hash could be used multiple times
110+
if (_streamHashCodeMap.containsKey(hashCode)) {
111+
int count = _streamHashCodeMap[hashCode] ?? 0;
112+
final updatedCount = count + 1;
113+
_streamHashCodeMap[hashCode] = updatedCount;
114+
hashCode = '$hashCode-$updatedCount';
115+
} else {
116+
// initial stream
117+
_streamHashCodeMap[hashCode] = 0;
118+
hashCode = '$hashCode-0';
119+
}
111120
}
121+
return hashCode;
122+
}
123+
124+
@override
125+
Stream<DatabaseEventPlatform> observe(
126+
QueryModifiers modifiers, DatabaseEventType eventType) {
127+
database_interop.Query instance = _getQueryDelegateInstance(modifiers);
128+
final appName =
129+
_database.app != null ? _database.app!.name : Firebase.app().name;
130+
131+
// Purely for unsubscribing purposes in debug mode on "hot restart"
132+
// if not running in debug mode, hashCode won't be used
133+
String hashCode = _createHashCode(modifiers, eventType, appName);
112134

113135
switch (eventType) {
114136
case DatabaseEventType.childAdded:

0 commit comments

Comments
 (0)