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.
- package/Libraries/AppDelegate/RCTAppDelegate.mm +4 -1
- package/Libraries/AppDelegate/RCTRootViewFactory.h +8 -0
- package/Libraries/AppDelegate/RCTRootViewFactory.mm +11 -3
- package/Libraries/Components/Pressable/Pressable.js +11 -7
- package/Libraries/Components/TextInput/TextInput.js +6 -3
- package/Libraries/Core/ReactNativeVersion.js +1 -1
- package/Libraries/Text/TextInput/Multiline/RCTUITextView.mm +6 -0
- package/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h +1 -0
- package/Libraries/Text/TextInput/Singleline/RCTUITextField.mm +5 -0
- package/React/Base/RCTVersion.m +1 -1
- package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +10 -5
- package/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +4 -0
- package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +3 -2
- package/React/Views/ScrollView/RCTScrollView.m +32 -16
- package/ReactAndroid/gradle.properties +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java +2 -4
- package/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java +5 -2
- package/ReactAndroid/src/main/java/com/facebook/react/modules/core/ReactAndroidHWInputDeviceHelper.java +20 -0
- package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java +20 -15
- package/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactSurfaceView.java +5 -0
- package/ReactCommon/cxxreact/ReactNativeVersion.h +1 -1
- package/ReactCommon/jsc/JSCRuntime.cpp +30 -2
- package/ReactCommon/jsi/jsi/decorator.h +7 -0
- package/ReactCommon/jsi/jsi/jsi.h +7 -0
- package/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp +53 -10
- package/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputEventEmitter.cpp +53 -1
- package/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp +0 -1
- package/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Legacy.cpp +3 -3
- package/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp +28 -28
- package/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.h +3 -3
- package/ReactCommon/react/renderer/runtimescheduler/Task.cpp +9 -7
- package/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp +46 -0
- package/cli.js +11 -3
- package/package.json +13 -13
- package/scripts/cocoapods/privacy_manifest_utils.rb +8 -7
- package/scripts/codegen/generate-artifacts-executor.js +1 -1
- package/scripts/ios-configure-glog.sh +9 -2
- package/scripts/react_native_pods.rb +3 -1
- package/sdks/.hermesversion +1 -1
- package/sdks/hermesc/osx-bin/hermes +0 -0
- package/sdks/hermesc/osx-bin/hermesc +0 -0
- package/sdks/hermesc/win64-bin/hermesc.exe +0 -0
- package/template/package.json +6 -6
- package/types/public/ReactNativeTVTypes.d.ts +2 -2
- 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
|
-
|
|
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
|
-
|
|
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 (
|
|
396
|
+
if (evt.eventType === 'select') {
|
|
393
397
|
// $FlowFixMe[incompatible-exact]
|
|
394
398
|
onPress && onPress(evt);
|
|
395
399
|
}
|
|
396
400
|
// $FlowFixMe[prop-missing]
|
|
397
|
-
if (
|
|
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:
|
|
1141
|
+
selection: Selection,
|
|
1143
1142
|
mostRecentEventCount: number,
|
|
1144
|
-
|}>({
|
|
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,
|
|
@@ -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;
|
package/React/Base/RCTVersion.m
CHANGED
|
@@ -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
|
|
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
|
|
1005
|
+
[self scrollToVerticalOffset:0.0];
|
|
1006
1006
|
} else if(context.focusHeading == UIFocusHeadingDown && self.snapToEnd) {
|
|
1007
|
-
[self
|
|
1007
|
+
[self scrollToVerticalOffset:self.scrollView.contentSize.height];
|
|
1008
1008
|
} else if(context.focusHeading == UIFocusHeadingLeft && self.snapToStart) {
|
|
1009
|
-
[self
|
|
1009
|
+
[self scrollToHorizontalOffset:0.0];
|
|
1010
1010
|
} else if(context.focusHeading == UIFocusHeadingRight && self.snapToEnd) {
|
|
1011
|
-
[self
|
|
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)
|
|
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)
|
|
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
|
|
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
|
|
1168
|
+
// NSLog(@"Swiped down to %f", newOffset);
|
|
1169
|
+
[self scrollToVerticalOffset:newOffset];
|
|
1162
1170
|
}
|
|
1163
1171
|
|
|
1164
1172
|
- (void)swipedLeft
|
|
1165
1173
|
{
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
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
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
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
|
|
@@ -433,10 +433,8 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
|
|
|
433
433
|
|
|
434
434
|
if (mShouldLogContentAppeared) {
|
|
435
435
|
mShouldLogContentAppeared = false;
|
|
436
|
-
|
|
437
|
-
|
|
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
|
-
|
|
103
|
-
|
|
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
|
/**
|
|
@@ -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
|
-
|
|
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
|
-
|
|
1528
|
-
|
|
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) {
|
|
@@ -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
|
-
|
|
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
|
|