@react-native-firebase/database 26.4.0 → 27.0.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 +21 -0
- package/RNFBDatabase.podspec +8 -2
- package/android/build.gradle +5 -0
- package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseKeepSyncedRegistry.java +106 -0
- package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseListenerRegistry.kt +133 -0
- package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseQueryRegistry.kt +76 -0
- package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseTransactionRegistry.kt +67 -0
- package/android/src/main/java/io/invertase/firebase/database/UniversalFirebaseDatabaseOnDisconnectModule.java +102 -9
- package/android/src/reactnative/java/io/invertase/firebase/database/NativeRNFBTurboDatabaseOnDisconnect.java +6 -11
- package/android/src/reactnative/java/io/invertase/firebase/database/NativeRNFBTurboDatabaseQuery.java +93 -2
- package/android/src/reactnative/java/io/invertase/firebase/database/generated/java/com/facebook/fbreact/specs/NativeRNFBTurboDatabaseQuerySpec.java +4 -0
- package/android/src/reactnative/java/io/invertase/firebase/database/generated/jni/RNFBDatabaseTurboModules-generated.cpp +6 -0
- package/android/src/reactnative/java/io/invertase/firebase/database/generated/jni/react/renderer/components/RNFBDatabaseTurboModules/RNFBDatabaseTurboModulesJSI.h +12 -0
- package/android/src/test/java/io/invertase/firebase/database/RNFBDatabaseKeepSyncedRegistryTest.java +174 -0
- package/android/src/test/java/io/invertase/firebase/database/RNFBDatabaseListenerRegistryTest.java +31 -0
- package/android/src/test/java/io/invertase/firebase/database/RNFBDatabaseQueryRegistryTest.java +55 -0
- package/android/src/test/java/io/invertase/firebase/database/UniversalFirebaseDatabaseOnDisconnectCancelErrorTest.java +137 -0
- package/dist/module/DatabaseQuery.js +52 -1
- package/dist/module/DatabaseQuery.js.map +1 -1
- package/dist/module/DatabaseSyncTree.js +1 -1
- package/dist/module/DatabaseSyncTree.js.map +1 -1
- package/dist/module/index.js +1 -2
- package/dist/module/index.js.map +1 -1
- package/dist/module/modular/query.js +1 -1
- package/dist/module/modular/query.js.map +1 -1
- package/dist/module/version.js +1 -1
- package/dist/module/web/RNFBDatabaseModule.js +7 -0
- package/dist/module/web/RNFBDatabaseModule.js.map +1 -1
- package/dist/typescript/lib/DatabaseQuery.d.ts +25 -0
- package/dist/typescript/lib/DatabaseQuery.d.ts.map +1 -1
- package/dist/typescript/lib/index.d.ts +1 -1
- package/dist/typescript/lib/types/internal.d.ts +2 -0
- package/dist/typescript/lib/types/internal.d.ts.map +1 -1
- package/dist/typescript/lib/version.d.ts +1 -1
- package/dist/typescript/lib/web/RNFBDatabaseModule.d.ts.map +1 -1
- package/ios/RNFBDatabase/RNFBDatabaseListenerRegistry.m +24 -17
- package/ios/RNFBDatabase/RNFBDatabaseListenerRegistryStorage.swift +81 -0
- package/ios/RNFBDatabase/RNFBDatabaseNullSentinelDecoder.h +36 -0
- package/ios/RNFBDatabase/RNFBDatabaseNullSentinelDecoder.m +48 -0
- package/ios/RNFBDatabase/RNFBDatabaseOnDisconnectHelper.m +29 -26
- package/ios/RNFBDatabase/RNFBDatabaseQueryHelper.h +7 -0
- package/ios/RNFBDatabase/RNFBDatabaseQueryHelper.m +20 -0
- package/ios/RNFBDatabase/RNFBDatabaseQueryModule.mm +14 -0
- package/ios/RNFBDatabase/RNFBDatabaseQueryRegistry.m +25 -51
- package/ios/RNFBDatabase/RNFBDatabaseQueryRegistryStorage.swift +104 -0
- package/ios/RNFBDatabase/RNFBDatabaseReferenceHelper.m +14 -12
- package/ios/RNFBDatabase/RNFBDatabaseTransactionHelper.m +3 -1
- package/ios/RNFBDatabase.xcodeproj/project.pbxproj +12 -0
- package/ios/RNFBDatabaseUnitTests/RNFBDatabaseListenerRegistryStorageTests.swift +86 -0
- package/ios/RNFBDatabaseUnitTests/RNFBDatabaseNullSentinelDecodeTests.m +118 -0
- package/ios/RNFBDatabaseUnitTests/RNFBDatabaseQueryRegistryStorageTests.swift +181 -0
- package/ios/RNFBDatabaseUnitTests/RNFBDatabaseUnitTests.xcodeproj/project.pbxproj +42 -0
- package/ios/generated/RNFBDatabaseTurboModules/RNFBDatabaseTurboModules-generated.mm +7 -0
- package/ios/generated/RNFBDatabaseTurboModules/RNFBDatabaseTurboModules.h +6 -0
- package/ios/generated/RNFBDatabaseTurboModulesJSI.h +12 -0
- package/lib/DatabaseQuery.ts +56 -0
- package/lib/DatabaseSyncTree.ts +1 -1
- package/lib/index.ts +1 -1
- package/lib/modular/query.ts +1 -1
- package/lib/types/internal.ts +2 -0
- package/lib/version.ts +1 -1
- package/lib/web/RNFBDatabaseModule.ts +13 -0
- package/package.json +4 -4
- package/specs/NativeRNFBTurboDatabaseQuery.ts +1 -0
- package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseListenerRegistry.java +0 -117
- package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseQueryRegistry.java +0 -78
- package/android/src/main/java/io/invertase/firebase/database/RNFBDatabaseTransactionRegistry.java +0 -71
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this library except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import XCTest
|
|
18
|
+
|
|
19
|
+
final class RNFBDatabaseListenerRegistryStorageTests: XCTestCase {
|
|
20
|
+
private var storage: RNFBDatabaseListenerRegistryStorage!
|
|
21
|
+
|
|
22
|
+
override func setUp() {
|
|
23
|
+
super.setUp()
|
|
24
|
+
storage = RNFBDatabaseListenerRegistryStorage()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
func testPutIfAbsent_storesWhenFree_andIncrementsOccupancy() {
|
|
28
|
+
let handle = 7 as NSNumber
|
|
29
|
+
XCTAssertTrue(storage.putIfAbsent("k" as NSString, value: handle))
|
|
30
|
+
XCTAssertTrue(storage.get("k" as NSString) === handle)
|
|
31
|
+
XCTAssertTrue(storage.hasListeners)
|
|
32
|
+
XCTAssertTrue(storage.hasEventListener("k" as NSString))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
func testPutIfAbsent_collision_keepsExistingWithoutOccupancyBump() {
|
|
36
|
+
let first = 1 as NSNumber
|
|
37
|
+
let second = 2 as NSNumber
|
|
38
|
+
XCTAssertTrue(storage.putIfAbsent("k" as NSString, value: first))
|
|
39
|
+
XCTAssertFalse(storage.putIfAbsent("k" as NSString, value: second))
|
|
40
|
+
XCTAssertTrue(storage.get("k" as NSString) === first)
|
|
41
|
+
XCTAssertTrue(storage.hasListeners)
|
|
42
|
+
XCTAssertEqual(storage.takeAll().count, 1)
|
|
43
|
+
XCTAssertFalse(storage.hasListeners)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
func testGet_whenFree_isNil() {
|
|
47
|
+
XCTAssertNil(storage.get("missing" as NSString))
|
|
48
|
+
XCTAssertFalse(storage.hasEventListener("missing" as NSString))
|
|
49
|
+
XCTAssertFalse(storage.hasListeners)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
func testTake_removesAndDecrementsOccupancy() {
|
|
53
|
+
let handle = 7 as NSNumber
|
|
54
|
+
XCTAssertTrue(storage.putIfAbsent("k" as NSString, value: handle))
|
|
55
|
+
XCTAssertTrue(storage.take("k" as NSString) === handle)
|
|
56
|
+
XCTAssertNil(storage.get("k" as NSString))
|
|
57
|
+
XCTAssertFalse(storage.hasListeners)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
func testTake_whenFree_isNoOp() {
|
|
61
|
+
XCTAssertNil(storage.take("missing" as NSString))
|
|
62
|
+
XCTAssertFalse(storage.hasListeners)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
func testTakeAll_snapshotAndLeavesEmpty() {
|
|
66
|
+
XCTAssertTrue(storage.putIfAbsent("a" as NSString, value: 1 as NSNumber))
|
|
67
|
+
XCTAssertTrue(storage.putIfAbsent("b" as NSString, value: 2 as NSNumber))
|
|
68
|
+
let remaining = storage.takeAll()
|
|
69
|
+
XCTAssertEqual(remaining.count, 2)
|
|
70
|
+
XCTAssertFalse(storage.hasListeners)
|
|
71
|
+
XCTAssertNil(storage.get("a" as NSString))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
func testTakeAll_empty_isNoOp() {
|
|
75
|
+
XCTAssertEqual(storage.takeAll().count, 0)
|
|
76
|
+
XCTAssertFalse(storage.hasListeners)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
func testPut_afterTake_allowsReuse() {
|
|
80
|
+
XCTAssertTrue(storage.putIfAbsent("k" as NSString, value: 1 as NSNumber))
|
|
81
|
+
XCTAssertEqual(storage.take("k" as NSString) as? NSNumber, 1)
|
|
82
|
+
XCTAssertTrue(storage.putIfAbsent("k" as NSString, value: 2 as NSNumber))
|
|
83
|
+
XCTAssertEqual(storage.get("k" as NSString) as? NSNumber, 2)
|
|
84
|
+
XCTAssertTrue(storage.hasListeners)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this library except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#import <XCTest/XCTest.h>
|
|
19
|
+
|
|
20
|
+
#import "RNFBDatabaseNullSentinelDecoder.h"
|
|
21
|
+
|
|
22
|
+
@interface RNFBDatabaseNullSentinelDecodeTests : XCTestCase
|
|
23
|
+
@end
|
|
24
|
+
|
|
25
|
+
@implementation RNFBDatabaseNullSentinelDecodeTests
|
|
26
|
+
|
|
27
|
+
- (NSDictionary *)nullSentinel {
|
|
28
|
+
return @{@"__rnfbNull" : @YES};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
- (NSDictionary *)serverTimestamp {
|
|
32
|
+
return @{@".sv" : @"timestamp"};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
#pragma mark - Reference helper seams
|
|
36
|
+
|
|
37
|
+
- (void)testReferenceSet_topLevelNullSentinel_becomesNSNull {
|
|
38
|
+
NSDictionary *props = @{@"value" : [self nullSentinel]};
|
|
39
|
+
XCTAssertEqualObjects([RNFBDatabaseNullSentinelDecoder decodedValueFromProps:props],
|
|
40
|
+
[NSNull null]);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
- (void)testReferenceUpdate_childNullSentinel_becomesNSNull {
|
|
44
|
+
NSDictionary *props = @{@"values" : @{@"a" : [self nullSentinel], @"b" : @1}};
|
|
45
|
+
NSDictionary *decoded = [RNFBDatabaseNullSentinelDecoder decodedValuesFromProps:props];
|
|
46
|
+
XCTAssertEqualObjects(decoded[@"a"], [NSNull null]);
|
|
47
|
+
XCTAssertEqualObjects(decoded[@"b"], @1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
- (void)testReferenceSetWithPriority_valueAndPrioritySentinels_becomeNSNull {
|
|
51
|
+
NSDictionary *props = @{
|
|
52
|
+
@"value" : [self nullSentinel],
|
|
53
|
+
@"priority" : [self nullSentinel],
|
|
54
|
+
};
|
|
55
|
+
XCTAssertEqualObjects([RNFBDatabaseNullSentinelDecoder decodedValueFromProps:props],
|
|
56
|
+
[NSNull null]);
|
|
57
|
+
XCTAssertEqualObjects([RNFBDatabaseNullSentinelDecoder decodedPriorityFromProps:props],
|
|
58
|
+
[NSNull null]);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
- (void)testReferenceSetPriority_sentinel_becomesNSNull {
|
|
62
|
+
NSDictionary *props = @{@"priority" : [self nullSentinel]};
|
|
63
|
+
XCTAssertEqualObjects([RNFBDatabaseNullSentinelDecoder decodedPriorityFromProps:props],
|
|
64
|
+
[NSNull null]);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
- (void)testReference_serverTimestampDictionary_unchanged {
|
|
68
|
+
NSDictionary *props = @{@"value" : [self serverTimestamp]};
|
|
69
|
+
XCTAssertEqualObjects([RNFBDatabaseNullSentinelDecoder decodedValueFromProps:props],
|
|
70
|
+
[self serverTimestamp]);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#pragma mark - OnDisconnect helper seams
|
|
74
|
+
|
|
75
|
+
- (void)testOnDisconnectSet_topLevelNullSentinel_becomesNSNull {
|
|
76
|
+
NSDictionary *props = @{@"value" : [self nullSentinel]};
|
|
77
|
+
XCTAssertEqualObjects([RNFBDatabaseNullSentinelDecoder decodedValueFromProps:props],
|
|
78
|
+
[NSNull null]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
- (void)testOnDisconnectSetWithPriority_valueAndPrioritySentinels_becomeNSNull {
|
|
82
|
+
NSDictionary *props = @{
|
|
83
|
+
@"value" : [self nullSentinel],
|
|
84
|
+
@"priority" : [self nullSentinel],
|
|
85
|
+
};
|
|
86
|
+
XCTAssertEqualObjects([RNFBDatabaseNullSentinelDecoder decodedValueFromProps:props],
|
|
87
|
+
[NSNull null]);
|
|
88
|
+
XCTAssertEqualObjects([RNFBDatabaseNullSentinelDecoder decodedPriorityFromProps:props],
|
|
89
|
+
[NSNull null]);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
- (void)testOnDisconnectUpdate_childNullSentinel_becomesNSNull {
|
|
93
|
+
NSDictionary *props = @{@"values" : @{@"gone" : [self nullSentinel]}};
|
|
94
|
+
NSDictionary *decoded = [RNFBDatabaseNullSentinelDecoder decodedValuesFromProps:props];
|
|
95
|
+
XCTAssertEqualObjects(decoded[@"gone"], [NSNull null]);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
- (void)testOnDisconnect_serverTimestampDictionary_unchanged {
|
|
99
|
+
NSDictionary *props = @{@"value" : [self serverTimestamp]};
|
|
100
|
+
XCTAssertEqualObjects([RNFBDatabaseNullSentinelDecoder decodedValueFromProps:props],
|
|
101
|
+
[self serverTimestamp]);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
#pragma mark - Transaction helper seam
|
|
105
|
+
|
|
106
|
+
- (void)testTransactionTryCommit_valueSentinel_becomesNSNull {
|
|
107
|
+
id decoded = [RNFBDatabaseNullSentinelDecoder decodedTransactionValue:[self nullSentinel]];
|
|
108
|
+
XCTAssertEqualObjects(decoded, [NSNull null]);
|
|
109
|
+
XCTAssertFalse([decoded isKindOfClass:[NSDictionary class]]);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
- (void)testTransactionTryCommit_serverTimestamp_unchanged {
|
|
113
|
+
XCTAssertEqualObjects(
|
|
114
|
+
[RNFBDatabaseNullSentinelDecoder decodedTransactionValue:[self serverTimestamp]],
|
|
115
|
+
[self serverTimestamp]);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@end
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this library except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import XCTest
|
|
18
|
+
|
|
19
|
+
private final class FakeQueryHandle: NSObject {
|
|
20
|
+
private(set) var removeCount = 0
|
|
21
|
+
var listeners = false
|
|
22
|
+
|
|
23
|
+
@objc func removeAllEventListeners() {
|
|
24
|
+
removeCount += 1
|
|
25
|
+
listeners = false
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@objc func hasListeners() -> Bool {
|
|
29
|
+
listeners
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
final class RNFBDatabaseQueryRegistryStorageTests: XCTestCase {
|
|
34
|
+
private var storage: RNFBDatabaseQueryRegistryStorage!
|
|
35
|
+
|
|
36
|
+
override func setUp() {
|
|
37
|
+
super.setUp()
|
|
38
|
+
storage = RNFBDatabaseQueryRegistryStorage()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
func testPutIfAbsent_storesWhenFree() {
|
|
42
|
+
let handle = FakeQueryHandle()
|
|
43
|
+
XCTAssertTrue(storage.putIfAbsent("q1" as NSString, value: handle))
|
|
44
|
+
XCTAssertTrue(storage.get("q1" as NSString) === handle)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func testPutIfAbsent_collision_keepsExisting() {
|
|
48
|
+
let first = FakeQueryHandle()
|
|
49
|
+
let second = FakeQueryHandle()
|
|
50
|
+
XCTAssertTrue(storage.putIfAbsent("q1" as NSString, value: first))
|
|
51
|
+
XCTAssertFalse(storage.putIfAbsent("q1" as NSString, value: second))
|
|
52
|
+
XCTAssertTrue(storage.get("q1" as NSString) === first)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
func testGet_whenFree_isNil() {
|
|
56
|
+
XCTAssertNil(storage.get("missing" as NSString))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func testTake_removesWithoutCallingRemove() {
|
|
60
|
+
let handle = FakeQueryHandle()
|
|
61
|
+
XCTAssertTrue(storage.putIfAbsent("q1" as NSString, value: handle))
|
|
62
|
+
XCTAssertTrue(storage.take("q1" as NSString) === handle)
|
|
63
|
+
XCTAssertNil(storage.get("q1" as NSString))
|
|
64
|
+
XCTAssertEqual(handle.removeCount, 0)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
func testTake_whenFree_isNil() {
|
|
68
|
+
XCTAssertNil(storage.take("missing" as NSString))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
func testTakeIfIdle_whenNoListeners_takes() {
|
|
72
|
+
let handle = FakeQueryHandle()
|
|
73
|
+
handle.listeners = false
|
|
74
|
+
XCTAssertTrue(storage.putIfAbsent("q" as NSString, value: handle))
|
|
75
|
+
XCTAssertTrue(storage.takeIfIdle("q" as NSString) === handle)
|
|
76
|
+
XCTAssertNil(storage.get("q" as NSString))
|
|
77
|
+
XCTAssertEqual(handle.removeCount, 0)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
func testTakeIfIdle_whenHasListeners_leavesMapping() {
|
|
81
|
+
let handle = FakeQueryHandle()
|
|
82
|
+
handle.listeners = true
|
|
83
|
+
XCTAssertTrue(storage.putIfAbsent("q" as NSString, value: handle))
|
|
84
|
+
XCTAssertNil(storage.takeIfIdle("q" as NSString))
|
|
85
|
+
XCTAssertTrue(storage.get("q" as NSString) === handle)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
func testTakeIfIdle_whenMissing_isNil() {
|
|
89
|
+
XCTAssertNil(storage.takeIfIdle("missing" as NSString))
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
func testTakeIfIdle_objectWithoutHasListeners_leavesMapping() {
|
|
93
|
+
let plain = NSObject()
|
|
94
|
+
XCTAssertTrue(storage.putIfAbsent("p" as NSString, value: plain))
|
|
95
|
+
XCTAssertNil(storage.takeIfIdle("p" as NSString))
|
|
96
|
+
XCTAssertTrue(storage.get("p" as NSString) === plain)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
func testTakeIfIdle_putBackWhenListenersAppearAfterTake() {
|
|
100
|
+
let gated = PutBackGatedQueryHandle()
|
|
101
|
+
gated.listeners = false
|
|
102
|
+
gated.onSecondHasListenersEnter = {
|
|
103
|
+
gated.listeners = true
|
|
104
|
+
}
|
|
105
|
+
XCTAssertTrue(storage.putIfAbsent("g" as NSString, value: gated))
|
|
106
|
+
XCTAssertNil(storage.takeIfIdle("g" as NSString))
|
|
107
|
+
XCTAssertTrue(storage.get("g" as NSString) === gated)
|
|
108
|
+
XCTAssertEqual(gated.callCount, 2)
|
|
109
|
+
XCTAssertEqual(gated.removeCount, 0)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
func testTakeIfIdle_putBackFails_clearsOrphanListeners() {
|
|
113
|
+
let orphan = PutBackGatedQueryHandle()
|
|
114
|
+
orphan.listeners = false
|
|
115
|
+
XCTAssertTrue(storage.putIfAbsent("q" as NSString, value: orphan))
|
|
116
|
+
|
|
117
|
+
let replacement = FakeQueryHandle()
|
|
118
|
+
replacement.listeners = true
|
|
119
|
+
orphan.onSecondHasListenersEnter = { [weak self] in
|
|
120
|
+
guard let self = self else { return }
|
|
121
|
+
XCTAssertTrue(self.storage.putIfAbsent("q" as NSString, value: replacement))
|
|
122
|
+
orphan.listeners = true
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
XCTAssertNil(storage.takeIfIdle("q" as NSString))
|
|
126
|
+
XCTAssertTrue(storage.get("q" as NSString) === replacement)
|
|
127
|
+
XCTAssertEqual(orphan.removeCount, 1)
|
|
128
|
+
XCTAssertFalse(orphan.listeners)
|
|
129
|
+
XCTAssertEqual(replacement.removeCount, 0)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
func testRemoveAll_removesSnapshotAndLeavesEmpty() {
|
|
133
|
+
let a = FakeQueryHandle()
|
|
134
|
+
let b = FakeQueryHandle()
|
|
135
|
+
XCTAssertTrue(storage.putIfAbsent("a" as NSString, value: a))
|
|
136
|
+
XCTAssertTrue(storage.putIfAbsent("b" as NSString, value: b))
|
|
137
|
+
storage.removeAll()
|
|
138
|
+
XCTAssertEqual(a.removeCount, 1)
|
|
139
|
+
XCTAssertEqual(b.removeCount, 1)
|
|
140
|
+
XCTAssertNil(storage.get("a" as NSString))
|
|
141
|
+
XCTAssertNil(storage.get("b" as NSString))
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
func testRemoveAll_objectWithoutRemove_doesNotCrash() {
|
|
145
|
+
let plain = NSObject()
|
|
146
|
+
XCTAssertTrue(storage.putIfAbsent("p" as NSString, value: plain))
|
|
147
|
+
storage.removeAll()
|
|
148
|
+
XCTAssertNil(storage.get("p" as NSString))
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
func testPut_afterTake_allowsReuse() {
|
|
152
|
+
let first = FakeQueryHandle()
|
|
153
|
+
let second = FakeQueryHandle()
|
|
154
|
+
XCTAssertTrue(storage.putIfAbsent("q1" as NSString, value: first))
|
|
155
|
+
XCTAssertTrue(storage.take("q1" as NSString) === first)
|
|
156
|
+
XCTAssertTrue(storage.putIfAbsent("q1" as NSString, value: second))
|
|
157
|
+
XCTAssertTrue(storage.get("q1" as NSString) === second)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/// First `hasListeners` returns idle; second returns `listeners` (settable between calls).
|
|
162
|
+
private final class PutBackGatedQueryHandle: NSObject {
|
|
163
|
+
private(set) var removeCount = 0
|
|
164
|
+
private(set) var callCount = 0
|
|
165
|
+
var listeners = false
|
|
166
|
+
var onSecondHasListenersEnter: (() -> Void)?
|
|
167
|
+
|
|
168
|
+
@objc func removeAllEventListeners() {
|
|
169
|
+
removeCount += 1
|
|
170
|
+
listeners = false
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
@objc func hasListeners() -> Bool {
|
|
174
|
+
callCount += 1
|
|
175
|
+
if callCount == 1 {
|
|
176
|
+
return false
|
|
177
|
+
}
|
|
178
|
+
onSecondHasListenersEnter?()
|
|
179
|
+
return listeners
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -12,7 +12,15 @@
|
|
|
12
12
|
D8000000000000000000000C /* RNFBDatabaseListenerRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D80000000000000000000009 /* RNFBDatabaseListenerRegistryTests.m */; };
|
|
13
13
|
D80000000000000000000020 /* RNFBDatabaseQueryRegistry.m in Sources */ = {isa = PBXBuildFile; fileRef = D80000000000000000000021 /* RNFBDatabaseQueryRegistry.m */; };
|
|
14
14
|
D80000000000000000000022 /* RNFBDatabaseQueryRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D80000000000000000000023 /* RNFBDatabaseQueryRegistryTests.m */; };
|
|
15
|
+
D80000000000000000000030 /* RNFBNullSentinelDecoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80000000000000000000031 /* RNFBNullSentinelDecoding.swift */; };
|
|
16
|
+
D80000000000000000000032 /* RNFBDatabaseNullSentinelDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = D80000000000000000000033 /* RNFBDatabaseNullSentinelDecoder.m */; };
|
|
17
|
+
D80000000000000000000038 /* RNFBDatabaseNullSentinelDecodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D80000000000000000000039 /* RNFBDatabaseNullSentinelDecodeTests.m */; };
|
|
15
18
|
D80000000000000000000015 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D80000000000000000000014 /* XCTest.framework */; };
|
|
19
|
+
D80000000000000000000025 /* RNFBHandleMapStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80000000000000000000026 /* RNFBHandleMapStorage.swift */; };
|
|
20
|
+
D80000000000000000000028 /* RNFBDatabaseListenerRegistryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80000000000000000000027 /* RNFBDatabaseListenerRegistryStorage.swift */; };
|
|
21
|
+
D8000000000000000000002A /* RNFBDatabaseListenerRegistryStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80000000000000000000029 /* RNFBDatabaseListenerRegistryStorageTests.swift */; };
|
|
22
|
+
D8000000000000000000002C /* RNFBDatabaseQueryRegistryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8000000000000000000002B /* RNFBDatabaseQueryRegistryStorage.swift */; };
|
|
23
|
+
D8000000000000000000002E /* RNFBDatabaseQueryRegistryStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8000000000000000000002D /* RNFBDatabaseQueryRegistryStorageTests.swift */; };
|
|
16
24
|
/* End PBXBuildFile section */
|
|
17
25
|
|
|
18
26
|
/* Begin PBXFileReference section */
|
|
@@ -25,7 +33,16 @@
|
|
|
25
33
|
D80000000000000000000024 /* RNFBDatabaseQueryRegistry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNFBDatabaseQueryRegistry.h; path = ../RNFBDatabase/RNFBDatabaseQueryRegistry.h; sourceTree = SOURCE_ROOT; };
|
|
26
34
|
D80000000000000000000021 /* RNFBDatabaseQueryRegistry.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = RNFBDatabaseQueryRegistry.m; path = ../RNFBDatabase/RNFBDatabaseQueryRegistry.m; sourceTree = SOURCE_ROOT; };
|
|
27
35
|
D80000000000000000000023 /* RNFBDatabaseQueryRegistryTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNFBDatabaseQueryRegistryTests.m; sourceTree = "<group>"; };
|
|
36
|
+
D80000000000000000000031 /* RNFBNullSentinelDecoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RNFBNullSentinelDecoding.swift; path = ../../../app/ios/RNFBApp/RNFBNullSentinelDecoding.swift; sourceTree = SOURCE_ROOT; };
|
|
37
|
+
D80000000000000000000033 /* RNFBDatabaseNullSentinelDecoder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = RNFBDatabaseNullSentinelDecoder.m; path = ../RNFBDatabase/RNFBDatabaseNullSentinelDecoder.m; sourceTree = SOURCE_ROOT; };
|
|
38
|
+
D8000000000000000000003B /* RNFBDatabaseNullSentinelDecoder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNFBDatabaseNullSentinelDecoder.h; path = ../RNFBDatabase/RNFBDatabaseNullSentinelDecoder.h; sourceTree = SOURCE_ROOT; };
|
|
39
|
+
D80000000000000000000039 /* RNFBDatabaseNullSentinelDecodeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNFBDatabaseNullSentinelDecodeTests.m; sourceTree = "<group>"; };
|
|
28
40
|
D80000000000000000000014 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/MacOSX.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
|
|
41
|
+
D80000000000000000000026 /* RNFBHandleMapStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RNFBHandleMapStorage.swift; path = ../../../app/ios/RNFBApp/RNFBHandleMapStorage.swift; sourceTree = SOURCE_ROOT; };
|
|
42
|
+
D80000000000000000000027 /* RNFBDatabaseListenerRegistryStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RNFBDatabaseListenerRegistryStorage.swift; path = ../RNFBDatabase/RNFBDatabaseListenerRegistryStorage.swift; sourceTree = SOURCE_ROOT; };
|
|
43
|
+
D80000000000000000000029 /* RNFBDatabaseListenerRegistryStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNFBDatabaseListenerRegistryStorageTests.swift; sourceTree = "<group>"; };
|
|
44
|
+
D8000000000000000000002B /* RNFBDatabaseQueryRegistryStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RNFBDatabaseQueryRegistryStorage.swift; path = ../RNFBDatabase/RNFBDatabaseQueryRegistryStorage.swift; sourceTree = SOURCE_ROOT; };
|
|
45
|
+
D8000000000000000000002D /* RNFBDatabaseQueryRegistryStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNFBDatabaseQueryRegistryStorageTests.swift; sourceTree = "<group>"; };
|
|
29
46
|
/* End PBXFileReference section */
|
|
30
47
|
|
|
31
48
|
/* Begin PBXFrameworksBuildPhase section */
|
|
@@ -61,12 +78,21 @@
|
|
|
61
78
|
children = (
|
|
62
79
|
D80000000000000000000007 /* RNFBHandleMap.h */,
|
|
63
80
|
D80000000000000000000008 /* RNFBHandleMap.m */,
|
|
81
|
+
D80000000000000000000026 /* RNFBHandleMapStorage.swift */,
|
|
64
82
|
D80000000000000000000016 /* RNFBDatabaseListenerRegistry.h */,
|
|
65
83
|
D80000000000000000000018 /* RNFBDatabaseListenerRegistry.m */,
|
|
84
|
+
D80000000000000000000027 /* RNFBDatabaseListenerRegistryStorage.swift */,
|
|
66
85
|
D80000000000000000000024 /* RNFBDatabaseQueryRegistry.h */,
|
|
67
86
|
D80000000000000000000021 /* RNFBDatabaseQueryRegistry.m */,
|
|
87
|
+
D80000000000000000000031 /* RNFBNullSentinelDecoding.swift */,
|
|
88
|
+
D8000000000000000000003B /* RNFBDatabaseNullSentinelDecoder.h */,
|
|
89
|
+
D80000000000000000000033 /* RNFBDatabaseNullSentinelDecoder.m */,
|
|
90
|
+
D8000000000000000000002B /* RNFBDatabaseQueryRegistryStorage.swift */,
|
|
68
91
|
D80000000000000000000009 /* RNFBDatabaseListenerRegistryTests.m */,
|
|
92
|
+
D80000000000000000000029 /* RNFBDatabaseListenerRegistryStorageTests.swift */,
|
|
69
93
|
D80000000000000000000023 /* RNFBDatabaseQueryRegistryTests.m */,
|
|
94
|
+
D80000000000000000000039 /* RNFBDatabaseNullSentinelDecodeTests.m */,
|
|
95
|
+
D8000000000000000000002D /* RNFBDatabaseQueryRegistryStorageTests.swift */,
|
|
70
96
|
);
|
|
71
97
|
name = Sources;
|
|
72
98
|
sourceTree = "<group>";
|
|
@@ -129,10 +155,18 @@
|
|
|
129
155
|
buildActionMask = 2147483647;
|
|
130
156
|
files = (
|
|
131
157
|
D8000000000000000000000A /* RNFBHandleMap.m in Sources */,
|
|
158
|
+
D80000000000000000000025 /* RNFBHandleMapStorage.swift in Sources */,
|
|
159
|
+
D80000000000000000000028 /* RNFBDatabaseListenerRegistryStorage.swift in Sources */,
|
|
132
160
|
D8000000000000000000000B /* RNFBDatabaseListenerRegistry.m in Sources */,
|
|
161
|
+
D8000000000000000000002C /* RNFBDatabaseQueryRegistryStorage.swift in Sources */,
|
|
133
162
|
D80000000000000000000020 /* RNFBDatabaseQueryRegistry.m in Sources */,
|
|
163
|
+
D80000000000000000000030 /* RNFBNullSentinelDecoding.swift in Sources */,
|
|
164
|
+
D80000000000000000000032 /* RNFBDatabaseNullSentinelDecoder.m in Sources */,
|
|
134
165
|
D8000000000000000000000C /* RNFBDatabaseListenerRegistryTests.m in Sources */,
|
|
166
|
+
D8000000000000000000002A /* RNFBDatabaseListenerRegistryStorageTests.swift in Sources */,
|
|
135
167
|
D80000000000000000000022 /* RNFBDatabaseQueryRegistryTests.m in Sources */,
|
|
168
|
+
D80000000000000000000038 /* RNFBDatabaseNullSentinelDecodeTests.m in Sources */,
|
|
169
|
+
D8000000000000000000002E /* RNFBDatabaseQueryRegistryStorageTests.swift in Sources */,
|
|
136
170
|
);
|
|
137
171
|
runOnlyForDeploymentPostprocessing = 0;
|
|
138
172
|
};
|
|
@@ -181,8 +215,10 @@
|
|
|
181
215
|
CODE_SIGNING_REQUIRED = NO;
|
|
182
216
|
CODE_SIGN_IDENTITY = "-";
|
|
183
217
|
COMBINE_HIDPI_IMAGES = YES;
|
|
218
|
+
DEFINES_MODULE = YES;
|
|
184
219
|
GENERATE_INFOPLIST_FILE = YES;
|
|
185
220
|
HEADER_SEARCH_PATHS = (
|
|
221
|
+
"$(SRCROOT)",
|
|
186
222
|
"$(SRCROOT)/../../../app/ios",
|
|
187
223
|
"$(SRCROOT)/../../../app/ios/RNFBApp",
|
|
188
224
|
"$(SRCROOT)/../RNFBDatabase",
|
|
@@ -204,6 +240,8 @@
|
|
|
204
240
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
205
241
|
SDKROOT = macosx;
|
|
206
242
|
SUPPORTED_PLATFORMS = macosx;
|
|
243
|
+
SWIFT_OBJC_INTERFACE_HEADER_NAME = "RNFBHandleMapStorage-Swift.inc";
|
|
244
|
+
SWIFT_VERSION = 5.10;
|
|
207
245
|
};
|
|
208
246
|
name = Debug;
|
|
209
247
|
};
|
|
@@ -215,8 +253,10 @@
|
|
|
215
253
|
CODE_SIGNING_REQUIRED = NO;
|
|
216
254
|
CODE_SIGN_IDENTITY = "-";
|
|
217
255
|
COMBINE_HIDPI_IMAGES = YES;
|
|
256
|
+
DEFINES_MODULE = YES;
|
|
218
257
|
GENERATE_INFOPLIST_FILE = YES;
|
|
219
258
|
HEADER_SEARCH_PATHS = (
|
|
259
|
+
"$(SRCROOT)",
|
|
220
260
|
"$(SRCROOT)/../../../app/ios",
|
|
221
261
|
"$(SRCROOT)/../../../app/ios/RNFBApp",
|
|
222
262
|
"$(SRCROOT)/../RNFBDatabase",
|
|
@@ -231,6 +271,8 @@
|
|
|
231
271
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
232
272
|
SDKROOT = macosx;
|
|
233
273
|
SUPPORTED_PLATFORMS = macosx;
|
|
274
|
+
SWIFT_OBJC_INTERFACE_HEADER_NAME = "RNFBHandleMapStorage-Swift.inc";
|
|
275
|
+
SWIFT_VERSION = 5.10;
|
|
234
276
|
};
|
|
235
277
|
name = Release;
|
|
236
278
|
};
|
|
@@ -141,6 +141,10 @@ namespace facebook::react {
|
|
|
141
141
|
return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "once", @selector(once:dbURL:path:modifiers:eventType:resolve:reject:), args, count);
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
static facebook::jsi::Value __hostFunction_NativeRNFBTurboDatabaseQuerySpecJSI_get(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
145
|
+
return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "get", @selector(get:dbURL:path:modifiers:resolve:reject:), args, count);
|
|
146
|
+
}
|
|
147
|
+
|
|
144
148
|
static facebook::jsi::Value __hostFunction_NativeRNFBTurboDatabaseQuerySpecJSI_on(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
145
149
|
return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "on", @selector(on:dbURL:props:), args, count);
|
|
146
150
|
}
|
|
@@ -159,6 +163,9 @@ namespace facebook::react {
|
|
|
159
163
|
methodMap_["once"] = MethodMetadata {5, __hostFunction_NativeRNFBTurboDatabaseQuerySpecJSI_once};
|
|
160
164
|
|
|
161
165
|
|
|
166
|
+
methodMap_["get"] = MethodMetadata {4, __hostFunction_NativeRNFBTurboDatabaseQuerySpecJSI_get};
|
|
167
|
+
|
|
168
|
+
|
|
162
169
|
methodMap_["on"] = MethodMetadata {3, __hostFunction_NativeRNFBTurboDatabaseQuerySpecJSI_on};
|
|
163
170
|
|
|
164
171
|
|
|
@@ -135,6 +135,12 @@ namespace facebook::react {
|
|
|
135
135
|
eventType:(NSString *)eventType
|
|
136
136
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
137
137
|
reject:(RCTPromiseRejectBlock)reject;
|
|
138
|
+
- (void)get:(NSString *)app
|
|
139
|
+
dbURL:(NSString *)dbURL
|
|
140
|
+
path:(NSString *)path
|
|
141
|
+
modifiers:(NSArray *)modifiers
|
|
142
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
143
|
+
reject:(RCTPromiseRejectBlock)reject;
|
|
138
144
|
- (void)on:(NSString *)app
|
|
139
145
|
dbURL:(NSString *)dbURL
|
|
140
146
|
props:(NSDictionary *)props;
|
|
@@ -170,6 +170,7 @@ public:
|
|
|
170
170
|
protected:
|
|
171
171
|
NativeRNFBTurboDatabaseQueryCxxSpec(std::shared_ptr<CallInvoker> jsInvoker) : TurboModule(std::string{NativeRNFBTurboDatabaseQueryCxxSpec::kModuleName}, jsInvoker) {
|
|
172
172
|
methodMap_["once"] = MethodMetadata {.argCount = 5, .invoker = __once};
|
|
173
|
+
methodMap_["get"] = MethodMetadata {.argCount = 4, .invoker = __get};
|
|
173
174
|
methodMap_["on"] = MethodMetadata {.argCount = 3, .invoker = __on};
|
|
174
175
|
methodMap_["off"] = MethodMetadata {.argCount = 2, .invoker = __off};
|
|
175
176
|
methodMap_["keepSynced"] = MethodMetadata {.argCount = 6, .invoker = __keepSynced};
|
|
@@ -188,6 +189,17 @@ private:
|
|
|
188
189
|
count <= 4 ? throw jsi::JSError(rt, "Expected argument in position 4 to be passed") : args[4].asString(rt));
|
|
189
190
|
}
|
|
190
191
|
|
|
192
|
+
static jsi::Value __get(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
|
|
193
|
+
static_assert(
|
|
194
|
+
bridging::getParameterCount(&T::get) == 5,
|
|
195
|
+
"Expected get(...) to have 5 parameters");
|
|
196
|
+
return bridging::callFromJs<jsi::Value>(rt, &T::get, static_cast<NativeRNFBTurboDatabaseQueryCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
|
|
197
|
+
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt),
|
|
198
|
+
count <= 1 ? throw jsi::JSError(rt, "Expected argument in position 1 to be passed") : args[1].asString(rt),
|
|
199
|
+
count <= 2 ? throw jsi::JSError(rt, "Expected argument in position 2 to be passed") : args[2].asString(rt),
|
|
200
|
+
count <= 3 ? throw jsi::JSError(rt, "Expected argument in position 3 to be passed") : args[3].asObject(rt).asArray(rt));
|
|
201
|
+
}
|
|
202
|
+
|
|
191
203
|
static jsi::Value __on(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
|
|
192
204
|
static_assert(
|
|
193
205
|
bridging::getParameterCount(&T::on) == 4,
|
package/lib/DatabaseQuery.ts
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import {
|
|
19
19
|
isBoolean,
|
|
20
20
|
isFunction,
|
|
21
|
+
isIOS,
|
|
21
22
|
isNull,
|
|
22
23
|
isNumber,
|
|
23
24
|
isObject,
|
|
@@ -45,6 +46,20 @@ const eventTypes = [
|
|
|
45
46
|
'child_removed',
|
|
46
47
|
] as const;
|
|
47
48
|
|
|
49
|
+
/** `.info/*` is client-local metadata that only the listener path serves. */
|
|
50
|
+
const INFO_PATH = /^\/?\.info(\/|$)/;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* True when native/web get already produced an observer-shaped `database/*` code.
|
|
54
|
+
* Android Query.get() non-denial failures use SharedUtils.getExceptionMap → `database/unknown`;
|
|
55
|
+
* those still fall through to `once('value')`. Specific codes mean once already ran (or a
|
|
56
|
+
* denial was mapped to once's code) — do not ask the server again.
|
|
57
|
+
*/
|
|
58
|
+
function isObserverShapedDatabaseError(error: unknown): boolean {
|
|
59
|
+
const code = (error as { code?: string } | null | undefined)?.code;
|
|
60
|
+
return typeof code === 'string' && code.startsWith('database/') && code !== 'database/unknown';
|
|
61
|
+
}
|
|
62
|
+
|
|
48
63
|
type DatabaseReferenceConstructor = new (
|
|
49
64
|
database: DatabaseInternal,
|
|
50
65
|
path: string,
|
|
@@ -316,6 +331,47 @@ export default class DatabaseQuery extends ReferenceBase implements Query {
|
|
|
316
331
|
return callback as (a: DataSnapshot | null, b?: string | null) => void;
|
|
317
332
|
}
|
|
318
333
|
|
|
334
|
+
/**
|
|
335
|
+
* One-shot read backing modular `get()`: on Android and web, the SDKs' single request/response
|
|
336
|
+
* read (`Query.get()`, web `get()`) instead of the listen -> data -> unlisten exchange
|
|
337
|
+
* `once('value')` performs. On iOS the native `get` is still the single-event observer; see
|
|
338
|
+
* `RNFBDatabaseQueryHelper get:`.
|
|
339
|
+
*
|
|
340
|
+
* Callers keep `once('value')` semantics, with one exception noted below:
|
|
341
|
+
* - iOS failures pass straight through (native get already is `once`).
|
|
342
|
+
* - On Android / web, an error whose code is already a specific `database/*` value (anything
|
|
343
|
+
* other than `database/unknown`) passes through: Android maps Query.get() permission denials
|
|
344
|
+
* to the same code `once` gives, and when native get already used `addOnceValueEventListener`
|
|
345
|
+
* (persistence, logging, keepSynced) its cancellations are those codes too — do not call
|
|
346
|
+
* `once('value')` again. Only a plain Query.get() failure is rejected as `database/unknown`
|
|
347
|
+
* (SharedUtils.getExceptionMap); that path still retries as `once('value')`.
|
|
348
|
+
* - Queries (any orderBy / limit / range modifier) use `once('value')`: `get()` parity for
|
|
349
|
+
* unindexed queries is unverified.
|
|
350
|
+
* - `.info/*` paths use `once('value')`: the one-shot reads go to the server tree, not the
|
|
351
|
+
* client-local `.info` tree.
|
|
352
|
+
*
|
|
353
|
+
* The exception: the one-shot read resolves with the server's reply as it was when the read was
|
|
354
|
+
* sent, where `once('value')` layered this client's pending writes over it. A local write made to
|
|
355
|
+
* an overlapping path while a `get()` is in flight is therefore not in its result, as with the
|
|
356
|
+
* web SDK's `get()`.
|
|
357
|
+
*/
|
|
358
|
+
_get(): Promise<DataSnapshot> {
|
|
359
|
+
const modifiers = this._modifiers._copy().toArray();
|
|
360
|
+
if (modifiers.length > 0 || INFO_PATH.test(this.path)) {
|
|
361
|
+
return this.once('value');
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return this._database.native.get(this.path, modifiers).then(
|
|
365
|
+
result => new DatabaseDataSnapshot(this.ref, result),
|
|
366
|
+
error => {
|
|
367
|
+
if (isIOS || isObserverShapedDatabaseError(error)) {
|
|
368
|
+
return Promise.reject(error);
|
|
369
|
+
}
|
|
370
|
+
return this.once('value');
|
|
371
|
+
},
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
|
|
319
375
|
once(
|
|
320
376
|
eventType: EventType,
|
|
321
377
|
successCallBack?: (a: DataSnapshot, b?: string | null) => any,
|
package/lib/DatabaseSyncTree.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { isString } from '@react-native-firebase/app/dist/module/common';
|
|
19
19
|
import NativeError from '@react-native-firebase/app/dist/module/internal/NativeFirebaseError';
|
|
20
20
|
import SharedEventEmitter from '@react-native-firebase/app/dist/module/internal/SharedEventEmitter';
|
|
21
|
-
import { getReactNativeModule } from '@react-native-firebase/app/dist/module/internal
|
|
21
|
+
import { getReactNativeModule } from '@react-native-firebase/app/dist/module/internal';
|
|
22
22
|
import DatabaseDataSnapshot from './DatabaseDataSnapshot';
|
|
23
23
|
import type {
|
|
24
24
|
DatabaseChildSnapshotResultInternal,
|
package/lib/index.ts
CHANGED
|
@@ -25,9 +25,9 @@ import type { FirebaseApp } from '@react-native-firebase/app';
|
|
|
25
25
|
import {
|
|
26
26
|
FirebaseModule,
|
|
27
27
|
getOrCreateModularInstance,
|
|
28
|
+
setReactNativeModule,
|
|
28
29
|
} from '@react-native-firebase/app/dist/module/internal';
|
|
29
30
|
import type { ModuleConfig } from '@react-native-firebase/app/dist/module/internal';
|
|
30
|
-
import { setReactNativeModule } from '@react-native-firebase/app/dist/module/internal/nativeModule';
|
|
31
31
|
import type { ReactNativeFirebase } from '@react-native-firebase/app';
|
|
32
32
|
import type {
|
|
33
33
|
DatabaseInternal,
|
package/lib/modular/query.ts
CHANGED
|
@@ -318,7 +318,7 @@ export function setWithPriority(
|
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
export function get(queryRef: Query): Promise<DataSnapshot> {
|
|
321
|
-
return (queryRef as QueryWithSubscriptionMethodsInternal).
|
|
321
|
+
return (queryRef as QueryWithSubscriptionMethodsInternal)._get();
|
|
322
322
|
}
|
|
323
323
|
|
|
324
324
|
export function off(
|