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.
- package/Libraries/AppDelegate/RCTAppDelegate.h +0 -2
- package/Libraries/AppDelegate/RCTAppDelegate.mm +20 -13
- package/Libraries/Components/Touchable/TouchableBounce.js +4 -0
- package/Libraries/Components/Touchable/TouchableHighlight.js +5 -1
- package/Libraries/Components/Touchable/TouchableNativeFeedback.js +1 -0
- package/Libraries/Components/Touchable/TouchableOpacity.js +1 -0
- package/Libraries/Components/Touchable/TouchableWithoutFeedback.js +1 -0
- package/Libraries/Core/ReactNativeVersion.js +2 -2
- package/Libraries/Inspector/NetworkOverlay.js +1 -1
- package/Libraries/Lists/FillRateHelper.js +3 -3
- package/Libraries/Lists/FlatList.d.ts +1 -1
- package/Libraries/Lists/FlatList.js +2 -2
- package/Libraries/Lists/SectionList.d.ts +1 -1
- package/Libraries/Lists/SectionList.js +2 -2
- package/Libraries/Lists/SectionListModern.js +2 -2
- package/Libraries/Lists/ViewabilityHelper.js +3 -3
- package/Libraries/Lists/VirtualizeUtils.js +2 -2
- package/Libraries/Lists/VirtualizedList.js +9 -2094
- package/Libraries/Lists/VirtualizedListContext.js +2 -2
- package/Libraries/Lists/VirtualizedSectionList.js +3 -3
- package/Libraries/Modal/Modal.js +1 -1
- package/Libraries/Utilities/ReactNativeTestTools.js +1 -1
- package/Libraries/promiseRejectionTrackingOptions.js +5 -0
- package/README.md +11 -10
- package/React/Base/RCTVersion.m +2 -2
- package/React/CoreModules/RCTDeviceInfo.mm +33 -0
- package/React/CoreModules/RCTRedBox.mm +17 -0
- package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +24 -1
- package/React/Modules/RCTUIManager.m +9 -3
- package/React/Views/RCTTVView.m +23 -0
- package/React/Views/ScrollView/RCTScrollView.m +81 -0
- package/ReactAndroid/build.gradle +1 -1
- package/ReactAndroid/gradle.properties +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java +1 -0
- package/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/MessageQueueThreadHandler.java +11 -0
- package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java +2 -2
- package/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java +9 -1
- package/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java +29 -4
- package/ReactCommon/cxxreact/ReactNativeVersion.h +2 -2
- package/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp +2 -2
- package/ReactCommon/react/renderer/core/EventEmitter.cpp +14 -6
- package/ReactCommon/react/renderer/debug/flags.h +2 -4
- package/ReactCommon/react/renderer/debug/tests/DebugStringConvertibleTest.cpp +2 -0
- package/ReactCommon/react/renderer/templateprocessor/tests/UITemplateProcessorTest.cpp +4 -0
- package/package.json +11 -10
- package/scripts/cocoapods/utils.rb +4 -40
- package/scripts/react-native-xcode.sh +1 -1
- package/scripts/react_native_pods.rb +1 -2
- package/sdks/.hermesversion +1 -1
- package/sdks/hermes-engine/utils/replace_hermes_version.js +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/Gemfile +4 -2
- package/template/metro.config.js +23 -2
- package/template/package.json +5 -5
- package/third-party-podspecs/boost.podspec +1 -1
- 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;
|
package/Libraries/Modal/Modal.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
79
|
+
Expo SDK 50 or greater, and react-native-tvos 0.73.x or later, are required.
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
## _(New)_ How to support TV specific file extensions
|
|
82
82
|
|
|
83
|
-
|
|
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
|
-
|
|
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
|
-
|
|
91
|
-
|
|
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
|
|
package/React/Base/RCTVersion.m
CHANGED
|
@@ -23,8 +23,8 @@ NSDictionary* RCTGetReactNativeVersion(void)
|
|
|
23
23
|
__rnVersion = @{
|
|
24
24
|
RCTVersionMajor: @(0),
|
|
25
25
|
RCTVersionMinor: @(73),
|
|
26
|
-
RCTVersionPatch: @(
|
|
27
|
-
RCTVersionPrerelease: @"
|
|
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
|
-
|
|
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:
|
|
189
|
-
lazilyLoadIfNecessary:YES]];
|
|
195
|
+
object:a11yManagerStrongObject];
|
|
190
196
|
});
|
|
191
197
|
#if !TARGET_OS_TV
|
|
192
198
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
package/React/Views/RCTTVView.m
CHANGED
|
@@ -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://
|
|
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)
|
|
@@ -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
|
|
package/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/MessageQueueThreadHandler.java
CHANGED
|
@@ -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
|
}
|
package/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
719
|
-
|
|
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 =
|
|
21
|
-
std::string_view Prerelease = "
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
*
|
|
22
|
-
*
|
|
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
|
|
27
|
-
prefixedType
|
|
28
|
-
|
|
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
|
-
|
|
37
|
+
prefixedType[0] = static_cast<char>(toupper(prefixedType[0]));
|
|
38
|
+
return "top" + prefixedType;
|
|
31
39
|
}
|
|
32
40
|
|
|
33
41
|
std::mutex& EventEmitter::DispatchMutex() {
|