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
@@ -201,10 +201,11 @@ RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrollView, NSInt
201
201
 
202
202
  UIEdgeInsets newEdgeInsets = _scrollView.contentInset;
203
203
  CGFloat inset = MAX(scrollViewLowerY - keyboardEndFrame.origin.y, 0);
204
+ const auto &props = static_cast<const ScrollViewProps &>(*_props);
204
205
  if (isInverted) {
205
- newEdgeInsets.top = MAX(inset, _scrollView.contentInset.top);
206
+ newEdgeInsets.top = MAX(inset, props.contentInset.top);
206
207
  } else {
207
- newEdgeInsets.bottom = MAX(inset, _scrollView.contentInset.bottom);
208
+ newEdgeInsets.bottom = MAX(inset, props.contentInset.bottom);
208
209
  }
209
210
 
210
211
  CGPoint newContentOffset = _scrollView.contentOffset;
@@ -222,12 +223,6 @@ RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrollView, NSInt
222
223
  contentDiff = keyboardEndFrame.origin.y - keyboardBeginFrame.origin.y;
223
224
  }
224
225
  } else {
225
- CGRect viewIntersection = CGRectIntersection(self.firstResponderFocus, keyboardEndFrame);
226
-
227
- if (CGRectIsNull(viewIntersection)) {
228
- return;
229
- }
230
-
231
226
  // Inner text field focused
232
227
  CGFloat focusEnd = CGRectGetMaxY(self.firstResponderFocus);
233
228
  if (focusEnd > keyboardEndFrame.origin.y) {
@@ -259,7 +254,7 @@ RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrollView, NSInt
259
254
  animations:^{
260
255
  self->_scrollView.contentInset = newEdgeInsets;
261
256
  self->_scrollView.verticalScrollIndicatorInsets = newEdgeInsets;
262
- [self scrollToOffset:newContentOffset animated:NO];
257
+ [self scrollTo:newContentOffset.x y:newContentOffset.y animated:NO];
263
258
  }
264
259
  completion:nil];
265
260
  }
@@ -1028,18 +1023,23 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
1028
1023
  [self sendBlurNotification];
1029
1024
  [self removeSwipeGestureRecognizers];
1030
1025
  [self resignFirstResponder];
1031
- // if we leave the scroll view and go up, then scroll to top; if going down,
1032
- // scroll to bottom
1033
- // Similarly for left and right
1026
+ // If scrolling is enabled:
1027
+ // - Scroll to the top when moving up and to the bottom when moving down.
1028
+ // - Similarly, scroll towards leading edge when moving towards leading edge and to the trailing edge when moving towards the trailing edge.
1029
+ BOOL isRTL = [[RCTI18nUtil sharedInstance] isRTL];
1030
+ BOOL isMovingTowardsLeadingEdge = (isRTL ? context.focusHeading == UIFocusHeadingRight : context.focusHeading == UIFocusHeadingLeft);
1031
+ BOOL isMovingTowardsTrailingEdge = (isRTL ? context.focusHeading == UIFocusHeadingLeft : context.focusHeading == UIFocusHeadingRight);
1034
1032
  RCTEnhancedScrollView *scrollView = (RCTEnhancedScrollView *)_scrollView;
1035
- if (context.focusHeading == UIFocusHeadingUp && scrollView.snapToStart) {
1036
- [self swipeVerticalScrollToOffset:0.0];
1037
- } else if(context.focusHeading == UIFocusHeadingDown && scrollView.snapToEnd) {
1038
- [self swipeVerticalScrollToOffset:scrollView.contentSize.height];
1039
- } else if(context.focusHeading == UIFocusHeadingLeft && scrollView.snapToStart) {
1040
- [self swipeHorizontalScrollToOffset:0.0];
1041
- } else if(context.focusHeading == UIFocusHeadingRight && scrollView.snapToEnd) {
1042
- [self swipeHorizontalScrollToOffset:scrollView.contentSize.width];
1033
+ if (scrollView.isScrollEnabled) {
1034
+ if (context.focusHeading == UIFocusHeadingUp && scrollView.snapToStart) {
1035
+ [self scrollToVerticalOffset:0.0];
1036
+ } else if(context.focusHeading == UIFocusHeadingDown && scrollView.snapToEnd) {
1037
+ [self scrollToVerticalOffset:scrollView.contentSize.height];
1038
+ } else if(isMovingTowardsLeadingEdge && scrollView.snapToStart) {
1039
+ [self scrollToHorizontalOffset:0.0];
1040
+ } else if(isMovingTowardsTrailingEdge && scrollView.snapToEnd) {
1041
+ [self scrollToHorizontalOffset:scrollView.contentSize.width];
1042
+ }
1043
1043
  }
1044
1044
  }
1045
1045
  }
@@ -1098,23 +1098,32 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
1098
1098
 
1099
1099
  - (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
1100
1100
  {
1101
+ // If the previously focused item is this view and scrolling is disabled, defer to the superclass
1102
+ if (context.previouslyFocusedItem == self && !self.scrollView.isScrollEnabled) {
1103
+ return [super shouldUpdateFocusInContext:context];
1104
+ }
1105
+
1101
1106
  // Determine if the layout is Right-to-Left
1102
1107
  BOOL isRTL = [[RCTI18nUtil sharedInstance] isRTL];
1103
1108
  BOOL isHorizontal = _scrollView.contentSize.width > self.frame.size.width;
1104
1109
  // Adjust for horizontal scrolling with RTL support
1105
1110
  if (isHorizontal) {
1106
- BOOL isNavigatingToEnd = (isRTL ? context.focusHeading == UIFocusHeadingLeft : context.focusHeading == UIFocusHeadingRight);
1107
- BOOL isNavigatingToStart = (isRTL ? context.focusHeading == UIFocusHeadingRight : context.focusHeading == UIFocusHeadingLeft);
1111
+ BOOL isMovingTowardsLeadingEdge = (isRTL ? context.focusHeading == UIFocusHeadingRight : context.focusHeading == UIFocusHeadingLeft);
1112
+ BOOL isMovingTowardsTrailingEdge = (isRTL ? context.focusHeading == UIFocusHeadingLeft : context.focusHeading == UIFocusHeadingRight);
1113
+
1114
+ BOOL isScrollingToLeading = (isMovingTowardsLeadingEdge && self.scrollView.contentOffset.x > 0);
1115
+ BOOL isScrollingToTrailing = (isMovingTowardsTrailingEdge && self.scrollView.contentOffset.x < self.scrollView.contentSize.width - MAX(self.scrollView.visibleSize.width, 1));
1108
1116
 
1109
- if ((isNavigatingToEnd && self.scrollView.contentOffset.x < self.scrollView.contentSize.width - self.scrollView.visibleSize.width) ||
1110
- (isNavigatingToStart && self.scrollView.contentOffset.x > 0)) {
1111
- return [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem];
1117
+ if (isScrollingToLeading || isScrollingToTrailing) {
1118
+ return (context.nextFocusedItem && [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem]);
1112
1119
  }
1113
1120
  } else {
1114
1121
  // Handle vertical scrolling as before
1115
- if ((context.focusHeading == UIFocusHeadingUp && self.scrollView.contentOffset.y > 0) ||
1116
- (context.focusHeading == UIFocusHeadingDown && self.scrollView.contentOffset.y < self.scrollView.contentSize.height - self.scrollView.visibleSize.height)) {
1117
- return [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem];
1122
+ BOOL isMovingUp = (context.focusHeading == UIFocusHeadingUp && self.scrollView.contentOffset.y > 0);
1123
+ BOOL isMovingDown = (context.focusHeading == UIFocusHeadingDown && self.scrollView.contentOffset.y < self.scrollView.contentSize.height - MAX(self.scrollView.visibleSize.height, 1));
1124
+
1125
+ if (isMovingUp || isMovingDown) {
1126
+ return (context.nextFocusedItem && [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem]);
1118
1127
  }
1119
1128
  }
1120
1129
  return [super shouldUpdateFocusInContext:context];
@@ -1158,60 +1167,100 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
1158
1167
  return duration;
1159
1168
  }
1160
1169
 
1161
- - (void)swipeVerticalScrollToOffset:(CGFloat)yOffset
1170
+ - (void)scrollToVerticalOffset:(CGFloat)yOffset
1162
1171
  {
1163
1172
  _blockFirstTouch = NO;
1164
1173
  dispatch_async(dispatch_get_main_queue(), ^{
1165
1174
  CGFloat limitedOffset = yOffset;
1175
+
1176
+ // Ensure content size and visible size are non-negative
1177
+ CGFloat contentHeight = MAX(self.scrollView.contentSize.height, 0.0);
1178
+ CGFloat visibleHeight = MAX(self.scrollView.visibleSize.height, 0.0);
1179
+
1180
+ // Compute the maximum offset, ensuring it's non-negative
1181
+ CGFloat maxOffset = MAX(contentHeight - visibleHeight, 0.0);
1182
+
1183
+ // Clamp the offset within valid bounds
1166
1184
  limitedOffset = MAX(limitedOffset, 0.0);
1167
- limitedOffset = MIN(limitedOffset, self.scrollView.contentSize.height - self.scrollView.visibleSize.height);
1185
+ limitedOffset = MIN(limitedOffset, maxOffset);
1186
+
1168
1187
  [UIView animateWithDuration:[self swipeDuration] animations:^{
1169
1188
  self.scrollView.contentOffset =
1170
- CGPointMake(self.scrollView.contentOffset.x, limitedOffset);
1189
+ CGPointMake(self.scrollView.contentOffset.x, limitedOffset);
1171
1190
  }];
1172
1191
  });
1173
1192
  }
1174
1193
 
1175
- - (void)swipeHorizontalScrollToOffset:(CGFloat)xOffset
1194
+ - (void)scrollToHorizontalOffset:(CGFloat)xOffset
1176
1195
  {
1177
1196
  _blockFirstTouch = NO;
1178
1197
  dispatch_async(dispatch_get_main_queue(), ^{
1179
1198
  CGFloat limitedOffset = xOffset;
1199
+
1200
+ // Ensure content size and visible size are non-negative
1201
+ CGFloat contentWidth = MAX(self.scrollView.contentSize.width, 0.0);
1202
+ CGFloat visibleWidth = MAX(self.scrollView.visibleSize.width, 0.0);
1203
+
1204
+ // Compute the maximum offset, ensuring it's non-negative
1205
+ CGFloat maxOffset = MAX(contentWidth - visibleWidth, 0.0);
1206
+
1207
+ // Clamp the offset within valid bounds
1180
1208
  limitedOffset = MAX(limitedOffset, 0.0);
1181
- limitedOffset = MIN(limitedOffset, self.scrollView.contentSize.width - self.scrollView.visibleSize.width);
1209
+ limitedOffset = MIN(limitedOffset, maxOffset);
1210
+
1182
1211
  [UIView animateWithDuration:[self swipeDuration] animations:^{
1183
1212
  self.scrollView.contentOffset =
1184
- CGPointMake(limitedOffset, self.scrollView.contentOffset.y);
1213
+ CGPointMake(limitedOffset, self.scrollView.contentOffset.y);
1185
1214
  }];
1186
1215
  });
1187
1216
  }
1188
1217
 
1189
1218
  - (void)swipedUp
1190
1219
  {
1220
+ if (!self.scrollView.scrollEnabled) {
1221
+ return;
1222
+ }
1223
+
1191
1224
  CGFloat newOffset = self.scrollView.contentOffset.y - [self swipeVerticalInterval];
1192
1225
  // NSLog(@"Swiped up to %f", newOffset);
1193
- [self swipeVerticalScrollToOffset:newOffset];
1226
+ [self scrollToVerticalOffset:newOffset];
1194
1227
  }
1195
1228
 
1196
1229
  - (void)swipedDown
1197
1230
  {
1231
+ if (!self.scrollView.scrollEnabled) {
1232
+ return;
1233
+ }
1234
+
1198
1235
  CGFloat newOffset = self.scrollView.contentOffset.y + [self swipeVerticalInterval];
1199
1236
  // NSLog(@"Swiped down to %f", newOffset);
1200
- [self swipeVerticalScrollToOffset:newOffset];
1237
+ [self scrollToVerticalOffset:newOffset];
1201
1238
  }
1202
1239
 
1203
1240
  - (void)swipedLeft
1204
1241
  {
1205
- CGFloat newOffset = self.scrollView.contentOffset.x - [self swipeHorizontalInterval];
1242
+ if (!self.scrollView.scrollEnabled) {
1243
+ return;
1244
+ }
1245
+
1246
+ BOOL isRTL = [[RCTI18nUtil sharedInstance] isRTL];
1247
+ NSInteger horizontalInterval = [self swipeHorizontalInterval];
1248
+ CGFloat newOffset = self.scrollView.contentOffset.x + (isRTL ? horizontalInterval : -horizontalInterval);
1206
1249
  // NSLog(@"Swiped left to %f", newOffset);
1207
- [self swipeHorizontalScrollToOffset:newOffset];
1250
+ [self scrollToHorizontalOffset:newOffset];
1208
1251
  }
1209
1252
 
1210
1253
  - (void)swipedRight
1211
1254
  {
1212
- CGFloat newOffset = self.scrollView.contentOffset.x + [self swipeHorizontalInterval];
1255
+ if (!self.scrollView.scrollEnabled) {
1256
+ return;
1257
+ }
1258
+
1259
+ BOOL isRTL = [[RCTI18nUtil sharedInstance] isRTL];
1260
+ NSInteger horizontalInterval = [self swipeHorizontalInterval];
1261
+ CGFloat newOffset = self.scrollView.contentOffset.x + (isRTL ? -horizontalInterval : horizontalInterval);
1213
1262
  // NSLog(@"Swiped right to %f", newOffset);
1214
- [self swipeHorizontalScrollToOffset:newOffset];
1263
+ [self scrollToHorizontalOffset:newOffset];
1215
1264
  }
1216
1265
 
1217
1266
  - (void)addSwipeGestureRecognizers
@@ -68,6 +68,8 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
68
68
  * later comparison insensitive to them.
69
69
  */
70
70
  NSDictionary<NSAttributedStringKey, id> *_originalTypingAttributes;
71
+
72
+ BOOL _hasInputAccessoryView;
71
73
  }
72
74
 
73
75
  #pragma mark - UIView overrides
@@ -99,9 +101,11 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
99
101
  NSMutableDictionary<NSAttributedStringKey, id> *defaultAttributes =
100
102
  [_backedTextInputView.defaultTextAttributes mutableCopy];
101
103
 
104
+ #if !TARGET_OS_MACCATALYST
102
105
  RCTWeakEventEmitterWrapper *eventEmitterWrapper = [RCTWeakEventEmitterWrapper new];
103
106
  eventEmitterWrapper.eventEmitter = _eventEmitter;
104
107
  defaultAttributes[RCTAttributedStringEventEmitterKey] = eventEmitterWrapper;
108
+ #endif
105
109
 
106
110
  _backedTextInputView.defaultTextAttributes = defaultAttributes;
107
111
  }
@@ -262,8 +266,10 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
262
266
  if (newTextInputProps.textAttributes != oldTextInputProps.textAttributes) {
263
267
  NSMutableDictionary<NSAttributedStringKey, id> *defaultAttributes =
264
268
  RCTNSTextAttributesFromTextAttributes(newTextInputProps.getEffectiveTextAttributes(RCTFontSizeMultiplier()));
269
+ #if !TARGET_OS_MACCATALYST
265
270
  defaultAttributes[RCTAttributedStringEventEmitterKey] =
266
271
  _backedTextInputView.defaultTextAttributes[RCTAttributedStringEventEmitterKey];
272
+ #endif
267
273
  _backedTextInputView.defaultTextAttributes = defaultAttributes;
268
274
  }
269
275
 
@@ -609,10 +615,12 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
609
615
  keyboardType == UIKeyboardTypeDecimalPad || keyboardType == UIKeyboardTypeASCIICapableNumberPad) &&
610
616
  (containsKeyType || containsInputAccessoryViewButtonLabel);
611
617
 
612
- if ((_backedTextInputView.inputAccessoryView != nil) == shouldHaveInputAccessoryView) {
618
+ if (_hasInputAccessoryView == shouldHaveInputAccessoryView) {
613
619
  return;
614
620
  }
615
621
 
622
+ _hasInputAccessoryView = shouldHaveInputAccessoryView;
623
+
616
624
  #if !TARGET_OS_TV
617
625
  if (shouldHaveInputAccessoryView) {
618
626
  NSString *buttonLabel = inputAccessoryViewButtonLabel != nil ? inputAccessoryViewButtonLabel
@@ -641,6 +649,8 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
641
649
 
642
650
  - (void)handleInputAccessoryDoneButton
643
651
  {
652
+ // Ignore the value of whether we submitted; just make sure the submit event is called if necessary.
653
+ [self textInputShouldSubmitOnReturn];
644
654
  if ([self textInputShouldReturn]) {
645
655
  [_backedTextInputView endEditing:YES];
646
656
  }
@@ -585,7 +585,7 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
585
585
  [self handleFocusGuide];
586
586
  }
587
587
 
588
- if (context.nextFocusedView == self && self.isUserInteractionEnabled && ![self isTVFocusGuide]) {
588
+ if (context.nextFocusedView == self) {
589
589
  if(_eventEmitter) _eventEmitter->onFocus();
590
590
 
591
591
  [self becomeFirstResponder];
@@ -594,7 +594,10 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
594
594
  [self addParallaxMotionEffects];
595
595
  [self sendFocusNotification:context];
596
596
  } completion:^(void){}];
597
- } else {
597
+ // Without this check, onBlur would also trigger when `TVFocusGuideView` transfers focus to its children.
598
+ // [self isTVFocusGuide] is false when autofocus and destinations are not used, so we cannot use that.
599
+ // Generally speaking, it would happen for any non-collapsable `View`.
600
+ } else if (context.previouslyFocusedView == self) {
598
601
  if (_eventEmitter) _eventEmitter->onBlur();
599
602
 
600
603
  [self disableDirectionalFocusGuides];
@@ -303,7 +303,7 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
303
303
  [self handleFocusGuide];
304
304
  }
305
305
 
306
- if (context.nextFocusedView == self && ![self isTVFocusGuide] && self.isTVSelectable ) {
306
+ if (context.nextFocusedView == self) {
307
307
  if (self.onFocus) self.onFocus(nil);
308
308
  [self becomeFirstResponder];
309
309
  [self enableDirectionalFocusGuides];
@@ -311,7 +311,10 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
311
311
  [self addParallaxMotionEffects];
312
312
  [self sendFocusNotification:context];
313
313
  } completion:^(void){}];
314
- } else {
314
+ // Without this check, onBlur would also trigger when `TVFocusGuideView` transfers focus to its children.
315
+ // [self isTVFocusGuide] is false when autofocus and destinations are not used, so we cannot use that.
316
+ // Generally speaking, it would happen for any non-collapsable `View`.
317
+ } else if (context.previouslyFocusedView == self ) {
315
318
  if (self.onBlur) self.onBlur(nil);
316
319
  [self disableDirectionalFocusGuides];
317
320
  [coordinator addCoordinatedAnimations:^(void){
@@ -1025,19 +1025,23 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1025
1025
  [self sendBlurNotification];
1026
1026
  [self removeSwipeGestureRecognizers];
1027
1027
  [self resignFirstResponder];
1028
- // if we leave the scroll view and go up, then scroll to top; if going down,
1029
- // scroll to bottom
1030
- // Similarly for left and right
1031
- if (context.focusHeading == UIFocusHeadingUp && self.snapToStart) {
1032
- [self scrollToVerticalOffset:0.0];
1033
- } else if(context.focusHeading == UIFocusHeadingDown && self.snapToEnd) {
1034
- [self scrollToVerticalOffset:self.scrollView.contentSize.height];
1035
- } else if(context.focusHeading == UIFocusHeadingLeft && self.snapToStart) {
1036
- [self scrollToHorizontalOffset:0.0];
1037
- } else if(context.focusHeading == UIFocusHeadingRight && self.snapToEnd) {
1038
- [self scrollToHorizontalOffset:self.scrollView.contentSize.width];
1028
+ // If scrolling is enabled:
1029
+ // - Scroll to the top when moving up and to the bottom when moving down.
1030
+ // - Similarly, scroll towards leading edge when moving towards leading edge and to the trailing edge when moving towards the trailing edge.
1031
+ BOOL isRTL = [[RCTI18nUtil sharedInstance] isRTL];
1032
+ BOOL isMovingTowardsLeadingEdge = (isRTL ? context.focusHeading == UIFocusHeadingRight : context.focusHeading == UIFocusHeadingLeft);
1033
+ BOOL isMovingTowardsTrailingEdge = (isRTL ? context.focusHeading == UIFocusHeadingLeft : context.focusHeading == UIFocusHeadingRight);
1034
+ if (self.scrollView.isScrollEnabled) {
1035
+ if (context.focusHeading == UIFocusHeadingUp && self.snapToStart) {
1036
+ [self scrollToVerticalOffset:0.0];
1037
+ } else if(context.focusHeading == UIFocusHeadingDown && self.snapToEnd) {
1038
+ [self scrollToVerticalOffset:self.scrollView.contentSize.height];
1039
+ } else if(isMovingTowardsLeadingEdge && self.snapToStart) {
1040
+ [self scrollToHorizontalOffset:0.0];
1041
+ } else if(isMovingTowardsLeadingEdge && self.snapToEnd) {
1042
+ [self scrollToHorizontalOffset:self.scrollView.contentSize.width];
1043
+ }
1039
1044
  }
1040
-
1041
1045
  }
1042
1046
  }
1043
1047
 
@@ -1093,22 +1097,31 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1093
1097
 
1094
1098
  - (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
1095
1099
  {
1100
+ // If the previously focused item is this view and scrolling is disabled, defer to the superclass
1101
+ if (context.previouslyFocusedItem == self && !self.scrollView.isScrollEnabled) {
1102
+ return [super shouldUpdateFocusInContext:context];
1103
+ }
1104
+
1096
1105
  // Determine if the layout is Right-to-Left
1097
1106
  BOOL isRTL = [[RCTI18nUtil sharedInstance] isRTL];
1098
1107
  // Adjust for horizontal scrolling with RTL support
1099
1108
  if ([self isHorizontal:self.scrollView]) {
1100
- BOOL isNavigatingToEnd = (isRTL ? context.focusHeading == UIFocusHeadingLeft : context.focusHeading == UIFocusHeadingRight);
1101
- BOOL isNavigatingToStart = (isRTL ? context.focusHeading == UIFocusHeadingRight : context.focusHeading == UIFocusHeadingLeft);
1109
+ BOOL isMovingTowardsLeadingEdge = (isRTL ? context.focusHeading == UIFocusHeadingRight : context.focusHeading == UIFocusHeadingLeft);
1110
+ BOOL isMovingTowardsTrailingEdge = (isRTL ? context.focusHeading == UIFocusHeadingLeft : context.focusHeading == UIFocusHeadingRight);
1111
+
1112
+ BOOL isScrollingToLeading = (isMovingTowardsLeadingEdge && self.scrollView.contentOffset.x > 0);
1113
+ BOOL isScrollingToTrailing = (isMovingTowardsTrailingEdge && self.scrollView.contentOffset.x < self.scrollView.contentSize.width - MAX(self.scrollView.visibleSize.width, 1));
1102
1114
 
1103
- if ((isNavigatingToEnd && self.scrollView.contentOffset.x < self.scrollView.contentSize.width - self.scrollView.visibleSize.width) ||
1104
- (isNavigatingToStart && self.scrollView.contentOffset.x > 0)) {
1105
- return [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem];
1115
+ if (isScrollingToLeading || isScrollingToTrailing) {
1116
+ return (context.nextFocusedItem && [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem]);
1106
1117
  }
1107
1118
  } else {
1108
1119
  // Handle vertical scrolling as before
1109
- if ((context.focusHeading == UIFocusHeadingUp && self.scrollView.contentOffset.y > 0) ||
1110
- (context.focusHeading == UIFocusHeadingDown && self.scrollView.contentOffset.y < self.scrollView.contentSize.height - self.scrollView.visibleSize.height)) {
1111
- return [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem];
1120
+ BOOL isMovingUp = (context.focusHeading == UIFocusHeadingUp && self.scrollView.contentOffset.y > 0);
1121
+ BOOL isMovingDown = (context.focusHeading == UIFocusHeadingDown && self.scrollView.contentOffset.y < self.scrollView.contentSize.height - MAX(self.scrollView.visibleSize.height, 1));
1122
+
1123
+ if (isMovingUp || isMovingDown) {
1124
+ return (context.nextFocusedItem && [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem]);
1112
1125
  }
1113
1126
  }
1114
1127
  return [super shouldUpdateFocusInContext:context];
@@ -1154,11 +1167,21 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1154
1167
  _blockFirstTouch = NO;
1155
1168
  dispatch_async(dispatch_get_main_queue(), ^{
1156
1169
  CGFloat limitedOffset = yOffset;
1170
+
1171
+ // Ensure content size and visible size are non-negative
1172
+ CGFloat contentHeight = MAX(self.scrollView.contentSize.height, 0.0);
1173
+ CGFloat visibleHeight = MAX(self.scrollView.visibleSize.height, 0.0);
1174
+
1175
+ // Compute the maximum offset, ensuring it's non-negative
1176
+ CGFloat maxOffset = MAX(contentHeight - visibleHeight, 0.0);
1177
+
1178
+ // Clamp the offset within valid bounds
1157
1179
  limitedOffset = MAX(limitedOffset, 0.0);
1158
- limitedOffset = MIN(limitedOffset, self.scrollView.contentSize.height - self.scrollView.visibleSize.height);
1180
+ limitedOffset = MIN(limitedOffset, maxOffset);
1181
+
1159
1182
  [UIView animateWithDuration:[self swipeDuration] animations:^{
1160
1183
  self.scrollView.contentOffset =
1161
- CGPointMake(self.scrollView.contentOffset.x, limitedOffset);
1184
+ CGPointMake(self.scrollView.contentOffset.x, limitedOffset);
1162
1185
  }];
1163
1186
  });
1164
1187
  }
@@ -1168,11 +1191,21 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1168
1191
  _blockFirstTouch = NO;
1169
1192
  dispatch_async(dispatch_get_main_queue(), ^{
1170
1193
  CGFloat limitedOffset = xOffset;
1194
+
1195
+ // Ensure content size and visible size are non-negative
1196
+ CGFloat contentWidth = MAX(self.scrollView.contentSize.width, 0.0);
1197
+ CGFloat visibleWidth = MAX(self.scrollView.visibleSize.width, 0.0);
1198
+
1199
+ // Compute the maximum offset, ensuring it's non-negative
1200
+ CGFloat maxOffset = MAX(contentWidth - visibleWidth, 0.0);
1201
+
1202
+ // Clamp the offset within valid bounds
1171
1203
  limitedOffset = MAX(limitedOffset, 0.0);
1172
- limitedOffset = MIN(limitedOffset, self.scrollView.contentSize.width - self.scrollView.visibleSize.width);
1204
+ limitedOffset = MIN(limitedOffset, maxOffset);
1205
+
1173
1206
  [UIView animateWithDuration:[self swipeDuration] animations:^{
1174
1207
  self.scrollView.contentOffset =
1175
- CGPointMake(limitedOffset, self.scrollView.contentOffset.y);
1208
+ CGPointMake(limitedOffset, self.scrollView.contentOffset.y);
1176
1209
  }];
1177
1210
  });
1178
1211
  }
@@ -1205,7 +1238,9 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1205
1238
  return;
1206
1239
  }
1207
1240
 
1208
- CGFloat newOffset = self.scrollView.contentOffset.x - [self swipeHorizontalInterval];
1241
+ BOOL isRTL = [[RCTI18nUtil sharedInstance] isRTL];
1242
+ NSInteger horizontalInterval = [self swipeHorizontalInterval];
1243
+ CGFloat newOffset = self.scrollView.contentOffset.x + (isRTL ? horizontalInterval : -horizontalInterval);
1209
1244
  // NSLog(@"Swiped left to %f", newOffset);
1210
1245
  [self scrollToHorizontalOffset:newOffset];
1211
1246
  }
@@ -1216,7 +1251,9 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1216
1251
  return;
1217
1252
  }
1218
1253
 
1219
- CGFloat newOffset = self.scrollView.contentOffset.x + [self swipeHorizontalInterval];
1254
+ BOOL isRTL = [[RCTI18nUtil sharedInstance] isRTL];
1255
+ NSInteger horizontalInterval = [self swipeHorizontalInterval];
1256
+ CGFloat newOffset = self.scrollView.contentOffset.x + (isRTL ? -horizontalInterval : horizontalInterval);
1220
1257
  // NSLog(@"Swiped right to %f", newOffset);
1221
1258
  [self scrollToHorizontalOffset:newOffset];
1222
1259
  }
@@ -608,6 +608,7 @@ public abstract class com/facebook/react/bridge/BaseJavaModule : com/facebook/re
608
608
  protected final fun getReactApplicationContextIfActiveOrWarn ()Lcom/facebook/react/bridge/ReactApplicationContext;
609
609
  public fun initialize ()V
610
610
  public fun invalidate ()V
611
+ protected fun setEventEmitterCallback (Lcom/facebook/react/bridge/CxxCallbackImpl;)V
611
612
  }
612
613
 
613
614
  public abstract interface class com/facebook/react/bridge/BridgeReactContext$RCTDeviceEventEmitter : com/facebook/react/bridge/JavaScriptModule {
@@ -2963,6 +2964,7 @@ public abstract interface annotation class com/facebook/react/module/annotations
2963
2964
  public final class com/facebook/react/module/model/ReactModuleInfo {
2964
2965
  public static final field Companion Lcom/facebook/react/module/model/ReactModuleInfo$Companion;
2965
2966
  public fun <init> (Ljava/lang/String;Ljava/lang/String;ZZZZ)V
2967
+ public fun <init> (Ljava/lang/String;Ljava/lang/String;ZZZZZ)V
2966
2968
  public final fun canOverrideExistingModule ()Z
2967
2969
  public static final fun classIsTurboModule (Ljava/lang/Class;)Z
2968
2970
  public final fun className ()Ljava/lang/String;
@@ -7442,6 +7444,7 @@ public class com/facebook/react/views/text/TextAttributeProps {
7442
7444
  public static final field TA_KEY_LETTER_SPACING S
7443
7445
  public static final field TA_KEY_LINE_BREAK_STRATEGY S
7444
7446
  public static final field TA_KEY_LINE_HEIGHT S
7447
+ public static final field TA_KEY_MAX_FONT_SIZE_MULTIPLIER S
7445
7448
  public static final field TA_KEY_OPACITY S
7446
7449
  public static final field TA_KEY_ROLE S
7447
7450
  public static final field TA_KEY_TEXT_DECORATION_COLOR S
@@ -7474,6 +7477,7 @@ public class com/facebook/react/views/text/TextAttributeProps {
7474
7477
  protected field mLetterSpacingInput F
7475
7478
  protected field mLineHeight F
7476
7479
  protected field mLineHeightInput F
7480
+ protected field mMaxFontSizeMultiplier F
7477
7481
  protected field mNumberOfLines I
7478
7482
  protected field mOpacity F
7479
7483
  protected field mRole Lcom/facebook/react/uimanager/ReactAccessibilityDelegate$Role;
@@ -7835,6 +7839,7 @@ public class com/facebook/react/views/view/ReactViewGroup : android/view/ViewGro
7835
7839
  protected fun dispatchSetPressed (Z)V
7836
7840
  public fun draw (Landroid/graphics/Canvas;)V
7837
7841
  protected fun drawChild (Landroid/graphics/Canvas;Landroid/view/View;J)Z
7842
+ public fun endViewTransition (Landroid/view/View;)V
7838
7843
  protected fun getChildDrawingOrder (II)I
7839
7844
  public fun getClippingRect (Landroid/graphics/Rect;)V
7840
7845
  public fun getHitSlopRect ()Landroid/graphics/Rect;
@@ -39,9 +39,19 @@ if (PROJECT_ROOT_DIR)
39
39
  # variable is defined if user need to access it.
40
40
  endif ()
41
41
 
42
- file(GLOB input_SRC CONFIGURE_DEPENDS
43
- ${REACT_ANDROID_DIR}/cmake-utils/default-app-setup/*.cpp
44
- ${BUILD_DIR}/generated/autolinking/src/main/jni/*.cpp)
42
+ file(GLOB override_cpp_SRC CONFIGURE_DEPENDS *.cpp)
43
+ # We check if the user is providing a custom OnLoad.cpp file. If so, we pick that
44
+ # for compilation. Otherwise we fallback to using the `default-app-setup/OnLoad.cpp`
45
+ # file instead.
46
+ if(override_cpp_SRC)
47
+ file(GLOB input_SRC CONFIGURE_DEPENDS
48
+ *.cpp
49
+ ${BUILD_DIR}/generated/autolinking/src/main/jni/*.cpp)
50
+ else()
51
+ file(GLOB input_SRC CONFIGURE_DEPENDS
52
+ ${REACT_ANDROID_DIR}/cmake-utils/default-app-setup/*.cpp
53
+ ${BUILD_DIR}/generated/autolinking/src/main/jni/*.cpp)
54
+ endif()
45
55
 
46
56
  add_library(${CMAKE_PROJECT_NAME} SHARED ${input_SRC})
47
57
 
@@ -29,12 +29,7 @@
29
29
 
30
30
  #include <DefaultComponentsRegistry.h>
31
31
  #include <DefaultTurboModuleManagerDelegate.h>
32
- #if __has_include("<autolinking.h>")
33
- #define AUTOLINKING_AVAILABLE 1
34
32
  #include <autolinking.h>
35
- #else
36
- #define AUTOLINKING_AVAILABLE 0
37
- #endif
38
33
  #include <fbjni/fbjni.h>
39
34
  #include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
40
35
  #include <rncore.h>
@@ -61,10 +56,8 @@ void registerComponents(
61
56
  REACT_NATIVE_APP_COMPONENT_REGISTRATION(registry);
62
57
  #endif
63
58
 
64
- #if AUTOLINKING_AVAILABLE
65
59
  // And we fallback to the components autolinked
66
60
  autolinking_registerProviders(registry);
67
- #endif
68
61
  }
69
62
 
70
63
  std::shared_ptr<TurboModule> cxxModuleProvider(
@@ -78,12 +71,8 @@ std::shared_ptr<TurboModule> cxxModuleProvider(
78
71
  // return std::make_shared<NativeCxxModuleExample>(jsInvoker);
79
72
  // }
80
73
 
81
- #if AUTOLINKING_AVAILABLE
82
74
  // And we fallback to the CXX module providers autolinked
83
75
  return autolinking_cxxModuleProvider(name, jsInvoker);
84
- #endif
85
-
86
- return nullptr;
87
76
  }
88
77
 
89
78
  std::shared_ptr<TurboModule> javaModuleProvider(
@@ -112,12 +101,10 @@ std::shared_ptr<TurboModule> javaModuleProvider(
112
101
  return module;
113
102
  }
114
103
 
115
- #if AUTOLINKING_AVAILABLE
116
104
  // And we fallback to the module providers autolinked
117
105
  if (auto module = autolinking_ModuleProvider(name, params)) {
118
106
  return module;
119
107
  }
120
- #endif
121
108
 
122
109
  return nullptr;
123
110
  }
@@ -1,4 +1,4 @@
1
- VERSION_NAME=0.77.0-0rc1
1
+ VERSION_NAME=0.77.1-0
2
2
  react.internal.publishingGroup=io.github.react-native-tvos
3
3
 
4
4
  android.useAndroidX=true
@@ -31,4 +31,4 @@ binaryCompatibilityValidator.ignoredPackages=com.facebook.debug,\
31
31
  binaryCompatibilityValidator.nonPublicMarkers=com.facebook.react.common.annotations.VisibleForTesting,\
32
32
  com.facebook.react.common.annotations.UnstableReactNativeAPI
33
33
  binaryCompatibilityValidator.validationDisabled=true
34
- binaryCompatibilityValidator.outputApiFileName=ReactAndroid
34
+ binaryCompatibilityValidator.outputApiFileName=ReactAndroid
@@ -179,31 +179,30 @@ public abstract class HeadlessJsTaskService extends Service implements HeadlessJ
179
179
  }
180
180
 
181
181
  private void createReactContextAndScheduleTask(final HeadlessJsTaskConfig taskConfig) {
182
- final ReactHost reactHost = getReactHost();
183
-
184
- if (reactHost == null) { // old arch
185
- final ReactInstanceManager reactInstanceManager =
186
- getReactNativeHost().getReactInstanceManager();
187
-
188
- reactInstanceManager.addReactInstanceEventListener(
182
+ if (ReactNativeFeatureFlags.enableBridgelessArchitecture()) {
183
+ final ReactHost reactHost = getReactHost();
184
+ reactHost.addReactInstanceEventListener(
189
185
  new ReactInstanceEventListener() {
190
186
  @Override
191
187
  public void onReactContextInitialized(@NonNull ReactContext reactContext) {
192
188
  invokeStartTask(reactContext, taskConfig);
193
- reactInstanceManager.removeReactInstanceEventListener(this);
189
+ reactHost.removeReactInstanceEventListener(this);
194
190
  }
195
191
  });
196
- reactInstanceManager.createReactContextInBackground();
197
- } else { // new arch
198
- reactHost.addReactInstanceEventListener(
192
+ reactHost.start();
193
+ } else {
194
+ final ReactInstanceManager reactInstanceManager =
195
+ getReactNativeHost().getReactInstanceManager();
196
+
197
+ reactInstanceManager.addReactInstanceEventListener(
199
198
  new ReactInstanceEventListener() {
200
199
  @Override
201
200
  public void onReactContextInitialized(@NonNull ReactContext reactContext) {
202
201
  invokeStartTask(reactContext, taskConfig);
203
- reactHost.removeReactInstanceEventListener(this);
202
+ reactInstanceManager.removeReactInstanceEventListener(this);
204
203
  }
205
204
  });
206
- reactHost.start();
205
+ reactInstanceManager.createReactContextInBackground();
207
206
  }
208
207
  }
209
208
  }