react-native-tvos 0.74.1-0 → 0.74.3-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 (46) hide show
  1. package/Libraries/AppDelegate/RCTAppDelegate.mm +4 -1
  2. package/Libraries/AppDelegate/RCTRootViewFactory.h +8 -0
  3. package/Libraries/AppDelegate/RCTRootViewFactory.mm +11 -3
  4. package/Libraries/Components/Pressable/Pressable.js +11 -7
  5. package/Libraries/Components/TextInput/TextInput.js +6 -3
  6. package/Libraries/Core/ReactNativeVersion.js +1 -1
  7. package/Libraries/Text/TextInput/Multiline/RCTUITextView.mm +6 -0
  8. package/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h +1 -0
  9. package/Libraries/Text/TextInput/Singleline/RCTUITextField.mm +5 -0
  10. package/React/Base/RCTVersion.m +1 -1
  11. package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +10 -5
  12. package/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +4 -0
  13. package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +3 -2
  14. package/React/Views/ScrollView/RCTScrollView.m +32 -16
  15. package/ReactAndroid/gradle.properties +1 -1
  16. package/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java +2 -4
  17. package/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java +5 -2
  18. package/ReactAndroid/src/main/java/com/facebook/react/modules/core/ReactAndroidHWInputDeviceHelper.java +20 -0
  19. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java +1 -1
  20. package/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java +20 -15
  21. package/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactSurfaceView.java +5 -0
  22. package/ReactCommon/cxxreact/ReactNativeVersion.h +1 -1
  23. package/ReactCommon/jsc/JSCRuntime.cpp +30 -2
  24. package/ReactCommon/jsi/jsi/decorator.h +7 -0
  25. package/ReactCommon/jsi/jsi/jsi.h +7 -0
  26. package/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp +53 -10
  27. package/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputEventEmitter.cpp +53 -1
  28. package/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp +0 -1
  29. package/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Legacy.cpp +3 -3
  30. package/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp +28 -28
  31. package/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.h +3 -3
  32. package/ReactCommon/react/renderer/runtimescheduler/Task.cpp +9 -7
  33. package/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp +46 -0
  34. package/cli.js +11 -3
  35. package/package.json +13 -13
  36. package/scripts/cocoapods/privacy_manifest_utils.rb +8 -7
  37. package/scripts/codegen/generate-artifacts-executor.js +1 -1
  38. package/scripts/ios-configure-glog.sh +9 -2
  39. package/scripts/react_native_pods.rb +3 -1
  40. package/sdks/.hermesversion +1 -1
  41. package/sdks/hermesc/osx-bin/hermes +0 -0
  42. package/sdks/hermesc/osx-bin/hermesc +0 -0
  43. package/sdks/hermesc/win64-bin/hermesc.exe +0 -0
  44. package/template/package.json +6 -6
  45. package/types/public/ReactNativeTVTypes.d.ts +2 -2
  46. package/ReactCommon/react/renderer/runtimescheduler/ErrorUtils.h +0 -34
@@ -65,7 +65,6 @@
65
65
  [RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self;
66
66
  }
67
67
  [self _logWarnIfCreateRootViewWithBridgeIsOverridden];
68
- [self customizeRootView:(RCTRootView *)rootView];
69
68
 
70
69
  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
71
70
  UIViewController *rootViewController = [self createRootViewController];
@@ -271,6 +270,10 @@
271
270
  return [weakSelf createBridgeWithDelegate:delegate launchOptions:launchOptions];
272
271
  };
273
272
 
273
+ configuration.customizeRootView = ^(UIView *_Nonnull rootView) {
274
+ [weakSelf customizeRootView:(RCTRootView *)rootView];
275
+ };
276
+
274
277
  return [[RCTRootViewFactory alloc] initWithConfiguration:configuration andTurboModuleManagerDelegate:self];
275
278
  }
276
279
 
@@ -23,6 +23,7 @@ typedef UIView *_Nonnull (
23
23
  ^RCTCreateRootViewWithBridgeBlock)(RCTBridge *bridge, NSString *moduleName, NSDictionary *initProps);
24
24
  typedef RCTBridge *_Nonnull (
25
25
  ^RCTCreateBridgeWithDelegateBlock)(id<RCTBridgeDelegate> delegate, NSDictionary *launchOptions);
26
+ typedef void (^RCTCustomizeRootViewBlock)(UIView *rootView);
26
27
  typedef NSURL *_Nullable (^RCTSourceURLForBridgeBlock)(RCTBridge *bridge);
27
28
  typedef NSURL *_Nullable (^RCTBundleURLBlock)(void);
28
29
  typedef NSArray<id<RCTBridgeModule>> *_Nonnull (^RCTExtraModulesForBridgeBlock)(RCTBridge *bridge);
@@ -91,6 +92,13 @@ typedef BOOL (^RCTBridgeDidNotFindModuleBlock)(RCTBridge *bridge, NSString *modu
91
92
  */
92
93
  @property (nonatomic, nullable) RCTCreateBridgeWithDelegateBlock createBridgeWithDelegate;
93
94
 
95
+ /**
96
+ * Block that allows to customize the rootView that is passed to React Native.
97
+ *
98
+ * @parameter: rootView - The root view to customize.
99
+ */
100
+ @property (nonatomic, nullable) RCTCustomizeRootViewBlock customizeRootView;
101
+
94
102
  @end
95
103
 
96
104
  #pragma mark - RCTRootViewFactory
@@ -145,17 +145,25 @@ static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabri
145
145
  initWithSurface:surface
146
146
  sizeMeasureMode:RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact];
147
147
 
148
+ if (self->_configuration.customizeRootView != nil) {
149
+ self->_configuration.customizeRootView(surfaceHostingProxyRootView);
150
+ }
148
151
  return surfaceHostingProxyRootView;
149
152
  }
150
153
 
151
154
  [self createBridgeIfNeeded:launchOptions];
152
155
  [self createBridgeAdapterIfNeeded];
153
156
 
157
+ UIView *rootView;
154
158
  if (self->_configuration.createRootViewWithBridge != nil) {
155
- return self->_configuration.createRootViewWithBridge(self.bridge, moduleName, initProps);
159
+ rootView = self->_configuration.createRootViewWithBridge(self.bridge, moduleName, initProps);
160
+ } else {
161
+ rootView = [self createRootViewWithBridge:self.bridge moduleName:moduleName initProps:initProps];
156
162
  }
157
-
158
- return [self createRootViewWithBridge:self.bridge moduleName:moduleName initProps:initProps];
163
+ if (self->_configuration.customizeRootView != nil) {
164
+ self->_configuration.customizeRootView(rootView);
165
+ }
166
+ return rootView;
159
167
  }
160
168
 
161
169
  - (RCTBridge *)createBridgeWithDelegate:(id<RCTBridgeDelegate>)delegate launchOptions:(NSDictionary *)launchOptions
@@ -275,6 +275,9 @@ function Pressable(props: Props, forwardedRef): React.Node {
275
275
 
276
276
  const [focused, setFocused] = useState(false);
277
277
 
278
+ const shouldUpdatePressed =
279
+ typeof children === 'function' || typeof style === 'function';
280
+
278
281
  let _accessibilityState = {
279
282
  busy: ariaBusy ?? accessibilityState?.busy,
280
283
  checked: ariaChecked ?? accessibilityState?.checked,
@@ -334,7 +337,7 @@ function Pressable(props: Props, forwardedRef): React.Node {
334
337
  if (android_rippleConfig != null) {
335
338
  android_rippleConfig.onPressIn(event);
336
339
  }
337
- setPressed(true);
340
+ shouldUpdatePressed && setPressed(true);
338
341
  if (onPressIn != null) {
339
342
  onPressIn(event);
340
343
  }
@@ -344,7 +347,7 @@ function Pressable(props: Props, forwardedRef): React.Node {
344
347
  if (android_rippleConfig != null) {
345
348
  android_rippleConfig.onPressOut(event);
346
349
  }
347
- setPressed(false);
350
+ shouldUpdatePressed && setPressed(false);
348
351
  if (onPressOut != null) {
349
352
  onPressOut(event);
350
353
  }
@@ -369,6 +372,7 @@ function Pressable(props: Props, forwardedRef): React.Node {
369
372
  onPressOut,
370
373
  pressRetentionOffset,
371
374
  setPressed,
375
+ shouldUpdatePressed,
372
376
  unstable_pressDelay,
373
377
  ],
374
378
  );
@@ -380,26 +384,26 @@ function Pressable(props: Props, forwardedRef): React.Node {
380
384
  if (isTVSelectable !== false || focusable !== false) {
381
385
  // $FlowFixMe[prop-missing]
382
386
  if (evt?.eventType === 'focus') {
383
- setFocused(true);
387
+ shouldUpdatePressed && setFocused(true);
384
388
  onFocus && onFocus(evt);
385
389
  // $FlowFixMe[prop-missing]
386
390
  } else if (evt.eventType === 'blur') {
387
391
  onBlur && onBlur(evt);
388
- setFocused(false);
392
+ shouldUpdatePressed && setFocused(false);
389
393
  }
390
394
  }
391
395
  // $FlowFixMe[prop-missing]
392
- if (focused && evt.eventType === 'select') {
396
+ if (evt.eventType === 'select') {
393
397
  // $FlowFixMe[incompatible-exact]
394
398
  onPress && onPress(evt);
395
399
  }
396
400
  // $FlowFixMe[prop-missing]
397
- if (focused && evt.eventType === 'longSelect') {
401
+ if (evt.eventType === 'longSelect') {
398
402
  // $FlowFixMe[incompatible-exact]
399
403
  onLongPress && onLongPress(evt);
400
404
  }
401
405
  },
402
- [focused, onBlur, onFocus, onLongPress, onPress, focusable, isTVSelectable],
406
+ [focused, onBlur, onFocus, onLongPress, onPress, focusable, isTVSelectable, shouldUpdatePressed],
403
407
  );
404
408
 
405
409
  React.useEffect(() => {
@@ -1136,12 +1136,14 @@ function InternalTextInput(props: Props): React.Node {
1136
1136
  };
1137
1137
 
1138
1138
  const [mostRecentEventCount, setMostRecentEventCount] = useState<number>(0);
1139
-
1140
1139
  const [lastNativeText, setLastNativeText] = useState<?Stringish>(props.value);
1141
1140
  const [lastNativeSelectionState, setLastNativeSelection] = useState<{|
1142
- selection: ?Selection,
1141
+ selection: Selection,
1143
1142
  mostRecentEventCount: number,
1144
- |}>({selection, mostRecentEventCount});
1143
+ |}>({
1144
+ selection: {start: -1, end: -1},
1145
+ mostRecentEventCount: mostRecentEventCount,
1146
+ });
1145
1147
 
1146
1148
  const lastNativeSelection = lastNativeSelectionState.selection;
1147
1149
 
@@ -1506,6 +1508,7 @@ function InternalTextInput(props: Props): React.Node {
1506
1508
  onSelectionChange={_onSelectionChange}
1507
1509
  onSelectionChangeShouldSetResponder={emptyFunctionThatReturnsTrue}
1508
1510
  selection={selection}
1511
+ selectionColor={selectionColor}
1509
1512
  style={StyleSheet.compose(
1510
1513
  useMultilineDefaultStyle ? styles.multilineDefault : null,
1511
1514
  style,
@@ -16,7 +16,7 @@ const version: $ReadOnly<{
16
16
  }> = {
17
17
  major: 0,
18
18
  minor: 74,
19
- patch: 1,
19
+ patch: 3,
20
20
  prerelease: '0',
21
21
  };
22
22
 
@@ -165,6 +165,12 @@ static UIColor *defaultPlaceholderColor(void)
165
165
  [super setSelectedTextRange:selectedTextRange];
166
166
  }
167
167
 
168
+ // After restoring the previous cursor position, we manually trigger the scroll to the new cursor position (PR 38679).
169
+ - (void)scrollRangeToVisible:(NSRange)range
170
+ {
171
+ [super scrollRangeToVisible:range];
172
+ }
173
+
168
174
  - (void)paste:(id)sender
169
175
  {
170
176
  _textWasPasted = YES;
@@ -43,6 +43,7 @@ NS_ASSUME_NONNULL_BEGIN
43
43
  // If the change was a result of user actions (like typing or touches), we MUST notify the delegate.
44
44
  - (void)setSelectedTextRange:(nullable UITextRange *)selectedTextRange NS_UNAVAILABLE;
45
45
  - (void)setSelectedTextRange:(nullable UITextRange *)selectedTextRange notifyDelegate:(BOOL)notifyDelegate;
46
+ - (void)scrollRangeToVisible:(NSRange)selectedTextRange;
46
47
 
47
48
  // This protocol disallows direct access to `text` property because
48
49
  // unwise usage of it can break the `attributeText` behavior.
@@ -201,6 +201,11 @@
201
201
  [super setSelectedTextRange:selectedTextRange];
202
202
  }
203
203
 
204
+ - (void)scrollRangeToVisible:(NSRange)range
205
+ {
206
+ // Singleline TextInput does not require scrolling after calling setSelectedTextRange (PR 38679).
207
+ }
208
+
204
209
  - (void)paste:(id)sender
205
210
  {
206
211
  _textWasPasted = YES;
@@ -23,7 +23,7 @@ NSDictionary* RCTGetReactNativeVersion(void)
23
23
  __rnVersion = @{
24
24
  RCTVersionMajor: @(0),
25
25
  RCTVersionMinor: @(74),
26
- RCTVersionPatch: @(1),
26
+ RCTVersionPatch: @(3),
27
27
  RCTVersionPrerelease: @"0",
28
28
  };
29
29
  });
@@ -131,8 +131,8 @@ static void RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrol
131
131
  {
132
132
  if (self = [super initWithFrame:frame]) {
133
133
  _props = ScrollViewShadowNode::defaultSharedProps();
134
-
135
134
  _scrollView = [[RCTEnhancedScrollView alloc] initWithFrame:self.bounds];
135
+ _scrollView.clipsToBounds = _props->getClipsContentToBounds();
136
136
  _scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
137
137
  _scrollView.delaysContentTouches = NO;
138
138
  ((RCTEnhancedScrollView *)_scrollView).overridingDelegate = self;
@@ -270,6 +270,11 @@ static void RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrol
270
270
  }
271
271
  }
272
272
 
273
+ // Overflow prop
274
+ if (oldScrollViewProps.getClipsContentToBounds() != newScrollViewProps.getClipsContentToBounds()) {
275
+ _scrollView.clipsToBounds = newScrollViewProps.getClipsContentToBounds();
276
+ }
277
+
273
278
  MAP_SCROLL_VIEW_PROP(zoomScale);
274
279
 
275
280
  if (oldScrollViewProps.contentInset != newScrollViewProps.contentInset) {
@@ -1008,28 +1013,28 @@ static void RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrol
1008
1013
  - (void)swipedUp
1009
1014
  {
1010
1015
  CGFloat newOffset = self.scrollView.contentOffset.y - [self swipeVerticalInterval];
1011
- NSLog(@"Swiped up to %f", newOffset);
1016
+ // NSLog(@"Swiped up to %f", newOffset);
1012
1017
  [self swipeVerticalScrollToOffset:newOffset];
1013
1018
  }
1014
1019
 
1015
1020
  - (void)swipedDown
1016
1021
  {
1017
1022
  CGFloat newOffset = self.scrollView.contentOffset.y + [self swipeVerticalInterval];
1018
- NSLog(@"Swiped down to %f", newOffset);
1023
+ // NSLog(@"Swiped down to %f", newOffset);
1019
1024
  [self swipeVerticalScrollToOffset:newOffset];
1020
1025
  }
1021
1026
 
1022
1027
  - (void)swipedLeft
1023
1028
  {
1024
1029
  CGFloat newOffset = self.scrollView.contentOffset.x - [self swipeHorizontalInterval];
1025
- NSLog(@"Swiped left to %f", newOffset);
1030
+ // NSLog(@"Swiped left to %f", newOffset);
1026
1031
  [self swipeHorizontalScrollToOffset:newOffset];
1027
1032
  }
1028
1033
 
1029
1034
  - (void)swipedRight
1030
1035
  {
1031
1036
  CGFloat newOffset = self.scrollView.contentOffset.x + [self swipeHorizontalInterval];
1032
- NSLog(@"Swiped right to %f", newOffset);
1037
+ // NSLog(@"Swiped right to %f", newOffset);
1033
1038
  [self swipeHorizontalScrollToOffset:newOffset];
1034
1039
  }
1035
1040
 
@@ -597,6 +597,9 @@ using namespace facebook::react;
597
597
  UITextRange *selectedRange = _backedTextInputView.selectedTextRange;
598
598
  NSInteger oldTextLength = _backedTextInputView.attributedText.string.length;
599
599
  _backedTextInputView.attributedText = attributedString;
600
+ // Updating the UITextView attributedText, for example changing the lineHeight, the color or adding
601
+ // a new paragraph with \n, causes the cursor to move to the end of the Text and scroll.
602
+ // This is fixed by restoring the cursor position and scrolling to that position (iOS issue 652653).
600
603
  if (selectedRange.empty) {
601
604
  // Maintaining a cursor position relative to the end of the old text.
602
605
  NSInteger offsetStart = [_backedTextInputView offsetFromPosition:_backedTextInputView.beginningOfDocument
@@ -607,6 +610,7 @@ using namespace facebook::react;
607
610
  offset:newOffset];
608
611
  [_backedTextInputView setSelectedTextRange:[_backedTextInputView textRangeFromPosition:position toPosition:position]
609
612
  notifyDelegate:YES];
613
+ [_backedTextInputView scrollRangeToVisible:NSMakeRange(offsetStart, 0)];
610
614
  }
611
615
  [self _restoreTextSelection];
612
616
  _lastStringStateWasUpdatedWith = attributedString;
@@ -1278,8 +1278,9 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle)
1278
1278
  // iOS draws borders in front of the content whereas CSS draws them behind
1279
1279
  // the content. For this reason, only use iOS border drawing when clipping
1280
1280
  // or when the border is hidden.
1281
- borderMetrics.borderWidths.left == 0 ||
1282
- colorComponentsFromColor(borderMetrics.borderColors.left).alpha == 0 || self.clipsToBounds);
1281
+ borderMetrics.borderWidths.left == 0 || self.clipsToBounds ||
1282
+ (colorComponentsFromColor(borderMetrics.borderColors.left).alpha == 0 &&
1283
+ (*borderMetrics.borderColors.left).getUIColor() != nullptr));
1283
1284
 
1284
1285
  CGColorRef backgroundColor = [_backgroundColor resolvedColorWithTraitCollection:self.traitCollection].CGColor;
1285
1286
 
@@ -1002,13 +1002,13 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1002
1002
  // scroll to bottom
1003
1003
  // Similarly for left and right
1004
1004
  if (context.focusHeading == UIFocusHeadingUp && self.snapToStart) {
1005
- [self swipeVerticalScrollToOffset:0.0];
1005
+ [self scrollToVerticalOffset:0.0];
1006
1006
  } else if(context.focusHeading == UIFocusHeadingDown && self.snapToEnd) {
1007
- [self swipeVerticalScrollToOffset:self.scrollView.contentSize.height];
1007
+ [self scrollToVerticalOffset:self.scrollView.contentSize.height];
1008
1008
  } else if(context.focusHeading == UIFocusHeadingLeft && self.snapToStart) {
1009
- [self swipeHorizontalScrollToOffset:0.0];
1009
+ [self scrollToHorizontalOffset:0.0];
1010
1010
  } else if(context.focusHeading == UIFocusHeadingRight && self.snapToEnd) {
1011
- [self swipeHorizontalScrollToOffset:self.scrollView.contentSize.width];
1011
+ [self scrollToHorizontalOffset:self.scrollView.contentSize.width];
1012
1012
  }
1013
1013
 
1014
1014
  }
@@ -1119,7 +1119,7 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1119
1119
  return duration;
1120
1120
  }
1121
1121
 
1122
- - (void)swipeVerticalScrollToOffset:(CGFloat)yOffset
1122
+ - (void)scrollToVerticalOffset:(CGFloat)yOffset
1123
1123
  {
1124
1124
  _blockFirstTouch = NO;
1125
1125
  dispatch_async(dispatch_get_main_queue(), ^{
@@ -1133,7 +1133,7 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1133
1133
  });
1134
1134
  }
1135
1135
 
1136
- - (void)swipeHorizontalScrollToOffset:(CGFloat)xOffset
1136
+ - (void)scrollToHorizontalOffset:(CGFloat)xOffset
1137
1137
  {
1138
1138
  _blockFirstTouch = NO;
1139
1139
  dispatch_async(dispatch_get_main_queue(), ^{
@@ -1149,30 +1149,46 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1149
1149
 
1150
1150
  - (void)swipedUp
1151
1151
  {
1152
+ if (!self.scrollView.scrollEnabled) {
1153
+ return;
1154
+ }
1155
+
1152
1156
  CGFloat newOffset = self.scrollView.contentOffset.y - [self swipeVerticalInterval];
1153
- NSLog(@"Swiped up to %f", newOffset);
1154
- [self swipeVerticalScrollToOffset:newOffset];
1157
+ // NSLog(@"Swiped up to %f", newOffset);
1158
+ [self scrollToVerticalOffset:newOffset];
1155
1159
  }
1156
1160
 
1157
1161
  - (void)swipedDown
1158
1162
  {
1163
+ if (!self.scrollView.scrollEnabled) {
1164
+ return;
1165
+ }
1166
+
1159
1167
  CGFloat newOffset = self.scrollView.contentOffset.y + [self swipeVerticalInterval];
1160
- NSLog(@"Swiped down to %f", newOffset);
1161
- [self swipeVerticalScrollToOffset:newOffset];
1168
+ // NSLog(@"Swiped down to %f", newOffset);
1169
+ [self scrollToVerticalOffset:newOffset];
1162
1170
  }
1163
1171
 
1164
1172
  - (void)swipedLeft
1165
1173
  {
1166
- CGFloat newOffset = self.scrollView.contentOffset.x - [self swipeHorizontalInterval];
1167
- NSLog(@"Swiped left to %f", newOffset);
1168
- [self swipeHorizontalScrollToOffset:newOffset];
1174
+ if (!self.scrollView.scrollEnabled) {
1175
+ return;
1176
+ }
1177
+
1178
+ CGFloat newOffset = self.scrollView.contentOffset.x - [self swipeHorizontalInterval];
1179
+ // NSLog(@"Swiped left to %f", newOffset);
1180
+ [self scrollToHorizontalOffset:newOffset];
1169
1181
  }
1170
1182
 
1171
1183
  - (void)swipedRight
1172
1184
  {
1173
- CGFloat newOffset = self.scrollView.contentOffset.x + [self swipeHorizontalInterval];
1174
- NSLog(@"Swiped right to %f", newOffset);
1175
- [self swipeHorizontalScrollToOffset:newOffset];
1185
+ if (!self.scrollView.scrollEnabled) {
1186
+ return;
1187
+ }
1188
+
1189
+ CGFloat newOffset = self.scrollView.contentOffset.x + [self swipeHorizontalInterval];
1190
+ // NSLog(@"Swiped right to %f", newOffset);
1191
+ [self scrollToHorizontalOffset:newOffset];
1176
1192
  }
1177
1193
 
1178
1194
  - (void)addSwipeGestureRecognizers
@@ -1,4 +1,4 @@
1
- VERSION_NAME=0.74.1-0
1
+ VERSION_NAME=0.74.3-0
2
2
  # react.internal.publishingGroup=com.facebook.react
3
3
  # For TV use this group
4
4
  react.internal.publishingGroup=io.github.react-native-tvos
@@ -433,10 +433,8 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
433
433
 
434
434
  if (mShouldLogContentAppeared) {
435
435
  mShouldLogContentAppeared = false;
436
-
437
- if (mJSModuleName != null) {
438
- ReactMarker.logMarker(ReactMarkerConstants.CONTENT_APPEARED, mJSModuleName, mRootViewTag);
439
- }
436
+ String jsModuleName = getJSModuleName();
437
+ ReactMarker.logMarker(ReactMarkerConstants.CONTENT_APPEARED, jsModuleName, mRootViewTag);
440
438
  }
441
439
  }
442
440
 
@@ -99,8 +99,11 @@ public class JavaTimerManager {
99
99
  }
100
100
 
101
101
  // If the JS thread is busy for multiple frames we cancel any other pending runnable.
102
- if (mCurrentIdleCallbackRunnable != null) {
103
- mCurrentIdleCallbackRunnable.cancel();
102
+ // We also capture the idleCallbackRunnable to tentatively fix:
103
+ // https://github.com/facebook/react-native/issues/44842
104
+ IdleCallbackRunnable idleCallbackRunnable = mCurrentIdleCallbackRunnable;
105
+ if (idleCallbackRunnable != null) {
106
+ idleCallbackRunnable.cancel();
104
107
  }
105
108
 
106
109
  mCurrentIdleCallbackRunnable = new IdleCallbackRunnable(frameTimeNanos);
@@ -63,6 +63,21 @@ public class ReactAndroidHWInputDeviceHelper {
63
63
  .put(KeyEvent.KEYCODE_9, "9")
64
64
  .put(KeyEvent.KEYCODE_CHANNEL_DOWN, "channelDown")
65
65
  .put(KeyEvent.KEYCODE_CHANNEL_UP, "channelUp")
66
+ .put(KeyEvent.KEYCODE_BOOKMARK, "bookmark")
67
+ .put(KeyEvent.KEYCODE_AVR_INPUT, "avrInput")
68
+ .put(KeyEvent.KEYCODE_AVR_POWER, "avrPower")
69
+ .put(KeyEvent.KEYCODE_DVR, "dvr")
70
+ .put(KeyEvent.KEYCODE_GUIDE, "guide")
71
+ .put(KeyEvent.KEYCODE_PROG_RED, "red")
72
+ .put(KeyEvent.KEYCODE_PROG_GREEN, "green")
73
+ .put(KeyEvent.KEYCODE_PROG_BLUE, "blue")
74
+ .put(KeyEvent.KEYCODE_PROG_YELLOW, "yellow")
75
+ .put(KeyEvent.KEYCODE_STB_INPUT, "stbInput")
76
+ .put(KeyEvent.KEYCODE_STB_POWER, "stbPower")
77
+ .put(KeyEvent.KEYCODE_TV, "tv")
78
+ .put(KeyEvent.KEYCODE_TV_INPUT, "tvInput")
79
+ .put(KeyEvent.KEYCODE_WINDOW, "window")
80
+ .put(KeyEvent.KEYCODE_TV_TELETEXT, "teletext")
66
81
  .build();
67
82
 
68
83
  private static final Map<Integer, String> KEY_EVENTS_LONG_PRESS_ACTIONS =
@@ -75,6 +90,11 @@ public class ReactAndroidHWInputDeviceHelper {
75
90
  .put(KeyEvent.KEYCODE_DPAD_RIGHT, "longRight")
76
91
  .put(KeyEvent.KEYCODE_DPAD_DOWN, "longDown")
77
92
  .put(KeyEvent.KEYCODE_DPAD_LEFT, "longLeft")
93
+ .put(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, "longPlayPause")
94
+ .put(KeyEvent.KEYCODE_MEDIA_REWIND, "longRewind")
95
+ .put(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, "longFastForward")
96
+ .put(KeyEvent.KEYCODE_CHANNEL_DOWN, "longChannelDown")
97
+ .put(KeyEvent.KEYCODE_CHANNEL_UP, "longChannelUp")
78
98
  .build();
79
99
 
80
100
  /**
@@ -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", 74,
20
- "patch", 1,
20
+ "patch", 3,
21
21
  "prerelease", "0");
22
22
  }
@@ -378,7 +378,9 @@ public class ReactHostImpl implements ReactHost {
378
378
  public ReactSurface createSurface(
379
379
  Context context, String moduleName, @Nullable Bundle initialProps) {
380
380
  ReactSurfaceImpl surface = new ReactSurfaceImpl(context, moduleName, initialProps);
381
- surface.attachView(new ReactSurfaceView(context, surface));
381
+ ReactSurfaceView surfaceView = new ReactSurfaceView(context, surface);
382
+ surfaceView.setShouldLogContentAppeared(true);
383
+ surface.attachView(surfaceView);
382
384
  surface.attach(this);
383
385
  return surface;
384
386
  }
@@ -653,11 +655,12 @@ public class ReactHostImpl implements ReactHost {
653
655
  ReactContext currentContext = getCurrentReactContext();
654
656
  if (currentContext != null) {
655
657
  currentContext.onActivityResult(activity, requestCode, resultCode, data);
658
+ } else {
659
+ ReactSoftExceptionLogger.logSoftException(
660
+ TAG,
661
+ new ReactNoCrashSoftException(
662
+ "Tried to access onActivityResult while context is not ready"));
656
663
  }
657
- ReactSoftExceptionLogger.logSoftException(
658
- TAG,
659
- new ReactNoCrashSoftException(
660
- "Tried to access onActivityResult while context is not ready"));
661
664
  }
662
665
 
663
666
  /* To be called when focus has changed for the hosting window. */
@@ -670,11 +673,12 @@ public class ReactHostImpl implements ReactHost {
670
673
  ReactContext currentContext = getCurrentReactContext();
671
674
  if (currentContext != null) {
672
675
  currentContext.onWindowFocusChange(hasFocus);
676
+ } else {
677
+ ReactSoftExceptionLogger.logSoftException(
678
+ TAG,
679
+ new ReactNoCrashSoftException(
680
+ "Tried to access onWindowFocusChange while context is not ready"));
673
681
  }
674
- ReactSoftExceptionLogger.logSoftException(
675
- TAG,
676
- new ReactNoCrashSoftException(
677
- "Tried to access onWindowFocusChange while context is not ready"));
678
682
  }
679
683
 
680
684
  /* This method will give JS the opportunity to receive intents via Linking.
@@ -701,10 +705,11 @@ public class ReactHostImpl implements ReactHost {
701
705
  }
702
706
  }
703
707
  currentContext.onNewIntent(getCurrentActivity(), intent);
708
+ } else {
709
+ ReactSoftExceptionLogger.logSoftException(
710
+ TAG,
711
+ new ReactNoCrashSoftException("Tried to access onNewIntent while context is not ready"));
704
712
  }
705
- ReactSoftExceptionLogger.logSoftException(
706
- TAG,
707
- new ReactNoCrashSoftException("Tried to access onNewIntent while context is not ready"));
708
713
  }
709
714
 
710
715
  @ThreadConfined(UI)
@@ -1523,9 +1528,9 @@ public class ReactHostImpl implements ReactHost {
1523
1528
 
1524
1529
  // Step 3: Stop all React Native surfaces
1525
1530
  stopAttachedSurfaces(method, reactInstance);
1526
-
1527
- // TODO(T161461674): Should we clear mAttachedSurfaces?
1528
- // Not clearing mAttachedSurfaces could lead to a memory leak.
1531
+ synchronized (mAttachedSurfaces) {
1532
+ mAttachedSurfaces.clear();
1533
+ }
1529
1534
 
1530
1535
  return task;
1531
1536
  },
@@ -189,6 +189,11 @@ public class ReactSurfaceView extends ReactRootView {
189
189
  return UIManagerType.FABRIC;
190
190
  }
191
191
 
192
+ @Override
193
+ public String getJSModuleName() {
194
+ return mSurface.getModuleName();
195
+ }
196
+
192
197
  @Override
193
198
  protected void dispatchJSTouchEvent(MotionEvent event) {
194
199
  if (mJSTouchDispatcher == null) {
@@ -17,7 +17,7 @@ namespace facebook::react {
17
17
  constexpr struct {
18
18
  int32_t Major = 0;
19
19
  int32_t Minor = 74;
20
- int32_t Patch = 1;
20
+ int32_t Patch = 3;
21
21
  std::string_view Prerelease = "0";
22
22
  } ReactNativeVersion;
23
23
 
@@ -13,8 +13,8 @@
13
13
  #include <atomic>
14
14
  #include <condition_variable>
15
15
  #include <cstdlib>
16
+ #include <deque>
16
17
  #include <mutex>
17
- #include <queue>
18
18
  #include <sstream>
19
19
  #include <thread>
20
20
 
@@ -51,6 +51,12 @@ class JSCRuntime : public jsi::Runtime {
51
51
  const std::shared_ptr<const jsi::Buffer>& buffer,
52
52
  const std::string& sourceURL) override;
53
53
 
54
+ // If we use this interface to implement microtasks in the host we need to
55
+ // polyfill `Promise` to use these methods, because JSC doesn't currently
56
+ // support providing a custom queue for its built-in implementation.
57
+ // Not doing this would result in a non-compliant behavior, as microtasks
58
+ // wouldn't execute in the order in which they were queued.
59
+ void queueMicrotask(const jsi::Function& callback) override;
54
60
  bool drainMicrotasks(int maxMicrotasksHint = -1) override;
55
61
 
56
62
  jsi::Object global() override;
@@ -265,6 +271,7 @@ class JSCRuntime : public jsi::Runtime {
265
271
  std::atomic<bool> ctxInvalid_;
266
272
  std::string desc_;
267
273
  JSValueRef nativeStateSymbol_ = nullptr;
274
+ std::deque<jsi::Function> microtaskQueue_;
268
275
  #ifndef NDEBUG
269
276
  mutable std::atomic<intptr_t> objectCounter_;
270
277
  mutable std::atomic<intptr_t> symbolCounter_;
@@ -385,6 +392,10 @@ JSCRuntime::JSCRuntime(JSGlobalContextRef ctx)
385
392
  }
386
393
 
387
394
  JSCRuntime::~JSCRuntime() {
395
+ // We need to clear the microtask queue to remove all references to the
396
+ // callbacks, so objectCounter_ would be 0 below.
397
+ microtaskQueue_.clear();
398
+
388
399
  // On shutting down and cleaning up: when JSC is actually torn down,
389
400
  // it calls JSC::Heap::lastChanceToFinalize internally which
390
401
  // finalizes anything left over. But at this point,
@@ -441,7 +452,24 @@ jsi::Value JSCRuntime::evaluateJavaScript(
441
452
  return createValue(res);
442
453
  }
443
454
 
444
- bool JSCRuntime::drainMicrotasks(int maxMicrotasksHint) {
455
+ void JSCRuntime::queueMicrotask(const jsi::Function& callback) {
456
+ microtaskQueue_.emplace_back(
457
+ jsi::Value(*this, callback).asObject(*this).asFunction(*this));
458
+ }
459
+
460
+ bool JSCRuntime::drainMicrotasks(int /*maxMicrotasksHint*/) {
461
+ // Note that new jobs can be enqueued during the draining.
462
+ while (!microtaskQueue_.empty()) {
463
+ jsi::Function callback = std::move(microtaskQueue_.front());
464
+
465
+ // We need to pop before calling the callback because that might throw.
466
+ // When that happens, the host will call `drainMicrotasks` again to execute
467
+ // the remaining microtasks, and this one shouldn't run again.
468
+ microtaskQueue_.pop_front();
469
+
470
+ callback.call(*this);
471
+ }
472
+
445
473
  return true;
446
474
  }
447
475