react-native-unistyles 3.3.0 → 3.5.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.
Files changed (216) hide show
  1. package/README.md +26 -10
  2. package/android/build.gradle +12 -1
  3. package/android/src/main/cxx/NativeUnistylesModule.cpp +8 -4
  4. package/android/src/main/cxx/NativeUnistylesModule.h +5 -1
  5. package/components/native/ActivityIndicator/package.json +1 -1
  6. package/cxx/common/UnistylesConstants.h +1 -0
  7. package/cxx/converters/TransformOriginConverter.cpp +106 -0
  8. package/cxx/core/StyleSheetRegistry.cpp +9 -9
  9. package/cxx/core/StyleSheetRegistry.h +3 -3
  10. package/cxx/core/UnistyleWrapper.h +13 -1
  11. package/cxx/core/UnistylesRegistry.cpp +226 -54
  12. package/cxx/core/UnistylesRegistry.h +64 -7
  13. package/cxx/hybridObjects/HybridShadowRegistry.cpp +128 -11
  14. package/cxx/hybridObjects/HybridShadowRegistry.h +20 -0
  15. package/cxx/hybridObjects/HybridStyleSheet.cpp +20 -12
  16. package/cxx/hybridObjects/HybridUnistylesRuntime.cpp +11 -1
  17. package/cxx/hybridObjects/HybridUnistylesRuntime.h +4 -2
  18. package/cxx/parser/Parser.cpp +30 -3
  19. package/cxx/parser/Parser.h +1 -0
  20. package/cxx/shadowTree/NativeProps.h +88 -0
  21. package/cxx/shadowTree/ShadowLeafUpdate.h +8 -0
  22. package/cxx/shadowTree/ShadowTrafficController.h +18 -35
  23. package/cxx/shadowTree/ShadowTreeDiagnostics.cpp +339 -0
  24. package/cxx/shadowTree/ShadowTreeDiagnostics.h +18 -0
  25. package/cxx/shadowTree/ShadowTreeManager.cpp +14 -148
  26. package/cxx/shadowTree/ShadowTreeManager.h +2 -6
  27. package/diagnostics/package.json +6 -0
  28. package/ios/UnistylesModuleOnLoad.mm +9 -3
  29. package/lib/commonjs/components/native/ActivityIndicator.native.js +10 -0
  30. package/lib/commonjs/components/native/ActivityIndicator.native.js.map +1 -0
  31. package/lib/commonjs/components/native/Animated.js +2 -0
  32. package/lib/commonjs/components/native/Animated.js.map +1 -1
  33. package/lib/commonjs/components/native/NativeText.js +6 -0
  34. package/lib/commonjs/components/native/NativeText.js.map +1 -1
  35. package/lib/commonjs/components/native/NativeText.native.js +17 -1
  36. package/lib/commonjs/components/native/NativeText.native.js.map +1 -1
  37. package/lib/commonjs/components/native/NativeView.js +6 -0
  38. package/lib/commonjs/components/native/NativeView.js.map +1 -1
  39. package/lib/commonjs/components/native/NativeView.native.js +22 -3
  40. package/lib/commonjs/components/native/NativeView.native.js.map +1 -1
  41. package/lib/commonjs/components/native/Pressable.js +9 -3
  42. package/lib/commonjs/components/native/Pressable.js.map +1 -1
  43. package/lib/commonjs/components/native/Pressable.native.js +2 -0
  44. package/lib/commonjs/components/native/Pressable.native.js.map +1 -1
  45. package/lib/commonjs/components/native/TouchableHighlight.native.js +10 -0
  46. package/lib/commonjs/components/native/TouchableHighlight.native.js.map +1 -0
  47. package/lib/commonjs/core/createUnistylesActivityIndicator.js +41 -0
  48. package/lib/commonjs/core/createUnistylesActivityIndicator.js.map +1 -0
  49. package/lib/commonjs/core/createUnistylesElement.js +59 -14
  50. package/lib/commonjs/core/createUnistylesElement.js.map +1 -1
  51. package/lib/commonjs/core/createUnistylesTouchableHighlight.js +58 -0
  52. package/lib/commonjs/core/createUnistylesTouchableHighlight.js.map +1 -0
  53. package/lib/commonjs/core/index.js +14 -0
  54. package/lib/commonjs/core/index.js.map +1 -1
  55. package/lib/commonjs/diagnostics/index.js +13 -0
  56. package/lib/commonjs/diagnostics/index.js.map +1 -0
  57. package/lib/commonjs/diagnostics/types.js +2 -0
  58. package/lib/commonjs/diagnostics/types.js.map +1 -0
  59. package/lib/commonjs/diagnostics/verify.js +19 -0
  60. package/lib/commonjs/diagnostics/verify.js.map +1 -0
  61. package/lib/commonjs/diagnostics/verify.native.js +12 -0
  62. package/lib/commonjs/diagnostics/verify.native.js.map +1 -0
  63. package/lib/commonjs/index.js +38 -0
  64. package/lib/commonjs/index.js.map +1 -1
  65. package/lib/commonjs/mocks.js +41 -0
  66. package/lib/commonjs/mocks.js.map +1 -1
  67. package/lib/commonjs/reanimated/variant/useUpdateVariantColor.native.js +8 -0
  68. package/lib/commonjs/reanimated/variant/useUpdateVariantColor.native.js.map +1 -1
  69. package/lib/commonjs/specs/ShadowRegistry/index.js +97 -1
  70. package/lib/commonjs/specs/ShadowRegistry/index.js.map +1 -1
  71. package/lib/commonjs/specs/StyleSheet/index.js +5 -1
  72. package/lib/commonjs/specs/StyleSheet/index.js.map +1 -1
  73. package/lib/commonjs/specs/UnistylesRuntime/index.js.map +1 -1
  74. package/lib/commonjs/web/convert/object/boxShadow.js +2 -2
  75. package/lib/commonjs/web/convert/object/boxShadow.js.map +1 -1
  76. package/lib/commonjs/web/index.js.map +1 -1
  77. package/lib/commonjs/web/registry.js +3 -0
  78. package/lib/commonjs/web/registry.js.map +1 -1
  79. package/lib/module/components/native/ActivityIndicator.native.js +6 -0
  80. package/lib/module/components/native/ActivityIndicator.native.js.map +1 -0
  81. package/lib/module/components/native/Animated.js +3 -0
  82. package/lib/module/components/native/Animated.js.map +1 -1
  83. package/lib/module/components/native/NativeText.js +1 -1
  84. package/lib/module/components/native/NativeText.js.map +1 -1
  85. package/lib/module/components/native/NativeText.native.js +15 -1
  86. package/lib/module/components/native/NativeText.native.js.map +1 -1
  87. package/lib/module/components/native/NativeView.js +1 -0
  88. package/lib/module/components/native/NativeView.js.map +1 -1
  89. package/lib/module/components/native/NativeView.native.js +19 -2
  90. package/lib/module/components/native/NativeView.native.js.map +1 -1
  91. package/lib/module/components/native/Pressable.js +9 -3
  92. package/lib/module/components/native/Pressable.js.map +1 -1
  93. package/lib/module/components/native/Pressable.native.js +2 -0
  94. package/lib/module/components/native/Pressable.native.js.map +1 -1
  95. package/lib/module/components/native/TouchableHighlight.native.js +6 -0
  96. package/lib/module/components/native/TouchableHighlight.native.js.map +1 -0
  97. package/lib/module/core/createUnistylesActivityIndicator.js +35 -0
  98. package/lib/module/core/createUnistylesActivityIndicator.js.map +1 -0
  99. package/lib/module/core/createUnistylesElement.js +58 -13
  100. package/lib/module/core/createUnistylesElement.js.map +1 -1
  101. package/lib/module/core/createUnistylesTouchableHighlight.js +52 -0
  102. package/lib/module/core/createUnistylesTouchableHighlight.js.map +1 -0
  103. package/lib/module/core/index.js +2 -0
  104. package/lib/module/core/index.js.map +1 -1
  105. package/lib/module/diagnostics/index.js +5 -0
  106. package/lib/module/diagnostics/index.js.map +1 -0
  107. package/lib/module/diagnostics/types.js +2 -0
  108. package/lib/module/diagnostics/types.js.map +1 -0
  109. package/lib/module/diagnostics/verify.js +14 -0
  110. package/lib/module/diagnostics/verify.js.map +1 -0
  111. package/lib/module/diagnostics/verify.native.js +8 -0
  112. package/lib/module/diagnostics/verify.native.js.map +1 -0
  113. package/lib/module/index.js +2 -0
  114. package/lib/module/index.js.map +1 -1
  115. package/lib/module/mocks.js +41 -0
  116. package/lib/module/mocks.js.map +1 -1
  117. package/lib/module/reanimated/variant/useUpdateVariantColor.native.js +9 -1
  118. package/lib/module/reanimated/variant/useUpdateVariantColor.native.js.map +1 -1
  119. package/lib/module/specs/ShadowRegistry/index.js +97 -1
  120. package/lib/module/specs/ShadowRegistry/index.js.map +1 -1
  121. package/lib/module/specs/StyleSheet/index.js +6 -1
  122. package/lib/module/specs/StyleSheet/index.js.map +1 -1
  123. package/lib/module/specs/UnistylesRuntime/index.js.map +1 -1
  124. package/lib/module/web/convert/object/boxShadow.js +2 -2
  125. package/lib/module/web/convert/object/boxShadow.js.map +1 -1
  126. package/lib/module/web/index.js.map +1 -1
  127. package/lib/module/web/registry.js +3 -0
  128. package/lib/module/web/registry.js.map +1 -1
  129. package/lib/typescript/src/components/native/ActivityIndicator.native.d.ts +2 -0
  130. package/lib/typescript/src/components/native/ActivityIndicator.native.d.ts.map +1 -0
  131. package/lib/typescript/src/components/native/Animated.d.ts +16 -36
  132. package/lib/typescript/src/components/native/Animated.d.ts.map +1 -1
  133. package/lib/typescript/src/components/native/NativeText.d.ts +1 -1
  134. package/lib/typescript/src/components/native/NativeText.d.ts.map +1 -1
  135. package/lib/typescript/src/components/native/NativeText.native.d.ts.map +1 -1
  136. package/lib/typescript/src/components/native/NativeView.d.ts +1 -0
  137. package/lib/typescript/src/components/native/NativeView.d.ts.map +1 -1
  138. package/lib/typescript/src/components/native/NativeView.native.d.ts +2 -2
  139. package/lib/typescript/src/components/native/NativeView.native.d.ts.map +1 -1
  140. package/lib/typescript/src/components/native/Pressable.d.ts +4 -2
  141. package/lib/typescript/src/components/native/Pressable.d.ts.map +1 -1
  142. package/lib/typescript/src/components/native/Pressable.native.d.ts +5 -2
  143. package/lib/typescript/src/components/native/Pressable.native.d.ts.map +1 -1
  144. package/lib/typescript/src/components/native/TouchableHighlight.native.d.ts +2 -0
  145. package/lib/typescript/src/components/native/TouchableHighlight.native.d.ts.map +1 -0
  146. package/lib/typescript/src/core/createUnistylesActivityIndicator.d.ts +3 -0
  147. package/lib/typescript/src/core/createUnistylesActivityIndicator.d.ts.map +1 -0
  148. package/lib/typescript/src/core/createUnistylesElement.d.ts.map +1 -1
  149. package/lib/typescript/src/core/createUnistylesTouchableHighlight.d.ts +3 -0
  150. package/lib/typescript/src/core/createUnistylesTouchableHighlight.d.ts.map +1 -0
  151. package/lib/typescript/src/core/index.d.ts +2 -0
  152. package/lib/typescript/src/core/index.d.ts.map +1 -1
  153. package/lib/typescript/src/core/useUnistyles.d.ts +1 -1
  154. package/lib/typescript/src/diagnostics/index.d.ts +3 -0
  155. package/lib/typescript/src/diagnostics/index.d.ts.map +1 -0
  156. package/lib/typescript/src/diagnostics/types.d.ts +19 -0
  157. package/lib/typescript/src/diagnostics/types.d.ts.map +1 -0
  158. package/lib/typescript/src/diagnostics/verify.d.ts +3 -0
  159. package/lib/typescript/src/diagnostics/verify.d.ts.map +1 -0
  160. package/lib/typescript/src/diagnostics/verify.native.d.ts +3 -0
  161. package/lib/typescript/src/diagnostics/verify.native.d.ts.map +1 -0
  162. package/lib/typescript/src/index.d.ts +4 -1
  163. package/lib/typescript/src/index.d.ts.map +1 -1
  164. package/lib/typescript/src/reanimated/variant/useUpdateVariantColor.native.d.ts.map +1 -1
  165. package/lib/typescript/src/specs/ShadowRegistry/index.d.ts +5 -1
  166. package/lib/typescript/src/specs/ShadowRegistry/index.d.ts.map +1 -1
  167. package/lib/typescript/src/specs/ShadowRegistry/types.d.ts +3 -0
  168. package/lib/typescript/src/specs/ShadowRegistry/types.d.ts.map +1 -1
  169. package/lib/typescript/src/specs/StyleSheet/index.d.ts +4 -1
  170. package/lib/typescript/src/specs/StyleSheet/index.d.ts.map +1 -1
  171. package/lib/typescript/src/specs/UnistylesRuntime/index.d.ts +2 -2
  172. package/lib/typescript/src/specs/UnistylesRuntime/index.d.ts.map +1 -1
  173. package/lib/typescript/src/types/breakpoints.d.ts +1 -1
  174. package/lib/typescript/src/types/breakpoints.d.ts.map +1 -1
  175. package/lib/typescript/src/types/core.d.ts +7 -2
  176. package/lib/typescript/src/types/core.d.ts.map +1 -1
  177. package/lib/typescript/src/types/stylesheet.d.ts +3 -3
  178. package/lib/typescript/src/types/stylesheet.d.ts.map +1 -1
  179. package/lib/typescript/src/web/index.d.ts.map +1 -1
  180. package/lib/typescript/src/web/registry.d.ts.map +1 -1
  181. package/nitrogen/generated/android/kotlin/com/margelo/nitro/unistyles/Func_void_UnistylesNativeMiniRuntime.kt +0 -2
  182. package/nitrogen/generated/android/kotlin/com/margelo/nitro/unistyles/Func_void_std__vector_UnistyleDependency__UnistylesNativeMiniRuntime.kt +0 -2
  183. package/nitrogen/generated/android/kotlin/com/margelo/nitro/unistyles/HybridNativePlatformSpec.kt +2 -0
  184. package/package.json +35 -26
  185. package/plugin/index.d.ts +4 -4
  186. package/plugin/index.js +21 -5
  187. package/src/components/native/ActivityIndicator.native.tsx +5 -0
  188. package/src/components/native/Animated.tsx +18 -1
  189. package/src/components/native/NativeText.native.tsx +18 -1
  190. package/src/components/native/NativeText.tsx +1 -1
  191. package/src/components/native/NativeView.native.tsx +22 -2
  192. package/src/components/native/NativeView.tsx +2 -0
  193. package/src/components/native/Pressable.native.tsx +10 -2
  194. package/src/components/native/Pressable.tsx +15 -4
  195. package/src/components/native/TouchableHighlight.native.tsx +5 -0
  196. package/src/core/createUnistylesActivityIndicator.tsx +47 -0
  197. package/src/core/createUnistylesElement.tsx +45 -14
  198. package/src/core/createUnistylesTouchableHighlight.tsx +69 -0
  199. package/src/core/index.ts +2 -0
  200. package/src/diagnostics/index.ts +3 -0
  201. package/src/diagnostics/types.ts +25 -0
  202. package/src/diagnostics/verify.native.ts +7 -0
  203. package/src/diagnostics/verify.ts +13 -0
  204. package/src/index.ts +4 -1
  205. package/src/mocks.ts +41 -0
  206. package/src/reanimated/variant/useUpdateVariantColor.native.ts +7 -1
  207. package/src/specs/ShadowRegistry/index.ts +128 -1
  208. package/src/specs/ShadowRegistry/types.ts +3 -0
  209. package/src/specs/StyleSheet/index.ts +10 -2
  210. package/src/specs/UnistylesRuntime/index.ts +7 -2
  211. package/src/types/breakpoints.ts +5 -3
  212. package/src/types/core.ts +7 -28
  213. package/src/types/stylesheet.ts +3 -3
  214. package/src/web/convert/object/boxShadow.ts +2 -2
  215. package/src/web/index.ts +2 -1
  216. package/src/web/registry.ts +3 -0
@@ -6,6 +6,11 @@ using namespace facebook::react;
6
6
  jsi::Value HybridShadowRegistry::link(jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
7
7
  helpers::assertThat(rt, count == 2, "Unistyles: Invalid babel transform 'ShadowRegistry link' expected 2 arguments.");
8
8
 
9
+ // runtime was replaced, registry belongs to the next one
10
+ if (!this->_unistylesRuntime->ownsRegistry()) {
11
+ return jsi::Value::undefined();
12
+ }
13
+
9
14
  auto shadowNodeWrapper = getShadowNodeFromRef(rt, args[0]);
10
15
 
11
16
  std::vector<core::Unistyle::Shared> unistyleWrappers = core::unistyleFromValue(rt, args[1]);
@@ -87,37 +92,79 @@ jsi::Value HybridShadowRegistry::link(jsi::Runtime &rt, const jsi::Value &thisVa
87
92
  parser.rebuildUnistyleWithScopedTheme(rt, parsedStyleSheet, unistyleData);
88
93
  }
89
94
 
90
- // Initialize parsedStyle from the unistyle's current computed style from withUnistyles
95
+ // The shared cache can belong to another view by the time React attaches refs.
96
+ // Restore this view's arguments and variants before saving its native styles.
91
97
  if (!unistyleData->parsedStyle.has_value() && unistyle->parsedStyle.has_value()) {
92
- unistyleData->parsedStyle = jsi::Value(rt, unistyle->parsedStyle.value()).asObject(rt);
98
+ parser.rebuildUnistyleWithVariants(rt, unistyleData);
93
99
  }
94
100
 
95
101
  unistylesData.emplace_back(unistyleData);
96
102
  }
97
103
 
98
104
  std::optional<folly::dynamic> initialScopedUpdate;
105
+ bool shouldCommit = false;
99
106
 
100
- if (scopedTheme.has_value() || wasSuspended) {
101
- initialScopedUpdate = parser.parseStylesToShadowTreeStyles(rt, unistylesData);
107
+ if (scopedTheme.has_value()) {
108
+ initialScopedUpdate = parser.parseStylesToShadowTreeUpdates(rt, unistylesData);
109
+ shouldCommit = shadow::hasNativeProps(&shadowNodeWrapper->getFamily());
110
+ } else if (shadow::hasNativeProps(&shadowNodeWrapper->getFamily())) {
111
+ auto update = parser.parseStylesToShadowTreeUpdates(rt, unistylesData);
112
+
113
+ if (shadow::hasOutdatedNativeProps(&shadowNodeWrapper->getFamily(), update)) {
114
+ initialScopedUpdate = std::move(update);
115
+ shouldCommit = true;
116
+ }
102
117
  }
103
118
 
104
119
  registry.linkShadowNodeWithUnistyle(
105
120
  rt,
106
- &shadowNodeWrapper->getFamily(),
121
+ this->_unistylesRuntime->generation,
122
+ shadowNodeWrapper->getFamilyShared(),
107
123
  unistylesData,
108
124
  std::move(initialScopedUpdate)
109
125
  );
110
126
 
111
- if (wasSuspended) {
112
- shadow::ShadowTreeManager::updateShadowTree(rt);
127
+ if (shouldCommit) {
128
+ this->scheduleShadowTreeUpdate(rt);
113
129
  }
114
130
 
115
131
  return jsi::Value::undefined();
116
132
  }
117
133
 
134
+ void HybridShadowRegistry::scheduleShadowTreeUpdate(jsi::Runtime& rt) {
135
+ if (this->_isShadowTreeUpdateScheduled->exchange(true)) {
136
+ return;
137
+ }
138
+
139
+ auto isShadowTreeUpdateScheduled = this->_isShadowTreeUpdateScheduled;
140
+ auto unistylesRuntime = this->_unistylesRuntime;
141
+
142
+ rt.queueMicrotask(jsi::Function::createFromHostFunction(
143
+ rt,
144
+ jsi::PropNameID::forAscii(rt, "unistylesUpdateShadowTree"),
145
+ 0,
146
+ [isShadowTreeUpdateScheduled, unistylesRuntime](jsi::Runtime& rt, const jsi::Value&, const jsi::Value*, size_t) {
147
+ isShadowTreeUpdateScheduled->store(false);
148
+
149
+ // pending updates belong to the runtime that replaced this one
150
+ if (!unistylesRuntime->ownsRegistry()) {
151
+ return jsi::Value::undefined();
152
+ }
153
+
154
+ shadow::ShadowTreeManager::updateShadowTree(rt);
155
+
156
+ return jsi::Value::undefined();
157
+ }
158
+ ));
159
+ }
160
+
118
161
  jsi::Value HybridShadowRegistry::unlink(jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
119
162
  helpers::assertThat(rt, count == 1, "Unistyles: Invalid babel transform 'ShadowRegistry unlink' expected 1 argument.");
120
163
 
164
+ if (!this->_unistylesRuntime->ownsRegistry()) {
165
+ return jsi::Value::undefined();
166
+ }
167
+
121
168
  auto shadowNodeWrapper = getShadowNodeFromRef(rt, args[0]);
122
169
 
123
170
  auto& registry = core::UnistylesRegistry::get();
@@ -130,6 +177,10 @@ jsi::Value HybridShadowRegistry::unlink(jsi::Runtime &rt, const jsi::Value &this
130
177
  jsi::Value HybridShadowRegistry::suspend(jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
131
178
  helpers::assertThat(rt, count == 1, "Unistyles: Invalid babel transform 'ShadowRegistry suspend' expected 1 argument.");
132
179
 
180
+ if (!this->_unistylesRuntime->ownsRegistry()) {
181
+ return jsi::Value::undefined();
182
+ }
183
+
133
184
  auto shadowNodeWrapper = getShadowNodeFromRef(rt, args[0]);
134
185
  auto& registry = core::UnistylesRegistry::get();
135
186
 
@@ -139,6 +190,10 @@ jsi::Value HybridShadowRegistry::suspend(jsi::Runtime &rt, const jsi::Value &thi
139
190
  }
140
191
 
141
192
  jsi::Value HybridShadowRegistry::flush(jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
193
+ if (!this->_unistylesRuntime->ownsRegistry()) {
194
+ return jsi::Value::undefined();
195
+ }
196
+
142
197
  shadow::ShadowTreeManager::updateShadowTree(rt);
143
198
 
144
199
  return jsi::Value::undefined();
@@ -147,6 +202,10 @@ jsi::Value HybridShadowRegistry::flush(jsi::Runtime &rt, const jsi::Value &thisV
147
202
  jsi::Value HybridShadowRegistry::setScopedTheme(jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
148
203
  helpers::assertThat(rt, count == 1, "Unistyles: setScopedTheme expected 1 argument.");
149
204
 
205
+ if (!this->_unistylesRuntime->ownsRegistry()) {
206
+ return jsi::Value::undefined();
207
+ }
208
+
150
209
  auto& registry = core::UnistylesRegistry::get();
151
210
 
152
211
  if (args[0].isUndefined()) {
@@ -169,10 +228,68 @@ jsi::Value HybridShadowRegistry::getScopedTheme(jsi::Runtime &rt, const jsi::Val
169
228
  : jsi::Value::undefined();
170
229
  }
171
230
 
231
+ jsi::Value HybridShadowRegistry::verify(jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
232
+ return shadow::ShadowTreeDiagnostics::verify(rt, this->_unistylesRuntime);
233
+ }
234
+
235
+ jsi::Value HybridShadowRegistry::takeCommittedTags(jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
236
+ auto tags = core::UnistylesRegistry::get().takeCommittedTags();
237
+ auto result = jsi::Array(rt, tags.size());
238
+
239
+ for (size_t i = 0; i < tags.size(); i++) {
240
+ result.setValueAtIndex(rt, i, jsi::Value(static_cast<double>(tags[i])));
241
+ }
242
+
243
+ return result;
244
+ }
245
+
246
+ jsi::Value HybridShadowRegistry::refreshReactNodes(jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
247
+ helpers::assertThat(rt, count == 1 && args[0].isObject(), "Unistyles: refreshReactNodes expected to be called with an array of shadow nodes.");
248
+
249
+ auto nodes = args[0].asObject(rt).asArray(rt);
250
+ std::unordered_map<SurfaceId, std::shared_ptr<const RootShadowNode>> roots;
251
+
252
+ UIManagerBinding::getBinding(rt)->getUIManager().getShadowTreeRegistry().enumerate([&roots](const ShadowTree& shadowTree, bool&) {
253
+ roots.emplace(shadowTree.getSurfaceId(), shadowTree.getCurrentRevision().rootShadowNode);
254
+ });
255
+
256
+ for (size_t i = 0; i < nodes.size(rt); i++) {
257
+ auto node = nodes.getValueAtIndex(rt, i);
258
+
259
+ if (!node.isObject() || !node.asObject(rt).hasNativeState<ShadowNodeWrapper>(rt)) {
260
+ continue;
261
+ }
262
+
263
+ auto wrapper = node.asObject(rt).getNativeState<ShadowNodeWrapper>(rt);
264
+
265
+ // never committed, React cloned it in a render that isn't committed yet (eg. a transition that yielded)
266
+ // it holds what React rendered since, pointing it at the committed node would drop that
267
+ if (!wrapper->shadowNode->getHasBeenPromoted()) {
268
+ continue;
269
+ }
270
+
271
+ const auto& family = wrapper->shadowNode->getFamily();
272
+ auto rootIt = roots.find(family.getSurfaceId());
273
+
274
+ if (rootIt == roots.end()) {
275
+ continue;
276
+ }
277
+
278
+ auto ancestors = family.getAncestors(*rootIt->second);
279
+
280
+ // frozen nodes are not in the committed tree, nativeProps_DEPRECATED covers them
281
+ if (ancestors.empty()) {
282
+ continue;
283
+ }
284
+
285
+ const auto& [parent, index] = ancestors.back();
286
+
287
+ wrapper->shadowNode = parent.get().getChildren().at(index);
288
+ }
289
+
290
+ return jsi::Value::undefined();
291
+ }
292
+
172
293
  std::shared_ptr<const core::ShadowNode> HybridShadowRegistry::getShadowNodeFromRef(jsi::Runtime& rt, const jsi::Value& maybeRef) {
173
- #if REACT_NATIVE_VERSION_MINOR >= 81
174
294
  return Bridging<std::shared_ptr<const ShadowNode>>::fromJs(rt, maybeRef);
175
- #else
176
- return shadowNodeFromValue(rt, maybeRef);
177
- #endif
178
295
  }
@@ -6,7 +6,9 @@
6
6
  #include "UnistylesState.h"
7
7
  #include "UnistylesRegistry.h"
8
8
  #include "ShadowTreeManager.h"
9
+ #include "ShadowTreeDiagnostics.h"
9
10
  #include <cxxreact/ReactNativeVersion.h>
11
+ #include <atomic>
10
12
 
11
13
  namespace margelo::nitro::unistyles {
12
14
 
@@ -38,6 +40,18 @@ struct HybridShadowRegistry: public HybridUnistylesShadowRegistrySpec {
38
40
  const jsi::Value& thisValue,
39
41
  const jsi::Value* args,
40
42
  size_t count);
43
+ jsi::Value verify(jsi::Runtime& rt,
44
+ const jsi::Value& thisValue,
45
+ const jsi::Value* args,
46
+ size_t count);
47
+ jsi::Value takeCommittedTags(jsi::Runtime& rt,
48
+ const jsi::Value& thisValue,
49
+ const jsi::Value* args,
50
+ size_t count);
51
+ jsi::Value refreshReactNodes(jsi::Runtime& rt,
52
+ const jsi::Value& thisValue,
53
+ const jsi::Value* args,
54
+ size_t count);
41
55
 
42
56
  void loadHybridMethods() override {
43
57
  HybridUnistylesShadowRegistrySpec::loadHybridMethods();
@@ -49,13 +63,19 @@ struct HybridShadowRegistry: public HybridUnistylesShadowRegistrySpec {
49
63
  prototype.registerRawHybridMethod("flush", 0, &HybridShadowRegistry::flush);
50
64
  prototype.registerRawHybridMethod("setScopedTheme", 1, &HybridShadowRegistry::setScopedTheme);
51
65
  prototype.registerRawHybridMethod("getScopedTheme", 0, &HybridShadowRegistry::getScopedTheme);
66
+ prototype.registerRawHybridMethod("verify", 0, &HybridShadowRegistry::verify);
67
+ prototype.registerRawHybridMethod("takeCommittedTags", 0, &HybridShadowRegistry::takeCommittedTags);
68
+ prototype.registerRawHybridMethod("refreshReactNodes", 1, &HybridShadowRegistry::refreshReactNodes);
52
69
  });
53
70
  };
54
71
 
55
72
  std::shared_ptr<const core::ShadowNode> getShadowNodeFromRef(jsi::Runtime& rt, const jsi::Value& maybeRef);
56
73
 
57
74
  private:
75
+ void scheduleShadowTreeUpdate(jsi::Runtime& rt);
76
+
58
77
  std::shared_ptr<HybridUnistylesRuntime> _unistylesRuntime;
78
+ std::shared_ptr<std::atomic<bool>> _isShadowTreeUpdateScheduled = std::make_shared<std::atomic<bool>>(false);
59
79
  };
60
80
 
61
81
  }
@@ -16,12 +16,13 @@ double HybridStyleSheet::getHairlineWidth() {
16
16
  jsi::Value HybridStyleSheet::create(jsi::Runtime& rt, const jsi::Value &thisVal, const jsi::Value *arguments, size_t count) {
17
17
  helpers::assertThat(rt, count == 1, "StyleSheet.create expected to be called with one argument.");
18
18
  helpers::assertThat(rt, arguments[0].isObject(), "StyleSheet.create expected to be called with object or function.");
19
+ helpers::assertThat(rt, this->_unistylesRuntime->ownsRegistry(), helpers::RUNTIME_REPLACED_ERROR);
19
20
 
20
21
  auto thisStyleSheet = thisVal.asObject(rt);
21
22
  auto& registry = core::UnistylesRegistry::get();
22
23
 
23
24
  jsi::Object rawStyleSheet = arguments[0].asObject(rt);
24
- auto registeredStyleSheet = registry.addStyleSheetFromValue(rt, std::move(rawStyleSheet));
25
+ auto registeredStyleSheet = registry.addStyleSheetFromValue(rt, this->_unistylesRuntime->generation, std::move(rawStyleSheet));
25
26
 
26
27
  auto parser = parser::Parser(this->_unistylesRuntime);
27
28
 
@@ -34,6 +35,7 @@ jsi::Value HybridStyleSheet::create(jsi::Runtime& rt, const jsi::Value &thisVal,
34
35
  jsi::Value HybridStyleSheet::configure(jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *arguments, size_t count) {
35
36
  helpers::assertThat(rt, count == 1, "StyleSheet.configure expected to be called with one argument.");
36
37
  helpers::assertThat(rt, arguments[0].isObject(), "StyleSheet.configure expected to be called with object.");
38
+ helpers::assertThat(rt, this->_unistylesRuntime->ownsRegistry(), helpers::RUNTIME_REPLACED_ERROR);
37
39
 
38
40
  auto config = arguments[0].asObject(rt);
39
41
 
@@ -73,6 +75,8 @@ jsi::Value HybridStyleSheet::init(jsi::Runtime &rt, const jsi::Value &thisVal, c
73
75
  return jsi::Value::undefined();
74
76
  }
75
77
 
78
+ helpers::assertThat(rt, this->_unistylesRuntime->ownsRegistry(), helpers::RUNTIME_REPLACED_ERROR);
79
+
76
80
  // create new state
77
81
  auto& registry = core::UnistylesRegistry::get();
78
82
 
@@ -165,7 +169,7 @@ void HybridStyleSheet::parseThemes(jsi::Runtime &rt, jsi::Object themes) {
165
169
  helpers::enumerateJSIObject(rt, themes, [&](const std::string& propertyName, jsi::Value& propertyValue){
166
170
  helpers::assertThat(rt, propertyValue.isObject(), "StyleSheet.configure's registered theme '" + propertyName + "' must be an object.");
167
171
 
168
- registry.registerTheme(rt, propertyName, propertyValue);
172
+ registry.registerTheme(rt, this->_unistylesRuntime->generation, propertyName, propertyValue);
169
173
  });
170
174
  }
171
175
 
@@ -267,7 +271,8 @@ void HybridStyleSheet::onPlatformDependenciesChange(std::vector<UnistyleDependen
267
271
  this->_unistylesRuntime->runOnJSThread([weakSelf, dependencies](jsi::Runtime& rt) {
268
272
  auto self = std::dynamic_pointer_cast<HybridStyleSheet>(weakSelf.lock());
269
273
 
270
- if (!self) {
274
+ // runtime was replaced in the meantime, registry belongs to the next one
275
+ if (!self || !self->_unistylesRuntime->ownsRegistry()) {
271
276
  return;
272
277
  }
273
278
 
@@ -288,7 +293,7 @@ void HybridStyleSheet::onPlatformNativeDependenciesChange(std::vector<UnistyleDe
288
293
  this->_unistylesRuntime->runOnJSThread([weakSelf, dependencies, miniRuntime](jsi::Runtime& rt){
289
294
  auto self = std::dynamic_pointer_cast<HybridStyleSheet>(weakSelf.lock());
290
295
 
291
- if (!self) {
296
+ if (!self || !self->_unistylesRuntime->ownsRegistry()) {
292
297
  return;
293
298
  }
294
299
 
@@ -329,7 +334,7 @@ void HybridStyleSheet::onImeChange(UnistylesNativeMiniRuntime miniRuntime) {
329
334
  this->_unistylesRuntime->runOnJSThread([weakSelf, miniRuntime](jsi::Runtime& rt){
330
335
  auto self = std::dynamic_pointer_cast<HybridStyleSheet>(weakSelf.lock());
331
336
 
332
- if (!self) {
337
+ if (!self || !self->_unistylesRuntime->ownsRegistry()) {
333
338
  return;
334
339
  }
335
340
 
@@ -348,22 +353,25 @@ void HybridStyleSheet::applyDependencyChanges(jsi::Runtime& rt, std::vector<Unis
348
353
  // but their rawValue must still be refreshed so rerenders read fresh closures
349
354
  auto dependentStyleSheets = registry.getStyleSheetsToRefresh(dependencies);
350
355
 
351
- if (dependencyMap.empty() && dependentStyleSheets.empty()) {
352
- return;
353
- }
354
-
355
356
  // rebuild rawValue BEFORE notifying listeners so JS rerenders read fresh closures
356
- parser.rebuildUnistylesInDependencyMap(rt, dependencyMap, dependentStyleSheets, maybeMiniRuntime);
357
+ if (!dependencyMap.empty() || !dependentStyleSheets.empty()) {
358
+ parser.rebuildUnistylesInDependencyMap(rt, dependencyMap, dependentStyleSheets, maybeMiniRuntime);
359
+ }
357
360
 
358
361
  if (!dependencyMap.empty()) {
359
362
  parser.rebuildShadowLeafUpdates(rt, dependencyMap);
360
363
  }
361
364
 
362
- this->notifyJSListeners(dependencies);
365
+ std::vector<Tag> committedTags;
363
366
 
364
367
  if (!dependencyMap.empty()) {
365
- shadow::ShadowTreeManager::updateShadowTree(rt);
368
+ committedTags = shadow::ShadowTreeManager::updateShadowTree(rt);
366
369
  }
370
+
371
+ // after the commit, so listeners can point React at the committed nodes
372
+ registry.setCommittedTags(std::move(committedTags));
373
+ // also when no StyleSheet depends on the change, hooks (useUnistyles, withUnistyles, Display, Hide) may
374
+ this->notifyJSListeners(dependencies);
367
375
  }
368
376
 
369
377
  void HybridStyleSheet::notifyJSListeners(std::vector<UnistyleDependency>& dependencies) {
@@ -4,9 +4,18 @@
4
4
  using namespace margelo::nitro::unistyles;
5
5
 
6
6
  core::UnistylesState& HybridUnistylesRuntime::getState() {
7
+ // state belongs to the runtime that replaced this one
8
+ if (!this->ownsRegistry()) {
9
+ throw std::runtime_error(helpers::RUNTIME_REPLACED_ERROR);
10
+ }
11
+
7
12
  return core::UnistylesRegistry::get().getState();
8
13
  }
9
14
 
15
+ bool HybridUnistylesRuntime::ownsRegistry() const {
16
+ return core::UnistylesRegistry::get().isCurrent(this->generation);
17
+ }
18
+
10
19
  ColorScheme HybridUnistylesRuntime::getColorScheme() {
11
20
  auto colorScheme = this->_nativePlatform->getColorScheme();
12
21
 
@@ -166,13 +175,14 @@ jsi::Value HybridUnistylesRuntime::updateTheme(jsi::Runtime &rt, const jsi::Valu
166
175
  helpers::assertThat(rt, count == 2, "UnistylesRuntime.updateTheme expected to be called with 2 arguments.");
167
176
  helpers::assertThat(rt, args[0].isString(), "UnistylesRuntime.updateTheme expected first argument to be a string.");
168
177
  helpers::assertThat(rt, args[1].isObject(), "UnistylesRuntime.updateTheme expected first argument to be a function.");
178
+ helpers::assertThat(rt, this->ownsRegistry(), helpers::RUNTIME_REPLACED_ERROR);
169
179
 
170
180
  auto& registry = core::UnistylesRegistry::get();
171
181
  auto themeName = args[0].asString(rt).utf8(rt);
172
182
 
173
183
  helpers::assertThat(rt, args[1].asObject(rt).isFunction(rt), "UnistylesRuntime.updateTheme expected second argument to be a function.");
174
184
 
175
- registry.updateTheme(rt, themeName, args[1].asObject(rt).asFunction(rt));
185
+ registry.updateTheme(rt, this->generation, themeName, args[1].asObject(rt).asFunction(rt));
176
186
 
177
187
  this->_onDependenciesChange({UnistyleDependency::THEME});
178
188
 
@@ -13,8 +13,8 @@
13
13
  namespace margelo::nitro::unistyles {
14
14
 
15
15
  struct HybridUnistylesRuntime: public HybridUnistylesRuntimeSpec {
16
- HybridUnistylesRuntime(std::shared_ptr<HybridNativePlatformSpec> nativePlatform, std::function<void(std::function<void(jsi::Runtime&)>&&)> runOnJSThread)
17
- : HybridObject(TAG), _nativePlatform{nativePlatform}, runOnJSThread(std::move(runOnJSThread)) {}
16
+ HybridUnistylesRuntime(std::shared_ptr<HybridNativePlatformSpec> nativePlatform, std::function<void(std::function<void(jsi::Runtime&)>&&)> runOnJSThread, uint64_t generation)
17
+ : HybridObject(TAG), _nativePlatform{nativePlatform}, runOnJSThread(std::move(runOnJSThread)), generation{generation} {}
18
18
 
19
19
  jsi::Value getTheme(jsi::Runtime& rt,
20
20
  const jsi::Value& thisValue,
@@ -70,11 +70,13 @@ struct HybridUnistylesRuntime: public HybridUnistylesRuntimeSpec {
70
70
  std::unordered_map<std::string, double> getBreakpoints() override;
71
71
 
72
72
  core::UnistylesState& getState();
73
+ bool ownsRegistry() const;
73
74
  UnistylesCxxMiniRuntime buildMiniRuntimeFromNativeRuntime(UnistylesNativeMiniRuntime& nativeMiniRuntime);
74
75
  jsi::Value getMiniRuntimeAsValue(jsi::Runtime& rt, std::optional<UnistylesNativeMiniRuntime> maybeMiniRuntime);
75
76
  void includeDependenciesForColorSchemeChange(std::vector<UnistyleDependency>& deps);
76
77
  void calculateNewThemeAndDependencies(std::vector<UnistyleDependency>& deps);
77
78
  std::function<void(std::function<void(jsi::Runtime&)>&&)> runOnJSThread;
79
+ const uint64_t generation;
78
80
 
79
81
  private:
80
82
  std::shared_ptr<HybridNavigationBar> _navigationBar;
@@ -439,13 +439,12 @@ void parser::Parser::rebuildShadowLeafUpdates(jsi::Runtime& rt, core::Dependency
439
439
 
440
440
  for (const auto& [shadowNode, unistyles] : dependencyMap) {
441
441
  // Parse string colors (e.g., "#000000") to int representation
442
- auto rawProps = this->parseStylesToShadowTreeStyles(rt, unistyles);
442
+ auto rawProps = this->parseStylesToShadowTreeUpdates(rt, unistyles);
443
443
 
444
444
  updates.emplace(shadowNode, std::move(rawProps));
445
445
  }
446
446
 
447
- registry.trafficController.setUpdates(updates);
448
- registry.trafficController.resumeUnistylesTraffic();
447
+ registry.queueShadowLeafUpdatesUnsafe(updates);
449
448
  });
450
449
  }
451
450
 
@@ -1269,6 +1268,34 @@ folly::dynamic parser::Parser::parseStylesToShadowTreeStyles(jsi::Runtime& rt, c
1269
1268
  return jsi::dynamicFromValue(rt, jsi::Value(rt, convertedStyles));
1270
1269
  }
1271
1270
 
1271
+ // shadow tree updates skip the props an inline style sets last, React (or an animation) owns them
1272
+ // writing them would also leave them in nativeProps_DEPRECATED, which React Native applies to every later clone,
1273
+ // so a value captured at link time would undo what React rendered since
1274
+ folly::dynamic parser::Parser::parseStylesToShadowTreeUpdates(jsi::Runtime& rt, const std::vector<std::shared_ptr<UnistyleData>>& unistyles) {
1275
+ auto props = this->parseStylesToShadowTreeStyles(rt, unistyles);
1276
+ std::unordered_map<std::string, bool> isOwnedByInlineStyle;
1277
+
1278
+ for (const auto& unistyleData : unistyles) {
1279
+ if (!unistyleData->parsedStyle.has_value()) {
1280
+ continue;
1281
+ }
1282
+
1283
+ bool isInlineStyle = unistyleData->unistyle->styleKey == helpers::EXOTIC_STYLE_KEY;
1284
+
1285
+ helpers::enumerateJSIObject(rt, unistyleData->parsedStyle.value(), [&isOwnedByInlineStyle, isInlineStyle](const std::string& propName, jsi::Value&) {
1286
+ isOwnedByInlineStyle[propName] = isInlineStyle;
1287
+ });
1288
+ }
1289
+
1290
+ for (const auto& [propName, isInline] : isOwnedByInlineStyle) {
1291
+ if (isInline) {
1292
+ props.erase(propName);
1293
+ }
1294
+ }
1295
+
1296
+ return props;
1297
+ }
1298
+
1272
1299
 
1273
1300
  // check is styleKey contains color
1274
1301
  bool parser::Parser::isColor(const std::string& propertyName) {
@@ -28,6 +28,7 @@ struct Parser {
28
28
  void rebuildUnistylesInDependencyMap(jsi::Runtime& rt, core::DependencyMap& dependencyMap, std::vector<std::shared_ptr<core::StyleSheet>>& styleSheets, std::optional<UnistylesNativeMiniRuntime> maybeMiniRuntime);
29
29
  void rebuildShadowLeafUpdates(jsi::Runtime& rt, core::DependencyMap& dependencyMap);
30
30
  folly::dynamic parseStylesToShadowTreeStyles(jsi::Runtime& rt, const std::vector<std::shared_ptr<UnistyleData>>& unistyles);
31
+ folly::dynamic parseStylesToShadowTreeUpdates(jsi::Runtime& rt, const std::vector<std::shared_ptr<UnistyleData>>& unistyles);
31
32
  void rebuildUnistyle(jsi::Runtime& rt, Unistyle::Shared unistyle, const Variants& variants, std::optional<std::vector<folly::dynamic>>);
32
33
  void rebuildUnistyleWithScopedTheme(jsi::Runtime& rt, jsi::Value& jsScopedTheme, std::shared_ptr<core::UnistyleData> unistyleData);
33
34
  jsi::Value getParsedStyleSheetForScopedTheme(jsi::Runtime& rt, core::Unistyle::Shared unistyle, std::string& scopedTheme);
@@ -0,0 +1,88 @@
1
+ #pragma once
2
+
3
+ #include <folly/dynamic.h>
4
+ #include <react/renderer/core/ShadowNodeFamily.h>
5
+ #include <cxxreact/ReactNativeVersion.h>
6
+ #include <algorithm>
7
+ #include <cstdint>
8
+ #include <memory>
9
+ #include <mutex>
10
+ #include <string>
11
+
12
+ // React Native reads ShadowNodeFamily::nativeProps_DEPRECATED from any thread
13
+ // (eg. ShadowNode::clone during main thread layout) and since 0.86.1 guards every access with nativePropsMutex
14
+ // we need to hold the same lock, otherwise mutating / replacing the folly::dynamic races with RN copying it
15
+ #if REACT_NATIVE_VERSION_MINOR > 86 || (REACT_NATIVE_VERSION_MINOR == 86 && REACT_NATIVE_VERSION_PATCH >= 1)
16
+ #define UNISTYLES_HAS_NATIVE_PROPS_MUTEX 1
17
+ #else
18
+ #define UNISTYLES_HAS_NATIVE_PROPS_MUTEX 0
19
+ #endif
20
+
21
+ namespace margelo::nitro::unistyles::shadow {
22
+
23
+ using namespace facebook::react;
24
+
25
+ inline void mergeNativeProps(const ShadowNodeFamily* family, const folly::dynamic& props) {
26
+ #if UNISTYLES_HAS_NATIVE_PROPS_MUTEX
27
+ std::lock_guard<std::mutex> lock(family->nativePropsMutex);
28
+ #endif
29
+
30
+ if (family->nativeProps_DEPRECATED && family->nativeProps_DEPRECATED->isObject()) {
31
+ family->nativeProps_DEPRECATED->update(props);
32
+
33
+ return;
34
+ }
35
+
36
+ family->nativeProps_DEPRECATED = std::make_unique<folly::dynamic>(props);
37
+ }
38
+
39
+ inline bool hasNativeProps(const ShadowNodeFamily* family) {
40
+ #if UNISTYLES_HAS_NATIVE_PROPS_MUTEX
41
+ std::lock_guard<std::mutex> lock(family->nativePropsMutex);
42
+ #endif
43
+
44
+ return family->nativeProps_DEPRECATED && family->nativeProps_DEPRECATED->isObject();
45
+ }
46
+
47
+ inline bool isSamePropValue(const std::string& propName, const folly::dynamic& value, const folly::dynamic& otherValue) {
48
+ if (value == otherValue) {
49
+ return true;
50
+ }
51
+
52
+ if (!value.isNumber() || !otherValue.isNumber()) {
53
+ return false;
54
+ }
55
+
56
+ std::string lowercasePropName = propName;
57
+ std::transform(lowercasePropName.begin(), lowercasePropName.end(), lowercasePropName.begin(), ::tolower);
58
+
59
+ if (lowercasePropName.find("color") == std::string::npos) {
60
+ return false;
61
+ }
62
+
63
+ return static_cast<uint32_t>(static_cast<int64_t>(value.asDouble())) == static_cast<uint32_t>(static_cast<int64_t>(otherValue.asDouble()));
64
+ }
65
+
66
+ inline bool hasOutdatedNativeProps(const ShadowNodeFamily* family, const folly::dynamic& props) {
67
+ #if UNISTYLES_HAS_NATIVE_PROPS_MUTEX
68
+ std::lock_guard<std::mutex> lock(family->nativePropsMutex);
69
+ #endif
70
+
71
+ if (!family->nativeProps_DEPRECATED || !family->nativeProps_DEPRECATED->isObject() || !props.isObject()) {
72
+ return false;
73
+ }
74
+
75
+ auto& nativeProps = *family->nativeProps_DEPRECATED;
76
+
77
+ for (const auto& [propName, propValue] : props.items()) {
78
+ auto it = nativeProps.find(propName);
79
+
80
+ if (it != nativeProps.items().end() && !isSamePropValue(propName.asString(), it->second, propValue)) {
81
+ return true;
82
+ }
83
+ }
84
+
85
+ return false;
86
+ }
87
+
88
+ }
@@ -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,36 @@ 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 bool shouldStop() {
12
- return !_canCommit;
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
- inline void resumeUnistylesTraffic() {
20
- this->_canCommit = true;
15
+ _unistylesUpdates.insert_or_assign(key, PinnedShadowLeafUpdate{std::move(family), std::move(props)});
21
16
  }
22
17
 
23
- inline shadow::ShadowLeafUpdates& getUpdates() {
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 void setUpdates(shadow::ShadowLeafUpdates& newUpdates) {
23
+ inline std::shared_ptr<const ShadowNodeFamily> removeShadowNode(const ShadowNodeFamily* shadowNodeFamily) {
29
24
  // call it only within withLock!
30
- auto& targetUpdates = _unistylesUpdates;
25
+ // returns the pin, so the caller can release it after unlocking
26
+ auto it = _unistylesUpdates.find(shadowNodeFamily);
31
27
 
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);
28
+ if (it == _unistylesUpdates.end()) {
29
+ return nullptr;
30
+ }
37
31
 
38
- return;
39
- }
32
+ auto family = std::move(it->second.family);
40
33
 
41
- targetUpdates.emplace(pair.first, std::move(pair.second));
42
- });
43
- }
34
+ _unistylesUpdates.erase(it);
44
35
 
45
- inline void removeShadowNode(const ShadowNodeFamily* shadowNodeFamily) {
46
- // call it only within withLock!
47
- if (_unistylesUpdates.contains(shadowNodeFamily)) {
48
- _unistylesUpdates.erase(shadowNodeFamily);
49
- }
36
+ return family;
50
37
  }
51
38
 
52
- inline void restore() {
53
- // call it only within withLock!
54
-
55
- _unistylesUpdates = {};
56
- _canCommit = false;
39
+ inline size_t getUpdatesCount() const {
40
+ return _unistylesUpdates.size();
57
41
  }
58
42
 
59
43
  template <typename F>
@@ -64,8 +48,7 @@ struct ShadowTrafficController {
64
48
  }
65
49
 
66
50
  private:
67
- std::atomic<bool> _canCommit = false;
68
- shadow::ShadowLeafUpdates _unistylesUpdates{};
51
+ PinnedShadowLeafUpdates _unistylesUpdates{};
69
52
 
70
53
  // this struct should be accessed in thread-safe manner. Otherwise shadow tree updates
71
54
  // from different threads will break it