react-native-tvos 0.77.0-0rc1 → 0.77.1-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 (74) hide show
  1. package/Libraries/Animated/animations/Animation.js +1 -1
  2. package/Libraries/Components/Pressable/Pressable.d.ts +2 -2
  3. package/Libraries/Components/Pressable/Pressable.js +3 -3
  4. package/Libraries/Components/ScrollView/ScrollView.js +3 -2
  5. package/Libraries/Components/TV/TVFocusGuideView.js +2 -6
  6. package/Libraries/Components/TextInput/TextInput.js +1 -1
  7. package/Libraries/Components/Touchable/TouchableNativeFeedback.js +1 -1
  8. package/Libraries/Components/Touchable/TouchableOpacity.js +1 -2
  9. package/Libraries/Components/View/View.js +2 -3
  10. package/Libraries/Components/View/ViewNativeComponent.js +2 -2
  11. package/Libraries/Components/View/ViewPropTypes.d.ts +4 -2
  12. package/Libraries/Core/ReactNativeVersion.js +2 -2
  13. package/Libraries/Core/setUpDeveloperTools.js +2 -3
  14. package/Libraries/Image/RCTImageLoader.mm +9 -1
  15. package/Libraries/LogBox/LogBoxNotificationContainer.js +1 -1
  16. package/Libraries/LogBox/UI/LogBoxButton.js +2 -2
  17. package/Libraries/LogBox/UI/LogBoxNotification.js +1 -1
  18. package/Libraries/Pressability/Pressability.js +2 -2
  19. package/Libraries/Text/TextInput/RCTBaseTextInputView.mm +1 -1
  20. package/Libraries/Utilities/BackHandler.ios.js +3 -8
  21. package/Libraries/Utilities/HMRClient.js +0 -28
  22. package/Libraries/Utilities/HMRClientProdShim.js +0 -1
  23. package/Libraries/Utilities/setAndForwardRef.js +2 -2
  24. package/React/Base/RCTConvert.mm +3 -1
  25. package/React/Base/RCTVersion.m +2 -2
  26. package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +89 -40
  27. package/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +11 -1
  28. package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +5 -2
  29. package/React/Views/RCTTVView.m +5 -2
  30. package/React/Views/ScrollView/RCTScrollView.m +63 -26
  31. package/ReactAndroid/api/ReactAndroid.api +5 -0
  32. package/ReactAndroid/cmake-utils/ReactNative-application.cmake +13 -3
  33. package/ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp +0 -13
  34. package/ReactAndroid/gradle.properties +2 -2
  35. package/ReactAndroid/src/main/java/com/facebook/react/HeadlessJsTaskService.java +12 -13
  36. package/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java +8 -0
  37. package/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java +8 -0
  38. package/ReactAndroid/src/main/java/com/facebook/react/ReactDelegate.java +37 -0
  39. package/ReactAndroid/src/main/java/com/facebook/react/bridge/BaseJavaModule.java +1 -1
  40. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/StackTraceHelper.java +6 -2
  41. package/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java +15 -8
  42. package/ReactAndroid/src/main/java/com/facebook/react/module/model/ReactModuleInfo.kt +18 -0
  43. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java +2 -2
  44. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CompositeBackgroundDrawable.kt +9 -8
  45. package/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt +22 -2
  46. package/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java +16 -2
  47. package/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactClippingViewManager.kt +0 -3
  48. package/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java +33 -0
  49. package/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.kt +12 -0
  50. package/ReactCommon/cxxreact/ReactNativeVersion.h +2 -2
  51. package/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTInteropTurboModule.mm +13 -4
  52. package/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.h +5 -0
  53. package/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm +27 -9
  54. package/ReactCommon/react/renderer/attributedstring/TextAttributes.cpp +6 -0
  55. package/ReactCommon/react/renderer/attributedstring/TextAttributes.h +2 -0
  56. package/ReactCommon/react/renderer/attributedstring/conversions.h +5 -0
  57. package/ReactCommon/react/renderer/components/text/BaseTextProps.cpp +12 -0
  58. package/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h +2 -3
  59. package/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm +7 -3
  60. package/ReactCommon/react/runtime/ReactInstance.cpp +39 -35
  61. package/ReactCommon/react/runtime/ReactInstance.h +2 -1
  62. package/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm +3 -2
  63. package/index.js +5 -5
  64. package/package.json +13 -10
  65. package/scripts/cocoapods/utils.rb +6 -6
  66. package/scripts/codegen/generate-artifacts-executor.js +10 -0
  67. package/sdks/hermes-engine/hermes-engine.podspec +1 -1
  68. package/sdks/hermes-engine/hermes-utils.rb +2 -2
  69. package/sdks/hermesc/linux64-bin/hermesc +0 -0
  70. package/sdks/hermesc/osx-bin/hermes +0 -0
  71. package/sdks/hermesc/osx-bin/hermesc +0 -0
  72. package/sdks/hermesc/win64-bin/hermesc.exe +0 -0
  73. package/src/private/featureflags/ReactNativeFeatureFlags.js +2 -2
  74. package/types/public/ReactNativeTVTypes.d.ts +9 -7
@@ -144,6 +144,14 @@ public abstract class ReactActivity extends AppCompatActivity
144
144
  mDelegate.onConfigurationChanged(newConfig);
145
145
  }
146
146
 
147
+ public boolean onDialogKeyUp(int keyCode, KeyEvent event) {
148
+ return mDelegate != null && mDelegate.onDialogKeyUp(keyCode, event);
149
+ }
150
+
151
+ public boolean onDialogKeyDown(int keyCode, KeyEvent event) {
152
+ return mDelegate != null && mDelegate.onDialogKeyDown(keyCode, event);
153
+ }
154
+
147
155
  protected final ReactNativeHost getReactNativeHost() {
148
156
  return mDelegate.getReactNativeHost();
149
157
  }
@@ -199,6 +199,14 @@ public class ReactActivityDelegate {
199
199
  return mReactDelegate.onBackPressed();
200
200
  }
201
201
 
202
+ public boolean onDialogKeyUp(int keyCode, KeyEvent event) {
203
+ return mReactDelegate != null && mReactDelegate.onDialogKeyUp(keyCode, event);
204
+ }
205
+
206
+ public boolean onDialogKeyDown(int keyCode, KeyEvent event) {
207
+ return mReactDelegate != null && mReactDelegate.onDialogKeyDown(keyCode, event);
208
+ }
209
+
202
210
  public boolean onNewIntent(Intent intent) {
203
211
  return mReactDelegate.onNewIntent(intent);
204
212
  }
@@ -247,6 +247,43 @@ public class ReactDelegate {
247
247
  return false;
248
248
  }
249
249
 
250
+ /**
251
+ * Handles key up events for a dialog.
252
+ * <p>
253
+ * This method currently does not perform any action and always returns {@code false}. Subclasses
254
+ * can override this method to provide custom behavior.
255
+ * </p>
256
+ *
257
+ * @param keyCode the code of the key that was released
258
+ * @param event the {@link KeyEvent} object containing full information about the event
259
+ * @return {@code true} if the event was handled, {@code false} otherwise
260
+ *
261
+ * @see KeyEvent
262
+ * @see <a href="https://github.com/react-native-tvos/react-native-tvos/issues/829">GitHub Issue #829</a>
263
+ */
264
+ public boolean onDialogKeyUp(int keyCode, KeyEvent event) {
265
+ return false;
266
+ }
267
+
268
+ /**
269
+ * Handles key down events for a dialog.
270
+ * <p>
271
+ * This method currently does not perform any action and always returns {@code false}. Subclasses
272
+ * can override this method to provide custom behavior.
273
+ * </p>
274
+ *
275
+ * @param keyCode the code of the key that was pressed
276
+ * @param event the {@link KeyEvent} object containing full information about the event
277
+ * @return {@code false} indicating the event was not handled
278
+ *
279
+ * @see KeyEvent
280
+ * @see <a href="https://github.com/react-native-tvos/react-native-tvos/issues/829">GitHub Issue #829</a>
281
+ */
282
+ public boolean onDialogKeyDown(int keyCode, KeyEvent event) {
283
+ return false;
284
+ }
285
+
286
+
250
287
  public void reload() {
251
288
  DevSupportManager devSupportManager = getDevSupportManager();
252
289
  if (devSupportManager == null) {
@@ -134,7 +134,7 @@ public abstract class BaseJavaModule implements NativeModule {
134
134
  }
135
135
 
136
136
  @DoNotStrip
137
- private final void setEventEmitterCallback(CxxCallbackImpl eventEmitterCallback) {
137
+ protected void setEventEmitterCallback(CxxCallbackImpl eventEmitterCallback) {
138
138
  mEventEmitterCallback = eventEmitterCallback;
139
139
  }
140
140
  }
@@ -268,8 +268,12 @@ public class StackTraceHelper {
268
268
  List<ReadableMap> readableMapList = new ArrayList<>();
269
269
  for (ParsedError.StackFrame frame : frames) {
270
270
  JavaOnlyMap map = new JavaOnlyMap();
271
- map.putDouble(COLUMN_KEY, frame.getColumn());
272
- map.putDouble(LINE_NUMBER_KEY, frame.getLineNumber());
271
+ if (frame.getColumn() != null) {
272
+ map.putDouble(COLUMN_KEY, frame.getColumn());
273
+ }
274
+ if (frame.getLineNumber() != null) {
275
+ map.putDouble(LINE_NUMBER_KEY, frame.getLineNumber());
276
+ }
273
277
  map.putString(FILE_KEY, (String) frame.getFile());
274
278
  map.putString(METHOD_NAME_KEY, (String) frame.getMethodName());
275
279
  readableMapList.add(map);
@@ -330,14 +330,11 @@ public class MountingManager {
330
330
  @AnyThread
331
331
  @ThreadConfined(ANY)
332
332
  public @Nullable EventEmitterWrapper getEventEmitter(int surfaceId, int reactTag) {
333
- SurfaceMountingManager surfaceMountingManager =
334
- (surfaceId == ViewUtil.NO_SURFACE_ID
335
- ? getSurfaceManagerForView(reactTag)
336
- : getSurfaceManager(surfaceId));
337
- if (surfaceMountingManager == null) {
333
+ SurfaceMountingManager smm = getSurfaceMountingManager(surfaceId, reactTag);
334
+ if (smm == null) {
338
335
  return null;
339
336
  }
340
- return surfaceMountingManager.getEventEmitter(reactTag);
337
+ return smm.getEventEmitter(reactTag);
341
338
  }
342
339
 
343
340
  /**
@@ -434,11 +431,21 @@ public class MountingManager {
434
431
  boolean canCoalesceEvent,
435
432
  @Nullable WritableMap params,
436
433
  @EventCategoryDef int eventCategory) {
437
- @Nullable SurfaceMountingManager smm = getSurfaceManager(surfaceId);
434
+ SurfaceMountingManager smm = getSurfaceMountingManager(surfaceId, reactTag);
438
435
  if (smm == null) {
439
- // Cannot queue event without valid surface mountng manager. Do nothing here.
436
+ FLog.d(
437
+ TAG,
438
+ "Cannot queue event without valid surface mounting manager for tag: %d, surfaceId: %d",
439
+ reactTag,
440
+ surfaceId);
440
441
  return;
441
442
  }
442
443
  smm.enqueuePendingEvent(reactTag, eventName, canCoalesceEvent, params, eventCategory);
443
444
  }
445
+
446
+ private @Nullable SurfaceMountingManager getSurfaceMountingManager(int surfaceId, int reactTag) {
447
+ return (surfaceId == ViewUtil.NO_SURFACE_ID
448
+ ? getSurfaceManagerForView(reactTag)
449
+ : getSurfaceManager(surfaceId));
450
+ }
444
451
  }
@@ -21,6 +21,24 @@ public class ReactModuleInfo(
21
21
  public val isCxxModule: Boolean,
22
22
  public val isTurboModule: Boolean
23
23
  ) {
24
+
25
+ @Deprecated(
26
+ "This constructor is deprecated and will be removed in the future. Use ReactModuleInfo(String, String, boolean, boolean, boolean, boolean)]",
27
+ replaceWith =
28
+ ReplaceWith(
29
+ expression =
30
+ "ReactModuleInfo(name, className, canOverrideExistingModule, needsEagerInit, isCxxModule, isTurboModule)"),
31
+ level = DeprecationLevel.WARNING)
32
+ public constructor(
33
+ name: String,
34
+ className: String,
35
+ canOverrideExistingModule: Boolean,
36
+ needsEagerInit: Boolean,
37
+ @Suppress("UNUSED_PARAMETER") hasConstants: Boolean,
38
+ isCxxModule: Boolean,
39
+ isTurboModule: Boolean
40
+ ) : this(name, className, canOverrideExistingModule, needsEagerInit, isCxxModule, isTurboModule)
41
+
24
42
  public companion object {
25
43
  /**
26
44
  * Checks if the passed class is a TurboModule. Useful to populate the parameter [isTurboModule]
@@ -17,6 +17,6 @@ public class ReactNativeVersion {
17
17
  public static final Map<String, Object> VERSION = MapBuilder.<String, Object>of(
18
18
  "major", 0,
19
19
  "minor", 77,
20
- "patch", 0,
21
- "prerelease", "0rc1");
20
+ "patch", 1,
21
+ "prerelease", "0");
22
22
  }
@@ -16,6 +16,7 @@ import android.graphics.drawable.LayerDrawable
16
16
  import android.os.Build
17
17
  import com.facebook.react.common.annotations.UnstableReactNativeAPI
18
18
  import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
19
+ import com.facebook.react.uimanager.PixelUtil.dpToPx
19
20
  import com.facebook.react.uimanager.style.BorderInsets
20
21
  import com.facebook.react.uimanager.style.BorderRadiusStyle
21
22
 
@@ -200,14 +201,14 @@ internal class CompositeBackgroundDrawable(
200
201
  pathForOutline.addRoundRect(
201
202
  RectF(bounds),
202
203
  floatArrayOf(
203
- it.topLeft.horizontal + (computedBorderInsets?.left ?: 0f),
204
- it.topLeft.vertical + (computedBorderInsets?.top ?: 0f),
205
- it.topRight.horizontal + (computedBorderInsets?.right ?: 0f),
206
- it.topRight.vertical + (computedBorderInsets?.top ?: 0f),
207
- it.bottomRight.horizontal + (computedBorderInsets?.right ?: 0f),
208
- it.bottomRight.vertical + (computedBorderInsets?.bottom ?: 0f),
209
- it.bottomLeft.horizontal + (computedBorderInsets?.left ?: 0f),
210
- it.bottomLeft.vertical) + (computedBorderInsets?.bottom ?: 0f),
204
+ (it.topLeft.horizontal + (computedBorderInsets?.left ?: 0f)).dpToPx(),
205
+ (it.topLeft.vertical + (computedBorderInsets?.top ?: 0f)).dpToPx(),
206
+ (it.topRight.horizontal + (computedBorderInsets?.right ?: 0f)).dpToPx(),
207
+ (it.topRight.vertical + (computedBorderInsets?.top ?: 0f)).dpToPx(),
208
+ (it.bottomRight.horizontal + (computedBorderInsets?.right ?: 0f)).dpToPx(),
209
+ (it.bottomRight.vertical + (computedBorderInsets?.bottom ?: 0f)).dpToPx(),
210
+ (it.bottomLeft.horizontal + (computedBorderInsets?.left ?: 0f)).dpToPx(),
211
+ (it.bottomLeft.vertical + (computedBorderInsets?.bottom ?: 0f)).dpToPx()),
211
212
  Path.Direction.CW)
212
213
  }
213
214
 
@@ -29,6 +29,7 @@ import android.widget.FrameLayout
29
29
  import androidx.annotation.UiThread
30
30
  import com.facebook.common.logging.FLog
31
31
  import com.facebook.react.R
32
+ import com.facebook.react.ReactActivity
32
33
  import com.facebook.react.bridge.GuardedRunnable
33
34
  import com.facebook.react.bridge.LifecycleEventListener
34
35
  import com.facebook.react.bridge.ReactContext
@@ -275,12 +276,31 @@ public class ReactModalHostView(context: ThemedReactContext) :
275
276
  // activity expects to receive those events and react to them, ie. in the case of
276
277
  // the dev menu
277
278
  val innerCurrentActivity =
278
- (this@ReactModalHostView.context as ReactContext).currentActivity
279
+ (this@ReactModalHostView.context as ReactContext).currentActivity as? ReactActivity
279
280
  if (innerCurrentActivity != null) {
280
- return innerCurrentActivity.onKeyUp(keyCode, event)
281
+ return innerCurrentActivity.onDialogKeyUp(keyCode, event)
281
282
  }
282
283
  }
283
284
  }
285
+
286
+ // @see https://github.com/react-native-tvos/react-native-tvos/issues/829
287
+ if (event.action == KeyEvent.ACTION_DOWN) {
288
+ // Allow BACK and ESCAPE keys to propagate to the dialog's activity.
289
+ // Returning false ensures these keys are not consumed here, so they
290
+ // can be processed during the ACTION_UP event where their functionality
291
+ // is handled by JavaScript.
292
+ if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) {
293
+ return false
294
+ }
295
+
296
+ // Redirect all other key down events to the current activity.
297
+ val innerCurrentActivity =
298
+ (this@ReactModalHostView.context as ReactContext).currentActivity as? ReactActivity
299
+ if (innerCurrentActivity != null) {
300
+ return innerCurrentActivity.onDialogKeyDown(keyCode, event)
301
+ }
302
+ }
303
+
284
304
  return false
285
305
  }
286
306
  })
@@ -61,6 +61,7 @@ public class TextAttributeProps {
61
61
  public static final short TA_KEY_LINE_BREAK_STRATEGY = 25;
62
62
  public static final short TA_KEY_ROLE = 26;
63
63
  public static final short TA_KEY_TEXT_TRANSFORM = 27;
64
+ public static final short TA_KEY_MAX_FONT_SIZE_MULTIPLIER = 29;
64
65
 
65
66
  public static final int UNSET = -1;
66
67
 
@@ -81,6 +82,7 @@ public class TextAttributeProps {
81
82
  protected float mLineHeight = Float.NaN;
82
83
  protected boolean mIsColorSet = false;
83
84
  protected boolean mAllowFontScaling = true;
85
+ protected float mMaxFontSizeMultiplier = Float.NaN;
84
86
  protected int mColor;
85
87
  protected boolean mIsBackgroundColorSet = false;
86
88
  protected int mBackgroundColor;
@@ -227,6 +229,9 @@ public class TextAttributeProps {
227
229
  case TA_KEY_TEXT_TRANSFORM:
228
230
  result.setTextTransform(entry.getStringValue());
229
231
  break;
232
+ case TA_KEY_MAX_FONT_SIZE_MULTIPLIER:
233
+ result.setMaxFontSizeMultiplier((float) entry.getDoubleValue());
234
+ break;
230
235
  }
231
236
  }
232
237
 
@@ -243,6 +248,8 @@ public class TextAttributeProps {
243
248
  result.setLineHeight(getFloatProp(props, ViewProps.LINE_HEIGHT, ReactConstants.UNSET));
244
249
  result.setLetterSpacing(getFloatProp(props, ViewProps.LETTER_SPACING, Float.NaN));
245
250
  result.setAllowFontScaling(getBooleanProp(props, ViewProps.ALLOW_FONT_SCALING, true));
251
+ result.setMaxFontSizeMultiplier(
252
+ getFloatProp(props, ViewProps.MAX_FONT_SIZE_MULTIPLIER, Float.NaN));
246
253
  result.setFontSize(getFloatProp(props, ViewProps.FONT_SIZE, ReactConstants.UNSET));
247
254
  result.setColor(props.hasKey(ViewProps.COLOR) ? props.getInt(ViewProps.COLOR, 0) : null);
248
255
  result.setColor(
@@ -411,7 +418,14 @@ public class TextAttributeProps {
411
418
  mAllowFontScaling = allowFontScaling;
412
419
  setFontSize(mFontSizeInput);
413
420
  setLineHeight(mLineHeightInput);
414
- setLetterSpacing(mLetterSpacingInput);
421
+ }
422
+ }
423
+
424
+ private void setMaxFontSizeMultiplier(float maxFontSizeMultiplier) {
425
+ if (maxFontSizeMultiplier != mMaxFontSizeMultiplier) {
426
+ mMaxFontSizeMultiplier = maxFontSizeMultiplier;
427
+ setFontSize(mFontSizeInput);
428
+ setLineHeight(mLineHeightInput);
415
429
  }
416
430
  }
417
431
 
@@ -420,7 +434,7 @@ public class TextAttributeProps {
420
434
  if (fontSize != ReactConstants.UNSET) {
421
435
  fontSize =
422
436
  mAllowFontScaling
423
- ? (float) Math.ceil(PixelUtil.toPixelFromSP(fontSize))
437
+ ? (float) Math.ceil(PixelUtil.toPixelFromSP(fontSize, mMaxFontSizeMultiplier))
424
438
  : (float) Math.ceil(PixelUtil.toPixelFromDIP(fontSize));
425
439
  }
426
440
  mFontSize = (int) fontSize;
@@ -62,9 +62,6 @@ public abstract class ReactClippingViewManager<T : ReactViewGroup> : ViewGroupMa
62
62
  if (removeClippedSubviews) {
63
63
  val child = getChildAt(parent, index)
64
64
  if (child != null) {
65
- if (child.parent != null) {
66
- parent.removeView(child)
67
- }
68
65
  parent.removeViewWithSubviewClippingEnabled(child)
69
66
  }
70
67
  } else {
@@ -77,6 +77,8 @@ import com.facebook.react.uimanager.style.BorderRadiusProp;
77
77
  import com.facebook.react.uimanager.style.BorderStyle;
78
78
  import com.facebook.react.uimanager.style.LogicalEdge;
79
79
  import com.facebook.react.uimanager.style.Overflow;
80
+ import java.util.HashSet;
81
+ import java.util.Set;
80
82
 
81
83
  import java.lang.ref.WeakReference;
82
84
  import java.util.ArrayList;
@@ -108,6 +110,7 @@ public class ReactViewGroup extends ViewGroup
108
110
  private boolean trapFocusDown = false;
109
111
  private boolean trapFocusLeft = false;
110
112
  private boolean trapFocusRight = false;
113
+ public boolean hasTVPreferredFocus = false;
111
114
 
112
115
 
113
116
  /**
@@ -170,6 +173,7 @@ public class ReactViewGroup extends ViewGroup
170
173
  private @Nullable ViewGroupDrawingOrderHelper mDrawingOrderHelper;
171
174
  private float mBackfaceOpacity;
172
175
  private String mBackfaceVisibility;
176
+ private @Nullable Set<Integer> mChildrenRemovedWhileTransitioning;
173
177
 
174
178
  /**
175
179
  * Creates a new `ReactViewGroup` instance.
@@ -203,6 +207,7 @@ public class ReactViewGroup extends ViewGroup
203
207
  mDrawingOrderHelper = null;
204
208
  mBackfaceOpacity = 1.f;
205
209
  mBackfaceVisibility = "visible";
210
+ mChildrenRemovedWhileTransitioning = null;
206
211
  }
207
212
 
208
213
  /* package */ void recycleView() {
@@ -387,6 +392,7 @@ public class ReactViewGroup extends ViewGroup
387
392
  return;
388
393
  }
389
394
  mRemoveClippedSubviews = removeClippedSubviews;
395
+ mChildrenRemovedWhileTransitioning = null;
390
396
  if (removeClippedSubviews) {
391
397
  mClippingRect = new Rect();
392
398
  ReactClippingViewGroupHelper.calculateClippingRect(this, mClippingRect);
@@ -440,6 +446,26 @@ public class ReactViewGroup extends ViewGroup
440
446
  updateClippingToRect(mClippingRect);
441
447
  }
442
448
 
449
+ @Override
450
+ public void endViewTransition(View view) {
451
+ super.endViewTransition(view);
452
+ if (mChildrenRemovedWhileTransitioning != null) {
453
+ mChildrenRemovedWhileTransitioning.remove(view.getId());
454
+ }
455
+ }
456
+
457
+ private void trackChildViewTransition(int childId) {
458
+ if (mChildrenRemovedWhileTransitioning == null) {
459
+ mChildrenRemovedWhileTransitioning = new HashSet<>();
460
+ }
461
+ mChildrenRemovedWhileTransitioning.add(childId);
462
+ }
463
+
464
+ private boolean isChildRemovedWhileTransitioning(View child) {
465
+ return mChildrenRemovedWhileTransitioning != null
466
+ && mChildrenRemovedWhileTransitioning.contains(child.getId());
467
+ }
468
+
443
469
  private void updateClippingToRect(Rect clippingRect) {
444
470
  Assertions.assertNotNull(mAllChildren);
445
471
  int clippedSoFar = 0;
@@ -670,6 +696,12 @@ public class ReactViewGroup extends ViewGroup
670
696
  } else {
671
697
  setChildrenDrawingOrderEnabled(false);
672
698
  }
699
+
700
+ // The parent might not be null in case the child is transitioning.
701
+ if (child.getParent() != null) {
702
+ trackChildViewTransition(child.getId());
703
+ }
704
+
673
705
  super.onViewRemoved(child);
674
706
  }
675
707
 
@@ -799,6 +831,7 @@ public class ReactViewGroup extends ViewGroup
799
831
  }
800
832
  }
801
833
  removeViewsInLayout(index - clippedSoFar, 1);
834
+ invalidate();
802
835
  }
803
836
  removeFromArray(index);
804
837
  }
@@ -82,6 +82,7 @@ public open class ReactViewManager : ReactClippingViewManager<ReactViewGroup>()
82
82
  return view
83
83
  }
84
84
 
85
+
85
86
  @ReactProp(name = "accessible")
86
87
  public open fun setAccessible(view: ReactViewGroup, accessible: Boolean) {
87
88
  view.isFocusable = accessible
@@ -109,6 +110,17 @@ public open class ReactViewManager : ReactClippingViewManager<ReactViewGroup>()
109
110
 
110
111
  @ReactProp(name = "hasTVPreferredFocus")
111
112
  public open fun setTVPreferredFocus(view: ReactViewGroup, hasTVPreferredFocus: Boolean) {
113
+ /*
114
+ * React prop functions like this one gets called repeatedly on the New Architecture
115
+ * no matter the prop has changed or not. Contrary to others, `hasTVPreferredFocus` has
116
+ * a side effect, calling `requestFocus` function on the view which disrupts the user flow
117
+ * and should only called once when the property changes to `true.
118
+ * We keep a special state in the View class and run a comparison here to mitigate
119
+ * that problem.
120
+ */
121
+ if (view.hasTVPreferredFocus == hasTVPreferredFocus) return;
122
+ view.hasTVPreferredFocus = hasTVPreferredFocus;
123
+
112
124
  if (hasTVPreferredFocus) {
113
125
  view.isFocusable = true
114
126
  view.isFocusableInTouchMode = true
@@ -17,8 +17,8 @@ namespace facebook::react {
17
17
  constexpr struct {
18
18
  int32_t Major = 0;
19
19
  int32_t Minor = 77;
20
- int32_t Patch = 0;
21
- std::string_view Prerelease = "0rc1";
20
+ int32_t Patch = 1;
21
+ std::string_view Prerelease = "0";
22
22
  } ReactNativeVersion;
23
23
 
24
24
  } // namespace facebook::react
@@ -123,6 +123,15 @@ std::vector<ExportedMethod> parseExportedMethods(std::string moduleName, Class m
123
123
  NSArray<RCTMethodArgument *> *arguments;
124
124
  SEL objCMethodSelector = NSSelectorFromString(RCTParseMethodSignature(methodInfo->objcName, &arguments));
125
125
  NSMethodSignature *objCMethodSignature = [moduleClass instanceMethodSignatureForSelector:objCMethodSelector];
126
+ if (objCMethodSignature == nullptr) {
127
+ RCTLogWarn(
128
+ @"The objective-c `%s` method signature for the JS method `%@` can not be found in the ObjecitveC definition of the %s module.\nThe `%@` JS method will not be available.",
129
+ methodInfo->objcName,
130
+ jsMethodName,
131
+ moduleName.c_str(),
132
+ jsMethodName);
133
+ continue;
134
+ }
126
135
  std::string objCMethodReturnType = [objCMethodSignature methodReturnType];
127
136
 
128
137
  if (objCMethodSignature.numberOfArguments - 2 != [arguments count]) {
@@ -337,7 +346,7 @@ void ObjCInteropTurboModule::setInvocationArg(
337
346
  SEL selector = selectorForType(argumentType);
338
347
 
339
348
  if ([RCTConvert respondsToSelector:selector]) {
340
- id objCArg = TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_);
349
+ id objCArg = TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_, YES);
341
350
 
342
351
  if (objCArgType == @encode(char)) {
343
352
  char arg = RCTConvertTo<char>(selector, objCArg);
@@ -491,7 +500,7 @@ void ObjCInteropTurboModule::setInvocationArg(
491
500
  }
492
501
 
493
502
  RCTResponseSenderBlock arg =
494
- (RCTResponseSenderBlock)TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_);
503
+ (RCTResponseSenderBlock)TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_, YES);
495
504
  if (arg) {
496
505
  [retainedObjectsForInvocation addObject:arg];
497
506
  }
@@ -506,7 +515,7 @@ void ObjCInteropTurboModule::setInvocationArg(
506
515
  }
507
516
 
508
517
  RCTResponseSenderBlock senderBlock =
509
- (RCTResponseSenderBlock)TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_);
518
+ (RCTResponseSenderBlock)TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_, YES);
510
519
  RCTResponseErrorBlock arg = ^(NSError *error) {
511
520
  senderBlock(@[ RCTJSErrorFromNSError(error) ]);
512
521
  };
@@ -536,7 +545,7 @@ void ObjCInteropTurboModule::setInvocationArg(
536
545
  runtime, errorPrefix + "JavaScript argument must be a plain object. Got " + getType(runtime, jsiArg));
537
546
  }
538
547
 
539
- id arg = TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_);
548
+ id arg = TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_, YES);
540
549
 
541
550
  RCTManagedPointer *(*convert)(id, SEL, id) = (__typeof__(convert))objc_msgSend;
542
551
  RCTManagedPointer *box = convert([RCTCxxConvert class], selector, arg);
@@ -32,6 +32,11 @@ using EventEmitterCallback = std::function<void(const std::string &, id)>;
32
32
  namespace TurboModuleConvertUtils {
33
33
  jsi::Value convertObjCObjectToJSIValue(jsi::Runtime &runtime, id value);
34
34
  id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr<CallInvoker> jsInvoker);
35
+ id convertJSIValueToObjCObject(
36
+ jsi::Runtime &runtime,
37
+ const jsi::Value &value,
38
+ std::shared_ptr<CallInvoker> jsInvoker,
39
+ BOOL useNSNull);
35
40
  } // namespace TurboModuleConvertUtils
36
41
 
37
42
  template <>
@@ -111,21 +111,27 @@ static NSString *convertJSIStringToNSString(jsi::Runtime &runtime, const jsi::St
111
111
  return [NSString stringWithUTF8String:value.utf8(runtime).c_str()];
112
112
  }
113
113
 
114
- static NSArray *
115
- convertJSIArrayToNSArray(jsi::Runtime &runtime, const jsi::Array &value, std::shared_ptr<CallInvoker> jsInvoker)
114
+ static NSArray *convertJSIArrayToNSArray(
115
+ jsi::Runtime &runtime,
116
+ const jsi::Array &value,
117
+ std::shared_ptr<CallInvoker> jsInvoker,
118
+ BOOL useNSNull)
116
119
  {
117
120
  size_t size = value.size(runtime);
118
121
  NSMutableArray *result = [NSMutableArray new];
119
122
  for (size_t i = 0; i < size; i++) {
120
123
  // Insert kCFNull when it's `undefined` value to preserve the indices.
121
- id convertedObject = convertJSIValueToObjCObject(runtime, value.getValueAtIndex(runtime, i), jsInvoker);
124
+ id convertedObject = convertJSIValueToObjCObject(runtime, value.getValueAtIndex(runtime, i), jsInvoker, useNSNull);
122
125
  [result addObject:convertedObject ? convertedObject : (id)kCFNull];
123
126
  }
124
127
  return [result copy];
125
128
  }
126
129
 
127
- static NSDictionary *
128
- convertJSIObjectToNSDictionary(jsi::Runtime &runtime, const jsi::Object &value, std::shared_ptr<CallInvoker> jsInvoker)
130
+ static NSDictionary *convertJSIObjectToNSDictionary(
131
+ jsi::Runtime &runtime,
132
+ const jsi::Object &value,
133
+ std::shared_ptr<CallInvoker> jsInvoker,
134
+ BOOL useNSNull)
129
135
  {
130
136
  jsi::Array propertyNames = value.getPropertyNames(runtime);
131
137
  size_t size = propertyNames.size(runtime);
@@ -133,7 +139,7 @@ convertJSIObjectToNSDictionary(jsi::Runtime &runtime, const jsi::Object &value,
133
139
  for (size_t i = 0; i < size; i++) {
134
140
  jsi::String name = propertyNames.getValueAtIndex(runtime, i).getString(runtime);
135
141
  NSString *k = convertJSIStringToNSString(runtime, name);
136
- id v = convertJSIValueToObjCObject(runtime, value.getProperty(runtime, name), jsInvoker);
142
+ id v = convertJSIValueToObjCObject(runtime, value.getProperty(runtime, name), jsInvoker, useNSNull);
137
143
  if (v) {
138
144
  result[k] = v;
139
145
  }
@@ -161,9 +167,21 @@ convertJSIFunctionToCallback(jsi::Runtime &rt, jsi::Function &&function, std::sh
161
167
 
162
168
  id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr<CallInvoker> jsInvoker)
163
169
  {
164
- if (value.isUndefined() || value.isNull()) {
170
+ return convertJSIValueToObjCObject(runtime, value, jsInvoker, NO);
171
+ }
172
+
173
+ id convertJSIValueToObjCObject(
174
+ jsi::Runtime &runtime,
175
+ const jsi::Value &value,
176
+ std::shared_ptr<CallInvoker> jsInvoker,
177
+ BOOL useNSNull)
178
+ {
179
+ if (value.isUndefined() || (value.isNull() && !useNSNull)) {
165
180
  return nil;
166
181
  }
182
+ if (value.isNull() && useNSNull) {
183
+ return [NSNull null];
184
+ }
167
185
  if (value.isBool()) {
168
186
  return @(value.getBool());
169
187
  }
@@ -176,12 +194,12 @@ id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, s
176
194
  if (value.isObject()) {
177
195
  jsi::Object o = value.getObject(runtime);
178
196
  if (o.isArray(runtime)) {
179
- return convertJSIArrayToNSArray(runtime, o.getArray(runtime), jsInvoker);
197
+ return convertJSIArrayToNSArray(runtime, o.getArray(runtime), jsInvoker, useNSNull);
180
198
  }
181
199
  if (o.isFunction(runtime)) {
182
200
  return convertJSIFunctionToCallback(runtime, o.getFunction(runtime), jsInvoker);
183
201
  }
184
- return convertJSIObjectToNSDictionary(runtime, o, jsInvoker);
202
+ return convertJSIObjectToNSDictionary(runtime, o, jsInvoker, useNSNull);
185
203
  }
186
204
 
187
205
  throw std::runtime_error("Unsupported jsi::Value kind");
@@ -46,6 +46,9 @@ void TextAttributes::apply(TextAttributes textAttributes) {
46
46
  allowFontScaling = textAttributes.allowFontScaling.has_value()
47
47
  ? textAttributes.allowFontScaling
48
48
  : allowFontScaling;
49
+ maxFontSizeMultiplier = !std::isnan(textAttributes.maxFontSizeMultiplier)
50
+ ? textAttributes.maxFontSizeMultiplier
51
+ : maxFontSizeMultiplier;
49
52
  dynamicTypeRamp = textAttributes.dynamicTypeRamp.has_value()
50
53
  ? textAttributes.dynamicTypeRamp
51
54
  : dynamicTypeRamp;
@@ -168,6 +171,7 @@ bool TextAttributes::operator==(const TextAttributes& rhs) const {
168
171
  rhs.accessibilityRole,
169
172
  rhs.role,
170
173
  rhs.textTransform) &&
174
+ floatEquality(maxFontSizeMultiplier, rhs.maxFontSizeMultiplier) &&
171
175
  floatEquality(opacity, rhs.opacity) &&
172
176
  floatEquality(fontSize, rhs.fontSize) &&
173
177
  floatEquality(fontSizeMultiplier, rhs.fontSizeMultiplier) &&
@@ -211,6 +215,8 @@ SharedDebugStringConvertibleList TextAttributes::getDebugProps() const {
211
215
  debugStringConvertibleItem("fontStyle", fontStyle),
212
216
  debugStringConvertibleItem("fontVariant", fontVariant),
213
217
  debugStringConvertibleItem("allowFontScaling", allowFontScaling),
218
+ debugStringConvertibleItem(
219
+ "maxFontSizeMultiplier", maxFontSizeMultiplier),
214
220
  debugStringConvertibleItem("dynamicTypeRamp", dynamicTypeRamp),
215
221
  debugStringConvertibleItem("letterSpacing", letterSpacing),
216
222
 
@@ -51,6 +51,7 @@ class TextAttributes : public DebugStringConvertible {
51
51
  std::optional<FontStyle> fontStyle{};
52
52
  std::optional<FontVariant> fontVariant{};
53
53
  std::optional<bool> allowFontScaling{};
54
+ Float maxFontSizeMultiplier{std::numeric_limits<Float>::quiet_NaN()};
54
55
  std::optional<DynamicTypeRamp> dynamicTypeRamp{};
55
56
  Float letterSpacing{std::numeric_limits<Float>::quiet_NaN()};
56
57
  std::optional<TextTransform> textTransform{};
@@ -117,6 +118,7 @@ struct hash<facebook::react::TextAttributes> {
117
118
  textAttributes.opacity,
118
119
  textAttributes.fontFamily,
119
120
  textAttributes.fontSize,
121
+ textAttributes.maxFontSizeMultiplier,
120
122
  textAttributes.fontSizeMultiplier,
121
123
  textAttributes.fontWeight,
122
124
  textAttributes.fontStyle,
@@ -910,6 +910,7 @@ constexpr static MapBuffer::Key TA_KEY_LINE_BREAK_STRATEGY = 25;
910
910
  constexpr static MapBuffer::Key TA_KEY_ROLE = 26;
911
911
  constexpr static MapBuffer::Key TA_KEY_TEXT_TRANSFORM = 27;
912
912
  constexpr static MapBuffer::Key TA_KEY_ALIGNMENT_VERTICAL = 28;
913
+ constexpr static MapBuffer::Key TA_KEY_MAX_FONT_SIZE_MULTIPLIER = 29;
913
914
 
914
915
  // constants for ParagraphAttributes serialization
915
916
  constexpr static MapBuffer::Key PA_KEY_MAX_NUMBER_OF_LINES = 0;
@@ -1004,6 +1005,10 @@ inline MapBuffer toMapBuffer(const TextAttributes& textAttributes) {
1004
1005
  builder.putBool(
1005
1006
  TA_KEY_ALLOW_FONT_SCALING, *textAttributes.allowFontScaling);
1006
1007
  }
1008
+ if (!std::isnan(textAttributes.maxFontSizeMultiplier)) {
1009
+ builder.putDouble(
1010
+ TA_KEY_MAX_FONT_SIZE_MULTIPLIER, textAttributes.maxFontSizeMultiplier);
1011
+ }
1007
1012
  if (!std::isnan(textAttributes.letterSpacing)) {
1008
1013
  builder.putDouble(TA_KEY_LETTER_SPACING, textAttributes.letterSpacing);
1009
1014
  }