@react-native-firebase/database 26.3.3 → 26.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/RNFBDatabase.podspec +3 -1
- package/android/build.gradle +2 -0
- package/android/src/main/java/io/invertase/firebase/database/DatabaseAbortable.java +22 -0
- package/android/src/main/java/io/invertase/firebase/database/DatabaseQueryHandle.java +24 -0
- package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseListenerRegistry.java +117 -0
- package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseQueryRegistry.java +78 -0
- package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseTransactionRegistry.java +71 -0
- package/android/src/reactnative/java/io/invertase/firebase/database/NativeRNFBTurboDatabaseQuery.java +18 -22
- package/android/src/reactnative/java/io/invertase/firebase/database/NativeRNFBTurboDatabaseTransaction.java +12 -25
- package/android/src/reactnative/java/io/invertase/firebase/database/ReactNativeFirebaseDatabaseQuery.java +30 -33
- package/android/src/reactnative/java/io/invertase/firebase/database/ReactNativeFirebaseDatabaseTransactionHandler.java +3 -3
- package/android/src/test/java/io/invertase/firebase/database/RNFBDatabaseListenerRegistryTest.java +160 -0
- package/android/src/test/java/io/invertase/firebase/database/RNFBDatabaseQueryRegistryTest.java +434 -0
- package/android/src/test/java/io/invertase/firebase/database/RNFBDatabaseTransactionRegistryTest.java +199 -0
- package/dist/module/version.js +1 -1
- package/dist/typescript/lib/index.d.ts +1 -1
- package/dist/typescript/lib/version.d.ts +1 -1
- package/ios/RNFBDatabase/RNFBDatabaseListenerRegistry.h +37 -0
- package/ios/RNFBDatabase/RNFBDatabaseListenerRegistry.m +84 -0
- package/ios/RNFBDatabase/RNFBDatabaseQuery.h +3 -2
- package/ios/RNFBDatabase/RNFBDatabaseQuery.m +14 -13
- package/ios/RNFBDatabase/RNFBDatabaseQueryHelper.m +24 -21
- package/ios/RNFBDatabase/RNFBDatabaseQueryRegistry.h +37 -0
- package/ios/RNFBDatabase/RNFBDatabaseQueryRegistry.m +105 -0
- package/ios/RNFBDatabaseUnitTests/RNFBDatabaseListenerRegistryTests.m +90 -0
- package/ios/RNFBDatabaseUnitTests/RNFBDatabaseQueryRegistryTests.m +390 -0
- package/ios/RNFBDatabaseUnitTests/RNFBDatabaseUnitTests.xcodeproj/project.pbxproj +261 -0
- package/ios/RNFBDatabaseUnitTests/RNFBDatabaseUnitTests.xcodeproj/xcshareddata/xcschemes/RNFBDatabaseUnitTests.xcscheme +69 -0
- package/lib/version.ts +1 -1
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [26.4.0](https://github.com/invertase/react-native-firebase/compare/v26.3.3...v26.4.0) (2026-09-05)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- **database:** use RNFBHandleMap for transaction and query maps ([8adba4c](https://github.com/invertase/react-native-firebase/commit/8adba4cb88dffec2442ea0fe37738d1080d0a185))
|
|
11
|
+
|
|
6
12
|
## [26.3.3](https://github.com/invertase/react-native-firebase/compare/v26.3.2...v26.3.3) (2026-09-01)
|
|
7
13
|
|
|
8
14
|
### Reverts
|
package/RNFBDatabase.podspec
CHANGED
|
@@ -36,9 +36,11 @@ Pod::Spec.new do |s|
|
|
|
36
36
|
'ios/RNFBDatabase/RNFBDatabaseQueryModule.h',
|
|
37
37
|
'ios/RNFBDatabase/RNFBDatabaseOnDisconnectModule.h',
|
|
38
38
|
'ios/RNFBDatabase/RNFBDatabaseTransactionModule.h',
|
|
39
|
+
'ios/RNFBDatabase/RNFBDatabaseQueryRegistry.h',
|
|
40
|
+
'ios/RNFBDatabase/RNFBDatabaseListenerRegistry.h',
|
|
39
41
|
'ios/generated/**/*.h',
|
|
40
42
|
]
|
|
41
|
-
s.exclude_files = 'ios/generated/RCTThirdPartyComponentsProvider.*', 'ios/generated/RCTAppDependencyProvider.*', 'ios/generated/RCTModuleProviders.*', 'ios/generated/RCTModulesConformingToProtocolsProvider.*', 'ios/generated/RCTUnstableModulesRequiringMainQueueSetupProvider.*'
|
|
43
|
+
s.exclude_files = 'ios/generated/RCTThirdPartyComponentsProvider.*', 'ios/generated/RCTAppDependencyProvider.*', 'ios/generated/RCTModuleProviders.*', 'ios/generated/RCTModulesConformingToProtocolsProvider.*', 'ios/generated/RCTUnstableModulesRequiringMainQueueSetupProvider.*', 'ios/*UnitTests/**'
|
|
42
44
|
|
|
43
45
|
s.pod_target_xcconfig = {
|
|
44
46
|
"HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ios/generated/RNFBDatabaseTurboModules\" \"$(PODS_TARGET_SRCROOT)/ios/generated\"",
|
package/android/build.gradle
CHANGED
|
@@ -93,6 +93,8 @@ dependencies {
|
|
|
93
93
|
api appProject
|
|
94
94
|
implementation platform("com.google.firebase:firebase-bom:${ReactNative.ext.getVersion("firebase", "bom")}")
|
|
95
95
|
implementation "com.google.firebase:firebase-database"
|
|
96
|
+
|
|
97
|
+
testImplementation "junit:junit:4.13.2"
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
ReactNative.shared.applyPackageVersion()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
package io.invertase.firebase.database;
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this library except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
interface DatabaseAbortable {
|
|
21
|
+
void abort();
|
|
22
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
package io.invertase.firebase.database;
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this library except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
interface DatabaseQueryHandle {
|
|
21
|
+
void removeAllEventListeners();
|
|
22
|
+
|
|
23
|
+
Boolean hasListeners();
|
|
24
|
+
}
|
package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseListenerRegistry.java
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
package io.invertase.firebase.database;
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this library except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import io.invertase.firebase.common.RNFBHandleCollisionException;
|
|
21
|
+
import io.invertase.firebase.common.RNFBHandleMap;
|
|
22
|
+
import java.util.ArrayList;
|
|
23
|
+
import java.util.List;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Per-query value/child listener maps. Unique {@link #putValue}/{@link #putChild}; callers {@link
|
|
27
|
+
* #takeValue}/{@link #takeChild} or {@link #takeAll} then remove the SDK listener outside the
|
|
28
|
+
* HandleMap lock. Null values are not stored so {@link #hasListeners} can use occupancy.
|
|
29
|
+
*/
|
|
30
|
+
final class RNFBDatabaseListenerRegistry {
|
|
31
|
+
private final Object occupancyLock = new Object();
|
|
32
|
+
private final RNFBHandleMap<String, Object> valueListeners = new RNFBHandleMap<>();
|
|
33
|
+
private final RNFBHandleMap<String, Object> childListeners = new RNFBHandleMap<>();
|
|
34
|
+
private int occupancy;
|
|
35
|
+
|
|
36
|
+
void putValue(String eventRegistrationKey, Object listener) throws RNFBHandleCollisionException {
|
|
37
|
+
if (listener == null) {
|
|
38
|
+
throw new NullPointerException("listener");
|
|
39
|
+
}
|
|
40
|
+
synchronized (occupancyLock) {
|
|
41
|
+
valueListeners.put(eventRegistrationKey, listener);
|
|
42
|
+
occupancy++;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
void putChild(String eventRegistrationKey, Object listener) throws RNFBHandleCollisionException {
|
|
47
|
+
if (listener == null) {
|
|
48
|
+
throw new NullPointerException("listener");
|
|
49
|
+
}
|
|
50
|
+
synchronized (occupancyLock) {
|
|
51
|
+
childListeners.put(eventRegistrationKey, listener);
|
|
52
|
+
occupancy++;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
Object getValue(String eventRegistrationKey) {
|
|
57
|
+
return valueListeners.get(eventRegistrationKey);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
Object getChild(String eventRegistrationKey) {
|
|
61
|
+
return childListeners.get(eventRegistrationKey);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
Object takeValue(String eventRegistrationKey) {
|
|
65
|
+
synchronized (occupancyLock) {
|
|
66
|
+
Object listener = valueListeners.take(eventRegistrationKey);
|
|
67
|
+
if (listener != null) {
|
|
68
|
+
occupancy--;
|
|
69
|
+
}
|
|
70
|
+
return listener;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
Object takeChild(String eventRegistrationKey) {
|
|
75
|
+
synchronized (occupancyLock) {
|
|
76
|
+
Object listener = childListeners.take(eventRegistrationKey);
|
|
77
|
+
if (listener != null) {
|
|
78
|
+
occupancy--;
|
|
79
|
+
}
|
|
80
|
+
return listener;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
List<Object> takeAllValues() {
|
|
85
|
+
synchronized (occupancyLock) {
|
|
86
|
+
List<Object> remaining = valueListeners.takeAll();
|
|
87
|
+
occupancy -= remaining.size();
|
|
88
|
+
return remaining;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
List<Object> takeAllChildren() {
|
|
93
|
+
synchronized (occupancyLock) {
|
|
94
|
+
List<Object> remaining = childListeners.takeAll();
|
|
95
|
+
occupancy -= remaining.size();
|
|
96
|
+
return remaining;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
List<Object> takeAll() {
|
|
101
|
+
List<Object> remaining = new ArrayList<>();
|
|
102
|
+
remaining.addAll(takeAllValues());
|
|
103
|
+
remaining.addAll(takeAllChildren());
|
|
104
|
+
return remaining;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
boolean hasEventListener(String eventRegistrationKey) {
|
|
108
|
+
return valueListeners.get(eventRegistrationKey) != null
|
|
109
|
+
|| childListeners.get(eventRegistrationKey) != null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
boolean hasListeners() {
|
|
113
|
+
synchronized (occupancyLock) {
|
|
114
|
+
return occupancy > 0;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
package io.invertase.firebase.database;
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this library except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import io.invertase.firebase.common.RNFBHandleCollisionException;
|
|
21
|
+
import io.invertase.firebase.common.RNFBHandleMap;
|
|
22
|
+
import java.util.List;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Cached query map. Unique {@link #put}; callers {@link #take}, {@link #takeIfIdle}, or {@link
|
|
26
|
+
* #takeAllAndRemove} then {@code removeAllEventListeners()} outside the HandleMap lock.
|
|
27
|
+
*/
|
|
28
|
+
final class RNFBDatabaseQueryRegistry {
|
|
29
|
+
private final RNFBHandleMap<String, DatabaseQueryHandle> map = new RNFBHandleMap<>();
|
|
30
|
+
|
|
31
|
+
void put(String queryKey, DatabaseQueryHandle query) throws RNFBHandleCollisionException {
|
|
32
|
+
map.put(queryKey, query);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
DatabaseQueryHandle get(String queryKey) {
|
|
36
|
+
return map.get(queryKey);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
DatabaseQueryHandle take(String queryKey) {
|
|
40
|
+
return map.take(queryKey);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Removes the mapping when the query has no listeners. Reads {@link
|
|
45
|
+
* DatabaseQueryHandle#hasListeners()} outside the HandleMap lock (avoids nesting {@code
|
|
46
|
+
* occupancyLock}), then identity-takes and put-backs if listeners appeared.
|
|
47
|
+
*/
|
|
48
|
+
void takeIfIdle(String queryKey) {
|
|
49
|
+
DatabaseQueryHandle query = map.get(queryKey);
|
|
50
|
+
if (query == null) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (Boolean.TRUE.equals(query.hasListeners())) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
DatabaseQueryHandle taken = map.takeIf(queryKey, q -> q == query);
|
|
57
|
+
if (taken == null) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (Boolean.TRUE.equals(taken.hasListeners())) {
|
|
61
|
+
// Prefer put-back so an active query stays registered. If a concurrent put claimed the
|
|
62
|
+
// slot, putIfAbsent fails and this taken query would be an orphan with listeners — clear
|
|
63
|
+
// them so SDK callbacks are not left attached outside the registry.
|
|
64
|
+
if (!map.putIfAbsent(queryKey, taken)) {
|
|
65
|
+
taken.removeAllEventListeners();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
void takeAllAndRemove() {
|
|
71
|
+
List<DatabaseQueryHandle> remaining = map.takeAll();
|
|
72
|
+
for (DatabaseQueryHandle query : remaining) {
|
|
73
|
+
if (query != null) {
|
|
74
|
+
query.removeAllEventListeners();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseTransactionRegistry.java
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
package io.invertase.firebase.database;
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this library except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import io.invertase.firebase.common.RNFBHandleCollisionException;
|
|
21
|
+
import io.invertase.firebase.common.RNFBHandleMap;
|
|
22
|
+
import java.util.List;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Transaction handler map. Unique {@link #put} (occupied id aborts the incoming handler, then
|
|
26
|
+
* throws); callers {@link #take} or {@link #takeAllAndAbort} then {@code abort()} outside the
|
|
27
|
+
* HandleMap lock. Replace is {@link #registerReplacing} (atomic last wins).
|
|
28
|
+
*/
|
|
29
|
+
final class RNFBDatabaseTransactionRegistry {
|
|
30
|
+
private final RNFBHandleMap<Integer, DatabaseAbortable> map = new RNFBHandleMap<>();
|
|
31
|
+
|
|
32
|
+
void put(int transactionId, DatabaseAbortable handler) throws RNFBHandleCollisionException {
|
|
33
|
+
try {
|
|
34
|
+
map.put(transactionId, handler);
|
|
35
|
+
} catch (RNFBHandleCollisionException collision) {
|
|
36
|
+
if (handler != null) {
|
|
37
|
+
handler.abort();
|
|
38
|
+
}
|
|
39
|
+
throw collision;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Atomic replace (last wins). The replaced handler is not aborted (Firebase retry). */
|
|
44
|
+
void registerReplacing(int transactionId, DatabaseAbortable handler) {
|
|
45
|
+
map.putReplacing(transactionId, handler);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
DatabaseAbortable get(int transactionId) {
|
|
49
|
+
return map.get(transactionId);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
DatabaseAbortable take(int transactionId) {
|
|
53
|
+
return map.take(transactionId);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
void takeAndAbort(int transactionId) {
|
|
57
|
+
DatabaseAbortable handler = map.take(transactionId);
|
|
58
|
+
if (handler != null) {
|
|
59
|
+
handler.abort();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
void takeAllAndAbort() {
|
|
64
|
+
List<DatabaseAbortable> remaining = map.takeAll();
|
|
65
|
+
for (DatabaseAbortable handler : remaining) {
|
|
66
|
+
if (handler != null) {
|
|
67
|
+
handler.abort();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -26,11 +26,9 @@ import com.facebook.fbreact.specs.NativeRNFBTurboDatabaseQuerySpec;
|
|
|
26
26
|
import com.facebook.react.bridge.*;
|
|
27
27
|
import com.google.android.gms.tasks.Tasks;
|
|
28
28
|
import com.google.firebase.database.*;
|
|
29
|
+
import io.invertase.firebase.common.RNFBHandleCollisionException;
|
|
29
30
|
import io.invertase.firebase.common.ReactNativeFirebaseEventEmitter;
|
|
30
31
|
import io.invertase.firebase.common.ReactNativeFirebaseModule;
|
|
31
|
-
import java.util.HashMap;
|
|
32
|
-
import java.util.Iterator;
|
|
33
|
-
import java.util.Map;
|
|
34
32
|
import java.util.Objects;
|
|
35
33
|
import javax.annotation.Nonnull;
|
|
36
34
|
import javax.annotation.Nullable;
|
|
@@ -38,7 +36,7 @@ import javax.annotation.Nullable;
|
|
|
38
36
|
public class NativeRNFBTurboDatabaseQuery extends NativeRNFBTurboDatabaseQuerySpec {
|
|
39
37
|
private final DatabaseTurboModuleSupport turboSupport =
|
|
40
38
|
new DatabaseTurboModuleSupport("RNFBDatabaseQuery");
|
|
41
|
-
private
|
|
39
|
+
private final RNFBDatabaseQueryRegistry queryMap = new RNFBDatabaseQueryRegistry();
|
|
42
40
|
|
|
43
41
|
public NativeRNFBTurboDatabaseQuery(ReactApplicationContext reactContext) {
|
|
44
42
|
super(reactContext);
|
|
@@ -47,14 +45,7 @@ public class NativeRNFBTurboDatabaseQuery extends NativeRNFBTurboDatabaseQuerySp
|
|
|
47
45
|
@Override
|
|
48
46
|
@CallSuper
|
|
49
47
|
public void invalidate() {
|
|
50
|
-
|
|
51
|
-
while (refIterator.hasNext()) {
|
|
52
|
-
Map.Entry pair = (Map.Entry) refIterator.next();
|
|
53
|
-
ReactNativeFirebaseDatabaseQuery databaseQuery =
|
|
54
|
-
(ReactNativeFirebaseDatabaseQuery) pair.getValue();
|
|
55
|
-
databaseQuery.removeAllEventListeners();
|
|
56
|
-
refIterator.remove();
|
|
57
|
-
}
|
|
48
|
+
queryMap.takeAllAndRemove();
|
|
58
49
|
|
|
59
50
|
turboSupport.invalidate();
|
|
60
51
|
super.invalidate();
|
|
@@ -67,16 +58,23 @@ public class NativeRNFBTurboDatabaseQuery extends NativeRNFBTurboDatabaseQuerySp
|
|
|
67
58
|
|
|
68
59
|
private ReactNativeFirebaseDatabaseQuery getDatabaseQueryInstance(
|
|
69
60
|
String key, DatabaseReference reference, ReadableArray modifiers) {
|
|
70
|
-
|
|
61
|
+
DatabaseQueryHandle cached = queryMap.get(key);
|
|
71
62
|
|
|
72
|
-
if (
|
|
73
|
-
return
|
|
63
|
+
if (cached instanceof ReactNativeFirebaseDatabaseQuery) {
|
|
64
|
+
return (ReactNativeFirebaseDatabaseQuery) cached;
|
|
74
65
|
}
|
|
75
66
|
|
|
76
67
|
ReactNativeFirebaseDatabaseQuery databaseQuery =
|
|
77
68
|
new ReactNativeFirebaseDatabaseQuery(reference, modifiers);
|
|
78
69
|
|
|
79
|
-
|
|
70
|
+
try {
|
|
71
|
+
queryMap.put(key, databaseQuery);
|
|
72
|
+
} catch (RNFBHandleCollisionException collision) {
|
|
73
|
+
DatabaseQueryHandle winner = queryMap.get(key);
|
|
74
|
+
if (winner instanceof ReactNativeFirebaseDatabaseQuery) {
|
|
75
|
+
return (ReactNativeFirebaseDatabaseQuery) winner;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
80
78
|
return databaseQuery;
|
|
81
79
|
}
|
|
82
80
|
|
|
@@ -396,15 +394,13 @@ public class NativeRNFBTurboDatabaseQuery extends NativeRNFBTurboDatabaseQuerySp
|
|
|
396
394
|
|
|
397
395
|
@Override
|
|
398
396
|
public void off(String queryKey, String eventRegistrationKey) {
|
|
399
|
-
|
|
397
|
+
DatabaseQueryHandle handle = queryMap.get(queryKey);
|
|
400
398
|
|
|
401
|
-
if (
|
|
399
|
+
if (handle instanceof ReactNativeFirebaseDatabaseQuery) {
|
|
400
|
+
ReactNativeFirebaseDatabaseQuery databaseQuery = (ReactNativeFirebaseDatabaseQuery) handle;
|
|
402
401
|
databaseQuery.removeEventListener(eventRegistrationKey);
|
|
403
402
|
turboSupport.removeEventListeningExecutor(eventRegistrationKey);
|
|
404
|
-
|
|
405
|
-
if (!databaseQuery.hasListeners()) {
|
|
406
|
-
queryMap.remove(queryKey);
|
|
407
|
-
}
|
|
403
|
+
queryMap.takeIfIdle(queryKey);
|
|
408
404
|
}
|
|
409
405
|
}
|
|
410
406
|
|
|
@@ -20,7 +20,6 @@ package io.invertase.firebase.database;
|
|
|
20
20
|
import static io.invertase.firebase.database.UniversalFirebaseDatabaseCommon.getDatabaseForApp;
|
|
21
21
|
|
|
22
22
|
import android.os.AsyncTask;
|
|
23
|
-
import android.util.SparseArray;
|
|
24
23
|
import com.facebook.fbreact.specs.NativeRNFBTurboDatabaseTransactionSpec;
|
|
25
24
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
26
25
|
import com.facebook.react.bridge.ReadableMap;
|
|
@@ -31,8 +30,8 @@ import javax.annotation.Nonnull;
|
|
|
31
30
|
|
|
32
31
|
public class NativeRNFBTurboDatabaseTransaction extends NativeRNFBTurboDatabaseTransactionSpec {
|
|
33
32
|
private static final String SERVICE_NAME = "DatabaseTransaction";
|
|
34
|
-
private static
|
|
35
|
-
new
|
|
33
|
+
private static final RNFBDatabaseTransactionRegistry transactionHandlers =
|
|
34
|
+
new RNFBDatabaseTransactionRegistry();
|
|
36
35
|
private final DatabaseTurboModuleSupport turboSupport =
|
|
37
36
|
new DatabaseTurboModuleSupport("RNFBDatabaseTransaction");
|
|
38
37
|
|
|
@@ -42,17 +41,7 @@ public class NativeRNFBTurboDatabaseTransaction extends NativeRNFBTurboDatabaseT
|
|
|
42
41
|
|
|
43
42
|
@Override
|
|
44
43
|
public void invalidate() {
|
|
45
|
-
|
|
46
|
-
int key = transactionHandlers.keyAt(i);
|
|
47
|
-
ReactNativeFirebaseDatabaseTransactionHandler transactionHandler =
|
|
48
|
-
transactionHandlers.get(key);
|
|
49
|
-
|
|
50
|
-
if (transactionHandler != null) {
|
|
51
|
-
transactionHandler.abort();
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
transactionHandlers.clear();
|
|
44
|
+
transactionHandlers.takeAllAndAbort();
|
|
56
45
|
turboSupport.invalidate();
|
|
57
46
|
}
|
|
58
47
|
|
|
@@ -71,7 +60,7 @@ public class NativeRNFBTurboDatabaseTransaction extends NativeRNFBTurboDatabaseT
|
|
|
71
60
|
final ReactNativeFirebaseDatabaseTransactionHandler transactionHandler =
|
|
72
61
|
new ReactNativeFirebaseDatabaseTransactionHandler(
|
|
73
62
|
(int) transactionId, app, dbURL);
|
|
74
|
-
transactionHandlers.
|
|
63
|
+
transactionHandlers.registerReplacing((int) transactionId, transactionHandler);
|
|
75
64
|
final WritableMap updatesMap = transactionHandler.createUpdateMap(mutableData);
|
|
76
65
|
|
|
77
66
|
AsyncTask.execute(
|
|
@@ -109,14 +98,15 @@ public class NativeRNFBTurboDatabaseTransaction extends NativeRNFBTurboDatabaseT
|
|
|
109
98
|
@Override
|
|
110
99
|
public void onComplete(
|
|
111
100
|
DatabaseError error, boolean committed, DataSnapshot snapshot) {
|
|
112
|
-
|
|
113
|
-
transactionHandlers.get((int) transactionId);
|
|
101
|
+
DatabaseAbortable abortable = transactionHandlers.take((int) transactionId);
|
|
114
102
|
|
|
115
|
-
if (
|
|
116
|
-
transactionHandlers.delete((int) transactionId);
|
|
103
|
+
if (!(abortable instanceof ReactNativeFirebaseDatabaseTransactionHandler)) {
|
|
117
104
|
return;
|
|
118
105
|
}
|
|
119
106
|
|
|
107
|
+
ReactNativeFirebaseDatabaseTransactionHandler transactionHandler =
|
|
108
|
+
(ReactNativeFirebaseDatabaseTransactionHandler) abortable;
|
|
109
|
+
|
|
120
110
|
WritableMap resultMap =
|
|
121
111
|
transactionHandler.createResultMap(error, committed, snapshot);
|
|
122
112
|
|
|
@@ -129,8 +119,6 @@ public class NativeRNFBTurboDatabaseTransaction extends NativeRNFBTurboDatabaseT
|
|
|
129
119
|
resultMap,
|
|
130
120
|
app,
|
|
131
121
|
(int) transactionId));
|
|
132
|
-
|
|
133
|
-
transactionHandlers.delete((int) transactionId);
|
|
134
122
|
}
|
|
135
123
|
},
|
|
136
124
|
applyLocally);
|
|
@@ -140,11 +128,10 @@ public class NativeRNFBTurboDatabaseTransaction extends NativeRNFBTurboDatabaseT
|
|
|
140
128
|
@Override
|
|
141
129
|
public void transactionTryCommit(
|
|
142
130
|
String app, String dbURL, double transactionId, ReadableMap updates) {
|
|
143
|
-
|
|
144
|
-
transactionHandlers.get((int) transactionId);
|
|
131
|
+
DatabaseAbortable abortable = transactionHandlers.get((int) transactionId);
|
|
145
132
|
|
|
146
|
-
if (
|
|
147
|
-
|
|
133
|
+
if (abortable instanceof ReactNativeFirebaseDatabaseTransactionHandler) {
|
|
134
|
+
((ReactNativeFirebaseDatabaseTransactionHandler) abortable).signalUpdateReceived(updates);
|
|
148
135
|
}
|
|
149
136
|
}
|
|
150
137
|
}
|
|
@@ -25,15 +25,14 @@ import com.google.firebase.database.ChildEventListener;
|
|
|
25
25
|
import com.google.firebase.database.DatabaseReference;
|
|
26
26
|
import com.google.firebase.database.Query;
|
|
27
27
|
import com.google.firebase.database.ValueEventListener;
|
|
28
|
-
import
|
|
29
|
-
import java.util.
|
|
28
|
+
import io.invertase.firebase.common.RNFBHandleCollisionException;
|
|
29
|
+
import java.util.List;
|
|
30
30
|
import java.util.Map;
|
|
31
31
|
|
|
32
|
-
public class ReactNativeFirebaseDatabaseQuery {
|
|
32
|
+
public class ReactNativeFirebaseDatabaseQuery implements DatabaseQueryHandle {
|
|
33
33
|
|
|
34
34
|
public Query query;
|
|
35
|
-
|
|
36
|
-
private HashMap<String, ValueEventListener> valueEventListeners = new HashMap<>();
|
|
35
|
+
final RNFBDatabaseListenerRegistry listeners = new RNFBDatabaseListenerRegistry();
|
|
37
36
|
|
|
38
37
|
ReactNativeFirebaseDatabaseQuery(DatabaseReference reference, ReadableArray modifiers) {
|
|
39
38
|
this.query = reference;
|
|
@@ -78,7 +77,11 @@ public class ReactNativeFirebaseDatabaseQuery {
|
|
|
78
77
|
* @param listener
|
|
79
78
|
*/
|
|
80
79
|
public void addEventListener(String eventRegistrationKey, ValueEventListener listener) {
|
|
81
|
-
|
|
80
|
+
try {
|
|
81
|
+
listeners.putValue(eventRegistrationKey, listener);
|
|
82
|
+
} catch (RNFBHandleCollisionException | NullPointerException collision) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
82
85
|
query.addValueEventListener(listener);
|
|
83
86
|
}
|
|
84
87
|
|
|
@@ -89,7 +92,11 @@ public class ReactNativeFirebaseDatabaseQuery {
|
|
|
89
92
|
* @param listener
|
|
90
93
|
*/
|
|
91
94
|
public void addEventListener(String eventRegistrationKey, ChildEventListener listener) {
|
|
92
|
-
|
|
95
|
+
try {
|
|
96
|
+
listeners.putChild(eventRegistrationKey, listener);
|
|
97
|
+
} catch (RNFBHandleCollisionException | NullPointerException collision) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
93
100
|
query.addChildEventListener(listener);
|
|
94
101
|
}
|
|
95
102
|
|
|
@@ -117,36 +124,26 @@ public class ReactNativeFirebaseDatabaseQuery {
|
|
|
117
124
|
* @param eventRegistrationKey
|
|
118
125
|
*/
|
|
119
126
|
public void removeEventListener(String eventRegistrationKey) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
127
|
+
Object valueListener = listeners.takeValue(eventRegistrationKey);
|
|
128
|
+
if (valueListener instanceof ValueEventListener) {
|
|
129
|
+
query.removeEventListener((ValueEventListener) valueListener);
|
|
123
130
|
}
|
|
124
131
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
132
|
+
Object childListener = listeners.takeChild(eventRegistrationKey);
|
|
133
|
+
if (childListener instanceof ChildEventListener) {
|
|
134
|
+
query.removeEventListener((ChildEventListener) childListener);
|
|
128
135
|
}
|
|
129
136
|
}
|
|
130
137
|
|
|
131
138
|
/** Iterates over all current event listeners on the current query and removes each one */
|
|
139
|
+
@Override
|
|
132
140
|
public void removeAllEventListeners() {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
query.removeEventListener(valueEventListener);
|
|
140
|
-
valueIterator.remove();
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
Iterator childIterator = childEventListeners.entrySet().iterator();
|
|
144
|
-
|
|
145
|
-
while (childIterator.hasNext()) {
|
|
146
|
-
Map.Entry pair = (Map.Entry) childIterator.next();
|
|
147
|
-
ChildEventListener childEventListener = (ChildEventListener) pair.getValue();
|
|
148
|
-
query.removeEventListener(childEventListener);
|
|
149
|
-
childIterator.remove();
|
|
141
|
+
List<Object> remaining = listeners.takeAll();
|
|
142
|
+
for (Object listener : remaining) {
|
|
143
|
+
if (listener instanceof ValueEventListener) {
|
|
144
|
+
query.removeEventListener((ValueEventListener) listener);
|
|
145
|
+
} else if (listener instanceof ChildEventListener) {
|
|
146
|
+
query.removeEventListener((ChildEventListener) listener);
|
|
150
147
|
}
|
|
151
148
|
}
|
|
152
149
|
}
|
|
@@ -158,8 +155,7 @@ public class ReactNativeFirebaseDatabaseQuery {
|
|
|
158
155
|
* @return
|
|
159
156
|
*/
|
|
160
157
|
public Boolean hasEventListener(String eventRegistrationKey) {
|
|
161
|
-
return
|
|
162
|
-
|| childEventListeners.containsKey(eventRegistrationKey);
|
|
158
|
+
return listeners.hasEventListener(eventRegistrationKey);
|
|
163
159
|
}
|
|
164
160
|
|
|
165
161
|
/**
|
|
@@ -167,8 +163,9 @@ public class ReactNativeFirebaseDatabaseQuery {
|
|
|
167
163
|
*
|
|
168
164
|
* @return
|
|
169
165
|
*/
|
|
166
|
+
@Override
|
|
170
167
|
public Boolean hasListeners() {
|
|
171
|
-
return
|
|
168
|
+
return listeners.hasListeners();
|
|
172
169
|
}
|
|
173
170
|
|
|
174
171
|
/**
|
|
@@ -32,7 +32,7 @@ import java.util.concurrent.locks.Condition;
|
|
|
32
32
|
import java.util.concurrent.locks.ReentrantLock;
|
|
33
33
|
import javax.annotation.Nullable;
|
|
34
34
|
|
|
35
|
-
public class ReactNativeFirebaseDatabaseTransactionHandler {
|
|
35
|
+
public class ReactNativeFirebaseDatabaseTransactionHandler implements DatabaseAbortable {
|
|
36
36
|
private final ReentrantLock lock;
|
|
37
37
|
private final Condition condition;
|
|
38
38
|
public Object value;
|
|
@@ -81,8 +81,8 @@ public class ReactNativeFirebaseDatabaseTransactionHandler {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
void abort() {
|
|
84
|
+
@Override
|
|
85
|
+
public void abort() {
|
|
86
86
|
lock.lock();
|
|
87
87
|
try {
|
|
88
88
|
abort = true;
|