react-native-tvos 0.73.1-3 → 0.73.4-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 (58) hide show
  1. package/Libraries/AppDelegate/RCTAppDelegate.h +0 -2
  2. package/Libraries/AppDelegate/RCTAppDelegate.mm +20 -13
  3. package/Libraries/Components/Touchable/TouchableBounce.js +4 -0
  4. package/Libraries/Components/Touchable/TouchableHighlight.js +5 -1
  5. package/Libraries/Components/Touchable/TouchableNativeFeedback.js +1 -0
  6. package/Libraries/Components/Touchable/TouchableOpacity.js +1 -0
  7. package/Libraries/Components/Touchable/TouchableWithoutFeedback.js +1 -0
  8. package/Libraries/Core/ReactNativeVersion.js +2 -2
  9. package/Libraries/Inspector/NetworkOverlay.js +1 -1
  10. package/Libraries/Lists/FillRateHelper.js +3 -3
  11. package/Libraries/Lists/FlatList.d.ts +1 -1
  12. package/Libraries/Lists/FlatList.js +2 -2
  13. package/Libraries/Lists/SectionList.d.ts +1 -1
  14. package/Libraries/Lists/SectionList.js +2 -2
  15. package/Libraries/Lists/SectionListModern.js +2 -2
  16. package/Libraries/Lists/ViewabilityHelper.js +3 -3
  17. package/Libraries/Lists/VirtualizeUtils.js +2 -2
  18. package/Libraries/Lists/VirtualizedList.js +9 -2094
  19. package/Libraries/Lists/VirtualizedListContext.js +2 -2
  20. package/Libraries/Lists/VirtualizedSectionList.js +3 -3
  21. package/Libraries/Modal/Modal.js +1 -1
  22. package/Libraries/Utilities/ReactNativeTestTools.js +1 -1
  23. package/Libraries/promiseRejectionTrackingOptions.js +5 -0
  24. package/README.md +11 -10
  25. package/React/Base/RCTVersion.m +2 -2
  26. package/React/CoreModules/RCTDeviceInfo.mm +33 -0
  27. package/React/CoreModules/RCTRedBox.mm +17 -0
  28. package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +24 -1
  29. package/React/Modules/RCTUIManager.m +9 -3
  30. package/React/Views/RCTTVView.m +23 -0
  31. package/React/Views/ScrollView/RCTScrollView.m +81 -0
  32. package/ReactAndroid/build.gradle +1 -1
  33. package/ReactAndroid/gradle.properties +1 -1
  34. package/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java +1 -0
  35. package/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/MessageQueueThreadHandler.java +11 -0
  36. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java +2 -2
  37. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java +9 -1
  38. package/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java +29 -4
  39. package/ReactCommon/cxxreact/ReactNativeVersion.h +2 -2
  40. package/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp +2 -2
  41. package/ReactCommon/react/renderer/core/EventEmitter.cpp +14 -6
  42. package/ReactCommon/react/renderer/debug/flags.h +2 -4
  43. package/ReactCommon/react/renderer/debug/tests/DebugStringConvertibleTest.cpp +2 -0
  44. package/ReactCommon/react/renderer/templateprocessor/tests/UITemplateProcessorTest.cpp +4 -0
  45. package/package.json +11 -10
  46. package/scripts/cocoapods/utils.rb +4 -40
  47. package/scripts/react-native-xcode.sh +1 -1
  48. package/scripts/react_native_pods.rb +1 -2
  49. package/sdks/.hermesversion +1 -1
  50. package/sdks/hermes-engine/utils/replace_hermes_version.js +1 -1
  51. package/sdks/hermesc/osx-bin/hermes +0 -0
  52. package/sdks/hermesc/osx-bin/hermesc +0 -0
  53. package/sdks/hermesc/win64-bin/hermesc.exe +0 -0
  54. package/template/Gemfile +4 -2
  55. package/template/metro.config.js +23 -2
  56. package/template/package.json +5 -5
  57. package/third-party-podspecs/boost.podspec +1 -1
  58. package/types/index.d.ts +1 -1
@@ -10,9 +10,9 @@
10
10
 
11
11
  'use strict';
12
12
 
13
- import {typeof VirtualizedListContextResetter as VirtualizedListContextResetterType} from '@react-native/virtualized-lists';
13
+ import {typeof VirtualizedListContextResetter as VirtualizedListContextResetterType} from '@react-native-tvos/virtualized-lists';
14
14
 
15
15
  const VirtualizedListContextResetter: VirtualizedListContextResetterType =
16
- require('@react-native/virtualized-lists').VirtualizedListContextResetter;
16
+ require('@react-native-tvos/virtualized-lists').VirtualizedListContextResetter;
17
17
 
18
18
  module.exports = {VirtualizedListContextResetter};
@@ -10,13 +10,13 @@
10
10
 
11
11
  'use strict';
12
12
 
13
- import {typeof VirtualizedSectionList as VirtualizedSectionListType} from '@react-native/virtualized-lists';
13
+ import {typeof VirtualizedSectionList as VirtualizedSectionListType} from '@react-native-tvos/virtualized-lists';
14
14
 
15
15
  const VirtualizedSectionList: VirtualizedSectionListType =
16
- require('@react-native/virtualized-lists').VirtualizedSectionList;
16
+ require('@react-native-tvos/virtualized-lists').VirtualizedSectionList;
17
17
 
18
18
  export type {
19
19
  SectionBase,
20
20
  ScrollToLocationParamsType,
21
- } from '@react-native/virtualized-lists';
21
+ } from '@react-native-tvos/virtualized-lists';
22
22
  module.exports = VirtualizedSectionList;
@@ -17,7 +17,7 @@ import {type EventSubscription} from '../vendor/emitter/EventEmitter';
17
17
  import ModalInjection from './ModalInjection';
18
18
  import NativeModalManager from './NativeModalManager';
19
19
  import RCTModalHostView from './RCTModalHostViewNativeComponent';
20
- import {VirtualizedListContextResetter} from '@react-native/virtualized-lists';
20
+ import {VirtualizedListContextResetter} from '@react-native-tvos/virtualized-lists';
21
21
 
22
22
  const ScrollView = require('../Components/ScrollView/ScrollView');
23
23
  const View = require('../Components/View/View');
@@ -16,7 +16,7 @@ const Switch = require('../Components/Switch/Switch').default;
16
16
  const TextInput = require('../Components/TextInput/TextInput');
17
17
  const View = require('../Components/View/View');
18
18
  const Text = require('../Text/Text');
19
- const {VirtualizedList} = require('@react-native/virtualized-lists');
19
+ const {VirtualizedList} = require('@react-native-tvos/virtualized-lists');
20
20
  const React = require('react');
21
21
  const ShallowRenderer = require('react-shallow-renderer');
22
22
  const ReactTestRenderer = require('react-test-renderer');
@@ -34,6 +34,11 @@ let rejectionTrackingOptions: $NonMaybeType<Parameters<enable>[0]> = {
34
34
  ? rejection
35
35
  : JSON.stringify((rejection: $FlowFixMe));
36
36
  }
37
+ // It could although this object is not a standard error, it still has stack information to unwind
38
+ // $FlowFixMe ignore types just check if stack is there
39
+ if (rejection.stack && typeof rejection.stack === 'string') {
40
+ stack = rejection.stack;
41
+ }
37
42
  }
38
43
 
39
44
  const warning = `Possible unhandled promise rejection (id: ${id}):\n${
package/README.md CHANGED
@@ -74,21 +74,22 @@ See [this document](https://docs.expo.dev/bare/using-expo-cli/) for more details
74
74
 
75
75
  ## _(New)_ Using the Expo SDK with TV apps
76
76
 
77
- Starting with the Expo SDK 50 preview, and react-native-tvos 0.73.x, it will be possible to create Expo apps, and build them for TV via a new config plugin.
77
+ See the [Building Expo apps for TV](https://docs.expo.dev/guides/building-for-tv/) guide from Expo for details, including supported Expo modules and limitations.
78
78
 
79
- This functionality will be new in Expo SDK 50, and will be considered an experimental feature for now.
79
+ Expo SDK 50 or greater, and react-native-tvos 0.73.x or later, are required.
80
80
 
81
- The fastest way to generate a new project is described in the [TV Example](https://github.com/expo/examples/tree/master/with-tv) in the Expo examples repo.
81
+ ## _(New)_ How to support TV specific file extensions
82
82
 
83
- Besides most of the core Expo modules, these also work on TV:
83
+ The template contains an [example Metro configuration](./packages/react-native/template/metro.config.js) that allows Metro to resolve application source files with TV-specific code, indicated by specific file extensions (e.g. `*.ios.tv.tsx`, `*.android.tv.tsx`, `*.tv.tsx`). The config will work the same way with the other standard source file extensions (`.js`, etc.), as documented in [Metro docs](https://metrobundler.dev/docs/configuration/#sourceexts)
84
84
 
85
- - expo-av
86
- - expo-image
87
- - expo-localization
88
- - expo-updates
85
+ When this is enabled, Metro will resolve files in the following order of preference (and similarly for the other supported file extensions):
89
86
 
90
- TV does NOT support dev client (dev menu, dev launcher) at this time.
91
- TV does NOT support Expo Router at this time.
87
+ - `file.ios.tv.tsx` or `file.android.tv.tsx`
88
+ - `file.tv.tsx`
89
+ - `file.ios.tsx` or `file.android.tsx`
90
+ - `file.tsx`
91
+
92
+ This config is not enabled by default, since it will impact bundling performance, but is available for developers who need this capability.
92
93
 
93
94
  ## Code changes
94
95
 
@@ -23,8 +23,8 @@ NSDictionary* RCTGetReactNativeVersion(void)
23
23
  __rnVersion = @{
24
24
  RCTVersionMajor: @(0),
25
25
  RCTVersionMinor: @(73),
26
- RCTVersionPatch: @(1),
27
- RCTVersionPrerelease: @"3",
26
+ RCTVersionPatch: @(4),
27
+ RCTVersionPrerelease: @"0",
28
28
  };
29
29
  });
30
30
  return __rnVersion;
@@ -80,11 +80,44 @@ RCT_EXPORT_MODULE()
80
80
  object:nil];
81
81
 
82
82
  #endif
83
+
84
+ // TODO T175901725 - Registering the RCTDeviceInfo module to the notification is a short-term fix to unblock 0.73
85
+ // The actual behavior should be that the module is properly registered in the TurboModule/Bridge infrastructure
86
+ // and the infrastructure imperatively invoke the `invalidate` method, rather than listening to a notification.
87
+ // This is a temporary workaround until we can investigate the issue better as there might be other modules in a
88
+ // similar situation.
89
+ [[NSNotificationCenter defaultCenter] addObserver:self
90
+ selector:@selector(invalidate)
91
+ name:RCTBridgeWillInvalidateModulesNotification
92
+ object:nil];
83
93
  }
84
94
 
85
95
  - (void)invalidate
86
96
  {
87
97
  _invalidated = YES;
98
+ [self _cleanupObservers];
99
+ }
100
+
101
+ - (void)_cleanupObservers
102
+ {
103
+ [[NSNotificationCenter defaultCenter] removeObserver:self
104
+ name:RCTAccessibilityManagerDidUpdateMultiplierNotification
105
+ object:[_moduleRegistry moduleForName:"AccessibilityManager"]];
106
+ #if !TARGET_OS_TV
107
+ [[NSNotificationCenter defaultCenter] removeObserver:self
108
+ name:UIApplicationDidChangeStatusBarOrientationNotification
109
+ object:nil];
110
+ #endif
111
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
112
+
113
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:RCTUserInterfaceStyleDidChangeNotification object:nil];
114
+
115
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:RCTWindowFrameDidChangeNotification object:nil];
116
+
117
+ [[NSNotificationCenter defaultCenter] addObserver:self
118
+ selector:@selector(invalidate)
119
+ name:RCTBridgeWillInvalidateModulesNotification
120
+ object:nil];
88
121
  }
89
122
 
90
123
  static BOOL RCTIsIPhoneNotched()
@@ -302,6 +302,23 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder)
302
302
  return lineInfo;
303
303
  }
304
304
 
305
+ - (void)tableView:(UITableView *)tableView didUpdateFocusInContext:(UITableViewFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
306
+ {
307
+ UITableViewCell *previouslyFocusedItem = (UITableViewCell *)context.previouslyFocusedItem;
308
+ if ([previouslyFocusedItem isKindOfClass:[UITableViewCell class]]) {
309
+ [coordinator addCoordinatedUnfocusingAnimations:^(id<UIFocusAnimationContext> _Nonnull animationContext) {
310
+ previouslyFocusedItem.textLabel.textColor = [UIColor whiteColor];
311
+ } completion:nil];
312
+ }
313
+
314
+ UITableViewCell *nextFocusedItem = (UITableViewCell *)context.nextFocusedItem;
315
+ if ([nextFocusedItem isKindOfClass:[UITableViewCell class]]) {
316
+ [coordinator addCoordinatedFocusingAnimations:^(id<UIFocusAnimationContext> _Nonnull animationContext) {
317
+ nextFocusedItem.textLabel.textColor = [UIColor blackColor];
318
+ } completion:nil];
319
+ }
320
+ }
321
+
305
322
  #pragma mark - TableView
306
323
 
307
324
  - (NSInteger)numberOfSectionsInTableView:(__unused UITableView *)tableView
@@ -51,7 +51,8 @@ using namespace facebook::react;
51
51
  BOOL _removeClippedSubviews;
52
52
  NSMutableArray<UIView *> *_reactSubviews;
53
53
  BOOL _motionEffectsAdded;
54
- UITapGestureRecognizer *_selectRecognizer;
54
+ UITapGestureRecognizer *_selectRecognizer;
55
+ UILongPressGestureRecognizer * _longSelectRecognizer;
55
56
  NSSet<NSString *> *_Nullable _propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN;
56
57
  ParallaxProperties _tvParallaxProperties;
57
58
  BOOL _hasTVPreferredFocus;
@@ -249,6 +250,11 @@ using namespace facebook::react;
249
250
  [self sendNotificationWithEventType:@"select"];
250
251
  }
251
252
 
253
+ - (void)sendLongSelectNotification:(UIGestureRecognizer *)recognizer
254
+ {
255
+ [self sendNotificationWithEventType:@"longSelect"];
256
+ }
257
+
252
258
  - (void)sendNotificationWithEventType:(NSString * __nonnull)eventType
253
259
  {
254
260
  [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTTVNavigationEventNotification"
@@ -292,6 +298,13 @@ using namespace facebook::react;
292
298
  }
293
299
  }
294
300
 
301
+ - (void)handleLongSelect:(UIGestureRecognizer *)r
302
+ {
303
+ if (r.state == UIGestureRecognizerStateBegan) {
304
+ [self sendLongSelectNotification:r];
305
+ }
306
+ }
307
+
295
308
  - (void)addParallaxMotionEffects
296
309
  {
297
310
  if(!_tvParallaxProperties.enabled) {
@@ -887,11 +900,21 @@ using namespace facebook::react;
887
900
  action:@selector(handleSelect:)];
888
901
  recognizer.allowedPressTypes = @[ @(UIPressTypeSelect) ];
889
902
  _selectRecognizer = recognizer;
903
+
904
+ UILongPressGestureRecognizer *longRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongSelect:)];
905
+ recognizer.allowedPressTypes = @[ @(UIPressTypeSelect) ];
906
+ [self addGestureRecognizer:longRecognizer];
907
+ _longSelectRecognizer = longRecognizer;
908
+
890
909
  [self addGestureRecognizer:_selectRecognizer];
910
+ [self addGestureRecognizer:_longSelectRecognizer];
891
911
  } else {
892
912
  if (_selectRecognizer) {
893
913
  [self removeGestureRecognizer:_selectRecognizer];
894
914
  }
915
+ if (_longSelectRecognizer) {
916
+ [self removeGestureRecognizer:_longSelectRecognizer];
917
+ }
895
918
  }
896
919
  }
897
920
  // `tvParallaxProperties
@@ -179,14 +179,20 @@ RCT_EXPORT_MODULE()
179
179
  _componentDataByName[componentData.name] = componentData;
180
180
  }
181
181
  }
182
-
182
+ // Preload the a11yManager as the RCTUIManager needs it to listen for notification
183
+ // By eagerly preloading it in the setBridge method, we make sure that the manager is
184
+ // properly initialized in the Main Thread and that we do not incur in any race condition
185
+ // or concurrency problem.
186
+ id<RCTBridgeModule> a11yManager = [self->_bridge moduleForName:@"AccessibilityManager"
187
+ lazilyLoadIfNecessary:YES];
188
+ __weak NSObject * a11yManagerWeakObject = a11yManager;
183
189
  // This dispatch_async avoids a deadlock while configuring native modules
184
190
  dispatch_async(dispatch_get_main_queue(), ^{
191
+ __strong NSObject * a11yManagerStrongObject = a11yManagerWeakObject;
185
192
  [[NSNotificationCenter defaultCenter] addObserver:self
186
193
  selector:@selector(didReceiveNewContentSizeMultiplier)
187
194
  name:@"RCTAccessibilityManagerDidUpdateMultiplierNotification"
188
- object:[self->_bridge moduleForName:@"AccessibilityManager"
189
- lazilyLoadIfNecessary:YES]];
195
+ object:a11yManagerStrongObject];
190
196
  });
191
197
  #if !TARGET_OS_TV
192
198
  [[NSNotificationCenter defaultCenter] addObserver:self
@@ -22,6 +22,7 @@
22
22
  @implementation RCTTVView {
23
23
  __weak RCTBridge *_bridge;
24
24
  UITapGestureRecognizer *_selectRecognizer;
25
+ UILongPressGestureRecognizer * _longSelectRecognizer;
25
26
  BOOL motionEffectsAdded;
26
27
  NSArray* focusDestinations;
27
28
  id<UIFocusItem> previouslyFocusedItem;
@@ -79,11 +80,21 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
79
80
  action:@selector(handleSelect:)];
80
81
  recognizer.allowedPressTypes = @[ @(UIPressTypeSelect) ];
81
82
  _selectRecognizer = recognizer;
83
+
84
+ UILongPressGestureRecognizer *longRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongSelect:)];
85
+ recognizer.allowedPressTypes = @[ @(UIPressTypeSelect) ];
86
+ [self addGestureRecognizer:longRecognizer];
87
+ _longSelectRecognizer = longRecognizer;
88
+
82
89
  [self addGestureRecognizer:_selectRecognizer];
90
+ [self addGestureRecognizer:_longSelectRecognizer];
83
91
  } else {
84
92
  if (_selectRecognizer) {
85
93
  [self removeGestureRecognizer:_selectRecognizer];
86
94
  }
95
+ if (_longSelectRecognizer) {
96
+ [self removeGestureRecognizer:_longSelectRecognizer];
97
+ }
87
98
  }
88
99
  }
89
100
 
@@ -120,6 +131,13 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
120
131
  }
121
132
  }
122
133
 
134
+ - (void)handleLongSelect:(UIGestureRecognizer *)r
135
+ {
136
+ if (r.state == UIGestureRecognizerStateBegan) {
137
+ [self sendLongSelectNotification:r];
138
+ }
139
+ }
140
+
123
141
  - (BOOL)isUserInteractionEnabled
124
142
  {
125
143
  return YES;
@@ -414,6 +432,11 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
414
432
  [self sendNotificationWithEventType:@"select"];
415
433
  }
416
434
 
435
+ - (void)sendLongSelectNotification:(UIGestureRecognizer *)recognizer
436
+ {
437
+ [self sendNotificationWithEventType:@"longSelect"];
438
+ }
439
+
417
440
  - (void)sendNotificationWithEventType:(NSString * __nonnull)eventType
418
441
  {
419
442
  [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTTVNavigationEventNotification"
@@ -288,6 +288,7 @@
288
288
  NSString *_lastEmittedEventName;
289
289
  NSHashTable *_scrollListeners;
290
290
  NSMutableDictionary *_tvRemoteGestureRecognizers;
291
+ BOOL _blockFirstTouch;
291
292
  }
292
293
 
293
294
  - (void)_registerKeyboardListener
@@ -614,6 +615,10 @@ static inline void RCTApplyTransformationAccordingLayoutDirection(
614
615
 
615
616
  - (void)scrollToOffset:(CGPoint)offset animated:(BOOL)animated
616
617
  {
618
+ if ([self reactLayoutDirection] == UIUserInterfaceLayoutDirectionRightToLeft) {
619
+ offset.x = _scrollView.contentSize.width - _scrollView.frame.size.width - offset.x;
620
+ }
621
+
617
622
  if (!CGPointEqualToPoint(_scrollView.contentOffset, offset)) {
618
623
  CGRect maxRect = CGRectMake(
619
624
  fmin(-_scrollView.contentInset.left, 0),
@@ -985,7 +990,11 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
985
990
  [self becomeFirstResponder];
986
991
  [self addSwipeGestureRecognizers];
987
992
  [self sendFocusNotification];
993
+ // if we enter the scroll view from different view then block first touch event since it is the event that triggered the focus
994
+ _blockFirstTouch = (unsigned long)context.focusHeading != 0;
995
+ [self addArrowsListeners];
988
996
  } else if (context.previouslyFocusedView == self) {
997
+ [self removeArrowsListeners];
989
998
  [self sendBlurNotification];
990
999
  [self removeSwipeGestureRecognizers];
991
1000
  [self resignFirstResponder];
@@ -1005,6 +1014,76 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1005
1014
  }
1006
1015
  }
1007
1016
 
1017
+ - (void) addArrowsListeners {
1018
+ [[NSNotificationCenter defaultCenter] addObserver:self
1019
+ selector:@selector(handleTVNavigationEventNotification:)
1020
+ name:@"RCTTVNavigationEventNotification"
1021
+ object:nil];
1022
+ }
1023
+
1024
+ - (void) removeArrowsListeners {
1025
+ [[NSNotificationCenter defaultCenter] removeObserver:self
1026
+ name:@"RCTTVNavigationEventNotification"
1027
+ object:nil];
1028
+ }
1029
+
1030
+
1031
+ - (void)handleTVNavigationEventNotification:(NSNotification *)notif
1032
+ {
1033
+ NSArray *supportedEvents = [NSArray arrayWithObjects:@"up", @"down", @"left", @"right", nil];
1034
+
1035
+ if (notif.object == nil || notif.object[@"eventType"] == nil || ![supportedEvents containsObject:notif.object[@"eventType"]] ) {
1036
+ return;
1037
+ }
1038
+
1039
+ if (_blockFirstTouch) {
1040
+ _blockFirstTouch = NO;
1041
+ return;
1042
+ }
1043
+
1044
+ if (![self isHorizontal:self.scrollView]) {
1045
+ if ([notif.object[@"eventType"] isEqual: @"down"]) {
1046
+ [self swipedDown];
1047
+ return;
1048
+ }
1049
+
1050
+ if ([notif.object[@"eventType"] isEqual: @"up"]) {
1051
+ [self swipedUp];
1052
+ return;
1053
+ }
1054
+ }
1055
+
1056
+ if ([notif.object[@"eventType"] isEqual: @"left"]) {
1057
+ [self swipedLeft];
1058
+ return;
1059
+ }
1060
+
1061
+ if ([notif.object[@"eventType"] isEqual: @"right"]) {
1062
+ [self swipedRight];
1063
+ return;
1064
+ }
1065
+ }
1066
+
1067
+ - (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
1068
+ {
1069
+ // Keep focus inside the scroll view till the end of the content
1070
+ if ([self isHorizontal:self.scrollView]) {
1071
+ if ((context.focusHeading == UIFocusHeadingLeft && self.scrollView.contentOffset.x > 0)
1072
+ || (context.focusHeading == UIFocusHeadingRight && self.scrollView.contentOffset.x < self.scrollView.contentSize.width - self.scrollView.visibleSize.width)
1073
+ ) {
1074
+ return [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem];
1075
+ }
1076
+ } else {
1077
+ if ((context.focusHeading == UIFocusHeadingUp && self.scrollView.contentOffset.y > 0)
1078
+ || (context.focusHeading == UIFocusHeadingDown && self.scrollView.contentOffset.y < self.scrollView.contentSize.height - self.scrollView.visibleSize.height)
1079
+ ) {
1080
+ return [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem];
1081
+ }
1082
+ }
1083
+
1084
+ return [super shouldUpdateFocusInContext:context];
1085
+ }
1086
+
1008
1087
  - (void)sendFocusNotification
1009
1088
  {
1010
1089
  [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTTVNavigationEventNotification"
@@ -1044,6 +1123,7 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1044
1123
 
1045
1124
  - (void)swipeVerticalScrollToOffset:(CGFloat)yOffset
1046
1125
  {
1126
+ _blockFirstTouch = NO;
1047
1127
  dispatch_async(dispatch_get_main_queue(), ^{
1048
1128
  CGFloat limitedOffset = yOffset;
1049
1129
  limitedOffset = MAX(limitedOffset, 0.0);
@@ -1057,6 +1137,7 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
1057
1137
 
1058
1138
  - (void)swipeHorizontalScrollToOffset:(CGFloat)xOffset
1059
1139
  {
1140
+ _blockFirstTouch = NO;
1060
1141
  dispatch_async(dispatch_get_main_queue(), ^{
1061
1142
  CGFloat limitedOffset = xOffset;
1062
1143
  limitedOffset = MAX(limitedOffset, 0.0);
@@ -263,7 +263,7 @@ task createNativeDepsDirectories {
263
263
  }
264
264
 
265
265
  task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) {
266
- src("https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VERSION.replace("_", ".")}/source/boost_${BOOST_VERSION}.tar.gz")
266
+ src("https://archives.boost.io/release/${BOOST_VERSION.replace("_", ".")}/source/boost_${BOOST_VERSION}.tar.gz")
267
267
  onlyIfModified(true)
268
268
  overwrite(false)
269
269
  retries(5)
@@ -1,4 +1,4 @@
1
- VERSION_NAME=0.73.1-3
1
+ VERSION_NAME=0.73.4-0
2
2
 
3
3
  # react.internal.publishingGroup=com.facebook.react
4
4
  # For TV use this group
@@ -113,6 +113,7 @@ public abstract class ReactActivity extends AppCompatActivity
113
113
  @Override
114
114
  public void onRequestPermissionsResult(
115
115
  int requestCode, String[] permissions, int[] grantResults) {
116
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
116
117
  mDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
117
118
  }
118
119
 
@@ -10,6 +10,8 @@ package com.facebook.react.bridge.queue;
10
10
  import android.os.Handler;
11
11
  import android.os.Looper;
12
12
  import android.os.Message;
13
+ import com.facebook.common.logging.FLog;
14
+ import com.facebook.react.common.ReactConstants;
13
15
 
14
16
  /** Handler that can catch and dispatch Exceptions to an Exception handler. */
15
17
  public class MessageQueueThreadHandler extends Handler {
@@ -26,6 +28,15 @@ public class MessageQueueThreadHandler extends Handler {
26
28
  try {
27
29
  super.dispatchMessage(msg);
28
30
  } catch (Exception e) {
31
+ if (e instanceof NullPointerException) {
32
+ FLog.e(
33
+ ReactConstants.TAG,
34
+ "Caught NullPointerException when dispatching message in MessageQueueThreadHandler. This is likely caused by runnable"
35
+ + "(msg.callback) being nulled in Android Handler after dispatching and before handling (see T170239922 for more details)."
36
+ + "Currently we observe that it only happen once which is during initialisation. Due to fixing probably involve Android "
37
+ + "System code, we decide to ignore here for now and print an error message for debugging purpose in case this cause more serious issues in future.");
38
+ return;
39
+ }
29
40
  mExceptionHandler.handleException(e);
30
41
  }
31
42
  }
@@ -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", 73,
20
- "patch", 1,
21
- "prerelease", "3");
20
+ "patch", 4,
21
+ "prerelease", "0");
22
22
  }
@@ -206,7 +206,15 @@ import java.util.Set;
206
206
  }
207
207
  for (String oldKey : keysToNormalize) {
208
208
  Object value = events.get(oldKey);
209
- String newKey = "top" + oldKey.substring(0, 1).toUpperCase() + oldKey.substring(1);
209
+ String baseKey = "";
210
+ if (oldKey.startsWith("on")) {
211
+ // Drop "on" prefix.
212
+ baseKey = oldKey.substring(2);
213
+ } else {
214
+ // Capitalize first letter.
215
+ baseKey = oldKey.substring(0, 1).toUpperCase() + oldKey.substring(1);
216
+ }
217
+ String newKey = "top" + baseKey;
210
218
  events.put(newKey, value);
211
219
  }
212
220
  }
@@ -10,6 +10,8 @@ package com.facebook.react.views.textinput;
10
10
  import static com.facebook.react.uimanager.UIManagerHelper.getReactContext;
11
11
 
12
12
  import android.app.UiModeManager;
13
+ import android.content.ClipData;
14
+ import android.content.ClipboardManager;
13
15
  import android.content.Context;
14
16
  import android.content.res.Configuration;
15
17
  import android.graphics.Color;
@@ -212,7 +214,9 @@ public class ReactEditText extends AppCompatEditText {
212
214
  public void onDestroyActionMode(ActionMode mode) {}
213
215
  };
214
216
  setCustomSelectionActionModeCallback(customActionModeCallback);
215
- setCustomInsertionActionModeCallback(customActionModeCallback);
217
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
218
+ setCustomInsertionActionModeCallback(customActionModeCallback);
219
+ }
216
220
  }
217
221
 
218
222
  @Override
@@ -329,7 +333,26 @@ public class ReactEditText extends AppCompatEditText {
329
333
  @Override
330
334
  public boolean onTextContextMenuItem(int id) {
331
335
  if (id == android.R.id.paste) {
332
- id = android.R.id.pasteAsPlainText;
336
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
337
+ id = android.R.id.pasteAsPlainText;
338
+ } else {
339
+ ClipboardManager clipboard =
340
+ (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
341
+ ClipData previousClipData = clipboard.getPrimaryClip();
342
+ if (previousClipData != null) {
343
+ for (int i = 0; i < previousClipData.getItemCount(); i++) {
344
+ final CharSequence text = previousClipData.getItemAt(i).coerceToText(getContext());
345
+ final CharSequence paste = (text instanceof Spanned) ? text.toString() : text;
346
+ if (paste != null) {
347
+ ClipData clipData = ClipData.newPlainText(null, text);
348
+ clipboard.setPrimaryClip(clipData);
349
+ }
350
+ }
351
+ boolean actionPerformed = super.onTextContextMenuItem(id);
352
+ clipboard.setPrimaryClip(previousClipData);
353
+ return actionPerformed;
354
+ }
355
+ }
333
356
  }
334
357
  return super.onTextContextMenuItem(id);
335
358
  }
@@ -715,8 +738,10 @@ public class ReactEditText extends AppCompatEditText {
715
738
  }
716
739
  mDisableTextDiffing = false;
717
740
 
718
- if (getBreakStrategy() != reactTextUpdate.getTextBreakStrategy()) {
719
- setBreakStrategy(reactTextUpdate.getTextBreakStrategy());
741
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
742
+ if (getBreakStrategy() != reactTextUpdate.getTextBreakStrategy()) {
743
+ setBreakStrategy(reactTextUpdate.getTextBreakStrategy());
744
+ }
720
745
  }
721
746
 
722
747
  // Update cached spans (in Fabric only).
@@ -17,8 +17,8 @@ namespace facebook::react {
17
17
  constexpr struct {
18
18
  int32_t Major = 0;
19
19
  int32_t Minor = 73;
20
- int32_t Patch = 1;
21
- std::string_view Prerelease = "3";
20
+ int32_t Patch = 4;
21
+ std::string_view Prerelease = "0";
22
22
  } ReactNativeVersion;
23
23
 
24
24
  } // namespace facebook::react
@@ -991,7 +991,7 @@ void YogaLayoutableShadowNode::ensureConsistency() const {
991
991
  }
992
992
 
993
993
  void YogaLayoutableShadowNode::ensureYogaChildrenLookFine() const {
994
- #ifdef REACT_NATIVE_DEBUG
994
+ #if defined(REACT_NATIVE_DEBUG) && defined(WITH_FBSYSTRACE)
995
995
  // Checking that the shapes of Yoga node children object look fine.
996
996
  // This is the only heuristic that might produce false-positive results
997
997
  // (really broken dangled nodes might look fine). This is useful as an early
@@ -1009,7 +1009,7 @@ void YogaLayoutableShadowNode::ensureYogaChildrenLookFine() const {
1009
1009
  }
1010
1010
 
1011
1011
  void YogaLayoutableShadowNode::ensureYogaChildrenAlignment() const {
1012
- #ifdef REACT_NATIVE_DEBUG
1012
+ #if defined(REACT_NATIVE_DEBUG) && defined(WITH_FBSYSTRACE)
1013
1013
  // If the node is not a leaf node, checking that:
1014
1014
  // - All children are `YogaLayoutableShadowNode` subclasses.
1015
1015
  // - All Yoga children are owned/connected to corresponding children of
@@ -16,18 +16,26 @@
16
16
 
17
17
  namespace facebook::react {
18
18
 
19
+ static bool hasPrefix(const std::string& str, const std::string& prefix) {
20
+ return str.compare(0, prefix.length(), prefix) == 0;
21
+ }
22
+
19
23
  // TODO(T29874519): Get rid of "top" prefix once and for all.
20
24
  /*
21
- * Capitalizes the first letter of the event type and adds "top" prefix if
22
- * necessary (e.g. "layout" becames "topLayout").
25
+ * Replaces "on" with "top" if present. Or capitalizes the first letter and adds
26
+ * "top" prefix. E.g. "eventName" becomes "topEventName", "onEventName" also
27
+ * becomes "topEventName".
23
28
  */
24
29
  static std::string normalizeEventType(std::string type) {
25
30
  auto prefixedType = std::move(type);
26
- if (prefixedType.find("top", 0) != 0) {
27
- prefixedType.insert(0, "top");
28
- prefixedType[3] = static_cast<char>(toupper(prefixedType[3]));
31
+ if (facebook::react::hasPrefix(prefixedType, "top")) {
32
+ return prefixedType;
33
+ }
34
+ if (facebook::react::hasPrefix(prefixedType, "on")) {
35
+ return "top" + prefixedType.substr(2);
29
36
  }
30
- return prefixedType;
37
+ prefixedType[0] = static_cast<char>(toupper(prefixedType[0]));
38
+ return "top" + prefixedType;
31
39
  }
32
40
 
33
41
  std::mutex& EventEmitter::DispatchMutex() {