react-native-tvos 0.85.0-0rc5 → 0.85.2-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 (54) hide show
  1. package/Libraries/Components/ScrollView/AndroidHorizontalScrollViewNativeComponent.js +1 -0
  2. package/Libraries/Components/ScrollView/ScrollView.d.ts +7 -0
  3. package/Libraries/Components/ScrollView/ScrollView.js +6 -0
  4. package/Libraries/Components/ScrollView/ScrollViewNativeComponent.js +2 -0
  5. package/Libraries/Components/ScrollView/ScrollViewNativeComponentType.js +1 -0
  6. package/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js +5 -1
  7. package/Libraries/Components/TextInput/TextInput.flow.js +1 -0
  8. package/Libraries/Core/ReactNativeVersion.js +2 -2
  9. package/README.md +52 -0
  10. package/React/Base/RCTVersion.m +2 -2
  11. package/React/CoreModules/RCTDevLoadingView.mm +17 -0
  12. package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.h +1 -0
  13. package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm +78 -0
  14. package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +32 -6
  15. package/React/Views/ScrollView/RCTScrollViewManager.m +1 -0
  16. package/ReactAndroid/build.gradle.kts +2 -0
  17. package/ReactAndroid/gradle.properties +1 -1
  18. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +2 -2
  19. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android.kt +5 -1
  20. package/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkEventUtil.kt +8 -0
  21. package/ReactAndroid/src/main/java/com/facebook/react/modules/network/RequestBodyUtil.kt +2 -0
  22. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +2 -2
  23. package/ReactAndroid/src/main/java/com/facebook/react/views/common/UiModeUtils.kt +20 -0
  24. package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +57 -8
  25. package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.kt +5 -0
  26. package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +48 -2
  27. package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.kt +5 -0
  28. package/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java +28 -3
  29. package/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.kt +5 -0
  30. package/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputSubmitEditingEvent.kt +6 -1
  31. package/ReactCommon/React-Fabric.podspec +1 -0
  32. package/ReactCommon/cxxreact/ReactNativeVersion.h +3 -3
  33. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +2 -2
  34. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSExperimental.h +9 -1
  35. package/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm +3 -1
  36. package/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp +4 -1
  37. package/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp +58 -25
  38. package/ReactCommon/react/renderer/animationbackend/AnimationBackend.h +9 -0
  39. package/ReactCommon/react/renderer/animationbackend/AnimationChoreographer.h +5 -0
  40. package/ReactCommon/react/renderer/components/scrollview/BaseScrollViewProps.cpp +10 -0
  41. package/ReactCommon/react/renderer/components/scrollview/BaseScrollViewProps.h +1 -0
  42. package/ReactCommon/react/renderer/uimanager/UIManagerAnimationBackend.h +1 -0
  43. package/package.json +9 -9
  44. package/scripts/codegen/generate-artifacts-executor/generateAppDependencyProvider.js +4 -4
  45. package/scripts/codegen/generate-artifacts-executor/generateCustomURLHandlers.js +3 -3
  46. package/scripts/codegen/generate-artifacts-executor/generateNativeCode.js +2 -3
  47. package/scripts/codegen/generate-artifacts-executor/generatePackageSwift.js +2 -2
  48. package/scripts/codegen/generate-artifacts-executor/generateRCTModuleProviders.js +3 -2
  49. package/scripts/codegen/generate-artifacts-executor/generateRCTThirdPartyComponents.js +3 -2
  50. package/scripts/codegen/generate-artifacts-executor/generateReactCodegenPodspec.js +2 -2
  51. package/scripts/codegen/generate-artifacts-executor/generateUnstableModulesRequiringMainQueueSetupProvider.js +3 -3
  52. package/scripts/codegen/generate-artifacts-executor/utils.js +48 -0
  53. package/src/private/featureflags/ReactNativeFeatureFlags.js +2 -2
  54. package/types/public/ReactNativeTVTypes.d.ts +8 -0
@@ -39,6 +39,7 @@ import androidx.core.view.ViewCompat.FocusDirection;
39
39
  import com.facebook.common.logging.FLog;
40
40
  import com.facebook.infer.annotation.Assertions;
41
41
  import com.facebook.infer.annotation.Nullsafe;
42
+ import com.facebook.react.views.common.UiModeUtils;
42
43
  import com.facebook.react.R;
43
44
  import com.facebook.react.common.ReactConstants;
44
45
  import com.facebook.react.common.build.ReactBuildConfig;
@@ -139,6 +140,8 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
139
140
  private boolean mEmittedOverScrollSinceScrollBegin = false;
140
141
  private boolean mScrollsChildToFocus = true;
141
142
  private int mSnapToItemPadding;
143
+ private boolean mScrollAnimationEnabled = true;
144
+ private boolean mBlockScrollDelta = false;
142
145
 
143
146
  public ReactHorizontalScrollView(Context context) {
144
147
  this(context, null);
@@ -251,9 +254,23 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
251
254
  return mScrollEnabled && super.canScrollHorizontally(direction);
252
255
  }
253
256
 
257
+ @Override
258
+ public void computeScroll() {
259
+ if (UiModeUtils.isTVDevice(getContext())
260
+ && !mScrollAnimationEnabled
261
+ && mScroller != null
262
+ && !mScroller.isFinished()) {
263
+ // Jump instantly to the target position instead of animating.
264
+ scrollTo(mScroller.getFinalX(), mScroller.getFinalY());
265
+ mScroller.forceFinished(true);
266
+ return;
267
+ }
268
+ super.computeScroll();
269
+ }
270
+
254
271
  @Override
255
272
  protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
256
- if (!mScrollEnabled) {
273
+ if (!mScrollEnabled || mBlockScrollDelta) {
257
274
  return 0;
258
275
  }
259
276
  return super.computeScrollDeltaToGetChildRectOnScreen(rect);
@@ -400,6 +417,10 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
400
417
  mSnapToItemPadding = snapToItemPadding;
401
418
  }
402
419
 
420
+ public void setScrollAnimationEnabled(boolean scrollAnimationEnabled) {
421
+ mScrollAnimationEnabled = scrollAnimationEnabled;
422
+ }
423
+
403
424
  @Override
404
425
  protected float getLeftFadingEdgeStrength() {
405
426
  float max = Math.max(mFadingEdgeLengthStart, mFadingEdgeLengthEnd);
@@ -623,7 +644,11 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
623
644
  * unblocks such customization.
624
645
  */
625
646
  protected void requestChildFocusWithoutScroll(View child, View focused) {
647
+ // Temporarily block HorizontalScrollView's internal scrollToChild from running
648
+ // during super.requestChildFocus — we've already handled scrolling in requestChildFocus.
649
+ mBlockScrollDelta = true;
626
650
  super.requestChildFocus(child, focused);
651
+ mBlockScrollDelta = false;
627
652
  }
628
653
 
629
654
  @Override
@@ -839,21 +864,41 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
839
864
  public boolean arrowScroll(int direction) {
840
865
  boolean handled = false;
841
866
 
842
- if (mPagingEnabled) {
867
+ if (mSnapToAlignment == SNAP_ALIGNMENT_ITEM) {
868
+ // When snapToAlignment is "item", find the next focusable and request focus directly.
869
+ // This avoids super.arrowScroll() which starts its own scroll animation that conflicts
870
+ // with tryScrollSnapToChild's snap scrolling.
871
+ // requestChildFocus → tryScrollSnapToChild handles scrolling.
872
+ View currentFocused = findFocus();
873
+ View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction);
874
+ if (nextFocused != null && nextFocused != currentFocused && nextFocused != this) {
875
+ nextFocused.requestFocus(direction);
876
+ handled = true;
877
+ }
878
+ } else if (mPagingEnabled) {
843
879
  mPagedArrowScrolling = true;
844
880
 
845
881
  if (getChildCount() > 0) {
846
882
  View currentFocused = findFocus();
847
883
  View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction);
848
884
  View rootChild = getContentView();
849
- if (isDescendantOf(rootChild, nextFocused)) {
850
- if (!isScrolledInView(nextFocused) && !isMostlyScrolledInView(nextFocused)) {
851
- smoothScrollToNextPage(direction);
885
+ if (rootChild != null && nextFocused != null && isDescendantOf(rootChild, nextFocused)) {
886
+ if (mSnapToAlignment == SNAP_ALIGNMENT_ITEM) {
887
+ // When snapToAlignment is "item", don't use smoothScrollToNextPage (which scrolls
888
+ // by full page width and ignores snapToItemPadding). Instead just request focus —
889
+ // requestChildFocus → tryScrollSnapToChild handles scrolling with correct padding.
890
+ nextFocused.requestFocus();
891
+ } else {
892
+ if (!isScrolledInView(nextFocused) && !isMostlyScrolledInView(nextFocused)) {
893
+ smoothScrollToNextPage(direction);
894
+ }
895
+ nextFocused.requestFocus();
852
896
  }
853
- nextFocused.requestFocus();
854
897
  handled = true;
855
898
  } else {
856
- smoothScrollToNextPage(direction);
899
+ if (mSnapToAlignment != SNAP_ALIGNMENT_ITEM) {
900
+ smoothScrollToNextPage(direction);
901
+ }
857
902
  handled = true;
858
903
  }
859
904
  }
@@ -1652,7 +1697,11 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
1652
1697
  * scroll view and state. Calling raw `smoothScrollTo` doesn't update state.
1653
1698
  */
1654
1699
  public void reactSmoothScrollTo(int x, int y) {
1655
- ReactScrollViewHelper.smoothScrollTo(this, x, y);
1700
+ if (mScrollAnimationEnabled || !UiModeUtils.isTVDevice(getContext())) {
1701
+ ReactScrollViewHelper.smoothScrollTo(this, x, y);
1702
+ } else {
1703
+ scrollTo(x, y);
1704
+ }
1656
1705
  setPendingContentOffsets(x, y);
1657
1706
  }
1658
1707
 
@@ -169,6 +169,11 @@ constructor(private val fpsListener: FpsListener? = null) :
169
169
  view.setSnapToItemPadding(px)
170
170
  }
171
171
 
172
+ @ReactProp(name = "scrollAnimationEnabled", defaultBoolean = true)
173
+ public fun setScrollAnimationEnabled(view: ReactHorizontalScrollView, value: Boolean) {
174
+ view.setScrollAnimationEnabled(value)
175
+ }
176
+
172
177
  @ReactProp(name = ReactClippingViewGroupHelper.PROP_REMOVE_CLIPPED_SUBVIEWS)
173
178
  public fun setRemoveClippedSubviews(
174
179
  view: ReactHorizontalScrollView,
@@ -24,6 +24,7 @@ import android.graphics.Rect;
24
24
  import android.graphics.drawable.ColorDrawable;
25
25
  import android.graphics.drawable.Drawable;
26
26
  import android.os.Build;
27
+ import android.view.FocusFinder;
27
28
  import android.view.KeyEvent;
28
29
  import android.view.MotionEvent;
29
30
  import android.view.View;
@@ -38,6 +39,7 @@ import androidx.core.view.ViewCompat.FocusDirection;
38
39
  import com.facebook.common.logging.FLog;
39
40
  import com.facebook.infer.annotation.Assertions;
40
41
  import com.facebook.infer.annotation.Nullsafe;
42
+ import com.facebook.react.views.common.UiModeUtils;
41
43
  import com.facebook.react.R;
42
44
  import com.facebook.react.bridge.ReadableMap;
43
45
  import com.facebook.react.common.ReactConstants;
@@ -136,6 +138,8 @@ public class ReactScrollView extends ScrollView
136
138
  private boolean mEmittedOverScrollSinceScrollBegin;
137
139
  private boolean mScrollsChildToFocus = true;
138
140
  private int mSnapToItemPadding;
141
+ private boolean mScrollAnimationEnabled = true;
142
+ private boolean mBlockScrollDelta = false;
139
143
 
140
144
  public ReactScrollView(Context context) {
141
145
  this(context, null);
@@ -366,6 +370,10 @@ public class ReactScrollView extends ScrollView
366
370
  mSnapToItemPadding = snapToItemPadding;
367
371
  }
368
372
 
373
+ public void setScrollAnimationEnabled(boolean scrollAnimationEnabled) {
374
+ mScrollAnimationEnabled = scrollAnimationEnabled;
375
+ }
376
+
369
377
  @Override
370
378
  protected float getTopFadingEdgeStrength() {
371
379
  float max = Math.max(mFadingEdgeLengthStart, mFadingEdgeLengthEnd);
@@ -544,6 +552,24 @@ public class ReactScrollView extends ScrollView
544
552
  return true;
545
553
  }
546
554
 
555
+ @Override
556
+ public boolean arrowScroll(int direction) {
557
+ if (mSnapToAlignment == SNAP_ALIGNMENT_ITEM) {
558
+ // When snapToAlignment is "item", find the next focusable and request focus directly.
559
+ // This avoids super.arrowScroll() which starts its own scroll animation that conflicts
560
+ // with tryScrollSnapToChild's snap scrolling.
561
+ // requestChildFocus → tryScrollSnapToChild handles scrolling.
562
+ View currentFocused = findFocus();
563
+ View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction);
564
+ if (nextFocused != null && nextFocused != currentFocused && nextFocused != this) {
565
+ nextFocused.requestFocus(direction);
566
+ return true;
567
+ }
568
+ return false;
569
+ }
570
+ return super.arrowScroll(direction);
571
+ }
572
+
547
573
  /**
548
574
  * Since ReactScrollView handles layout changes on JS side, it does not call super.onlayout due to
549
575
  * which mIsLayoutDirty flag in ScrollView remains true and prevents scrolling to child when
@@ -567,7 +593,9 @@ public class ReactScrollView extends ScrollView
567
593
  * unblocks such customization.
568
594
  */
569
595
  protected void requestChildFocusWithoutScroll(View child, View focused) {
596
+ mBlockScrollDelta = true;
570
597
  super.requestChildFocus(child, focused);
598
+ mBlockScrollDelta = false;
571
599
  }
572
600
 
573
601
  @Override
@@ -619,9 +647,23 @@ public class ReactScrollView extends ScrollView
619
647
  }
620
648
  }
621
649
 
650
+ @Override
651
+ public void computeScroll() {
652
+ if (UiModeUtils.isTVDevice(getContext())
653
+ && !mScrollAnimationEnabled
654
+ && mScroller != null
655
+ && !mScroller.isFinished()) {
656
+ // Jump instantly to the target position instead of animating.
657
+ scrollTo(mScroller.getFinalX(), mScroller.getFinalY());
658
+ mScroller.forceFinished(true);
659
+ return;
660
+ }
661
+ super.computeScroll();
662
+ }
663
+
622
664
  @Override
623
665
  protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
624
- if (!mScrollEnabled) {
666
+ if (!mScrollEnabled || mBlockScrollDelta) {
625
667
  return 0;
626
668
  }
627
669
  return super.computeScrollDeltaToGetChildRectOnScreen(rect);
@@ -1396,7 +1438,11 @@ public class ReactScrollView extends ScrollView
1396
1438
  * scroll view and state. Calling raw `smoothScrollTo` doesn't update state.
1397
1439
  */
1398
1440
  public void reactSmoothScrollTo(int x, int y) {
1399
- ReactScrollViewHelper.smoothScrollTo(this, x, y);
1441
+ if (mScrollAnimationEnabled || !UiModeUtils.isTVDevice(getContext())) {
1442
+ ReactScrollViewHelper.smoothScrollTo(this, x, y);
1443
+ } else {
1444
+ scrollTo(x, y);
1445
+ }
1400
1446
  setPendingContentOffsets(x, y);
1401
1447
  }
1402
1448
 
@@ -153,6 +153,11 @@ constructor(private val fpsListener: FpsListener? = null) :
153
153
  view.setSnapToItemPadding(px)
154
154
  }
155
155
 
156
+ @ReactProp(name = "scrollAnimationEnabled", defaultBoolean = true)
157
+ public fun setScrollAnimationEnabled(view: ReactScrollView, value: Boolean) {
158
+ view.setScrollAnimationEnabled(value)
159
+ }
160
+
156
161
  @ReactProp(name = ReactClippingViewGroupHelper.PROP_REMOVE_CLIPPED_SUBVIEWS)
157
162
  public fun setRemoveClippedSubviews(view: ReactScrollView, removeClippedSubviews: Boolean) {
158
163
  view.removeClippedSubviews = removeClippedSubviews
@@ -30,6 +30,7 @@ import androidx.appcompat.widget.TintContextWrapper;
30
30
  import androidx.core.view.AccessibilityDelegateCompat;
31
31
  import androidx.core.view.ViewCompat;
32
32
  import androidx.customview.widget.ExploreByTouchHelper;
33
+ import com.facebook.react.views.common.UiModeUtils;
33
34
  import com.facebook.common.logging.FLog;
34
35
  import com.facebook.infer.annotation.Assertions;
35
36
  import com.facebook.infer.annotation.Nullsafe;
@@ -153,14 +154,38 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie
153
154
  // mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED |
154
155
  // LAYOUT_DIRECTION_INHERIT;
155
156
  setEnabled(true);
156
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
157
- setFocusable(View.FOCUSABLE_AUTO);
158
- }
157
+ // Changed from FOCUSABLE_AUTO to NOT_FOCUSABLE to prevent Android TV's
158
+ // focus engine from landing on text views when no other views are available
159
+ setFocusable(View.NOT_FOCUSABLE);
159
160
 
160
161
  setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE);
161
162
  updateView(); // call after changing ellipsizeLocation in particular
162
163
  }
163
164
 
165
+ // On Android TV, prevent text views from becoming focusable.
166
+ // ReactTextAnchorViewManager.setAccessible() ties isFocusable to the
167
+ // "accessible" prop, which causes Android TV's D-pad focus engine to land
168
+ // on text views inside recycled list items (FlashList), triggering
169
+ // requestChildFocus -> scrollToChild scroll jumps.
170
+ // On non-TV devices, focusability is left unchanged for accessibility.
171
+ @Override
172
+ public void setFocusable(boolean focusable) {
173
+ if (UiModeUtils.isTVDevice(getContext())) {
174
+ super.setFocusable(false);
175
+ } else {
176
+ super.setFocusable(focusable);
177
+ }
178
+ }
179
+
180
+ @Override
181
+ public void setFocusable(int focusable) {
182
+ if (UiModeUtils.isTVDevice(getContext())) {
183
+ super.setFocusable(View.NOT_FOCUSABLE);
184
+ } else {
185
+ super.setFocusable(focusable);
186
+ }
187
+ }
188
+
164
189
  private static WritableMap inlineViewJson(
165
190
  int visibility, int index, int left, int top, int right, int bottom) {
166
191
  WritableMap json = Arguments.createMap();
@@ -925,6 +925,11 @@ public open class ReactTextInputManager public constructor() :
925
925
  reactContext.surfaceId,
926
926
  editText.id,
927
927
  editText.text.toString(),
928
+ action = when (actionId) {
929
+ EditorInfo.IME_ACTION_NEXT -> ReactTextInputSubmitEditingEvent.ACTION_NEXT
930
+ EditorInfo.IME_ACTION_PREVIOUS -> ReactTextInputSubmitEditingEvent.ACTION_PREVIOUS
931
+ else -> ReactTextInputSubmitEditingEvent.ACTION_SUBMIT
932
+ },
928
933
  )
929
934
  )
930
935
  }
@@ -11,11 +11,12 @@ import com.facebook.react.bridge.Arguments
11
11
  import com.facebook.react.bridge.WritableMap
12
12
  import com.facebook.react.uimanager.events.Event
13
13
 
14
- /** Event emitted by EditText native view when the user submits the text. */
14
+ /** Event emitted by EditText native view when the user triggers an IME editor action. */
15
15
  internal class ReactTextInputSubmitEditingEvent(
16
16
  surfaceId: Int,
17
17
  viewId: Int,
18
18
  private val text: String,
19
+ private val action: String = ACTION_SUBMIT,
19
20
  ) : Event<ReactTextInputSubmitEditingEvent>(surfaceId, viewId) {
20
21
  override fun getEventName(): String = EVENT_NAME
21
22
 
@@ -23,6 +24,7 @@ internal class ReactTextInputSubmitEditingEvent(
23
24
  return Arguments.createMap().apply {
24
25
  putInt("target", viewTag)
25
26
  putString("text", text)
27
+ putString("action", action)
26
28
  }
27
29
  }
28
30
 
@@ -30,5 +32,8 @@ internal class ReactTextInputSubmitEditingEvent(
30
32
 
31
33
  companion object {
32
34
  private const val EVENT_NAME = "topSubmitEditing"
35
+ const val ACTION_SUBMIT = "submit"
36
+ const val ACTION_NEXT = "next"
37
+ const val ACTION_PREVIOUS = "previous"
33
38
  }
34
39
  }
@@ -60,6 +60,7 @@ Pod::Spec.new do |s|
60
60
  ss.source_files = podspec_sources("react/renderer/animated/**/*.{m,mm,cpp,h}", "react/renderer/animated/**/*.{h}")
61
61
  ss.exclude_files = "react/renderer/animated/tests"
62
62
  ss.header_dir = "react/renderer/animated"
63
+ ss.header_mappings_dir = "react/renderer/animated"
63
64
  end
64
65
 
65
66
  s.subspec "animations" do |ss|
@@ -14,15 +14,15 @@
14
14
 
15
15
  #define REACT_NATIVE_VERSION_MAJOR 0
16
16
  #define REACT_NATIVE_VERSION_MINOR 85
17
- #define REACT_NATIVE_VERSION_PATCH 0
17
+ #define REACT_NATIVE_VERSION_PATCH 2
18
18
 
19
19
  namespace facebook::react {
20
20
 
21
21
  struct ReactNativeVersionType {
22
22
  int32_t Major = 0;
23
23
  int32_t Minor = 85;
24
- int32_t Patch = 0;
25
- std::string_view Prerelease = "0rc5";
24
+ int32_t Patch = 2;
25
+ std::string_view Prerelease = "0";
26
26
  };
27
27
 
28
28
  constexpr ReactNativeVersionType ReactNativeVersion;
@@ -4,7 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
- * @generated SignedSource<<7f1c4037925fc37dcdcba51df968d503>>
7
+ * @generated SignedSource<<dc3f7d9199a0f5a94b0669de35fef7a4>>
8
8
  */
9
9
 
10
10
  /**
@@ -96,7 +96,7 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
96
96
  }
97
97
 
98
98
  bool enableCustomFocusSearchOnClippedElementsAndroid() override {
99
- return true;
99
+ return false;
100
100
  }
101
101
 
102
102
  bool enableDestroyShadowTreeRevisionAsync() override {
@@ -4,7 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
- * @generated SignedSource<<6a047fa1d33ea17ebd7ba8d0680ee1cc>>
7
+ * @generated SignedSource<<6ded821dda8049a32168bf82333dd4c3>>
8
8
  */
9
9
 
10
10
  /**
@@ -27,6 +27,10 @@ class ReactNativeFeatureFlagsOverridesOSSExperimental : public ReactNativeFeatur
27
27
  public:
28
28
  ReactNativeFeatureFlagsOverridesOSSExperimental() = default;
29
29
 
30
+ bool cxxNativeAnimatedEnabled() override {
31
+ return true;
32
+ }
33
+
30
34
  bool enableAccessibilityOrder() override {
31
35
  return true;
32
36
  }
@@ -42,6 +46,10 @@ class ReactNativeFeatureFlagsOverridesOSSExperimental : public ReactNativeFeatur
42
46
  bool preventShadowTreeCommitExhaustion() override {
43
47
  return true;
44
48
  }
49
+
50
+ bool useSharedAnimatedBackend() override {
51
+ return true;
52
+ }
45
53
  };
46
54
 
47
55
  } // namespace facebook::react
@@ -461,7 +461,9 @@ void ObjCTurboModule::performVoidMethodInvocation(
461
461
  @try {
462
462
  [inv invokeWithTarget:strongModule];
463
463
  } @catch (NSException *exception) {
464
- throw convertNSExceptionToJSError(runtime, exception, std::string{moduleName}, methodNameStr);
464
+ // Void methods are always async, re-throw instead of converting to
465
+ // JSError, same as the async branch in performMethodInvocation.
466
+ @throw exception;
465
467
  } @finally {
466
468
  [retainedObjectsForInvocation removeAllObjects];
467
469
  }
@@ -515,7 +515,10 @@ void NativeAnimatedNodesManager::handleAnimatedEvent(
515
515
  // frames.
516
516
  if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) {
517
517
  if (auto animationBackend = animationBackend_.lock()) {
518
- animationBackend->trigger();
518
+ animationBackend->pushAnimationMutations(
519
+ [this](AnimationTimestamp timestamp) -> AnimationMutations {
520
+ return pullAnimationMutations(timestamp);
521
+ });
519
522
  }
520
523
  } else {
521
524
  onRender();
@@ -24,10 +24,17 @@ static inline Props::Shared cloneProps(
24
24
  shadowNode.getSurfaceId(), *shadowNode.getContextContainer()};
25
25
  Props::Shared newProps;
26
26
  if (animatedProps.rawProps) {
27
- newProps = shadowNode.getComponentDescriptor().cloneProps(
28
- propsParserContext,
29
- shadowNode.getProps(),
30
- std::move(*animatedProps.rawProps));
27
+ if (ReactNativeFeatureFlags::enableFabricCommitBranching()) {
28
+ newProps = shadowNode.getComponentDescriptor().cloneProps(
29
+ propsParserContext,
30
+ shadowNode.getProps(),
31
+ std::move(*animatedProps.rawProps));
32
+ } else {
33
+ newProps = shadowNode.getComponentDescriptor().cloneProps(
34
+ propsParserContext,
35
+ shadowNode.getProps(),
36
+ RawProps(*animatedProps.rawProps));
37
+ }
31
38
  } else {
32
39
  newProps = shadowNode.getComponentDescriptor().cloneProps(
33
40
  propsParserContext, shadowNode.getProps(), {});
@@ -51,31 +58,27 @@ AnimationBackend::AnimationBackend(
51
58
  react_native_assert(uiManager_.expired() == false);
52
59
  }
53
60
 
54
- void AnimationBackend::onAnimationFrame(AnimationTimestamp timestamp) {
55
- std::vector<CallbackWithId> callbacksCopy;
56
- std::unordered_map<SurfaceId, SurfaceUpdates> surfaceUpdates;
57
- std::set<SurfaceId> asyncFlushSurfaces;
61
+ void AnimationBackend::unpackMutations(
62
+ AnimationMutations& mutations,
63
+ std::unordered_map<SurfaceId, SurfaceUpdates>& surfaceUpdates,
64
+ std::set<SurfaceId>& asyncFlushSurfaces) {
65
+ for (auto& mutation : mutations.batch) {
66
+ const auto family = mutation.family;
67
+ react_native_assert(family != nullptr);
58
68
 
59
- {
60
- std::lock_guard lock(mutex_);
61
- callbacksCopy = callbacks;
69
+ auto& [families, updates, hasLayoutUpdates] =
70
+ surfaceUpdates[family->getSurfaceId()];
71
+ hasLayoutUpdates |= mutation.hasLayoutUpdates;
72
+ families.insert(family);
73
+ updates[mutation.tag] = std::move(mutation.props);
62
74
  }
63
75
 
64
- for (auto& callbackWithId : callbacksCopy) {
65
- auto mutations = callbackWithId.callback(timestamp);
66
- asyncFlushSurfaces.merge(mutations.asyncFlushSurfaces);
67
- for (auto& mutation : mutations.batch) {
68
- const auto family = mutation.family;
69
- react_native_assert(family != nullptr);
70
-
71
- auto& [families, updates, hasLayoutUpdates] =
72
- surfaceUpdates[family->getSurfaceId()];
73
- hasLayoutUpdates |= mutation.hasLayoutUpdates;
74
- families.insert(family);
75
- updates[mutation.tag] = std::move(mutation.props);
76
- }
77
- }
76
+ asyncFlushSurfaces.merge(mutations.asyncFlushSurfaces);
77
+ }
78
78
 
79
+ void AnimationBackend::applySurfaceUpdates(
80
+ std::unordered_map<SurfaceId, SurfaceUpdates>& surfaceUpdates,
81
+ const std::set<SurfaceId>& asyncFlushSurfaces) {
79
82
  animatedPropsRegistry_->update(surfaceUpdates);
80
83
 
81
84
  for (auto& [surfaceId, updates] : surfaceUpdates) {
@@ -89,6 +92,30 @@ void AnimationBackend::onAnimationFrame(AnimationTimestamp timestamp) {
89
92
  requestAsyncFlushForSurfaces(asyncFlushSurfaces);
90
93
  }
91
94
 
95
+ void AnimationBackend::applyMutations(AnimationMutations mutations) {
96
+ std::unordered_map<SurfaceId, SurfaceUpdates> surfaceUpdates;
97
+ std::set<SurfaceId> asyncFlushSurfaces;
98
+ unpackMutations(mutations, surfaceUpdates, asyncFlushSurfaces);
99
+ applySurfaceUpdates(surfaceUpdates, asyncFlushSurfaces);
100
+ }
101
+
102
+ void AnimationBackend::onAnimationFrame(AnimationTimestamp timestamp) {
103
+ std::vector<CallbackWithId> callbacksCopy;
104
+
105
+ {
106
+ std::lock_guard lock(mutex_);
107
+ callbacksCopy = callbacks;
108
+ }
109
+
110
+ std::unordered_map<SurfaceId, SurfaceUpdates> surfaceUpdates;
111
+ std::set<SurfaceId> asyncFlushSurfaces;
112
+ for (auto& callbackWithId : callbacksCopy) {
113
+ auto mutations = callbackWithId.callback(timestamp);
114
+ unpackMutations(mutations, surfaceUpdates, asyncFlushSurfaces);
115
+ }
116
+ applySurfaceUpdates(surfaceUpdates, asyncFlushSurfaces);
117
+ }
118
+
92
119
  CallbackId AnimationBackend::start(const Callback& callback) {
93
120
  std::lock_guard lock(mutex_);
94
121
 
@@ -123,6 +150,12 @@ void AnimationBackend::trigger() {
123
150
  onAnimationFrame(std::chrono::steady_clock::now().time_since_epoch());
124
151
  }
125
152
 
153
+ void AnimationBackend::pushAnimationMutations(const Callback& callback) {
154
+ auto timestamp = animationChoreographer_->now();
155
+ auto mutations = callback(timestamp);
156
+ applyMutations(std::move(mutations));
157
+ }
158
+
126
159
  void AnimationBackend::commitUpdates(
127
160
  SurfaceId surfaceId,
128
161
  SurfaceUpdates& surfaceUpdates) {
@@ -60,10 +60,19 @@ class AnimationBackend : public UIManagerAnimationBackend {
60
60
 
61
61
  void onAnimationFrame(AnimationTimestamp timestamp) override;
62
62
  void trigger() override;
63
+ void pushAnimationMutations(const Callback &callback) override;
63
64
  CallbackId start(const Callback &callback) override;
64
65
  void stop(CallbackId callbackId) override;
65
66
 
66
67
  private:
68
+ void unpackMutations(
69
+ AnimationMutations &mutations,
70
+ std::unordered_map<SurfaceId, SurfaceUpdates> &surfaceUpdates,
71
+ std::set<SurfaceId> &asyncFlushSurfaces);
72
+ void applySurfaceUpdates(
73
+ std::unordered_map<SurfaceId, SurfaceUpdates> &surfaceUpdates,
74
+ const std::set<SurfaceId> &asyncFlushSurfaces);
75
+ void applyMutations(AnimationMutations mutations);
67
76
  std::vector<CallbackWithId> callbacks;
68
77
  std::shared_ptr<AnimatedPropsRegistry> animatedPropsRegistry_;
69
78
  std::shared_ptr<AnimationChoreographer> animationChoreographer_;
@@ -8,6 +8,7 @@
8
8
  #pragma once
9
9
 
10
10
  #include <react/renderer/uimanager/UIManagerAnimationBackend.h>
11
+ #include <react/timing/primitives.h>
11
12
 
12
13
  namespace facebook::react {
13
14
 
@@ -21,6 +22,10 @@ class AnimationChoreographer {
21
22
 
22
23
  virtual void resume() = 0;
23
24
  virtual void pause() = 0;
25
+ virtual AnimationTimestamp now() const
26
+ {
27
+ return HighResTimeStamp::now().toChronoSteadyClockTimePoint().time_since_epoch();
28
+ }
24
29
  void setAnimationBackend(std::weak_ptr<UIManagerAnimationBackend> animationBackend)
25
30
  {
26
31
  animationBackend_ = animationBackend;
@@ -41,6 +41,15 @@ BaseScrollViewProps::BaseScrollViewProps(
41
41
  "snapToItemPadding",
42
42
  sourceProps.snapToItemPadding,
43
43
  (Float)0)),
44
+ scrollAnimationEnabled(
45
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
46
+ ? sourceProps.scrollAnimationEnabled
47
+ : convertRawProp(
48
+ context,
49
+ rawProps,
50
+ "scrollAnimationEnabled",
51
+ sourceProps.scrollAnimationEnabled,
52
+ true)),
44
53
  #endif
45
54
  alwaysBounceHorizontal(
46
55
  ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
@@ -410,6 +419,7 @@ void BaseScrollViewProps::setProp(
410
419
  #if TARGET_OS_TV
411
420
  RAW_SET_PROP_SWITCH_CASE_BASIC(showsScrollIndex);
412
421
  RAW_SET_PROP_SWITCH_CASE_BASIC(snapToItemPadding);
422
+ RAW_SET_PROP_SWITCH_CASE_BASIC(scrollAnimationEnabled);
413
423
  #endif
414
424
  RAW_SET_PROP_SWITCH_CASE_BASIC(alwaysBounceHorizontal);
415
425
  RAW_SET_PROP_SWITCH_CASE_BASIC(alwaysBounceVertical);
@@ -32,6 +32,7 @@ class BaseScrollViewProps : public ViewProps {
32
32
  #if TARGET_OS_TV
33
33
  bool showsScrollIndex{true};
34
34
  Float snapToItemPadding{0};
35
+ bool scrollAnimationEnabled{true};
35
36
  #endif
36
37
  bool alwaysBounceHorizontal{};
37
38
  bool alwaysBounceVertical{};
@@ -31,6 +31,7 @@ class UIManagerAnimationBackend {
31
31
  virtual void stop(CallbackId callbackId) = 0;
32
32
  virtual void clearRegistry(SurfaceId surfaceId) = 0;
33
33
  virtual void trigger() = 0;
34
+ virtual void pushAnimationMutations(const Callback &callback) = 0;
34
35
  virtual void registerJSInvoker(std::shared_ptr<CallInvoker> jsInvoker) = 0;
35
36
  };
36
37