react-native-unistyles 3.3.0 → 3.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/README.md +3 -3
- package/cxx/core/UnistylesRegistry.cpp +135 -47
- package/cxx/core/UnistylesRegistry.h +23 -3
- package/cxx/hybridObjects/HybridShadowRegistry.cpp +4 -7
- package/cxx/parser/Parser.cpp +1 -2
- package/cxx/shadowTree/NativeProps.h +44 -0
- package/cxx/shadowTree/ShadowLeafUpdate.h +8 -0
- package/cxx/shadowTree/ShadowTrafficController.h +16 -37
- package/cxx/shadowTree/ShadowTreeManager.cpp +8 -148
- package/cxx/shadowTree/ShadowTreeManager.h +1 -5
- package/lib/commonjs/components/native/Animated.js +2 -0
- package/lib/commonjs/components/native/Animated.js.map +1 -1
- package/lib/commonjs/components/native/Pressable.js +2 -0
- package/lib/commonjs/components/native/Pressable.js.map +1 -1
- package/lib/commonjs/components/native/Pressable.native.js +2 -0
- package/lib/commonjs/components/native/Pressable.native.js.map +1 -1
- package/lib/commonjs/web/convert/object/boxShadow.js +2 -2
- package/lib/commonjs/web/convert/object/boxShadow.js.map +1 -1
- package/lib/commonjs/web/index.js.map +1 -1
- package/lib/module/components/native/Animated.js +3 -0
- package/lib/module/components/native/Animated.js.map +1 -1
- package/lib/module/components/native/Pressable.js +2 -0
- package/lib/module/components/native/Pressable.js.map +1 -1
- package/lib/module/components/native/Pressable.native.js +2 -0
- package/lib/module/components/native/Pressable.native.js.map +1 -1
- package/lib/module/web/convert/object/boxShadow.js +2 -2
- package/lib/module/web/convert/object/boxShadow.js.map +1 -1
- package/lib/module/web/index.js.map +1 -1
- package/lib/typescript/src/components/native/Animated.d.ts +16 -36
- package/lib/typescript/src/components/native/Animated.d.ts.map +1 -1
- package/lib/typescript/src/components/native/Pressable.d.ts +4 -2
- package/lib/typescript/src/components/native/Pressable.d.ts.map +1 -1
- package/lib/typescript/src/components/native/Pressable.native.d.ts +5 -2
- package/lib/typescript/src/components/native/Pressable.native.d.ts.map +1 -1
- package/lib/typescript/src/types/core.d.ts +7 -2
- package/lib/typescript/src/types/core.d.ts.map +1 -1
- package/lib/typescript/src/web/index.d.ts.map +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/unistyles/Func_void_UnistylesNativeMiniRuntime.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/unistyles/Func_void_std__vector_UnistyleDependency__UnistylesNativeMiniRuntime.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/unistyles/HybridNativePlatformSpec.kt +2 -0
- package/package.json +27 -26
- package/src/components/native/Animated.tsx +18 -1
- package/src/components/native/Pressable.native.tsx +10 -2
- package/src/components/native/Pressable.tsx +7 -1
- package/src/types/core.ts +7 -28
- package/src/web/convert/object/boxShadow.ts +2 -2
- package/src/web/index.ts +2 -1
package/README.md
CHANGED
|
@@ -58,9 +58,6 @@ Then follow [installation guides](https://unistyl.es/v3/start/getting-started) f
|
|
|
58
58
|
<a href="https://codemask.com">
|
|
59
59
|
<img src="https://avatars.githubusercontent.com/u/51229884?s=200&v=4" height="70px" width="70px" alt="codemask" />
|
|
60
60
|
</a>
|
|
61
|
-
<a href="https://galaxies.dev">
|
|
62
|
-
<img src="https://avatars.githubusercontent.com/u/118431096?s=200&v=4" height="70px" width="70px" alt="galaxies-dev" />
|
|
63
|
-
</a>
|
|
64
61
|
<a href="https://github.com/ryanlanciaux">
|
|
65
62
|
<img src="https://avatars.githubusercontent.com/u/85041?v=4" height="70px" width="70px" alt="ryanlanciaux" />
|
|
66
63
|
</a>
|
|
@@ -155,6 +152,9 @@ Then follow [installation guides](https://unistyl.es/v3/start/getting-started) f
|
|
|
155
152
|
<a href="https://github.com/AdiRishi">
|
|
156
153
|
<img src="https://avatars.githubusercontent.com/u/8351234?v=4" height="60px" width="60px" alt="AdiRishi" />
|
|
157
154
|
</a>
|
|
155
|
+
<a href="https://galaxies.dev">
|
|
156
|
+
<img src="https://avatars.githubusercontent.com/u/118431096?s=200&v=4" height="60px" width="60px" alt="galaxies-dev" />
|
|
157
|
+
</a>
|
|
158
158
|
|
|
159
159
|
## Sponsor my work
|
|
160
160
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#include "UnistylesRegistry.h"
|
|
2
2
|
#include "UnistylesState.h"
|
|
3
3
|
#include "Parser.h"
|
|
4
|
+
#include "NativeProps.h"
|
|
4
5
|
|
|
5
6
|
using namespace margelo::nitro::unistyles;
|
|
6
7
|
using namespace facebook;
|
|
@@ -60,37 +61,85 @@ void core::UnistylesRegistry::updateTheme(jsi::Runtime& rt, std::string& themeNa
|
|
|
60
61
|
|
|
61
62
|
void core::UnistylesRegistry::linkShadowNodeWithUnistyle(
|
|
62
63
|
jsi::Runtime& rt,
|
|
63
|
-
const ShadowNodeFamily
|
|
64
|
+
const std::shared_ptr<const ShadowNodeFamily>& shadowNodeFamily,
|
|
64
65
|
std::vector<std::shared_ptr<UnistyleData>>& unistylesData,
|
|
65
66
|
std::optional<folly::dynamic> initialScopedUpdate
|
|
66
67
|
) {
|
|
67
|
-
|
|
68
|
+
// released after unlocking, so a family destructor never runs within the lock
|
|
69
|
+
ReleasedFamilies releasedFamilies;
|
|
70
|
+
|
|
71
|
+
this->trafficController.withLock([this, &unistylesData, &shadowNodeFamily, &initialScopedUpdate, &releasedFamilies](){
|
|
72
|
+
auto family = shadowNodeFamily.get();
|
|
73
|
+
auto it = this->_shadowRegistry.find(family);
|
|
74
|
+
|
|
68
75
|
// Clear suspension state if this family was previously suspended
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
mutableFamily->nativeProps_DEPRECATED.reset();
|
|
76
|
+
if (it != this->_shadowRegistry.end() && it->second.isSuspended) {
|
|
77
|
+
shadow::resetNativeProps(family);
|
|
72
78
|
// Clear old registry entries to prevent stale UnistyleData accumulation
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
this->trafficController.removeShadowNode(shadowNodeFamily);
|
|
79
|
+
// and any stale traffic controller entry (e.g. from a theme change during suspension)
|
|
80
|
+
this->forgetFamilyUnsafe(family, releasedFamilies);
|
|
76
81
|
}
|
|
77
82
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
auto& linkedFamily = this->_shadowRegistry[family];
|
|
84
|
+
|
|
85
|
+
linkedFamily.family = shadowNodeFamily;
|
|
86
|
+
linkedFamily.unistyles.insert(linkedFamily.unistyles.end(), unistylesData.begin(), unistylesData.end());
|
|
81
87
|
|
|
82
88
|
// Required for scoped themes to apply on initial mount
|
|
83
89
|
if (initialScopedUpdate.has_value()) {
|
|
84
|
-
|
|
90
|
+
this->trafficController.setUpdate(shadowNodeFamily, std::move(*initialScopedUpdate));
|
|
91
|
+
}
|
|
85
92
|
|
|
86
|
-
|
|
87
|
-
|
|
93
|
+
// families unmounted without unlink (eg. while frozen) are pinned by us only
|
|
94
|
+
// sweep them once the registry doubles, so it's amortized O(1) per link
|
|
95
|
+
if (this->_shadowRegistry.size() >= this->_sweepThreshold) {
|
|
96
|
+
this->sweepUnownedFamiliesUnsafe(releasedFamilies);
|
|
97
|
+
this->_sweepThreshold = std::max(MIN_SWEEP_THRESHOLD, this->_shadowRegistry.size() * 2);
|
|
88
98
|
}
|
|
89
99
|
});
|
|
90
100
|
}
|
|
91
101
|
|
|
102
|
+
bool core::UnistylesRegistry::isOwnedOnlyByUnistyles(const LinkedFamily& linkedFamily) noexcept {
|
|
103
|
+
// shadow nodes own their family, so if nothing but our pin owns it, the family is not part of any
|
|
104
|
+
// shadow tree anymore. React Native can't get it back, as it only keeps weak references beside shadow nodes
|
|
105
|
+
return linkedFamily.family.use_count() == 1;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
void core::UnistylesRegistry::forgetFamilyUnsafe(const ShadowNodeFamily* shadowNodeFamily, ReleasedFamilies& releasedFamilies) {
|
|
109
|
+
auto it = this->_shadowRegistry.find(shadowNodeFamily);
|
|
110
|
+
|
|
111
|
+
if (it != this->_shadowRegistry.end()) {
|
|
112
|
+
releasedFamilies.emplace_back(std::move(it->second.family));
|
|
113
|
+
this->_shadowRegistry.erase(it);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (auto pendingFamily = this->trafficController.removeShadowNode(shadowNodeFamily)) {
|
|
117
|
+
releasedFamilies.emplace_back(std::move(pendingFamily));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
void core::UnistylesRegistry::sweepUnownedFamiliesUnsafe(ReleasedFamilies& releasedFamilies) {
|
|
122
|
+
std::vector<const ShadowNodeFamily*> unownedFamilies;
|
|
123
|
+
|
|
124
|
+
for (const auto& [family, linkedFamily] : this->_shadowRegistry) {
|
|
125
|
+
if (isOwnedOnlyByUnistyles(linkedFamily)) {
|
|
126
|
+
unownedFamilies.emplace_back(family);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
for (const auto* family : unownedFamilies) {
|
|
131
|
+
this->forgetFamilyUnsafe(family, releasedFamilies);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
92
135
|
void core::UnistylesRegistry::removeDuplicatedUnistyles(const ShadowNodeFamily *shadowNodeFamily, std::vector<core::Unistyle::Shared>& unistyles) {
|
|
93
|
-
auto
|
|
136
|
+
auto it = this->_shadowRegistry.find(shadowNodeFamily);
|
|
137
|
+
|
|
138
|
+
if (it == this->_shadowRegistry.end()) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
auto& targetFamilyUnistyles = it->second.unistyles;
|
|
94
143
|
|
|
95
144
|
unistyles.erase(
|
|
96
145
|
std::remove_if(
|
|
@@ -111,23 +160,27 @@ void core::UnistylesRegistry::removeDuplicatedUnistyles(const ShadowNodeFamily *
|
|
|
111
160
|
}
|
|
112
161
|
|
|
113
162
|
void core::UnistylesRegistry::unlinkShadowNodeWithUnistyles(const ShadowNodeFamily* shadowNodeFamily) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
this->
|
|
163
|
+
ReleasedFamilies releasedFamilies;
|
|
164
|
+
|
|
165
|
+
this->trafficController.withLock([this, shadowNodeFamily, &releasedFamilies](){
|
|
166
|
+
this->forgetFamilyUnsafe(shadowNodeFamily, releasedFamilies);
|
|
118
167
|
});
|
|
119
168
|
}
|
|
120
169
|
|
|
121
170
|
void core::UnistylesRegistry::suspendShadowNode(const ShadowNodeFamily* shadowNodeFamily) {
|
|
122
171
|
this->trafficController.withLock([this, shadowNodeFamily](){
|
|
123
|
-
|
|
124
|
-
|
|
172
|
+
auto it = this->_shadowRegistry.find(shadowNodeFamily);
|
|
173
|
+
|
|
174
|
+
if (it != this->_shadowRegistry.end()) {
|
|
175
|
+
it->second.isSuspended = true;
|
|
125
176
|
}
|
|
126
177
|
});
|
|
127
178
|
}
|
|
128
179
|
|
|
129
180
|
bool core::UnistylesRegistry::isSuspended(const ShadowNodeFamily* family) const noexcept {
|
|
130
|
-
|
|
181
|
+
auto it = this->_shadowRegistry.find(family);
|
|
182
|
+
|
|
183
|
+
return it != this->_shadowRegistry.end() && it->second.isSuspended;
|
|
131
184
|
}
|
|
132
185
|
|
|
133
186
|
std::shared_ptr<core::StyleSheet> core::UnistylesRegistry::addStyleSheet(jsi::Runtime& rt, core::StyleSheetType type, jsi::Object&& rawValue) {
|
|
@@ -143,36 +196,56 @@ core::DependencyMap core::UnistylesRegistry::buildDependencyMap(std::vector<Unis
|
|
|
143
196
|
core::DependencyMap dependencyMap;
|
|
144
197
|
|
|
145
198
|
std::unordered_set<UnistyleDependency> uniqueDependencies(deps.begin(), deps.end());
|
|
199
|
+
ReleasedFamilies releasedFamilies;
|
|
200
|
+
|
|
201
|
+
// families stay pinned by the registry until the shadow tree update is committed
|
|
202
|
+
this->trafficController.withLock([this, &dependencyMap, &uniqueDependencies, &releasedFamilies](){
|
|
203
|
+
// we iterate whole registry anyway, so it's a free moment to drop families unmounted without unlink
|
|
204
|
+
this->sweepUnownedFamiliesUnsafe(releasedFamilies);
|
|
205
|
+
|
|
206
|
+
for (const auto& [family, linkedFamily] : this->_shadowRegistry) {
|
|
207
|
+
bool hasAnyOfDependencies = false;
|
|
208
|
+
|
|
209
|
+
// Check if any dependency matches
|
|
210
|
+
for (const auto& unistyleData : linkedFamily.unistyles) {
|
|
211
|
+
for (const auto& dep : unistyleData->unistyle->dependencies) {
|
|
212
|
+
if (uniqueDependencies.count(dep)) {
|
|
213
|
+
hasAnyOfDependencies = true;
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
146
217
|
|
|
147
|
-
|
|
148
|
-
bool hasAnyOfDependencies = false;
|
|
149
|
-
|
|
150
|
-
// Check if any dependency matches
|
|
151
|
-
for (const auto& unistyleData : unistyles) {
|
|
152
|
-
for (const auto& dep : unistyleData->unistyle->dependencies) {
|
|
153
|
-
if (uniqueDependencies.count(dep)) {
|
|
154
|
-
hasAnyOfDependencies = true;
|
|
218
|
+
if (hasAnyOfDependencies) {
|
|
155
219
|
break;
|
|
156
|
-
}
|
|
220
|
+
};
|
|
157
221
|
}
|
|
158
222
|
|
|
159
|
-
if (hasAnyOfDependencies) {
|
|
160
|
-
|
|
161
|
-
}
|
|
223
|
+
if (!hasAnyOfDependencies) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
dependencyMap[family].insert(
|
|
228
|
+
dependencyMap[family].end(),
|
|
229
|
+
linkedFamily.unistyles.begin(),
|
|
230
|
+
linkedFamily.unistyles.end()
|
|
231
|
+
);
|
|
162
232
|
}
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
return dependencyMap;
|
|
236
|
+
}
|
|
163
237
|
|
|
164
|
-
|
|
238
|
+
void core::UnistylesRegistry::queueShadowLeafUpdatesUnsafe(shadow::ShadowLeafUpdates& updates) {
|
|
239
|
+
for (auto& [family, props] : updates) {
|
|
240
|
+
auto it = this->_shadowRegistry.find(family);
|
|
241
|
+
|
|
242
|
+
// family was unlinked in the meantime, there is nothing to update
|
|
243
|
+
if (it == this->_shadowRegistry.end()) {
|
|
165
244
|
continue;
|
|
166
245
|
}
|
|
167
246
|
|
|
168
|
-
|
|
169
|
-
dependencyMap[family].end(),
|
|
170
|
-
unistyles.begin(),
|
|
171
|
-
unistyles.end()
|
|
172
|
-
);
|
|
247
|
+
this->trafficController.setUpdate(it->second.family, std::move(props));
|
|
173
248
|
}
|
|
174
|
-
|
|
175
|
-
return dependencyMap;
|
|
176
249
|
}
|
|
177
250
|
|
|
178
251
|
// called from proxied function only, we don't know host
|
|
@@ -185,15 +258,15 @@ void core::UnistylesRegistry::shadowLeafUpdateFromUnistyle(jsi::Runtime& rt, Uni
|
|
|
185
258
|
? std::make_optional(maybePressableId.asString(rt).utf8(rt))
|
|
186
259
|
: std::nullopt;
|
|
187
260
|
|
|
188
|
-
for (const auto& [family,
|
|
189
|
-
for (const auto& unistyleData : unistyles) {
|
|
261
|
+
for (const auto& [family, linkedFamily] : this->_shadowRegistry) {
|
|
262
|
+
for (const auto& unistyleData : linkedFamily.unistyles) {
|
|
190
263
|
if (unistyleData->unistyle == unistyle) {
|
|
191
264
|
updates[family] = parser.parseStylesToShadowTreeStyles(rt, { unistyleData });
|
|
192
265
|
}
|
|
193
266
|
}
|
|
194
267
|
}
|
|
195
268
|
|
|
196
|
-
this->
|
|
269
|
+
this->queueShadowLeafUpdatesUnsafe(updates);
|
|
197
270
|
});
|
|
198
271
|
}
|
|
199
272
|
|
|
@@ -263,10 +336,25 @@ void core::UnistylesRegistry::setScopedTheme(std::optional<std::string> themeNam
|
|
|
263
336
|
}
|
|
264
337
|
|
|
265
338
|
void core::UnistylesRegistry::destroy() {
|
|
339
|
+
// called when module is invalidated, before React Native destroys the runtime
|
|
340
|
+
// pins must be released now, as families hold JS handles that can't outlive the runtime
|
|
341
|
+
ReleasedFamilies releasedFamilies;
|
|
342
|
+
|
|
343
|
+
this->trafficController.withLock([this, &releasedFamilies](){
|
|
344
|
+
for (auto& [_, linkedFamily] : this->_shadowRegistry) {
|
|
345
|
+
releasedFamilies.emplace_back(std::move(linkedFamily.family));
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
for (auto& [_, pendingUpdate] : this->trafficController.takeUpdates()) {
|
|
349
|
+
releasedFamilies.emplace_back(std::move(pendingUpdate.family));
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
this->_shadowRegistry.clear();
|
|
353
|
+
this->_sweepThreshold = MIN_SWEEP_THRESHOLD;
|
|
354
|
+
});
|
|
355
|
+
|
|
266
356
|
this->_state.reset();
|
|
267
357
|
this->_styleSheetRegistry.clear();
|
|
268
|
-
this->_shadowRegistry.clear();
|
|
269
|
-
this->_suspendedFamilies.clear();
|
|
270
358
|
this->_scopedTheme = std::nullopt;
|
|
271
359
|
_nextStyleSheetTag.store(0);
|
|
272
360
|
}
|
|
@@ -23,6 +23,12 @@ using namespace facebook::react;
|
|
|
23
23
|
|
|
24
24
|
using DependencyMap = std::unordered_map<const ShadowNodeFamily*, std::vector<std::shared_ptr<UnistyleData>>>;
|
|
25
25
|
|
|
26
|
+
struct LinkedFamily {
|
|
27
|
+
std::shared_ptr<const ShadowNodeFamily> family;
|
|
28
|
+
std::vector<std::shared_ptr<UnistyleData>> unistyles;
|
|
29
|
+
bool isSuspended = false;
|
|
30
|
+
};
|
|
31
|
+
|
|
26
32
|
struct UnistylesRegistry: public StyleSheetRegistry {
|
|
27
33
|
static UnistylesRegistry& get();
|
|
28
34
|
|
|
@@ -40,13 +46,15 @@ struct UnistylesRegistry: public StyleSheetRegistry {
|
|
|
40
46
|
UnistylesState& getState();
|
|
41
47
|
void createState();
|
|
42
48
|
std::vector<std::shared_ptr<core::StyleSheet>> getStyleSheetsToRefresh(std::vector<UnistyleDependency>& unistylesDependencies);
|
|
43
|
-
void linkShadowNodeWithUnistyle(jsi::Runtime& rt, const ShadowNodeFamily
|
|
49
|
+
void linkShadowNodeWithUnistyle(jsi::Runtime& rt, const std::shared_ptr<const ShadowNodeFamily>& shadowNodeFamily, std::vector<std::shared_ptr<UnistyleData>>& unistylesData, std::optional<folly::dynamic> initialScopedUpdate = std::nullopt);
|
|
44
50
|
void unlinkShadowNodeWithUnistyles(const ShadowNodeFamily*);
|
|
45
51
|
void suspendShadowNode(const ShadowNodeFamily*);
|
|
46
52
|
bool isSuspended(const ShadowNodeFamily*) const noexcept;
|
|
47
53
|
std::shared_ptr<core::StyleSheet> addStyleSheet(jsi::Runtime& rt, core::StyleSheetType type, jsi::Object&& rawValue);
|
|
48
54
|
DependencyMap buildDependencyMap(std::vector<UnistyleDependency>& deps);
|
|
49
55
|
void shadowLeafUpdateFromUnistyle(jsi::Runtime& rt, Unistyle::Shared unistyle, jsi::Value& maybePressableId);
|
|
56
|
+
// call it only within trafficController.withLock!
|
|
57
|
+
void queueShadowLeafUpdatesUnsafe(shadow::ShadowLeafUpdates& updates);
|
|
50
58
|
shadow::ShadowTrafficController trafficController{};
|
|
51
59
|
const std::optional<std::string> getScopedTheme();
|
|
52
60
|
void removeDuplicatedUnistyles(const ShadowNodeFamily* shadowNodeFamily, std::vector<core::Unistyle::Shared>& unistyles);
|
|
@@ -55,14 +63,26 @@ struct UnistylesRegistry: public StyleSheetRegistry {
|
|
|
55
63
|
void destroy();
|
|
56
64
|
|
|
57
65
|
private:
|
|
66
|
+
using ReleasedFamilies = std::vector<std::shared_ptr<const ShadowNodeFamily>>;
|
|
67
|
+
|
|
58
68
|
UnistylesRegistry() = default;
|
|
59
69
|
|
|
70
|
+
// call it only within trafficController.withLock!
|
|
71
|
+
// pins are moved to releasedFamilies, so they can be released after unlocking
|
|
72
|
+
void forgetFamilyUnsafe(const ShadowNodeFamily* shadowNodeFamily, ReleasedFamilies& releasedFamilies);
|
|
73
|
+
void sweepUnownedFamiliesUnsafe(ReleasedFamilies& releasedFamilies);
|
|
74
|
+
|
|
75
|
+
// React Native doesn't own the family anymore, it was unmounted without unlink
|
|
76
|
+
static bool isOwnedOnlyByUnistyles(const LinkedFamily& linkedFamily) noexcept;
|
|
77
|
+
|
|
78
|
+
static constexpr size_t MIN_SWEEP_THRESHOLD = 64;
|
|
79
|
+
|
|
60
80
|
static std::atomic<int> _nextStyleSheetTag;
|
|
61
81
|
std::optional<std::string> _scopedTheme{};
|
|
62
82
|
std::unique_ptr<UnistylesState> _state{};
|
|
63
83
|
std::unordered_map<int, std::shared_ptr<core::StyleSheet>> _styleSheetRegistry{};
|
|
64
|
-
std::unordered_map<const ShadowNodeFamily*,
|
|
65
|
-
|
|
84
|
+
std::unordered_map<const ShadowNodeFamily*, LinkedFamily> _shadowRegistry{};
|
|
85
|
+
size_t _sweepThreshold = MIN_SWEEP_THRESHOLD;
|
|
66
86
|
};
|
|
67
87
|
|
|
68
88
|
inline UnistylesRegistry& UnistylesRegistry::get() {
|
|
@@ -87,9 +87,10 @@ jsi::Value HybridShadowRegistry::link(jsi::Runtime &rt, const jsi::Value &thisVa
|
|
|
87
87
|
parser.rebuildUnistyleWithScopedTheme(rt, parsedStyleSheet, unistyleData);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
//
|
|
90
|
+
// The shared cache can belong to another view by the time React attaches refs.
|
|
91
|
+
// Restore this view's arguments and variants before saving its native styles.
|
|
91
92
|
if (!unistyleData->parsedStyle.has_value() && unistyle->parsedStyle.has_value()) {
|
|
92
|
-
|
|
93
|
+
parser.rebuildUnistyleWithVariants(rt, unistyleData);
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
unistylesData.emplace_back(unistyleData);
|
|
@@ -103,7 +104,7 @@ jsi::Value HybridShadowRegistry::link(jsi::Runtime &rt, const jsi::Value &thisVa
|
|
|
103
104
|
|
|
104
105
|
registry.linkShadowNodeWithUnistyle(
|
|
105
106
|
rt,
|
|
106
|
-
|
|
107
|
+
shadowNodeWrapper->getFamilyShared(),
|
|
107
108
|
unistylesData,
|
|
108
109
|
std::move(initialScopedUpdate)
|
|
109
110
|
);
|
|
@@ -170,9 +171,5 @@ jsi::Value HybridShadowRegistry::getScopedTheme(jsi::Runtime &rt, const jsi::Val
|
|
|
170
171
|
}
|
|
171
172
|
|
|
172
173
|
std::shared_ptr<const core::ShadowNode> HybridShadowRegistry::getShadowNodeFromRef(jsi::Runtime& rt, const jsi::Value& maybeRef) {
|
|
173
|
-
#if REACT_NATIVE_VERSION_MINOR >= 81
|
|
174
174
|
return Bridging<std::shared_ptr<const ShadowNode>>::fromJs(rt, maybeRef);
|
|
175
|
-
#else
|
|
176
|
-
return shadowNodeFromValue(rt, maybeRef);
|
|
177
|
-
#endif
|
|
178
175
|
}
|
package/cxx/parser/Parser.cpp
CHANGED
|
@@ -444,8 +444,7 @@ void parser::Parser::rebuildShadowLeafUpdates(jsi::Runtime& rt, core::Dependency
|
|
|
444
444
|
updates.emplace(shadowNode, std::move(rawProps));
|
|
445
445
|
}
|
|
446
446
|
|
|
447
|
-
registry.
|
|
448
|
-
registry.trafficController.resumeUnistylesTraffic();
|
|
447
|
+
registry.queueShadowLeafUpdatesUnsafe(updates);
|
|
449
448
|
});
|
|
450
449
|
}
|
|
451
450
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <folly/dynamic.h>
|
|
4
|
+
#include <react/renderer/core/ShadowNodeFamily.h>
|
|
5
|
+
#include <cxxreact/ReactNativeVersion.h>
|
|
6
|
+
#include <memory>
|
|
7
|
+
#include <mutex>
|
|
8
|
+
|
|
9
|
+
// React Native reads ShadowNodeFamily::nativeProps_DEPRECATED from any thread
|
|
10
|
+
// (eg. ShadowNode::clone during main thread layout) and since 0.86.1 guards every access with nativePropsMutex
|
|
11
|
+
// we need to hold the same lock, otherwise mutating / replacing the folly::dynamic races with RN copying it
|
|
12
|
+
#if REACT_NATIVE_VERSION_MINOR > 86 || (REACT_NATIVE_VERSION_MINOR == 86 && REACT_NATIVE_VERSION_PATCH >= 1)
|
|
13
|
+
#define UNISTYLES_HAS_NATIVE_PROPS_MUTEX 1
|
|
14
|
+
#else
|
|
15
|
+
#define UNISTYLES_HAS_NATIVE_PROPS_MUTEX 0
|
|
16
|
+
#endif
|
|
17
|
+
|
|
18
|
+
namespace margelo::nitro::unistyles::shadow {
|
|
19
|
+
|
|
20
|
+
using namespace facebook::react;
|
|
21
|
+
|
|
22
|
+
inline void mergeNativeProps(const ShadowNodeFamily* family, const folly::dynamic& props) {
|
|
23
|
+
#if UNISTYLES_HAS_NATIVE_PROPS_MUTEX
|
|
24
|
+
std::lock_guard<std::mutex> lock(family->nativePropsMutex);
|
|
25
|
+
#endif
|
|
26
|
+
|
|
27
|
+
if (family->nativeProps_DEPRECATED && family->nativeProps_DEPRECATED->isObject()) {
|
|
28
|
+
family->nativeProps_DEPRECATED->update(props);
|
|
29
|
+
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
family->nativeProps_DEPRECATED = std::make_unique<folly::dynamic>(props);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
inline void resetNativeProps(const ShadowNodeFamily* family) {
|
|
37
|
+
#if UNISTYLES_HAS_NATIVE_PROPS_MUTEX
|
|
38
|
+
std::lock_guard<std::mutex> lock(family->nativePropsMutex);
|
|
39
|
+
#endif
|
|
40
|
+
|
|
41
|
+
family->nativeProps_DEPRECATED.reset();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
#include <jsi/jsi.h>
|
|
4
4
|
#include <folly/dynamic.h>
|
|
5
|
+
#include <memory>
|
|
5
6
|
#include <react/renderer/uimanager/UIManager.h>
|
|
6
7
|
|
|
7
8
|
namespace margelo::nitro::unistyles::shadow {
|
|
@@ -12,4 +13,11 @@ using namespace facebook::react;
|
|
|
12
13
|
// translates Unistyles changes to unified shadow tree changes
|
|
13
14
|
using ShadowLeafUpdates = std::unordered_map<const ShadowNodeFamily*, folly::dynamic>;
|
|
14
15
|
|
|
16
|
+
struct PinnedShadowLeafUpdate {
|
|
17
|
+
std::shared_ptr<const ShadowNodeFamily> family;
|
|
18
|
+
folly::dynamic props;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
using PinnedShadowLeafUpdates = std::unordered_map<const ShadowNodeFamily*, PinnedShadowLeafUpdate>;
|
|
22
|
+
|
|
15
23
|
}
|
|
@@ -8,52 +8,32 @@ namespace margelo::nitro::unistyles::shadow {
|
|
|
8
8
|
// Like a traffic officer managing a jam, this struct ensures everything
|
|
9
9
|
// is synchronized within a set timeframe, controlling flow and preventing chaos.
|
|
10
10
|
struct ShadowTrafficController {
|
|
11
|
-
inline
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
inline void stopUnistylesTraffic() {
|
|
16
|
-
this->_canCommit = false;
|
|
17
|
-
}
|
|
11
|
+
inline void setUpdate(std::shared_ptr<const ShadowNodeFamily> family, folly::dynamic props) {
|
|
12
|
+
// call it only within withLock!
|
|
13
|
+
auto key = family.get();
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
this->_canCommit = true;
|
|
15
|
+
_unistylesUpdates.insert_or_assign(key, PinnedShadowLeafUpdate{std::move(family), std::move(props)});
|
|
21
16
|
}
|
|
22
17
|
|
|
23
|
-
inline
|
|
18
|
+
inline PinnedShadowLeafUpdates takeUpdates() {
|
|
24
19
|
// call it only within withLock!
|
|
25
|
-
return _unistylesUpdates;
|
|
20
|
+
return std::exchange(_unistylesUpdates, {});
|
|
26
21
|
}
|
|
27
22
|
|
|
28
|
-
inline
|
|
23
|
+
inline std::shared_ptr<const ShadowNodeFamily> removeShadowNode(const ShadowNodeFamily* shadowNodeFamily) {
|
|
29
24
|
// call it only within withLock!
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// this is important as overriding updates may skip some interim changes
|
|
33
|
-
// Unistyles emits different events so this will make sure that everything is synced
|
|
34
|
-
std::for_each(newUpdates.begin(), newUpdates.end(), [&targetUpdates](auto& pair){
|
|
35
|
-
if (targetUpdates.contains(pair.first)) {
|
|
36
|
-
targetUpdates[pair.first] = std::move(pair.second);
|
|
37
|
-
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
25
|
+
// returns the pin, so the caller can release it after unlocking
|
|
26
|
+
auto it = _unistylesUpdates.find(shadowNodeFamily);
|
|
40
27
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
inline void removeShadowNode(const ShadowNodeFamily* shadowNodeFamily) {
|
|
46
|
-
// call it only within withLock!
|
|
47
|
-
if (_unistylesUpdates.contains(shadowNodeFamily)) {
|
|
48
|
-
_unistylesUpdates.erase(shadowNodeFamily);
|
|
28
|
+
if (it == _unistylesUpdates.end()) {
|
|
29
|
+
return nullptr;
|
|
49
30
|
}
|
|
50
|
-
}
|
|
51
31
|
|
|
52
|
-
|
|
53
|
-
|
|
32
|
+
auto family = std::move(it->second.family);
|
|
33
|
+
|
|
34
|
+
_unistylesUpdates.erase(it);
|
|
54
35
|
|
|
55
|
-
|
|
56
|
-
_canCommit = false;
|
|
36
|
+
return family;
|
|
57
37
|
}
|
|
58
38
|
|
|
59
39
|
template <typename F>
|
|
@@ -64,8 +44,7 @@ struct ShadowTrafficController {
|
|
|
64
44
|
}
|
|
65
45
|
|
|
66
46
|
private:
|
|
67
|
-
|
|
68
|
-
shadow::ShadowLeafUpdates _unistylesUpdates{};
|
|
47
|
+
PinnedShadowLeafUpdates _unistylesUpdates{};
|
|
69
48
|
|
|
70
49
|
// this struct should be accessed in thread-safe manner. Otherwise shadow tree updates
|
|
71
50
|
// from different threads will break it
|