@callstack/brownie 0.0.1
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/Brownie.podspec +30 -0
- package/ios/BrownieFollyConvert.h +17 -0
- package/ios/BrownieModule.h +9 -0
- package/ios/BrownieModule.mm +55 -0
- package/ios/BrownieStore.swift +221 -0
- package/ios/BrownieStoreBridge.h +17 -0
- package/ios/BrownieStoreBridge.mm +72 -0
- package/ios/Generated/BrownfieldStore.swift +42 -0
- package/ios/Generated/SettingsStore.swift +33 -0
- package/lib/commonjs/NativeBrownieModule.js +2 -0
- package/lib/commonjs/NativeBrownieModule.js.map +1 -0
- package/lib/commonjs/index.js +2 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/scripts/brownfield.js +3 -0
- package/lib/commonjs/scripts/brownfield.js.map +1 -0
- package/lib/module/NativeBrownieModule.js +2 -0
- package/lib/module/NativeBrownieModule.js.map +1 -0
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/scripts/brownfield.js +3 -0
- package/lib/module/scripts/brownfield.js.map +1 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/commonjs/src/NativeBrownieModule.d.ts +10 -0
- package/lib/typescript/commonjs/src/NativeBrownieModule.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/index.d.ts +33 -0
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/scripts/brownfield.d.ts +3 -0
- package/lib/typescript/commonjs/src/scripts/brownfield.d.ts.map +1 -0
- package/lib/typescript/module/package.json +1 -0
- package/lib/typescript/module/src/NativeBrownieModule.d.ts +10 -0
- package/lib/typescript/module/src/NativeBrownieModule.d.ts.map +1 -0
- package/lib/typescript/module/src/index.d.ts +33 -0
- package/lib/typescript/module/src/index.d.ts.map +1 -0
- package/lib/typescript/module/src/scripts/brownfield.d.ts +3 -0
- package/lib/typescript/module/src/scripts/brownfield.d.ts.map +1 -0
- package/package.json +101 -0
- package/src/NativeBrownieModule.ts +11 -0
- package/src/index.ts +127 -0
- package/src/scripts/brownfield.ts +5 -0
package/Brownie.podspec
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |spec|
|
|
6
|
+
spec.name = "Brownie"
|
|
7
|
+
spec.version = package['version']
|
|
8
|
+
spec.summary = package['description']
|
|
9
|
+
spec.license = package['license']
|
|
10
|
+
|
|
11
|
+
spec.authors = package['author']
|
|
12
|
+
spec.homepage = package['homepage']
|
|
13
|
+
spec.platform = :ios, "14.0"
|
|
14
|
+
|
|
15
|
+
spec.module_name = "Brownie"
|
|
16
|
+
spec.source = { :git => "git@github.com:callstack/react-native-brownfield.git", :tag => "#{spec.version}" }
|
|
17
|
+
spec.source_files = [
|
|
18
|
+
"ios/**/*.{h,m,mm,swift}",
|
|
19
|
+
"cpp/**/*.{h,cpp}"
|
|
20
|
+
]
|
|
21
|
+
spec.private_header_files = "cpp/**/*.h"
|
|
22
|
+
|
|
23
|
+
spec.pod_target_xcconfig = {
|
|
24
|
+
'DEFINES_MODULE' => 'YES',
|
|
25
|
+
'CLANG_CXX_LANGUAGE_STANDARD' => 'c++20',
|
|
26
|
+
'HEADER_SEARCH_PATHS' => '$(inherited) "${PODS_ROOT}/boost" "${PODS_ROOT}/RCT-Folly" "${PODS_TARGET_SRCROOT}/cpp"'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
install_modules_dependencies(spec)
|
|
30
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#ifdef __cplusplus
|
|
4
|
+
|
|
5
|
+
#if __has_include(<react/utils/FollyConvert.h>)
|
|
6
|
+
// static libs / header maps (no use_frameworks!)
|
|
7
|
+
#import <react/utils/FollyConvert.h>
|
|
8
|
+
#elif __has_include("FollyConvert.h")
|
|
9
|
+
/// `use_frameworks! :linkage => :static` users will need to import FollyConvert this way
|
|
10
|
+
#import "FollyConvert.h"
|
|
11
|
+
#elif __has_include("RCTFollyConvert.h")
|
|
12
|
+
#import "RCTFollyConvert.h"
|
|
13
|
+
#else
|
|
14
|
+
#error "FollyConvert.h not found. Ensure React-utils & RCT-Folly pods are installed."
|
|
15
|
+
#endif
|
|
16
|
+
|
|
17
|
+
#endif
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#import "BrownieModule.h"
|
|
2
|
+
#import <jsi/jsi.h>
|
|
3
|
+
|
|
4
|
+
#if __has_include("Brownie/Brownie-Swift.h")
|
|
5
|
+
#import "Brownie/Brownie-Swift.h"
|
|
6
|
+
#else
|
|
7
|
+
#import "Brownie-Swift.h"
|
|
8
|
+
#endif
|
|
9
|
+
|
|
10
|
+
#import "BrownieInstaller.h"
|
|
11
|
+
|
|
12
|
+
using namespace facebook;
|
|
13
|
+
|
|
14
|
+
@interface BrownieModule (JSIBindings) <RCTTurboModuleWithJSIBindings>
|
|
15
|
+
@end
|
|
16
|
+
|
|
17
|
+
@implementation BrownieModule {
|
|
18
|
+
NSMutableDictionary<NSString *, NSObject *> *_notificationObservers;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
RCT_EXPORT_MODULE(Brownie);
|
|
22
|
+
|
|
23
|
+
- (instancetype)init {
|
|
24
|
+
self = [super init];
|
|
25
|
+
if (self) {
|
|
26
|
+
_notificationObservers = [NSMutableDictionary new];
|
|
27
|
+
[[NSNotificationCenter defaultCenter]
|
|
28
|
+
addObserver:self
|
|
29
|
+
selector:@selector(handleNotification:)
|
|
30
|
+
name:@"BrownieStoreUpdated"
|
|
31
|
+
object:nil];
|
|
32
|
+
}
|
|
33
|
+
return self;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
- (void)dealloc {
|
|
37
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
- (void)handleNotification:(NSNotification *)notification {
|
|
41
|
+
NSDictionary *userInfo = notification.userInfo ?: @{};
|
|
42
|
+
[self emitNativeStoreDidChange:userInfo];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
- (void)installJSIBindingsWithRuntime:(facebook::jsi::Runtime &)runtime
|
|
46
|
+
callInvoker:(const std::shared_ptr<facebook::react::CallInvoker> &)callinvoker {
|
|
47
|
+
brownie::BrownieInstaller::install(runtime);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
51
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params {
|
|
52
|
+
return std::make_shared<facebook::react::NativeBrownieModuleSpecJSI>(params);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@end
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Combine
|
|
3
|
+
import SwiftUI
|
|
4
|
+
|
|
5
|
+
extension Notification.Name {
|
|
6
|
+
public static let BrownieStoreUpdated = Notification.Name("BrownieStoreUpdated")
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
public protocol BrownieStoreProtocol: Codable {
|
|
10
|
+
static var storeName: String { get }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public extension BrownieStoreProtocol {
|
|
14
|
+
/// Registers the store with the given initial state.
|
|
15
|
+
static func register(_ initialState: Self) {
|
|
16
|
+
_ = Store(initialState, key: Self.storeName)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public struct StoreKey<State: Codable>: EnvironmentKey {
|
|
21
|
+
public static var defaultValue: Store<State> { fatalError("Store not provided") }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public extension EnvironmentValues {
|
|
25
|
+
func store<State>(_ type: State.Type) -> Store<State> {
|
|
26
|
+
self[StoreKey<State>.self]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public extension Binding {
|
|
31
|
+
/// Set value using closure that receives current value
|
|
32
|
+
func set(_ updater: (Value) -> Value) {
|
|
33
|
+
wrappedValue = updater(wrappedValue)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@MainActor
|
|
38
|
+
@propertyWrapper
|
|
39
|
+
public struct UseStore<State: BrownieStoreProtocol, Value: Equatable>: DynamicProperty {
|
|
40
|
+
@StateObject private var observer: SelectorObserver<State, Value>
|
|
41
|
+
private let keyPath: WritableKeyPath<State, Value>
|
|
42
|
+
|
|
43
|
+
public init(_ keyPath: WritableKeyPath<State, Value>) {
|
|
44
|
+
self.keyPath = keyPath
|
|
45
|
+
let key = State.storeName
|
|
46
|
+
guard let foundStore = StoreManager.shared.store(key: key, as: State.self) else {
|
|
47
|
+
fatalError("Store not found for key: \(key)")
|
|
48
|
+
}
|
|
49
|
+
self._observer = StateObject(wrappedValue: SelectorObserver(store: foundStore, keyPath: keyPath))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public var wrappedValue: Value {
|
|
53
|
+
observer.value
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public var projectedValue: Binding<Value> {
|
|
57
|
+
Binding(
|
|
58
|
+
get: { observer.store.get(keyPath) },
|
|
59
|
+
set: { observer.store.set(keyPath, to: $0) }
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/// Internal observer that only publishes when selected value changes.
|
|
65
|
+
@MainActor
|
|
66
|
+
class SelectorObserver<State: Codable, Value: Equatable>: ObservableObject {
|
|
67
|
+
let store: Store<State>
|
|
68
|
+
private let keyPath: KeyPath<State, Value>
|
|
69
|
+
@Published private(set) var value: Value
|
|
70
|
+
private var cancellable: AnyCancellable?
|
|
71
|
+
|
|
72
|
+
init(store: Store<State>, keyPath: KeyPath<State, Value>) {
|
|
73
|
+
self.store = store
|
|
74
|
+
self.keyPath = keyPath
|
|
75
|
+
self.value = store.state[keyPath: keyPath]
|
|
76
|
+
|
|
77
|
+
self.cancellable = store.$state
|
|
78
|
+
.map { $0[keyPath: keyPath] }
|
|
79
|
+
.removeDuplicates()
|
|
80
|
+
.sink { [weak self] newValue in
|
|
81
|
+
self?.value = newValue
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@objc public class StoreManager: NSObject {
|
|
87
|
+
@objc public static let shared = StoreManager()
|
|
88
|
+
|
|
89
|
+
private var stores: [String: Any] = [:]
|
|
90
|
+
private let lock = NSLock()
|
|
91
|
+
|
|
92
|
+
public func register<State: Codable>(store: Store<State>, key: String) {
|
|
93
|
+
lock.lock()
|
|
94
|
+
defer { lock.unlock() }
|
|
95
|
+
stores[key] = store
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public func store<State>(key: String, as type: State.Type) -> Store<State>? {
|
|
99
|
+
lock.lock()
|
|
100
|
+
defer { lock.unlock() }
|
|
101
|
+
return stores[key] as? Store<State>
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public func removeStore(key: String) {
|
|
105
|
+
lock.lock()
|
|
106
|
+
defer { lock.unlock() }
|
|
107
|
+
stores.removeValue(forKey: key)
|
|
108
|
+
BrownieStoreBridge.removeStore(withKey: key)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@objc public func snapshot(key: String) -> [String: Any]? {
|
|
112
|
+
return BrownieStoreBridge.getSnapshot(forStore: key) as? [String: Any]
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@objc public func setValue(key: String, property: String, value: Any) {
|
|
116
|
+
BrownieStoreBridge.setValue(value, forKey: property, inStore: key)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public extension StoreManager {
|
|
121
|
+
static func get<State>(key: String, as type: State.Type) -> Store<State>? {
|
|
122
|
+
shared.store(key: key, as: type)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public class Store<State: Codable>: ObservableObject {
|
|
127
|
+
private let storeKey: String
|
|
128
|
+
@Published public private(set) var state: State
|
|
129
|
+
private var notificationObserver: NSObjectProtocol?
|
|
130
|
+
|
|
131
|
+
public init(_ initialState: State, key: String) {
|
|
132
|
+
self.storeKey = key
|
|
133
|
+
self.state = initialState
|
|
134
|
+
|
|
135
|
+
BrownieStoreBridge.registerStore(withKey: key)
|
|
136
|
+
pushStateToCxx()
|
|
137
|
+
|
|
138
|
+
notificationObserver = NotificationCenter.default.addObserver(
|
|
139
|
+
forName: .BrownieStoreUpdated,
|
|
140
|
+
object: nil,
|
|
141
|
+
queue: .main
|
|
142
|
+
) { [weak self] notification in
|
|
143
|
+
self?.handleStoreUpdate(notification)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
StoreManager.shared.register(store: self, key: key)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
deinit {
|
|
150
|
+
if let observer = notificationObserver {
|
|
151
|
+
NotificationCenter.default.removeObserver(observer)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private func pushStateToCxx() {
|
|
156
|
+
guard let data = try? JSONEncoder().encode(state),
|
|
157
|
+
let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { return }
|
|
158
|
+
|
|
159
|
+
BrownieStoreBridge.setState(from: dict, forStore: storeKey)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private func handleStoreUpdate(_ notification: Notification) {
|
|
163
|
+
guard let updatedKey = notification.userInfo?["storeKey"] as? String,
|
|
164
|
+
updatedKey == storeKey else { return }
|
|
165
|
+
rebuildState()
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private func rebuildState() {
|
|
169
|
+
guard let snapshot = BrownieStoreBridge.getSnapshot(forStore: storeKey),
|
|
170
|
+
let data = try? JSONSerialization.data(withJSONObject: snapshot),
|
|
171
|
+
let newState = try? JSONDecoder().decode(State.self, from: data) else { return }
|
|
172
|
+
|
|
173
|
+
state = newState
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/// Update state using a closure
|
|
177
|
+
public func set(_ updater: (inout State) -> Void) {
|
|
178
|
+
updater(&state)
|
|
179
|
+
pushStateToCxx()
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/// Set property using type-safe keypath
|
|
183
|
+
public func set<Value>(_ keyPath: WritableKeyPath<State, Value>, to value: Value) {
|
|
184
|
+
state[keyPath: keyPath] = value
|
|
185
|
+
pushStateToCxx()
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/// Get property using type-safe keypath
|
|
189
|
+
public func get<Value>(_ keyPath: KeyPath<State, Value>) -> Value {
|
|
190
|
+
state[keyPath: keyPath]
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/// Convenience subscript for property access
|
|
194
|
+
public subscript<Value>(_ keyPath: KeyPath<State, Value>) -> Value {
|
|
195
|
+
state[keyPath: keyPath]
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// MARK: - UIKit Support
|
|
199
|
+
|
|
200
|
+
/// Subscribe to state changes with a closure. Returns a cancellation function.
|
|
201
|
+
public func subscribe(onChange: @escaping (State) -> Void) -> () -> Void {
|
|
202
|
+
let cancellable = $state.sink { state in
|
|
203
|
+
onChange(state)
|
|
204
|
+
}
|
|
205
|
+
return { cancellable.cancel() }
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/// Subscribe to specific property changes. Returns a cancellation function.
|
|
209
|
+
public func subscribe<Value: Equatable>(
|
|
210
|
+
_ keyPath: KeyPath<State, Value>,
|
|
211
|
+
onChange: @escaping (Value) -> Void
|
|
212
|
+
) -> () -> Void {
|
|
213
|
+
let cancellable = $state
|
|
214
|
+
.map { $0[keyPath: keyPath] }
|
|
215
|
+
.removeDuplicates()
|
|
216
|
+
.sink { value in
|
|
217
|
+
onChange(value)
|
|
218
|
+
}
|
|
219
|
+
return { cancellable.cancel() }
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
|
|
3
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
4
|
+
|
|
5
|
+
@interface BrownieStoreBridge : NSObject
|
|
6
|
+
|
|
7
|
+
+ (void)registerStoreWithKey:(NSString *)key NS_SWIFT_NAME(registerStore(withKey:));
|
|
8
|
+
+ (void)removeStoreWithKey:(NSString *)key NS_SWIFT_NAME(removeStore(withKey:));
|
|
9
|
+
|
|
10
|
+
+ (void)setValue:(id)value forKey:(NSString *)propKey inStore:(NSString *)storeKey NS_SWIFT_NAME(setValue(_:forKey:inStore:));
|
|
11
|
+
+ (nullable id)getValueForKey:(NSString *)propKey inStore:(NSString *)storeKey NS_SWIFT_NAME(getValue(forKey:inStore:));
|
|
12
|
+
+ (nullable NSDictionary *)getSnapshotForStore:(NSString *)storeKey NS_SWIFT_NAME(getSnapshot(forStore:));
|
|
13
|
+
+ (void)setStateFromDictionary:(NSDictionary *)dict forStore:(NSString *)storeKey NS_SWIFT_NAME(setState(from:forStore:));
|
|
14
|
+
|
|
15
|
+
@end
|
|
16
|
+
|
|
17
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#import "BrownieStoreBridge.h"
|
|
2
|
+
#import "BrownieStoreManager.h"
|
|
3
|
+
#import "BrownieStore.h"
|
|
4
|
+
#import "BrownieFollyConvert.h"
|
|
5
|
+
|
|
6
|
+
using namespace facebook::react;
|
|
7
|
+
|
|
8
|
+
@implementation BrownieStoreBridge
|
|
9
|
+
|
|
10
|
+
+ (void)registerStoreWithKey:(NSString *)key {
|
|
11
|
+
auto store = std::make_shared<brownie::BrownieStore>();
|
|
12
|
+
NSString *keyCopy = [key copy];
|
|
13
|
+
|
|
14
|
+
store->setChangeCallback([keyCopy]() {
|
|
15
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
16
|
+
[[NSNotificationCenter defaultCenter]
|
|
17
|
+
postNotificationName:@"BrownieStoreUpdated"
|
|
18
|
+
object:nil
|
|
19
|
+
userInfo:@{@"storeKey" : keyCopy}];
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
brownie::BrownieStoreManager::shared().registerStore(std::string([key UTF8String]),
|
|
24
|
+
store);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
+ (void)removeStoreWithKey:(NSString *)key {
|
|
28
|
+
brownie::BrownieStoreManager::shared().removeStore(std::string([key UTF8String]));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
+ (void)setValue:(id)value forKey:(NSString *)propKey inStore:(NSString *)storeKey {
|
|
32
|
+
auto store =
|
|
33
|
+
brownie::BrownieStoreManager::shared().getStore(std::string([storeKey UTF8String]));
|
|
34
|
+
if (!store) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
store->set(std::string([propKey UTF8String]), convertIdToFollyDynamic(value));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
+ (nullable id)getValueForKey:(NSString *)propKey inStore:(NSString *)storeKey {
|
|
42
|
+
auto store =
|
|
43
|
+
brownie::BrownieStoreManager::shared().getStore(std::string([storeKey UTF8String]));
|
|
44
|
+
if (!store) {
|
|
45
|
+
return nil;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return convertFollyDynamicToId(store->get(std::string([propKey UTF8String])));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
+ (nullable NSDictionary *)getSnapshotForStore:(NSString *)storeKey {
|
|
52
|
+
auto store =
|
|
53
|
+
brownie::BrownieStoreManager::shared().getStore(std::string([storeKey UTF8String]));
|
|
54
|
+
if (!store) {
|
|
55
|
+
return nil;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
id result = convertFollyDynamicToId(store->getSnapshot());
|
|
59
|
+
return [result isKindOfClass:[NSDictionary class]] ? result : nil;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
+ (void)setStateFromDictionary:(NSDictionary *)dict forStore:(NSString *)storeKey {
|
|
63
|
+
auto store =
|
|
64
|
+
brownie::BrownieStoreManager::shared().getStore(std::string([storeKey UTF8String]));
|
|
65
|
+
if (!store) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
store->setState(convertIdToFollyDynamic(dict));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// This file was generated from JSON Schema using quicktype, do not modify it directly.
|
|
2
|
+
// To parse the JSON, add this file to your project and do:
|
|
3
|
+
//
|
|
4
|
+
// let brownfieldStore = try? JSONDecoder().decode(BrownfieldStore.self, from: jsonData)
|
|
5
|
+
|
|
6
|
+
//
|
|
7
|
+
// Hashable or Equatable:
|
|
8
|
+
// The compiler will not be able to synthesize the implementation of Hashable or Equatable
|
|
9
|
+
// for types that require the use of JSONAny, nor will the implementation of Hashable be
|
|
10
|
+
// synthesized for types that have collections (such as arrays or dictionaries).
|
|
11
|
+
|
|
12
|
+
import Foundation
|
|
13
|
+
|
|
14
|
+
// MARK: - BrownfieldStore
|
|
15
|
+
public struct BrownfieldStore: Codable, Equatable {
|
|
16
|
+
public var counter: Double
|
|
17
|
+
public var user: User
|
|
18
|
+
|
|
19
|
+
public init(counter: Double, user: User) {
|
|
20
|
+
self.counter = counter
|
|
21
|
+
self.user = user
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
//
|
|
26
|
+
// Hashable or Equatable:
|
|
27
|
+
// The compiler will not be able to synthesize the implementation of Hashable or Equatable
|
|
28
|
+
// for types that require the use of JSONAny, nor will the implementation of Hashable be
|
|
29
|
+
// synthesized for types that have collections (such as arrays or dictionaries).
|
|
30
|
+
|
|
31
|
+
// MARK: - User
|
|
32
|
+
public struct User: Codable, Equatable {
|
|
33
|
+
public var name: String
|
|
34
|
+
|
|
35
|
+
public init(name: String) {
|
|
36
|
+
self.name = name
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
extension BrownfieldStore: BrownieStoreProtocol {
|
|
41
|
+
public static let storeName = "BrownfieldStore"
|
|
42
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// This file was generated from JSON Schema using quicktype, do not modify it directly.
|
|
2
|
+
// To parse the JSON, add this file to your project and do:
|
|
3
|
+
//
|
|
4
|
+
// let settingsStore = try? JSONDecoder().decode(SettingsStore.self, from: jsonData)
|
|
5
|
+
|
|
6
|
+
//
|
|
7
|
+
// Hashable or Equatable:
|
|
8
|
+
// The compiler will not be able to synthesize the implementation of Hashable or Equatable
|
|
9
|
+
// for types that require the use of JSONAny, nor will the implementation of Hashable be
|
|
10
|
+
// synthesized for types that have collections (such as arrays or dictionaries).
|
|
11
|
+
|
|
12
|
+
import Foundation
|
|
13
|
+
|
|
14
|
+
// MARK: - SettingsStore
|
|
15
|
+
public struct SettingsStore: Codable, Equatable {
|
|
16
|
+
public var notificationsEnabled, privacyMode: Bool
|
|
17
|
+
public var theme: Theme
|
|
18
|
+
|
|
19
|
+
public init(notificationsEnabled: Bool, privacyMode: Bool, theme: Theme) {
|
|
20
|
+
self.notificationsEnabled = notificationsEnabled
|
|
21
|
+
self.privacyMode = privacyMode
|
|
22
|
+
self.theme = theme
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public enum Theme: String, Codable, Equatable {
|
|
27
|
+
case dark = "dark"
|
|
28
|
+
case light = "light"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
extension SettingsStore: BrownieStoreProtocol {
|
|
32
|
+
public static let storeName = "SettingsStore"
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_reactNative","require","_default","exports","default","TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeBrownieModule.ts"],"mappings":"gFACA,IAAAA,YAAA,CAAAC,OAAA,iBAAmD,IAAAC,QAAA,CAAAC,OAAA,CAAAC,OAAA,CASpCC,gCAAmB,CAACC,YAAY,CAAO,SAAS,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault");Object.defineProperty(exports,"__esModule",{value:true});exports.getSnapshot=getSnapshot;exports.setState=setState;exports.subscribe=subscribe;exports.useStore=useStore;var _slicedToArray2=_interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));var _react=require("react");var _NativeBrownieModule=_interopRequireDefault(require("./NativeBrownieModule"));var stores=new Map();function getHostObject(key){return global.__brownieGetStore==null?void 0:global.__brownieGetStore(key);}function getOrCreateStore(key){var store=stores.get(key);if(!store){var _hostObject$unbox;var hostObject=getHostObject(key);store={hostObject:hostObject,snapshot:(_hostObject$unbox=hostObject==null||hostObject.unbox==null?void 0:hostObject.unbox())!=null?_hostObject$unbox:{},listeners:new Set()};stores.set(key,store);}return store;}function refreshSnapshot(key){var store=stores.get(key);if(store){var _store$hostObject$unb,_store$hostObject;store.snapshot=(_store$hostObject$unb=(_store$hostObject=store.hostObject)==null||_store$hostObject.unbox==null?void 0:_store$hostObject.unbox())!=null?_store$hostObject$unb:{};store.listeners.forEach(function(listener){return listener();});}}_NativeBrownieModule.default.nativeStoreDidChange(function(){stores.forEach(function(_,key){return refreshSnapshot(key);});});function subscribe(key,listener){var store=getOrCreateStore(key);store.listeners.add(listener);return function(){return store.listeners.delete(listener);};}function getSnapshot(key){var store=getOrCreateStore(key);return store.snapshot;}function setState(key,action){var store=getOrCreateStore(key);if(!store.hostObject)return;var partial=typeof action==='function'?action(store.snapshot):action;for(var _ref of Object.entries(partial)){var _ref2=(0,_slicedToArray2.default)(_ref,2);var prop=_ref2[0];var value=_ref2[1];store.hostObject[prop]=value;}}function useStore(key,selector){var sub=(0,_react.useCallback)(function(listener){return subscribe(key,listener);},[key]);var snap=(0,_react.useCallback)(function(){return selector(getSnapshot(key));},[key,selector]);var slice=(0,_react.useSyncExternalStore)(sub,snap,snap);(0,_react.useDebugValue)(slice);var boundSetState=(0,_react.useCallback)(function(action){return setState(key,action);},[key]);return[slice,boundSetState];}
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_react","require","_NativeBrownieModule","_interopRequireDefault","stores","Map","getHostObject","key","global","__brownieGetStore","getOrCreateStore","store","get","_hostObject$unbox","hostObject","snapshot","unbox","listeners","Set","set","refreshSnapshot","_store$hostObject$unb","_store$hostObject","forEach","listener","BrownieModule","nativeStoreDidChange","_","subscribe","add","delete","getSnapshot","setState","action","partial","_ref","Object","entries","_ref2","_slicedToArray2","default","prop","value","useStore","selector","sub","useCallback","snap","slice","useSyncExternalStore","useDebugValue","boundSetState"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_brownfieldCli","require","runCLI","process","argv"],"sourceRoot":"../../../src","sources":["scripts/brownfield.ts"],"mappings":"AAAA;AAEA,IAAAA,cAAA,CAAAC,OAAA,8BAEA,GAAAC,qBAAM,EAACC,OAAO,CAACC,IAAI,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_reactNative","require","_default","exports","default","TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeBrownieModule.ts"],"mappings":"gFACA,IAAAA,YAAA,CAAAC,OAAA,iBAAmD,IAAAC,QAAA,CAAAC,OAAA,CAAAC,OAAA,CASpCC,gCAAmB,CAACC,YAAY,CAAO,SAAS,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault");Object.defineProperty(exports,"__esModule",{value:true});exports.getSnapshot=getSnapshot;exports.setState=setState;exports.subscribe=subscribe;exports.useStore=useStore;var _slicedToArray2=_interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));var _react=require("react");var _NativeBrownieModule=_interopRequireDefault(require("./NativeBrownieModule"));var stores=new Map();function getHostObject(key){return global.__brownieGetStore==null?void 0:global.__brownieGetStore(key);}function getOrCreateStore(key){var store=stores.get(key);if(!store){var _hostObject$unbox;var hostObject=getHostObject(key);store={hostObject:hostObject,snapshot:(_hostObject$unbox=hostObject==null||hostObject.unbox==null?void 0:hostObject.unbox())!=null?_hostObject$unbox:{},listeners:new Set()};stores.set(key,store);}return store;}function refreshSnapshot(key){var store=stores.get(key);if(store){var _store$hostObject$unb,_store$hostObject;store.snapshot=(_store$hostObject$unb=(_store$hostObject=store.hostObject)==null||_store$hostObject.unbox==null?void 0:_store$hostObject.unbox())!=null?_store$hostObject$unb:{};store.listeners.forEach(function(listener){return listener();});}}_NativeBrownieModule.default.nativeStoreDidChange(function(){stores.forEach(function(_,key){return refreshSnapshot(key);});});function subscribe(key,listener){var store=getOrCreateStore(key);store.listeners.add(listener);return function(){return store.listeners.delete(listener);};}function getSnapshot(key){var store=getOrCreateStore(key);return store.snapshot;}function setState(key,action){var store=getOrCreateStore(key);if(!store.hostObject)return;var partial=typeof action==='function'?action(store.snapshot):action;for(var _ref of Object.entries(partial)){var _ref2=(0,_slicedToArray2.default)(_ref,2);var prop=_ref2[0];var value=_ref2[1];store.hostObject[prop]=value;}}function useStore(key,selector){var sub=(0,_react.useCallback)(function(listener){return subscribe(key,listener);},[key]);var snap=(0,_react.useCallback)(function(){return selector(getSnapshot(key));},[key,selector]);var slice=(0,_react.useSyncExternalStore)(sub,snap,snap);(0,_react.useDebugValue)(slice);var boundSetState=(0,_react.useCallback)(function(action){return setState(key,action);},[key]);return[slice,boundSetState];}
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_react","require","_NativeBrownieModule","_interopRequireDefault","stores","Map","getHostObject","key","global","__brownieGetStore","getOrCreateStore","store","get","_hostObject$unbox","hostObject","snapshot","unbox","listeners","Set","set","refreshSnapshot","_store$hostObject$unb","_store$hostObject","forEach","listener","BrownieModule","nativeStoreDidChange","_","subscribe","add","delete","getSnapshot","setState","action","partial","_ref","Object","entries","_ref2","_slicedToArray2","default","prop","value","useStore","selector","sub","useCallback","snap","slice","useSyncExternalStore","useDebugValue","boundSetState"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_brownfieldCli","require","runCLI","process","argv"],"sourceRoot":"../../../src","sources":["scripts/brownfield.ts"],"mappings":"AAAA;AAEA,IAAAA,cAAA,CAAAC,OAAA,8BAEA,GAAAC,qBAAM,EAACC,OAAO,CAACC,IAAI,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TurboModule, CodegenTypes } from 'react-native';
|
|
2
|
+
export interface Spec extends TurboModule {
|
|
3
|
+
nativeStoreDidChange: CodegenTypes.EventEmitter<{
|
|
4
|
+
key: string;
|
|
5
|
+
value: string;
|
|
6
|
+
}>;
|
|
7
|
+
}
|
|
8
|
+
declare const _default: Spec;
|
|
9
|
+
export default _default;
|
|
10
|
+
//# sourceMappingURL=NativeBrownieModule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NativeBrownieModule.d.ts","sourceRoot":"","sources":["../../../../src/NativeBrownieModule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG9D,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,oBAAoB,EAAE,YAAY,CAAC,YAAY,CAAC;QAC9C,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;CACJ;;AAED,wBAAiE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Empty interface for module augmentation.
|
|
3
|
+
* Users extend this in their *.brownie.ts files.
|
|
4
|
+
*/
|
|
5
|
+
export interface BrownieStores {
|
|
6
|
+
}
|
|
7
|
+
export interface BrownieStore {
|
|
8
|
+
}
|
|
9
|
+
type StoreListener = () => void;
|
|
10
|
+
/**
|
|
11
|
+
* Subscribe to store changes from native side.
|
|
12
|
+
* @returns Unsubscribe function
|
|
13
|
+
*/
|
|
14
|
+
export declare function subscribe<K extends keyof BrownieStores>(key: K, listener: StoreListener): () => void;
|
|
15
|
+
/**
|
|
16
|
+
* Get current store state snapshot.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getSnapshot<K extends keyof BrownieStores>(key: K): BrownieStores[K];
|
|
19
|
+
type SetStateAction<T> = Partial<T> | ((prevState: T) => Partial<T>);
|
|
20
|
+
/**
|
|
21
|
+
* Set a value in the native store.
|
|
22
|
+
*/
|
|
23
|
+
export declare function setState<K extends keyof BrownieStores>(key: K, action: SetStateAction<BrownieStores[K]>): void;
|
|
24
|
+
/**
|
|
25
|
+
* React hook for subscribing to a native store with selector.
|
|
26
|
+
* Inspired by Zustand's useStore implementation.
|
|
27
|
+
* @param key Store key registered in StoreManager
|
|
28
|
+
* @param selector Function to select a slice of state
|
|
29
|
+
* @returns Tuple of [selectedState, setState] for the store
|
|
30
|
+
*/
|
|
31
|
+
export declare function useStore<K extends keyof BrownieStores, U>(key: K, selector: (state: BrownieStores[K]) => U): [U, (action: SetStateAction<BrownieStores[K]>) => void];
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,WAAW,aAAa;CAAG;AAEjC,MAAM,WAAW,YAAY;CAAG;AAEhC,KAAK,aAAa,GAAG,MAAM,IAAI,CAAC;AA2ChC;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,aAAa,EACrD,GAAG,EAAE,CAAC,EACN,QAAQ,EAAE,aAAa,GACtB,MAAM,IAAI,CAIZ;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,aAAa,EACvD,GAAG,EAAE,CAAC,GACL,aAAa,CAAC,CAAC,CAAC,CAGlB;AAED,KAAK,cAAc,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAErE;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,aAAa,EACpD,GAAG,EAAE,CAAC,EACN,MAAM,EAAE,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,GACvC,IAAI,CAYN;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,aAAa,EAAE,CAAC,EACvD,GAAG,EAAE,CAAC,EACN,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,GACvC,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAiBzD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brownfield.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/brownfield.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TurboModule, CodegenTypes } from 'react-native';
|
|
2
|
+
export interface Spec extends TurboModule {
|
|
3
|
+
nativeStoreDidChange: CodegenTypes.EventEmitter<{
|
|
4
|
+
key: string;
|
|
5
|
+
value: string;
|
|
6
|
+
}>;
|
|
7
|
+
}
|
|
8
|
+
declare const _default: Spec;
|
|
9
|
+
export default _default;
|
|
10
|
+
//# sourceMappingURL=NativeBrownieModule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NativeBrownieModule.d.ts","sourceRoot":"","sources":["../../../../src/NativeBrownieModule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG9D,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,oBAAoB,EAAE,YAAY,CAAC,YAAY,CAAC;QAC9C,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;CACJ;;AAED,wBAAiE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Empty interface for module augmentation.
|
|
3
|
+
* Users extend this in their *.brownie.ts files.
|
|
4
|
+
*/
|
|
5
|
+
export interface BrownieStores {
|
|
6
|
+
}
|
|
7
|
+
export interface BrownieStore {
|
|
8
|
+
}
|
|
9
|
+
type StoreListener = () => void;
|
|
10
|
+
/**
|
|
11
|
+
* Subscribe to store changes from native side.
|
|
12
|
+
* @returns Unsubscribe function
|
|
13
|
+
*/
|
|
14
|
+
export declare function subscribe<K extends keyof BrownieStores>(key: K, listener: StoreListener): () => void;
|
|
15
|
+
/**
|
|
16
|
+
* Get current store state snapshot.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getSnapshot<K extends keyof BrownieStores>(key: K): BrownieStores[K];
|
|
19
|
+
type SetStateAction<T> = Partial<T> | ((prevState: T) => Partial<T>);
|
|
20
|
+
/**
|
|
21
|
+
* Set a value in the native store.
|
|
22
|
+
*/
|
|
23
|
+
export declare function setState<K extends keyof BrownieStores>(key: K, action: SetStateAction<BrownieStores[K]>): void;
|
|
24
|
+
/**
|
|
25
|
+
* React hook for subscribing to a native store with selector.
|
|
26
|
+
* Inspired by Zustand's useStore implementation.
|
|
27
|
+
* @param key Store key registered in StoreManager
|
|
28
|
+
* @param selector Function to select a slice of state
|
|
29
|
+
* @returns Tuple of [selectedState, setState] for the store
|
|
30
|
+
*/
|
|
31
|
+
export declare function useStore<K extends keyof BrownieStores, U>(key: K, selector: (state: BrownieStores[K]) => U): [U, (action: SetStateAction<BrownieStores[K]>) => void];
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,WAAW,aAAa;CAAG;AAEjC,MAAM,WAAW,YAAY;CAAG;AAEhC,KAAK,aAAa,GAAG,MAAM,IAAI,CAAC;AA2ChC;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,aAAa,EACrD,GAAG,EAAE,CAAC,EACN,QAAQ,EAAE,aAAa,GACtB,MAAM,IAAI,CAIZ;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,aAAa,EACvD,GAAG,EAAE,CAAC,GACL,aAAa,CAAC,CAAC,CAAC,CAGlB;AAED,KAAK,cAAc,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAErE;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,aAAa,EACpD,GAAG,EAAE,CAAC,EACN,MAAM,EAAE,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,GACvC,IAAI,CAYN;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,aAAa,EAAE,CAAC,EACvD,GAAG,EAAE,CAAC,EACN,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,GACvC,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAiBzD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brownfield.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/brownfield.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@callstack/brownie",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"author": "Oskar Kwaśniewski <oskarkwasniewski@icloud.com>",
|
|
6
|
+
"bin": {
|
|
7
|
+
"brownfield": "./lib/commonjs/scripts/brownfield.js"
|
|
8
|
+
},
|
|
9
|
+
"contributors": [
|
|
10
|
+
"Artur Morys-Magiera <artus9033@gmail.com>",
|
|
11
|
+
"Oskar Kwasniewski <oskarkwasniewski@icloud.com>"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/callstack/react-native-brownfield",
|
|
14
|
+
"description": "Shared state management between React Native and Native apps",
|
|
15
|
+
"main": "lib/commonjs/index",
|
|
16
|
+
"module": "lib/module/index",
|
|
17
|
+
"types": "./lib/typescript/commonjs/src/index.d.ts",
|
|
18
|
+
"react-native": "src/index",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"source": "./src/index.ts",
|
|
22
|
+
"import": {
|
|
23
|
+
"types": "./lib/typescript/module/src/index.d.ts",
|
|
24
|
+
"default": "./lib/module/index.js"
|
|
25
|
+
},
|
|
26
|
+
"require": {
|
|
27
|
+
"types": "./lib/typescript/commonjs/src/index.d.ts",
|
|
28
|
+
"default": "./lib/commonjs/index.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"lint": "eslint .",
|
|
35
|
+
"typecheck": "tsc --noEmit",
|
|
36
|
+
"build": "bob build",
|
|
37
|
+
"dev": "nodemon --watch src --ext js,ts,json --exec \"bob build\""
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"brownie",
|
|
41
|
+
"react-native-brownfield",
|
|
42
|
+
"react native brownfield",
|
|
43
|
+
"native",
|
|
44
|
+
"shared state",
|
|
45
|
+
"react native integration"
|
|
46
|
+
],
|
|
47
|
+
"files": [
|
|
48
|
+
"src",
|
|
49
|
+
"lib",
|
|
50
|
+
"ios",
|
|
51
|
+
"*.podspec",
|
|
52
|
+
"!ios/build",
|
|
53
|
+
"!**/__tests__",
|
|
54
|
+
"!**/__fixtures__",
|
|
55
|
+
"!**/__mocks__",
|
|
56
|
+
"!**/.*"
|
|
57
|
+
],
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"access": "public",
|
|
60
|
+
"tag": "alpha"
|
|
61
|
+
},
|
|
62
|
+
"peerDependencies": {
|
|
63
|
+
"react": "*",
|
|
64
|
+
"react-native": "*"
|
|
65
|
+
},
|
|
66
|
+
"dependencies": {
|
|
67
|
+
"@callstack/brownfield-cli": "^1.0.0",
|
|
68
|
+
"quicktype-core": "^23.2.6",
|
|
69
|
+
"quicktype-typescript-input": "^23.2.6",
|
|
70
|
+
"ts-morph": "^27.0.2"
|
|
71
|
+
},
|
|
72
|
+
"devDependencies": {
|
|
73
|
+
"@babel/core": "^7.25.2",
|
|
74
|
+
"@babel/preset-env": "^7.25.3",
|
|
75
|
+
"@babel/preset-typescript": "^7.27.1",
|
|
76
|
+
"@babel/runtime": "^7.25.0",
|
|
77
|
+
"@react-native/babel-preset": "0.82.1",
|
|
78
|
+
"@react-native/eslint-config": "0.82.1",
|
|
79
|
+
"@types/node": "^25.0.8",
|
|
80
|
+
"@types/react": "^19.1.1",
|
|
81
|
+
"eslint": "^9.28.0",
|
|
82
|
+
"globals": "^16.2.0",
|
|
83
|
+
"import": "^0.0.6",
|
|
84
|
+
"nodemon": "^3.1.11",
|
|
85
|
+
"react": "19.1.1",
|
|
86
|
+
"react-native": "0.82.1",
|
|
87
|
+
"react-native-builder-bob": "^0.40.17",
|
|
88
|
+
"typescript": "5.9.3"
|
|
89
|
+
},
|
|
90
|
+
"codegenConfig": {
|
|
91
|
+
"name": "Brownie",
|
|
92
|
+
"type": "modules",
|
|
93
|
+
"jsSrcsDir": "./src",
|
|
94
|
+
"android": {
|
|
95
|
+
"javaPackageName": "com.callstack.brownie"
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"engines": {
|
|
99
|
+
"node": ">=20"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TurboModule, CodegenTypes } from 'react-native';
|
|
2
|
+
import { TurboModuleRegistry } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export interface Spec extends TurboModule {
|
|
5
|
+
nativeStoreDidChange: CodegenTypes.EventEmitter<{
|
|
6
|
+
key: string;
|
|
7
|
+
value: string;
|
|
8
|
+
}>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default TurboModuleRegistry.getEnforcing<Spec>('Brownie');
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { useCallback, useDebugValue, useSyncExternalStore } from 'react';
|
|
2
|
+
import BrownieModule from './NativeBrownieModule';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Empty interface for module augmentation.
|
|
6
|
+
* Users extend this in their *.brownie.ts files.
|
|
7
|
+
*/
|
|
8
|
+
export interface BrownieStores {}
|
|
9
|
+
|
|
10
|
+
export interface BrownieStore {}
|
|
11
|
+
|
|
12
|
+
type StoreListener = () => void;
|
|
13
|
+
|
|
14
|
+
type HostObject = any;
|
|
15
|
+
|
|
16
|
+
interface StoreCache {
|
|
17
|
+
hostObject: HostObject;
|
|
18
|
+
snapshot: Record<string, unknown>;
|
|
19
|
+
listeners: Set<StoreListener>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const stores = new Map<string, StoreCache>();
|
|
23
|
+
|
|
24
|
+
function getHostObject(key: string): HostObject {
|
|
25
|
+
// @ts-expect-error - untyped global prop set by BrownieInstaller.cpp
|
|
26
|
+
return global.__brownieGetStore?.(key);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getOrCreateStore(key: string): StoreCache {
|
|
30
|
+
let store = stores.get(key);
|
|
31
|
+
if (!store) {
|
|
32
|
+
const hostObject = getHostObject(key);
|
|
33
|
+
store = {
|
|
34
|
+
hostObject,
|
|
35
|
+
snapshot: hostObject?.unbox?.() ?? {},
|
|
36
|
+
listeners: new Set(),
|
|
37
|
+
};
|
|
38
|
+
stores.set(key, store);
|
|
39
|
+
}
|
|
40
|
+
return store;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function refreshSnapshot(key: string): void {
|
|
44
|
+
const store = stores.get(key);
|
|
45
|
+
if (store) {
|
|
46
|
+
store.snapshot = store.hostObject?.unbox?.() ?? {};
|
|
47
|
+
store.listeners.forEach((listener) => listener());
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
BrownieModule.nativeStoreDidChange(() => {
|
|
52
|
+
stores.forEach((_, key) => refreshSnapshot(key));
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Subscribe to store changes from native side.
|
|
57
|
+
* @returns Unsubscribe function
|
|
58
|
+
*/
|
|
59
|
+
export function subscribe<K extends keyof BrownieStores>(
|
|
60
|
+
key: K,
|
|
61
|
+
listener: StoreListener
|
|
62
|
+
): () => void {
|
|
63
|
+
const store = getOrCreateStore(key as string);
|
|
64
|
+
store.listeners.add(listener);
|
|
65
|
+
return () => store.listeners.delete(listener);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get current store state snapshot.
|
|
70
|
+
*/
|
|
71
|
+
export function getSnapshot<K extends keyof BrownieStores>(
|
|
72
|
+
key: K
|
|
73
|
+
): BrownieStores[K] {
|
|
74
|
+
const store = getOrCreateStore(key as string);
|
|
75
|
+
return store.snapshot as BrownieStores[K];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
type SetStateAction<T> = Partial<T> | ((prevState: T) => Partial<T>);
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Set a value in the native store.
|
|
82
|
+
*/
|
|
83
|
+
export function setState<K extends keyof BrownieStores>(
|
|
84
|
+
key: K,
|
|
85
|
+
action: SetStateAction<BrownieStores[K]>
|
|
86
|
+
): void {
|
|
87
|
+
const store = getOrCreateStore(key as string);
|
|
88
|
+
if (!store.hostObject) return;
|
|
89
|
+
|
|
90
|
+
const partial =
|
|
91
|
+
typeof action === 'function'
|
|
92
|
+
? action(store.snapshot as BrownieStores[K])
|
|
93
|
+
: action;
|
|
94
|
+
|
|
95
|
+
for (const [prop, value] of Object.entries(partial)) {
|
|
96
|
+
store.hostObject[prop] = value;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* React hook for subscribing to a native store with selector.
|
|
102
|
+
* Inspired by Zustand's useStore implementation.
|
|
103
|
+
* @param key Store key registered in StoreManager
|
|
104
|
+
* @param selector Function to select a slice of state
|
|
105
|
+
* @returns Tuple of [selectedState, setState] for the store
|
|
106
|
+
*/
|
|
107
|
+
export function useStore<K extends keyof BrownieStores, U>(
|
|
108
|
+
key: K,
|
|
109
|
+
selector: (state: BrownieStores[K]) => U
|
|
110
|
+
): [U, (action: SetStateAction<BrownieStores[K]>) => void] {
|
|
111
|
+
const sub = useCallback(
|
|
112
|
+
(listener: () => void) => subscribe(key, listener),
|
|
113
|
+
[key]
|
|
114
|
+
);
|
|
115
|
+
const snap = useCallback(() => selector(getSnapshot(key)), [key, selector]);
|
|
116
|
+
|
|
117
|
+
const slice = useSyncExternalStore(sub, snap, snap);
|
|
118
|
+
|
|
119
|
+
useDebugValue(slice);
|
|
120
|
+
|
|
121
|
+
const boundSetState = useCallback(
|
|
122
|
+
(action: SetStateAction<BrownieStores[K]>) => setState(key, action),
|
|
123
|
+
[key]
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
return [slice, boundSetState];
|
|
127
|
+
}
|