react-native-tvos 0.76.2-0 → 0.76.5-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/Libraries/Core/ReactNativeVersion.js +1 -1
  2. package/Libraries/Text/TextInput/RCTBaseTextInputView.mm +1 -0
  3. package/React/Base/RCTVersion.m +1 -1
  4. package/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.h +1 -1
  5. package/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.mm +8 -18
  6. package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.h +3 -0
  7. package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +17 -18
  8. package/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +8 -2
  9. package/React/Views/RCTFont.h +2 -0
  10. package/React/Views/RCTFont.mm +4 -5
  11. package/React/Views/RCTTVView.h +3 -8
  12. package/React/Views/RCTView.h +19 -0
  13. package/React/Views/ScrollView/RCTScrollView.h +3 -1
  14. package/React/Views/ScrollView/RCTScrollView.m +6 -0
  15. package/ReactAndroid/api/ReactAndroid.api +2 -0
  16. package/ReactAndroid/cmake-utils/ReactNative-application.cmake +2 -3
  17. package/ReactAndroid/gradle.properties +1 -1
  18. package/ReactAndroid/hermes-engine/build.gradle.kts +4 -0
  19. package/ReactAndroid/src/main/java/com/facebook/react/HeadlessJsTaskService.java +12 -13
  20. package/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +8 -2
  21. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java +1 -1
  22. package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +11 -3
  23. package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +11 -3
  24. package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.kt +25 -0
  25. package/ReactCommon/cxxreact/ReactNativeVersion.h +1 -1
  26. package/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTFontUtils.mm +10 -10
  27. package/ReactCommon/react/renderer/uimanager/UIManager.cpp +29 -8
  28. package/ReactCommon/react/runtime/ReactInstance.cpp +39 -35
  29. package/ReactCommon/react/runtime/ReactInstance.h +2 -1
  30. package/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm +3 -2
  31. package/jest/setup.js +5 -1
  32. package/package.json +8 -8
  33. package/scripts/codegen/generate-artifacts-executor.js +68 -7
  34. package/sdks/hermesc/osx-bin/hermes +0 -0
  35. package/sdks/hermesc/osx-bin/hermesc +0 -0
  36. package/sdks/hermesc/win64-bin/hermesc.exe +0 -0
  37. package/sdks/hermesc/win64-bin/msvcp140.dll +0 -0
  38. package/sdks/hermesc/win64-bin/vcruntime140.dll +0 -0
  39. package/sdks/hermesc/win64-bin/vcruntime140_1.dll +0 -0
@@ -16,7 +16,7 @@ const version: $ReadOnly<{
16
16
  }> = {
17
17
  major: 0,
18
18
  minor: 76,
19
- patch: 2,
19
+ patch: 5,
20
20
  prerelease: '0',
21
21
  };
22
22
 
@@ -36,6 +36,7 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
36
36
  {
37
37
  if (![self isDescendantOfView:scrollView]) {
38
38
  // View is outside scroll view
39
+ scrollView.firstResponderViewOutsideScrollView = self.backedTextInputView;
39
40
  return;
40
41
  }
41
42
 
@@ -23,7 +23,7 @@ NSDictionary* RCTGetReactNativeVersion(void)
23
23
  __rnVersion = @{
24
24
  RCTVersionMajor: @(0),
25
25
  RCTVersionMinor: @(76),
26
- RCTVersionPatch: @(2),
26
+ RCTVersionPatch: @(5),
27
27
  RCTVersionPrerelease: @"0",
28
28
  };
29
29
  });
@@ -10,7 +10,7 @@
10
10
  /**
11
11
  * UIView class for root <ModalHostView> component.
12
12
  */
13
- @interface RCTModalHostViewComponentView : RCTViewComponentView
13
+ @interface RCTModalHostViewComponentView : RCTViewComponentView <UIAdaptivePresentationControllerDelegate>
14
14
 
15
15
  /**
16
16
  * Subclasses may override this method and present the modal on different view controller.
@@ -189,9 +189,7 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
189
189
  completion:(void (^)(void))completion
190
190
  {
191
191
  UIViewController *controller = [self reactViewController];
192
- [[self _topMostViewControllerFrom:controller] presentViewController:modalViewController
193
- animated:animated
194
- completion:completion];
192
+ [controller presentViewController:modalViewController animated:animated completion:completion];
195
193
  }
196
194
 
197
195
  - (void)dismissViewController:(UIViewController *)modalViewController
@@ -206,6 +204,8 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
206
204
  {
207
205
  BOOL shouldBePresented = !_isPresented && _shouldPresent && self.window;
208
206
  if (shouldBePresented) {
207
+ self.viewController.presentationController.delegate = self;
208
+
209
209
  _isPresented = YES;
210
210
  [self presentViewController:self.viewController
211
211
  animated:_shouldAnimatePresentation
@@ -331,24 +331,14 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
331
331
  [childComponentView removeFromSuperview];
332
332
  }
333
333
 
334
- #pragma mark - Private
334
+ #pragma mark - UIAdaptivePresentationControllerDelegate
335
335
 
336
- - (UIViewController *)_topMostViewControllerFrom:(UIViewController *)rootViewController
336
+ - (void)presentationControllerDidAttemptToDismiss:(UIPresentationController *)controller
337
337
  {
338
- UIViewController *topController = rootViewController;
339
- while (topController.presentedViewController) {
340
- topController = topController.presentedViewController;
341
- }
342
- if ([topController isKindOfClass:[UINavigationController class]]) {
343
- UINavigationController *navigationController = (UINavigationController *)topController;
344
- topController = navigationController.visibleViewController;
345
- return [self _topMostViewControllerFrom:topController];
346
- } else if ([topController isKindOfClass:[UITabBarController class]]) {
347
- UITabBarController *tabBarController = (UITabBarController *)topController;
348
- topController = tabBarController.selectedViewController;
349
- return [self _topMostViewControllerFrom:topController];
338
+ auto eventEmitter = [self modalEventEmitter];
339
+ if (eventEmitter) {
340
+ eventEmitter->onRequestClose({});
350
341
  }
351
- return topController;
352
342
  }
353
343
 
354
344
  @end
@@ -38,6 +38,9 @@ NS_ASSUME_NONNULL_BEGIN
38
38
  /** Focus area of newly-activated text input relative to the window to compare against UIKeyboardFrameBegin/End */
39
39
  @property (nonatomic, assign) CGRect firstResponderFocus;
40
40
 
41
+ /** newly-activated text input outside of the scroll view */
42
+ @property (nonatomic, weak) UIView *firstResponderViewOutsideScrollView;
43
+
41
44
  /*
42
45
  * Returns the subview of the scroll view that the component uses to mount all subcomponents into. That's useful to
43
46
  * separate component views from auxiliary views to be able to reliably implement pull-to-refresh- and RTL-related
@@ -194,16 +194,18 @@ RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrollView, NSInt
194
194
  UIViewAnimationCurve curve =
195
195
  (UIViewAnimationCurve)[notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] unsignedIntegerValue];
196
196
  CGRect keyboardEndFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
197
+ CGRect keyboardBeginFrame = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
197
198
 
198
199
  CGPoint absoluteViewOrigin = [self convertPoint:self.bounds.origin toView:nil];
199
200
  CGFloat scrollViewLowerY = isInverted ? absoluteViewOrigin.y : absoluteViewOrigin.y + self.bounds.size.height;
200
201
 
201
202
  UIEdgeInsets newEdgeInsets = _scrollView.contentInset;
202
203
  CGFloat inset = MAX(scrollViewLowerY - keyboardEndFrame.origin.y, 0);
204
+ const auto &props = static_cast<const ScrollViewProps &>(*_props);
203
205
  if (isInverted) {
204
- newEdgeInsets.top = MAX(inset, _scrollView.contentInset.top);
206
+ newEdgeInsets.top = MAX(inset, props.contentInset.top);
205
207
  } else {
206
- newEdgeInsets.bottom = MAX(inset, _scrollView.contentInset.bottom);
208
+ newEdgeInsets.bottom = MAX(inset, props.contentInset.bottom);
207
209
  }
208
210
 
209
211
  CGPoint newContentOffset = _scrollView.contentOffset;
@@ -215,21 +217,18 @@ RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrollView, NSInt
215
217
  from:self
216
218
  forEvent:nil]) {
217
219
  if (CGRectEqualToRect(_firstResponderFocus, CGRectNull)) {
218
- // Text input view is outside of the scroll view.
219
- return;
220
- }
221
-
222
- CGRect viewIntersection = CGRectIntersection(self.firstResponderFocus, keyboardEndFrame);
223
-
224
- if (CGRectIsNull(viewIntersection)) {
225
- return;
226
- }
227
-
228
- // Inner text field focused
229
- CGFloat focusEnd = CGRectGetMaxY(self.firstResponderFocus);
230
- if (focusEnd > keyboardEndFrame.origin.y) {
231
- // Text field active region is below visible area with keyboard - update diff to bring into view
232
- contentDiff = keyboardEndFrame.origin.y - focusEnd;
220
+ UIView *inputAccessoryView = _firstResponderViewOutsideScrollView.inputAccessoryView;
221
+ if (inputAccessoryView) {
222
+ // Text input view is within the inputAccessoryView.
223
+ contentDiff = keyboardEndFrame.origin.y - keyboardBeginFrame.origin.y;
224
+ }
225
+ } else {
226
+ // Inner text field focused
227
+ CGFloat focusEnd = CGRectGetMaxY(self.firstResponderFocus);
228
+ if (focusEnd > keyboardEndFrame.origin.y) {
229
+ // Text field active region is below visible area with keyboard - update diff to bring into view
230
+ contentDiff = keyboardEndFrame.origin.y - focusEnd;
231
+ }
233
232
  }
234
233
  }
235
234
 
@@ -255,7 +254,7 @@ RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrollView, NSInt
255
254
  animations:^{
256
255
  self->_scrollView.contentInset = newEdgeInsets;
257
256
  self->_scrollView.verticalScrollIndicatorInsets = newEdgeInsets;
258
- [self scrollToOffset:newContentOffset animated:NO];
257
+ [self scrollTo:newContentOffset.x y:newContentOffset.y animated:NO];
259
258
  }
260
259
  completion:nil];
261
260
  }
@@ -126,6 +126,7 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
126
126
  {
127
127
  if (![self isDescendantOfView:scrollView.scrollView] || !_backedTextInputView.isFirstResponder) {
128
128
  // View is outside scroll view or it's not a first responder.
129
+ scrollView.firstResponderViewOutsideScrollView = _backedTextInputView;
129
130
  return;
130
131
  }
131
132
 
@@ -443,10 +444,15 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
443
444
 
444
445
  - (void)textInputDidChangeSelection
445
446
  {
446
- [self _updateTypingAttributes];
447
447
  if (_comingFromJS) {
448
448
  return;
449
449
  }
450
+
451
+ // T207198334: Setting a new AttributedString (_comingFromJS) will trigger a selection change before the backing
452
+ // string is updated, so indicies won't point to what we want yet. Only respond to user selection change, and let
453
+ // `_setAttributedString` handle updating typing attributes if content changes.
454
+ [self _updateTypingAttributes];
455
+
450
456
  const auto &props = static_cast<const TextInputProps &>(*_props);
451
457
  if (props.traits.multiline && ![_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) {
452
458
  [self textInputDidChange];
@@ -716,7 +722,7 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
716
722
  // https://github.com/facebook/react-native/blob/3102a58df38d96f3dacef0530e4dbb399037fcd2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/span/SetSpanOperation.kt#L30
717
723
  - (void)_updateTypingAttributes
718
724
  {
719
- if (_backedTextInputView.attributedText.length > 0) {
725
+ if (_backedTextInputView.attributedText.length > 0 && _backedTextInputView.selectedTextRange != nil) {
720
726
  NSUInteger offsetStart = [_backedTextInputView offsetFromPosition:_backedTextInputView.beginningOfDocument
721
727
  toPosition:_backedTextInputView.selectedTextRange.start];
722
728
 
@@ -10,6 +10,7 @@
10
10
  #import <React/RCTConvert.h>
11
11
 
12
12
  typedef UIFont * (^RCTFontHandler)(CGFloat fontSize, NSString *fontWeightDescription);
13
+ typedef CGFloat RCTFontWeight;
13
14
 
14
15
  /**
15
16
  * React Native will use the System font for rendering by default. If you want to
@@ -19,6 +20,7 @@ typedef UIFont * (^RCTFontHandler)(CGFloat fontSize, NSString *fontWeightDescrip
19
20
  */
20
21
  RCT_EXTERN void RCTSetDefaultFontHandler(RCTFontHandler handler);
21
22
  RCT_EXTERN BOOL RCTHasFontHandlerSet(void);
23
+ RCT_EXTERN RCTFontWeight RCTGetFontWeight(UIFont *font);
22
24
 
23
25
  @interface RCTFont : NSObject
24
26
 
@@ -11,8 +11,7 @@
11
11
 
12
12
  #import <CoreText/CoreText.h>
13
13
 
14
- typedef CGFloat RCTFontWeight;
15
- static RCTFontWeight weightOfFont(UIFont *font)
14
+ RCTFontWeight RCTGetFontWeight(UIFont *font)
16
15
  {
17
16
  static NSArray<NSString *> *weightSuffixes;
18
17
  static NSArray<NSNumber *> *fontWeights;
@@ -405,7 +404,7 @@ RCT_ARRAY_CONVERTER(RCTFontVariantDescriptor)
405
404
  if (font) {
406
405
  familyName = font.familyName ?: defaultFontFamily;
407
406
  fontSize = font.pointSize ?: defaultFontSize;
408
- fontWeight = weightOfFont(font);
407
+ fontWeight = RCTGetFontWeight(font);
409
408
  isItalic = isItalicFont(font);
410
409
  isCondensed = isCondensedFont(font);
411
410
  }
@@ -453,7 +452,7 @@ RCT_ARRAY_CONVERTER(RCTFontVariantDescriptor)
453
452
  // It's actually a font name, not a font family name,
454
453
  // but we'll do what was meant, not what was said.
455
454
  familyName = font.familyName;
456
- fontWeight = weight ? fontWeight : weightOfFont(font);
455
+ fontWeight = weight ? fontWeight : RCTGetFontWeight(font);
457
456
  isItalic = style ? isItalic : isItalicFont(font);
458
457
  isCondensed = isCondensedFont(font);
459
458
  } else {
@@ -476,7 +475,7 @@ RCT_ARRAY_CONVERTER(RCTFontVariantDescriptor)
476
475
  for (NSString *name in names) {
477
476
  UIFont *match = [UIFont fontWithName:name size:fontSize];
478
477
  if (isItalic == isItalicFont(match) && isCondensed == isCondensedFont(match)) {
479
- CGFloat testWeight = weightOfFont(match);
478
+ CGFloat testWeight = RCTGetFontWeight(match);
480
479
  if (ABS(testWeight - fontWeight) < ABS(closestWeight - fontWeight)) {
481
480
  font = match;
482
481
  closestWeight = testWeight;
@@ -57,18 +57,13 @@
57
57
  @property (nonatomic, assign) BOOL trapFocusLeft;
58
58
  @property (nonatomic, assign) BOOL trapFocusRight;
59
59
 
60
-
61
- /**
62
- * Focus
63
- */
60
+ // These handlers are defined in RCTView
61
+ /*
64
62
  @property (nonatomic, copy) RCTBubblingEventBlock onFocus;
65
63
  @property (nonatomic, copy) RCTBubblingEventBlock onBlur;
66
-
67
- /**
68
- * TV Press Handlers
69
- */
70
64
  @property (nonatomic, copy) RCTDirectEventBlock onPressIn;
71
65
  @property (nonatomic, copy) RCTDirectEventBlock onPressOut;
66
+ */
72
67
 
73
68
  - (instancetype)initWithBridge:(RCTBridge *)bridge;
74
69
 
@@ -123,6 +123,25 @@ extern const UIAccessibilityTraits SwitchAccessibilityTrait;
123
123
 
124
124
  @property (nonatomic, assign) RCTCursor cursor;
125
125
 
126
+ #if TARGET_OS_TV
127
+ // For Paper, all views on TV might have focus, blur, pressIn, pressOut handlers,
128
+ // so we need to add these properties here and not in RCTTVView,
129
+ // since some views do not inherit from RCTTVView.
130
+
131
+ /**
132
+ * Focus
133
+ */
134
+ @property (nonatomic, copy) RCTBubblingEventBlock onFocus;
135
+ @property (nonatomic, copy) RCTBubblingEventBlock onBlur;
136
+
137
+ /**
138
+ * TV Press Handlers
139
+ */
140
+ @property (nonatomic, copy) RCTDirectEventBlock onPressIn;
141
+ @property (nonatomic, copy) RCTDirectEventBlock onPressOut;
142
+
143
+ #endif // TARGET_OS_TV
144
+
126
145
  /**
127
146
  * (Experimental and unused for Paper) Pointer event handlers.
128
147
  */
@@ -56,9 +56,11 @@
56
56
  @property (nonatomic, assign) BOOL snapToEnd;
57
57
  @property (nonatomic, copy) NSString *snapToAlignment;
58
58
  @property (nonatomic, assign) BOOL inverted;
59
+ @property (nonatomic, assign) BOOL showsScrollIndex;
59
60
  /** Focus area of newly-activated text input relative to the window to compare against UIKeyboardFrameBegin/End */
60
61
  @property (nonatomic, assign) CGRect firstResponderFocus;
61
- @property (nonatomic, assign) BOOL showsScrollIndex;
62
+ /** newly-activated text input outside of the scroll view */
63
+ @property (nonatomic, weak) UIView *firstResponderViewOutsideScrollView;
62
64
 
63
65
  // NOTE: currently these event props are only declared so we can export the
64
66
  // event names to JS - we don't call the blocks directly because scroll events
@@ -363,6 +363,12 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
363
363
  if (!didFocusExternalTextField && focusEnd > endFrame.origin.y) {
364
364
  // Text field active region is below visible area with keyboard - update diff to bring into view
365
365
  contentDiff = endFrame.origin.y - focusEnd;
366
+ } else {
367
+ UIView *inputAccessoryView = _firstResponderViewOutsideScrollView.inputAccessoryView;
368
+ if (inputAccessoryView) {
369
+ // Text input view is within the inputAccessoryView.
370
+ contentDiff = endFrame.origin.y - beginFrame.origin.y;
371
+ }
366
372
  }
367
373
  } else if (endFrame.origin.y <= beginFrame.origin.y) {
368
374
  // Keyboard opened for other reason
@@ -7280,6 +7280,8 @@ public final class com/facebook/react/views/scroll/ReactScrollViewHelper {
7280
7280
  public static final field SNAP_ALIGNMENT_END I
7281
7281
  public static final field SNAP_ALIGNMENT_START I
7282
7282
  public static final fun addScrollListener (Lcom/facebook/react/views/scroll/ReactScrollViewHelper$ScrollListener;)V
7283
+ public static final fun dispatchMomentumEndOnAnimationEnd (Landroid/view/ViewGroup;)V
7284
+ public static final fun emitLayoutChangeEvent (Landroid/view/ViewGroup;)V
7283
7285
  public static final fun emitLayoutEvent (Landroid/view/ViewGroup;)V
7284
7286
  public static final fun emitScrollBeginDragEvent (Landroid/view/ViewGroup;)V
7285
7287
  public static final fun emitScrollEndDragEvent (Landroid/view/ViewGroup;FF)V
@@ -31,9 +31,8 @@ if(CCACHE_FOUND)
31
31
  endif(CCACHE_FOUND)
32
32
 
33
33
  set(BUILD_DIR ${PROJECT_BUILD_DIR})
34
- if(CMAKE_HOST_WIN32)
35
- string(REPLACE "\\" "/" BUILD_DIR ${BUILD_DIR})
36
- endif()
34
+ file(TO_CMAKE_PATH "${BUILD_DIR}" BUILD_DIR)
35
+ file(TO_CMAKE_PATH "${REACT_ANDROID_DIR}" REACT_ANDROID_DIR)
37
36
 
38
37
  file(GLOB input_SRC CONFIGURE_DEPENDS
39
38
  ${REACT_ANDROID_DIR}/cmake-utils/default-app-setup/*.cpp
@@ -1,4 +1,4 @@
1
- VERSION_NAME=0.76.2-0
1
+ VERSION_NAME=0.76.5-0
2
2
  react.internal.publishingGroup=io.github.react-native-tvos
3
3
 
4
4
  android.useAndroidX=true
@@ -38,9 +38,13 @@ fun getSDKPath(): String {
38
38
  fun getSDKManagerPath(): String {
39
39
  val metaSdkManagerPath = File("${getSDKPath()}/cmdline-tools/latest/bin/sdkmanager")
40
40
  val ossSdkManagerPath = File("${getSDKPath()}/tools/bin/sdkmanager")
41
+ val windowsMetaSdkManagerPath = File("${getSDKPath()}/cmdline-tools/latest/bin/sdkmanager.bat")
42
+ val windowsOssSdkManagerPath = File("${getSDKPath()}/tools/bin/sdkmanager.bat")
41
43
  return when {
42
44
  metaSdkManagerPath.exists() -> metaSdkManagerPath.absolutePath
45
+ windowsMetaSdkManagerPath.exists() -> windowsMetaSdkManagerPath.absolutePath
43
46
  ossSdkManagerPath.exists() -> ossSdkManagerPath.absolutePath
47
+ windowsOssSdkManagerPath.exists() -> windowsOssSdkManagerPath.absolutePath
44
48
  else -> throw GradleException("Could not find sdkmanager executable.")
45
49
  }
46
50
  }
@@ -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 (ReactFeatureFlags.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
  }
@@ -449,12 +449,18 @@ public class FabricUIManager
449
449
 
450
450
  @Override
451
451
  public void markActiveTouchForTag(int surfaceId, int reactTag) {
452
- mMountingManager.getSurfaceManager(surfaceId).markActiveTouchForTag(reactTag);
452
+ SurfaceMountingManager surfaceMountingManager = mMountingManager.getSurfaceManager(surfaceId);
453
+ if (surfaceMountingManager != null) {
454
+ surfaceMountingManager.markActiveTouchForTag(reactTag);
455
+ }
453
456
  }
454
457
 
455
458
  @Override
456
459
  public void sweepActiveTouchForTag(int surfaceId, int reactTag) {
457
- mMountingManager.getSurfaceManager(surfaceId).sweepActiveTouchForTag(reactTag);
460
+ SurfaceMountingManager surfaceMountingManager = mMountingManager.getSurfaceManager(surfaceId);
461
+ if (surfaceMountingManager != null) {
462
+ surfaceMountingManager.sweepActiveTouchForTag(reactTag);
463
+ }
458
464
  }
459
465
 
460
466
  /**
@@ -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", 76,
20
- "patch", 2,
20
+ "patch", 5,
21
21
  "prerelease", "0");
22
22
  }
@@ -1552,12 +1552,20 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
1552
1552
  DEFAULT_FLING_ANIMATOR.cancel();
1553
1553
 
1554
1554
  // Update the fling animator with new values
1555
- DEFAULT_FLING_ANIMATOR
1556
- .setDuration(ReactScrollViewHelper.getDefaultScrollAnimationDuration(getContext()))
1557
- .setIntValues(start, end);
1555
+ int duration = ReactScrollViewHelper.getDefaultScrollAnimationDuration(getContext());
1556
+ DEFAULT_FLING_ANIMATOR.setDuration(duration).setIntValues(start, end);
1558
1557
 
1559
1558
  // Start the animator
1560
1559
  DEFAULT_FLING_ANIMATOR.start();
1560
+
1561
+ if (mSendMomentumEvents) {
1562
+ int xVelocity = 0;
1563
+ if (duration > 0) {
1564
+ xVelocity = (end - start) / duration;
1565
+ }
1566
+ ReactScrollViewHelper.emitScrollMomentumBeginEvent(this, xVelocity, 0);
1567
+ ReactScrollViewHelper.dispatchMomentumEndOnAnimationEnd(this);
1568
+ }
1561
1569
  }
1562
1570
 
1563
1571
  @Override
@@ -1366,12 +1366,20 @@ public class ReactScrollView extends ScrollView
1366
1366
  DEFAULT_FLING_ANIMATOR.cancel();
1367
1367
 
1368
1368
  // Update the fling animator with new values
1369
- DEFAULT_FLING_ANIMATOR
1370
- .setDuration(ReactScrollViewHelper.getDefaultScrollAnimationDuration(getContext()))
1371
- .setIntValues(start, end);
1369
+ int duration = ReactScrollViewHelper.getDefaultScrollAnimationDuration(getContext());
1370
+ DEFAULT_FLING_ANIMATOR.setDuration(duration).setIntValues(start, end);
1372
1371
 
1373
1372
  // Start the animator
1374
1373
  DEFAULT_FLING_ANIMATOR.start();
1374
+
1375
+ if (mSendMomentumEvents) {
1376
+ int yVelocity = 0;
1377
+ if (duration > 0) {
1378
+ yVelocity = (end - start) / duration;
1379
+ }
1380
+ ReactScrollViewHelper.emitScrollMomentumBeginEvent(this, 0, yVelocity);
1381
+ ReactScrollViewHelper.dispatchMomentumEndOnAnimationEnd(this);
1382
+ }
1375
1383
  }
1376
1384
 
1377
1385
  @NonNull
@@ -412,6 +412,31 @@ public object ReactScrollViewHelper {
412
412
  })
413
413
  }
414
414
 
415
+ @JvmStatic
416
+ public fun <T> dispatchMomentumEndOnAnimationEnd(scrollView: T) where
417
+ T : HasFlingAnimator?,
418
+ T : HasScrollEventThrottle?,
419
+ T : ViewGroup {
420
+ scrollView
421
+ .getFlingAnimator()
422
+ .addListener(
423
+ object : Animator.AnimatorListener {
424
+ override fun onAnimationStart(animator: Animator) = Unit
425
+
426
+ override fun onAnimationEnd(animator: Animator) {
427
+ emitScrollMomentumEndEvent(scrollView)
428
+ animator.removeListener(this)
429
+ }
430
+
431
+ override fun onAnimationCancel(animator: Animator) {
432
+ emitScrollMomentumEndEvent(scrollView)
433
+ animator.removeListener(this)
434
+ }
435
+
436
+ override fun onAnimationRepeat(animator: Animator) = Unit
437
+ })
438
+ }
439
+
415
440
  @JvmStatic
416
441
  public fun <T> predictFinalScrollPosition(
417
442
  scrollView: T,
@@ -17,7 +17,7 @@ namespace facebook::react {
17
17
  constexpr struct {
18
18
  int32_t Major = 0;
19
19
  int32_t Minor = 76;
20
- int32_t Patch = 2;
20
+ int32_t Patch = 5;
21
21
  std::string_view Prerelease = "0";
22
22
  } ReactNativeVersion;
23
23
 
@@ -7,6 +7,7 @@
7
7
 
8
8
  #import "RCTFontUtils.h"
9
9
  #import <CoreText/CoreText.h>
10
+ #import <React/RCTFont.h>
10
11
 
11
12
  #import <algorithm>
12
13
  #import <cmath>
@@ -45,12 +46,6 @@ static RCTFontProperties RCTResolveFontProperties(
45
46
  return fontProperties;
46
47
  }
47
48
 
48
- static UIFontWeight RCTGetFontWeight(UIFont *font)
49
- {
50
- NSDictionary *traits = [font.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute];
51
- return [traits[UIFontWeightTrait] doubleValue];
52
- }
53
-
54
49
  static RCTFontStyle RCTGetFontStyle(UIFont *font)
55
50
  {
56
51
  NSDictionary *traits = [font.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute];
@@ -165,17 +160,22 @@ UIFont *RCTFontWithFontProperties(RCTFontProperties fontProperties)
165
160
  font = RCTDefaultFontWithFontProperties(fontProperties);
166
161
  } else {
167
162
  NSArray<NSString *> *fontNames = [UIFont fontNamesForFamilyName:fontProperties.family];
163
+ UIFontWeight fontWeight = fontProperties.weight;
168
164
 
169
165
  if (fontNames.count == 0) {
170
166
  // Gracefully handle being given a font name rather than font family, for
171
167
  // example: "Helvetica Light Oblique" rather than just "Helvetica".
172
168
  font = [UIFont fontWithName:fontProperties.family size:effectiveFontSize];
173
-
174
- if (!font) {
169
+ if (font) {
170
+ fontNames = [UIFont fontNamesForFamilyName:font.familyName];
171
+ fontWeight = fontWeight ?: RCTGetFontWeight(font);
172
+ } else {
175
173
  // Failback to system font.
176
174
  font = [UIFont systemFontOfSize:effectiveFontSize weight:fontProperties.weight];
177
175
  }
178
- } else {
176
+ }
177
+
178
+ if (fontNames.count > 0) {
179
179
  // Get the closest font that matches the given weight for the fontFamily
180
180
  CGFloat closestWeight = INFINITY;
181
181
  for (NSString *name in fontNames) {
@@ -186,7 +186,7 @@ UIFont *RCTFontWithFontProperties(RCTFontProperties fontProperties)
186
186
  }
187
187
 
188
188
  CGFloat testWeight = RCTGetFontWeight(fontMatch);
189
- if (ABS(testWeight - fontProperties.weight) < ABS(closestWeight - fontProperties.weight)) {
189
+ if (ABS(testWeight - fontWeight) < ABS(closestWeight - fontWeight)) {
190
190
  font = fontMatch;
191
191
  closestWeight = testWeight;
192
192
  }
@@ -126,20 +126,41 @@ std::shared_ptr<ShadowNode> UIManager::cloneNode(
126
126
 
127
127
  if (!rawProps.isEmpty()) {
128
128
  if (family.nativeProps_DEPRECATED != nullptr) {
129
+ // 1. update the nativeProps_DEPRECATED props.
130
+ //
131
+ // In this step, we want the most recent value for the props
132
+ // managed by setNativeProps.
129
133
  // Values in `rawProps` patch (take precedence over)
130
- // `nativeProps_DEPRECATED`. For example, if both `nativeProps_DEPRECATED`
131
- // and `rawProps` contain key 'A'. Value from `rawProps` overrides what
132
- // was previously in `nativeProps_DEPRECATED`.
134
+ // `nativeProps_DEPRECATED`. For example, if both
135
+ // `nativeProps_DEPRECATED` and `rawProps` contain key 'A'.
136
+ // Value from `rawProps` overrides what was previously in
137
+ // `nativeProps_DEPRECATED`. Notice that the `nativeProps_DEPRECATED`
138
+ // patch will not get more props from `rawProps`: if the key is not
139
+ // present in `nativeProps_DEPRECATED`, it will not be added.
140
+ //
141
+ // The result of this operation is the new `nativeProps_DEPRECATED`.
133
142
  family.nativeProps_DEPRECATED =
134
143
  std::make_unique<folly::dynamic>(mergeDynamicProps(
135
- *family.nativeProps_DEPRECATED,
136
- (folly::dynamic)rawProps,
144
+ *family.nativeProps_DEPRECATED, // source
145
+ (folly::dynamic)rawProps, // patch
137
146
  NullValueStrategy::Ignore));
138
147
 
148
+ // 2. Compute the final set of props.
149
+ //
150
+ // This step takes the new props handled by `setNativeProps` and
151
+ // merges them in the `rawProps` managed by React.
152
+ // The new props handled by `nativeProps` now takes precedence
153
+ // on the props handled by React, as we want to make sure that
154
+ // all the props are applied to the component.
155
+ // We use these finalProps as source of truth for the component.
156
+ auto finalProps = mergeDynamicProps(
157
+ (folly::dynamic)rawProps, // source
158
+ *family.nativeProps_DEPRECATED, // patch
159
+ NullValueStrategy::Override);
160
+
161
+ // 3. Clone the props by using finalProps.
139
162
  props = componentDescriptor.cloneProps(
140
- propsParserContext,
141
- shadowNode.getProps(),
142
- RawProps(*family.nativeProps_DEPRECATED));
163
+ propsParserContext, shadowNode.getProps(), RawProps(finalProps));
143
164
  } else {
144
165
  props = componentDescriptor.cloneProps(
145
166
  propsParserContext, shadowNode.getProps(), std::move(rawProps));
@@ -204,47 +204,51 @@ std::string simpleBasename(const std::string& path) {
204
204
  */
205
205
  void ReactInstance::loadScript(
206
206
  std::unique_ptr<const JSBigString> script,
207
- const std::string& sourceURL) {
207
+ const std::string& sourceURL,
208
+ std::function<void(jsi::Runtime& runtime)>&& completion) {
208
209
  auto buffer = std::make_shared<BigStringBuffer>(std::move(script));
209
210
  std::string scriptName = simpleBasename(sourceURL);
210
211
 
211
- runtimeScheduler_->scheduleWork(
212
- [this,
213
- scriptName,
214
- sourceURL,
215
- buffer = std::move(buffer),
216
- weakBufferedRuntimeExecuter = std::weak_ptr<BufferedRuntimeExecutor>(
217
- bufferedRuntimeExecutor_)](jsi::Runtime& runtime) {
218
- SystraceSection s("ReactInstance::loadScript");
219
- bool hasLogger(ReactMarker::logTaggedMarkerBridgelessImpl);
220
- if (hasLogger) {
221
- ReactMarker::logTaggedMarkerBridgeless(
222
- ReactMarker::RUN_JS_BUNDLE_START, scriptName.c_str());
223
- }
212
+ runtimeScheduler_->scheduleWork([this,
213
+ scriptName,
214
+ sourceURL,
215
+ buffer = std::move(buffer),
216
+ weakBufferedRuntimeExecuter =
217
+ std::weak_ptr<BufferedRuntimeExecutor>(
218
+ bufferedRuntimeExecutor_),
219
+ completion](jsi::Runtime& runtime) {
220
+ SystraceSection s("ReactInstance::loadScript");
221
+ bool hasLogger(ReactMarker::logTaggedMarkerBridgelessImpl);
222
+ if (hasLogger) {
223
+ ReactMarker::logTaggedMarkerBridgeless(
224
+ ReactMarker::RUN_JS_BUNDLE_START, scriptName.c_str());
225
+ }
224
226
 
225
- runtime.evaluateJavaScript(buffer, sourceURL);
227
+ runtime.evaluateJavaScript(buffer, sourceURL);
226
228
 
227
- /**
228
- * TODO(T183610671): We need a safe/reliable way to enable the js
229
- * pipeline from javascript. Remove this after we figure that out, or
230
- * after we just remove the js pipeline.
231
- */
232
- if (!jsErrorHandler_->hasHandledFatalError()) {
233
- jsErrorHandler_->setRuntimeReady();
234
- }
229
+ /**
230
+ * TODO(T183610671): We need a safe/reliable way to enable the js
231
+ * pipeline from javascript. Remove this after we figure that out, or
232
+ * after we just remove the js pipeline.
233
+ */
234
+ if (!jsErrorHandler_->hasHandledFatalError()) {
235
+ jsErrorHandler_->setRuntimeReady();
236
+ }
235
237
 
236
- if (hasLogger) {
237
- ReactMarker::logTaggedMarkerBridgeless(
238
- ReactMarker::RUN_JS_BUNDLE_STOP, scriptName.c_str());
239
- ReactMarker::logMarkerBridgeless(
240
- ReactMarker::INIT_REACT_RUNTIME_STOP);
241
- ReactMarker::logMarkerBridgeless(ReactMarker::APP_STARTUP_STOP);
242
- }
243
- if (auto strongBufferedRuntimeExecuter =
244
- weakBufferedRuntimeExecuter.lock()) {
245
- strongBufferedRuntimeExecuter->flush();
246
- }
247
- });
238
+ if (hasLogger) {
239
+ ReactMarker::logTaggedMarkerBridgeless(
240
+ ReactMarker::RUN_JS_BUNDLE_STOP, scriptName.c_str());
241
+ ReactMarker::logMarkerBridgeless(ReactMarker::INIT_REACT_RUNTIME_STOP);
242
+ ReactMarker::logMarkerBridgeless(ReactMarker::APP_STARTUP_STOP);
243
+ }
244
+ if (auto strongBufferedRuntimeExecuter =
245
+ weakBufferedRuntimeExecuter.lock()) {
246
+ strongBufferedRuntimeExecuter->flush();
247
+ }
248
+ if (completion) {
249
+ completion(runtime);
250
+ }
251
+ });
248
252
  }
249
253
 
250
254
  /*
@@ -48,7 +48,8 @@ class ReactInstance final : private jsinspector_modern::InstanceTargetDelegate {
48
48
 
49
49
  void loadScript(
50
50
  std::unique_ptr<const JSBigString> script,
51
- const std::string& sourceURL);
51
+ const std::string& sourceURL,
52
+ std::function<void(jsi::Runtime& runtime)>&& completion = nullptr);
52
53
 
53
54
  void registerSegment(uint32_t segmentId, const std::string& segmentPath);
54
55
 
@@ -470,8 +470,9 @@ void RCTInstanceSetRuntimeDiagnosticFlags(NSString *flags)
470
470
 
471
471
  auto script = std::make_unique<NSDataBigString>(source.data);
472
472
  const auto *url = deriveSourceURL(source.url).UTF8String;
473
- _reactInstance->loadScript(std::move(script), url);
474
- [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTInstanceDidLoadBundle" object:nil];
473
+ _reactInstance->loadScript(std::move(script), url, [](jsi::Runtime &_) {
474
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTInstanceDidLoadBundle" object:nil];
475
+ });
475
476
  }
476
477
 
477
478
  - (void)_handleJSError:(const JsErrorHandler::ParsedError &)error
package/jest/setup.js CHANGED
@@ -417,4 +417,8 @@ jest
417
417
  return jest.requireActual(
418
418
  '../Libraries/ReactNative/RendererImplementation',
419
419
  );
420
- });
420
+ })
421
+ .mock('../Libraries/Utilities/useColorScheme', () => ({
422
+ __esModule: true,
423
+ default: jest.fn().mockReturnValue('light'),
424
+ }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-tvos",
3
- "version": "0.76.2-0",
3
+ "version": "0.76.5-0",
4
4
  "description": "A framework for building native apps using React",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -110,13 +110,13 @@
110
110
  },
111
111
  "dependencies": {
112
112
  "@jest/create-cache-key-function": "^29.6.3",
113
- "@react-native/assets-registry": "0.76.2",
114
- "@react-native/codegen": "0.76.2",
115
- "@react-native/community-cli-plugin": "0.76.2",
116
- "@react-native/gradle-plugin": "0.76.2",
117
- "@react-native/js-polyfills": "0.76.2",
118
- "@react-native/normalize-colors": "0.76.2",
119
- "@react-native-tvos/virtualized-lists": "0.76.2-0",
113
+ "@react-native/assets-registry": "0.76.5",
114
+ "@react-native/codegen": "0.76.5",
115
+ "@react-native/community-cli-plugin": "0.76.5",
116
+ "@react-native/gradle-plugin": "0.76.5",
117
+ "@react-native/js-polyfills": "0.76.5",
118
+ "@react-native/normalize-colors": "0.76.5",
119
+ "@react-native-tvos/virtualized-lists": "0.76.5-0",
120
120
  "abort-controller": "^3.0.0",
121
121
  "anser": "^1.4.9",
122
122
  "ansi-regex": "^5.0.0",
@@ -152,6 +152,7 @@ function extractLibrariesFromJSON(configFile, dependencyPath) {
152
152
  const config = configFile.codegenConfig;
153
153
  return [
154
154
  {
155
+ libraryName: configFile.name,
155
156
  config,
156
157
  libraryPath: dependencyPath,
157
158
  },
@@ -252,19 +253,23 @@ function findExternalLibraries(pkgJson, projectRoot) {
252
253
  });
253
254
  }
254
255
 
255
- function findLibrariesFromReactNativeConfig(projectRoot) {
256
+ function readRNConfigJSFile(projectRoot) {
256
257
  const rnConfigFileName = 'react-native.config.js';
257
258
 
258
- console.log(
259
- `\n\n[Codegen] >>>>> Searching for codegen-enabled libraries in ${rnConfigFileName}`,
260
- );
261
-
262
259
  const rnConfigFilePath = path.resolve(projectRoot, rnConfigFileName);
263
260
 
264
261
  if (!fs.existsSync(rnConfigFilePath)) {
265
262
  return [];
266
263
  }
267
- const rnConfig = require(rnConfigFilePath);
264
+ return require(rnConfigFilePath);
265
+ }
266
+
267
+ function findLibrariesFromReactNativeConfig(projectRoot) {
268
+ console.log(
269
+ `\n\n[Codegen] >>>>> Searching for codegen-enabled libraries in react-native.config.js`,
270
+ );
271
+
272
+ const rnConfig = readRNConfigJSFile(projectRoot);
268
273
 
269
274
  if (rnConfig.dependencies == null) {
270
275
  return [];
@@ -290,6 +295,48 @@ function findLibrariesFromReactNativeConfig(projectRoot) {
290
295
  });
291
296
  }
292
297
 
298
+ // Function to look for libraries explicitly unlinked from the app
299
+ // through the react-native.config.js file.
300
+ // If this happens, it might be that the app does not need
301
+ // to generate code for that library as it won't be used by that platform
302
+ // @return { [libraryName: string]: [platform: string] }
303
+ function findNotLinkedLibraries(projectRoot) {
304
+ const rnConfig = readRNConfigJSFile(projectRoot);
305
+
306
+ if (rnConfig.dependencies == null) {
307
+ return {};
308
+ }
309
+
310
+ let notLinkedLibraries = {};
311
+
312
+ Object.keys(rnConfig.dependencies).forEach(name => {
313
+ const dependency = rnConfig.dependencies[name];
314
+ let notLinkedPlatforms = [];
315
+
316
+ // dependency.platforms might not be defined, as the format
317
+ // {
318
+ // "dependencies": {
319
+ // "dependency-name": {
320
+ // "root": "path/to/dependency",
321
+ // }
322
+ // }
323
+ // }
324
+ // is also supported.
325
+ // In this case, we assume that the library is linked to all platforms.
326
+ // We don't consider the case were `dependency-name.root` is equal to `null`, because that
327
+ // means that the library is not linked to the app at all, and in that case the dependency
328
+ // should be removed by the user.
329
+ dependency.platforms && Object.keys(dependency.platforms).forEach(platform => {
330
+ if (dependency.platforms[platform] == null) {
331
+ notLinkedPlatforms.push(platform);
332
+ }
333
+ });
334
+ notLinkedLibraries[name] = notLinkedPlatforms;
335
+
336
+ });
337
+ return notLinkedLibraries;
338
+ }
339
+
293
340
  function findProjectRootLibraries(pkgJson, projectRoot) {
294
341
  console.log('[Codegen] Searching for codegen-enabled libraries in the app.');
295
342
 
@@ -695,6 +742,8 @@ function execute(projectRoot, targetPlatform, baseOutputPath) {
695
742
  let platforms =
696
743
  targetPlatform === 'all' ? supportedPlatforms : [targetPlatform];
697
744
 
745
+ const notLinkedLibraries = findNotLinkedLibraries(projectRoot);
746
+
698
747
  for (const platform of platforms) {
699
748
  const outputPath = computeOutputPath(
700
749
  projectRoot,
@@ -703,7 +752,19 @@ function execute(projectRoot, targetPlatform, baseOutputPath) {
703
752
  platform,
704
753
  );
705
754
 
706
- const schemaInfos = generateSchemaInfos(libraries);
755
+ const schemaInfos = generateSchemaInfos(
756
+ libraries.filter(library => {
757
+ const unlinkedPlatforms = notLinkedLibraries[library.libraryName];
758
+ if (unlinkedPlatforms && unlinkedPlatforms.includes(platform)) {
759
+ console.log(
760
+ `[Codegen - ${library.libraryName}] Skipping Codegen on ${platform}`,
761
+ );
762
+ return false;
763
+ }
764
+ return true;
765
+ }),
766
+ );
767
+
707
768
  generateNativeCode(
708
769
  outputPath,
709
770
  schemaInfos.filter(schemaInfo =>
Binary file
Binary file
Binary file
Binary file