react-native-tvos 0.76.1-1 → 0.76.2-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/React-RCTAppDelegate.podspec +1 -1
- package/Libraries/Components/Pressable/Pressable.d.ts +8 -0
- package/Libraries/Components/Pressable/Pressable.js +4 -1
- package/Libraries/Core/ReactNativeVersion.js +2 -2
- package/Libraries/Core/setUpErrorHandling.js +1 -7
- package/Libraries/LogBox/Data/LogBoxData.js +2 -2
- package/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h +1 -0
- package/Libraries/Types/CoreEventTypes.d.ts +3 -3
- package/README.md +9 -7
- package/React/Base/RCTTVRemoteHandler.m +0 -19
- package/React/Base/RCTTVRemoteSelectHandler.h +27 -0
- package/React/Base/RCTTVRemoteSelectHandler.m +120 -0
- package/React/Base/RCTVersion.m +2 -2
- package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +12 -8
- package/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +47 -3
- package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.h +8 -0
- package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +35 -42
- package/React/Views/RCTTVView.h +6 -6
- package/React/Views/RCTTVView.m +34 -46
- package/React/Views/ScrollView/RCTScrollView.m +12 -8
- package/ReactAndroid/api/ReactAndroid.api +0 -1
- package/ReactAndroid/cmake-utils/ReactNative-application.cmake +1 -1
- package/ReactAndroid/gradle.properties +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.kt +2 -0
- package/ReactAndroid/src/main/java/com/facebook/react/modules/core/TimingModule.kt +0 -8
- package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java +2 -2
- package/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt +11 -3
- package/ReactCommon/cxxreact/ReactNativeVersion.h +2 -2
- package/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp +3 -2
- package/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.h +12 -1
- package/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm +165 -2
- package/cli.js +1 -1
- package/index.js +0 -4
- package/package.json +8 -8
- package/scripts/codegen/generate-artifacts-executor.js +3 -3
- 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/types/modules/Codegen.d.ts +6 -0
- package/types/public/ReactNativeTVTypes.d.ts +1 -1
- package/Libraries/Components/TabBarIOS/RCTTabBarItemNativeComponent.js +0 -99
- package/Libraries/Components/TabBarIOS/RCTTabBarNativeComponent.js +0 -32
- package/Libraries/Components/TabBarIOS/TabBarIOS.ios.js +0 -59
- package/Libraries/Components/TabBarIOS/TabBarIOS.js +0 -52
- package/Libraries/Components/TabBarIOS/TabBarIOSProps.js +0 -52
- package/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js +0 -177
- package/Libraries/Components/TabBarIOS/TabBarItemIOS.js +0 -55
- package/React/Views/RCTTabBar.h +0 -22
- package/React/Views/RCTTabBar.m +0 -237
- package/React/Views/RCTTabBarItem.h +0 -35
- package/React/Views/RCTTabBarItem.m +0 -139
- package/React/Views/RCTTabBarItemManager.h +0 -12
- package/React/Views/RCTTabBarItemManager.m +0 -38
- package/React/Views/RCTTabBarManager.h +0 -12
- package/React/Views/RCTTabBarManager.m +0 -81
package/React/Views/RCTTVView.h
CHANGED
|
@@ -10,9 +10,10 @@
|
|
|
10
10
|
|
|
11
11
|
#import <React/RCTView.h>
|
|
12
12
|
#import <React/RCTBridge.h>
|
|
13
|
+
#import <React/RCTTVRemoteSelectHandler.h>
|
|
13
14
|
|
|
14
15
|
// A RCTView with additional properties and methods for user interaction using the Apple TV focus engine.
|
|
15
|
-
@interface RCTTVView : RCTView
|
|
16
|
+
@interface RCTTVView : RCTView <RCTTVRemoteSelectHandlerDelegate>
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* TV event handlers
|
|
@@ -29,6 +30,10 @@
|
|
|
29
30
|
*/
|
|
30
31
|
@property (nonatomic, assign) BOOL hasTVPreferredFocus;
|
|
31
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Select and longSelect event handler
|
|
35
|
+
*/
|
|
36
|
+
@property (nonatomic, strong) RCTTVRemoteSelectHandler *tvRemoteSelectHandler;
|
|
32
37
|
/**
|
|
33
38
|
* Focus direction tags
|
|
34
39
|
*/
|
|
@@ -77,11 +82,6 @@
|
|
|
77
82
|
*/
|
|
78
83
|
- (void)sendBlurNotification:(UIFocusUpdateContext *)context;
|
|
79
84
|
|
|
80
|
-
/**
|
|
81
|
-
* Send Select Notification to listeners
|
|
82
|
-
*/
|
|
83
|
-
- (void)sendSelectNotification:(UIGestureRecognizer *)recognizer;
|
|
84
|
-
|
|
85
85
|
/**
|
|
86
86
|
* Adds Parallax Motion Effects if tvParallaxProperty is enabled
|
|
87
87
|
*/
|
package/React/Views/RCTTVView.m
CHANGED
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
|
|
23
23
|
@implementation RCTTVView {
|
|
24
24
|
__weak RCTBridge *_bridge;
|
|
25
|
-
UILongPressGestureRecognizer * _pressRecognizer;
|
|
26
25
|
BOOL motionEffectsAdded;
|
|
27
26
|
NSArray* focusDestinations;
|
|
28
27
|
id<UIFocusItem> previouslyFocusedItem;
|
|
@@ -76,65 +75,49 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
|
|
|
76
75
|
{
|
|
77
76
|
self->_isTVSelectable = isTVSelectable;
|
|
78
77
|
if (isTVSelectable && ![self isTVFocusGuide]) {
|
|
79
|
-
|
|
80
|
-
pressRecognizer.allowedPressTypes = @[ @(UIPressTypeSelect) ];
|
|
81
|
-
pressRecognizer.minimumPressDuration = 0;
|
|
82
|
-
|
|
83
|
-
_pressRecognizer = pressRecognizer;
|
|
84
|
-
|
|
85
|
-
[self addGestureRecognizer:_pressRecognizer];
|
|
78
|
+
self.tvRemoteSelectHandler = [[RCTTVRemoteSelectHandler alloc] initWithView:self];
|
|
86
79
|
} else {
|
|
87
|
-
|
|
88
|
-
[self removeGestureRecognizer:_pressRecognizer];
|
|
89
|
-
}
|
|
80
|
+
self.tvRemoteSelectHandler = nil;
|
|
90
81
|
}
|
|
91
82
|
}
|
|
92
83
|
|
|
93
|
-
- (void)
|
|
84
|
+
- (void)animatePressIn
|
|
94
85
|
{
|
|
95
86
|
if ([self.tvParallaxProperties[@"enabled"] boolValue] == YES) {
|
|
96
|
-
float magnification = [self.tvParallaxProperties[@"magnification"] floatValue];
|
|
97
87
|
float pressMagnification = [self.tvParallaxProperties[@"pressMagnification"] floatValue];
|
|
98
|
-
|
|
99
|
-
// Duration of press animation
|
|
100
88
|
float pressDuration = [self.tvParallaxProperties[@"pressDuration"] floatValue];
|
|
101
|
-
|
|
102
|
-
// Delay of press animation
|
|
103
|
-
float pressDelay = [self.tvParallaxProperties[@"pressDelay"] floatValue];
|
|
104
|
-
|
|
105
|
-
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:pressDelay]];
|
|
106
|
-
|
|
107
89
|
[UIView animateWithDuration:(pressDuration/2)
|
|
108
90
|
animations:^{
|
|
109
91
|
self.transform = CGAffineTransformMakeScale(pressMagnification, pressMagnification);
|
|
110
92
|
}
|
|
111
|
-
completion:^(__unused BOOL
|
|
112
|
-
[UIView animateWithDuration:(pressDuration/2)
|
|
113
|
-
animations:^{
|
|
114
|
-
self.transform = CGAffineTransformMakeScale(magnification, magnification);
|
|
115
|
-
}
|
|
116
|
-
completion:^(__unused BOOL finished2) {
|
|
117
|
-
}];
|
|
118
|
-
}];
|
|
93
|
+
completion:^(__unused BOOL finished){}];
|
|
119
94
|
}
|
|
120
95
|
}
|
|
121
96
|
|
|
122
|
-
- (void)
|
|
97
|
+
- (void) animatePressOut
|
|
123
98
|
{
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
default:
|
|
134
|
-
break;
|
|
99
|
+
if ([self.tvParallaxProperties[@"enabled"] boolValue] == YES) {
|
|
100
|
+
float magnification = [self.tvParallaxProperties[@"magnification"] floatValue];
|
|
101
|
+
float pressDuration = [self.tvParallaxProperties[@"pressDuration"] floatValue];
|
|
102
|
+
|
|
103
|
+
[UIView animateWithDuration:(pressDuration/2)
|
|
104
|
+
animations:^{
|
|
105
|
+
self.transform = CGAffineTransformMakeScale(magnification, magnification);
|
|
106
|
+
}
|
|
107
|
+
completion:^(__unused BOOL finished){}];
|
|
135
108
|
}
|
|
136
109
|
}
|
|
137
110
|
|
|
111
|
+
- (void)emitPressInEvent
|
|
112
|
+
{
|
|
113
|
+
if (self.onPressIn) self.onPressIn(nil);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
- (void)emitPressOutEvent
|
|
117
|
+
{
|
|
118
|
+
if (self.onPressOut) self.onPressOut(nil);
|
|
119
|
+
}
|
|
120
|
+
|
|
138
121
|
- (BOOL)isTVFocusGuide
|
|
139
122
|
{
|
|
140
123
|
return self.focusGuide != nil;
|
|
@@ -441,14 +424,19 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
|
|
|
441
424
|
[[NSNotificationCenter defaultCenter] postNavigationBlurEventWithTag:self.reactTag target:self.reactTag];
|
|
442
425
|
}
|
|
443
426
|
|
|
444
|
-
- (void)sendSelectNotification
|
|
427
|
+
- (void)sendSelectNotification
|
|
445
428
|
{
|
|
446
429
|
[[NSNotificationCenter defaultCenter] postNavigationPressEventWithType:RCTTVRemoteEventSelect keyAction:RCTTVRemoteEventKeyActionUp tag:self.reactTag target:self.reactTag];
|
|
447
430
|
}
|
|
448
431
|
|
|
449
|
-
- (void)
|
|
432
|
+
- (void)sendLongSelectBeganNotification
|
|
433
|
+
{
|
|
434
|
+
[[NSNotificationCenter defaultCenter] postNavigationPressEventWithType:RCTTVRemoteEventLongSelect keyAction:RCTTVRemoteEventKeyActionDown tag:self.reactTag target:self.reactTag];
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
- (void)sendLongSelectEndedNotification
|
|
450
438
|
{
|
|
451
|
-
[[NSNotificationCenter defaultCenter] postNavigationPressEventWithType:RCTTVRemoteEventLongSelect keyAction:
|
|
439
|
+
[[NSNotificationCenter defaultCenter] postNavigationPressEventWithType:RCTTVRemoteEventLongSelect keyAction:RCTTVRemoteEventKeyActionUp tag:self.reactTag target:self.reactTag];
|
|
452
440
|
}
|
|
453
441
|
|
|
454
442
|
- (RCTTVView *)getViewById:(NSNumber *)viewId {
|
|
@@ -468,7 +456,7 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
|
|
|
468
456
|
}
|
|
469
457
|
|
|
470
458
|
- (void)setNextFocusDown:(NSNumber *)nextFocusDown {
|
|
471
|
-
if (self.focusGuideDown != nil && nextFocusDown) {
|
|
459
|
+
if (self.focusGuideDown != nil && nextFocusDown == nil) {
|
|
472
460
|
[[self rootView] removeLayoutGuide:self.focusGuideDown];
|
|
473
461
|
self.focusGuideDown = nil;
|
|
474
462
|
} else {
|
|
@@ -488,7 +476,7 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
|
|
|
488
476
|
}
|
|
489
477
|
|
|
490
478
|
- (void)setNextFocusRight:(NSNumber *)nextFocusRight {
|
|
491
|
-
if (self.focusGuideRight != nil && nextFocusRight) {
|
|
479
|
+
if (self.focusGuideRight != nil && nextFocusRight == nil) {
|
|
492
480
|
[[self rootView] removeLayoutGuide:self.focusGuideRight];
|
|
493
481
|
self.focusGuideRight = nil;
|
|
494
482
|
} else {
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
#if TARGET_OS_TV
|
|
24
24
|
#import "RCTTVRemoteHandler.h"
|
|
25
25
|
#import "RCTTVNavigationEventNotification.h"
|
|
26
|
+
#import "React/RCTI18nUtil.h"
|
|
26
27
|
#endif
|
|
27
28
|
|
|
28
29
|
#if !TARGET_OS_TV
|
|
@@ -1086,21 +1087,24 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
|
|
|
1086
1087
|
|
|
1087
1088
|
- (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
|
|
1088
1089
|
{
|
|
1089
|
-
//
|
|
1090
|
+
// Determine if the layout is Right-to-Left
|
|
1091
|
+
BOOL isRTL = [[RCTI18nUtil sharedInstance] isRTL];
|
|
1092
|
+
// Adjust for horizontal scrolling with RTL support
|
|
1090
1093
|
if ([self isHorizontal:self.scrollView]) {
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
+
BOOL isNavigatingToEnd = (isRTL ? context.focusHeading == UIFocusHeadingLeft : context.focusHeading == UIFocusHeadingRight);
|
|
1095
|
+
BOOL isNavigatingToStart = (isRTL ? context.focusHeading == UIFocusHeadingRight : context.focusHeading == UIFocusHeadingLeft);
|
|
1096
|
+
|
|
1097
|
+
if ((isNavigatingToEnd && self.scrollView.contentOffset.x < self.scrollView.contentSize.width - self.scrollView.visibleSize.width) ||
|
|
1098
|
+
(isNavigatingToStart && self.scrollView.contentOffset.x > 0)) {
|
|
1094
1099
|
return [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem];
|
|
1095
1100
|
}
|
|
1096
1101
|
} else {
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1102
|
+
// Handle vertical scrolling as before
|
|
1103
|
+
if ((context.focusHeading == UIFocusHeadingUp && self.scrollView.contentOffset.y > 0) ||
|
|
1104
|
+
(context.focusHeading == UIFocusHeadingDown && self.scrollView.contentOffset.y < self.scrollView.contentSize.height - self.scrollView.visibleSize.height)) {
|
|
1100
1105
|
return [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem];
|
|
1101
1106
|
}
|
|
1102
1107
|
}
|
|
1103
|
-
|
|
1104
1108
|
return [super shouldUpdateFocusInContext:context];
|
|
1105
1109
|
}
|
|
1106
1110
|
|
|
@@ -3306,7 +3306,6 @@ public final class com/facebook/react/modules/core/TimingModule : com/facebook/f
|
|
|
3306
3306
|
public fun createTimer (DDDZ)V
|
|
3307
3307
|
public fun deleteTimer (D)V
|
|
3308
3308
|
public fun emitTimeDriftWarning (Ljava/lang/String;)V
|
|
3309
|
-
public fun initialize ()V
|
|
3310
3309
|
public fun invalidate ()V
|
|
3311
3310
|
public fun setSendIdleEvents (Z)V
|
|
3312
3311
|
}
|
|
@@ -36,7 +36,7 @@ if(CMAKE_HOST_WIN32)
|
|
|
36
36
|
endif()
|
|
37
37
|
|
|
38
38
|
file(GLOB input_SRC CONFIGURE_DEPENDS
|
|
39
|
-
|
|
39
|
+
${REACT_ANDROID_DIR}/cmake-utils/default-app-setup/*.cpp
|
|
40
40
|
${BUILD_DIR}/generated/autolinking/src/main/jni/*.cpp)
|
|
41
41
|
|
|
42
42
|
add_library(${CMAKE_PROJECT_NAME} SHARED ${input_SRC})
|
|
@@ -65,6 +65,7 @@ public open class JavaTimerManager(
|
|
|
65
65
|
|
|
66
66
|
init {
|
|
67
67
|
reactApplicationContext.addLifecycleEventListener(this)
|
|
68
|
+
HeadlessJsTaskContext.getInstance(reactApplicationContext).addTaskEventListener(this)
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
override fun onHostPause() {
|
|
@@ -103,6 +104,7 @@ public open class JavaTimerManager(
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
public open fun onInstanceDestroy() {
|
|
107
|
+
HeadlessJsTaskContext.getInstance(reactApplicationContext).removeTaskEventListener(this)
|
|
106
108
|
reactApplicationContext.removeLifecycleEventListener(this)
|
|
107
109
|
clearFrameCallback()
|
|
108
110
|
clearChoreographerIdleCallback()
|
|
@@ -12,7 +12,6 @@ import com.facebook.react.bridge.ReactApplicationContext
|
|
|
12
12
|
import com.facebook.react.bridge.WritableArray
|
|
13
13
|
import com.facebook.react.common.annotations.VisibleForTesting
|
|
14
14
|
import com.facebook.react.devsupport.interfaces.DevSupportManager
|
|
15
|
-
import com.facebook.react.jstasks.HeadlessJsTaskContext
|
|
16
15
|
import com.facebook.react.module.annotations.ReactModule
|
|
17
16
|
|
|
18
17
|
/** Native module for JS timer execution. Timers fire on frame boundaries. */
|
|
@@ -24,11 +23,6 @@ public class TimingModule(
|
|
|
24
23
|
private val javaTimerManager: JavaTimerManager =
|
|
25
24
|
JavaTimerManager(reactContext, this, ReactChoreographer.getInstance(), devSupportManager)
|
|
26
25
|
|
|
27
|
-
override fun initialize() {
|
|
28
|
-
HeadlessJsTaskContext.getInstance(getReactApplicationContext())
|
|
29
|
-
.addTaskEventListener(javaTimerManager)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
26
|
override fun createTimer(
|
|
33
27
|
callbackIDDouble: Double,
|
|
34
28
|
durationDouble: Double,
|
|
@@ -68,8 +62,6 @@ public class TimingModule(
|
|
|
68
62
|
}
|
|
69
63
|
|
|
70
64
|
override fun invalidate() {
|
|
71
|
-
val headlessJsTaskContext = HeadlessJsTaskContext.getInstance(getReactApplicationContext())
|
|
72
|
-
headlessJsTaskContext.removeTaskEventListener(javaTimerManager)
|
|
73
65
|
javaTimerManager.onInstanceDestroy()
|
|
74
66
|
}
|
|
75
67
|
|
|
@@ -303,7 +303,14 @@ public class ReactModalHostView(context: ThemedReactContext) :
|
|
|
303
303
|
* changed. This has the pleasant side-effect of us not having to preface all Modals with "top:
|
|
304
304
|
* statusBarHeight", since that margin will be included in the FrameLayout.
|
|
305
305
|
*/
|
|
306
|
-
get() =
|
|
306
|
+
get() =
|
|
307
|
+
FrameLayout(context).apply {
|
|
308
|
+
addView(dialogRootViewGroup)
|
|
309
|
+
if (!statusBarTranslucent) {
|
|
310
|
+
// this is needed to prevent content hiding behind systems bars < API 30
|
|
311
|
+
this.fitsSystemWindows = true
|
|
312
|
+
}
|
|
313
|
+
}
|
|
307
314
|
|
|
308
315
|
/**
|
|
309
316
|
* updateProperties will update the properties that do not require us to recreate the dialog
|
|
@@ -401,8 +408,9 @@ public class ReactModalHostView(context: ThemedReactContext) :
|
|
|
401
408
|
private var viewHeight = 0
|
|
402
409
|
private val jSTouchDispatcher: JSTouchDispatcher = JSTouchDispatcher(this)
|
|
403
410
|
private var jSPointerDispatcher: JSPointerDispatcher? = null
|
|
404
|
-
internal val androidHWInputDeviceHelper: ReactAndroidHWInputDeviceHelper
|
|
405
|
-
|
|
411
|
+
internal val androidHWInputDeviceHelper: ReactAndroidHWInputDeviceHelper by lazy {
|
|
412
|
+
ReactAndroidHWInputDeviceHelper()
|
|
413
|
+
}
|
|
406
414
|
|
|
407
415
|
internal val reactContext: ThemedReactContext
|
|
408
416
|
get() = context as ThemedReactContext
|
|
@@ -17,8 +17,8 @@ namespace facebook::react {
|
|
|
17
17
|
constexpr struct {
|
|
18
18
|
int32_t Major = 0;
|
|
19
19
|
int32_t Minor = 76;
|
|
20
|
-
int32_t Patch =
|
|
21
|
-
std::string_view Prerelease = "
|
|
20
|
+
int32_t Patch = 2;
|
|
21
|
+
std::string_view Prerelease = "0";
|
|
22
22
|
} ReactNativeVersion;
|
|
23
23
|
|
|
24
24
|
} // namespace facebook::react
|
|
@@ -83,7 +83,7 @@ AttributedString TextInputShadowNode::getAttributedString(
|
|
|
83
83
|
.string = getConcreteProps().text,
|
|
84
84
|
.textAttributes = textAttributes,
|
|
85
85
|
// TODO: Is this really meant to be by value?
|
|
86
|
-
.parentShadowView = ShadowView
|
|
86
|
+
.parentShadowView = ShadowView(*this)});
|
|
87
87
|
|
|
88
88
|
auto attachments = Attachments{};
|
|
89
89
|
BaseTextShadowNode::buildAttributedString(
|
|
@@ -110,7 +110,8 @@ void TextInputShadowNode::updateStateIfNeeded(
|
|
|
110
110
|
(!state.layoutManager || state.layoutManager == textLayoutManager_) &&
|
|
111
111
|
"`StateData` refers to a different `TextLayoutManager`");
|
|
112
112
|
|
|
113
|
-
if (state.reactTreeAttributedString
|
|
113
|
+
if (state.reactTreeAttributedString.isContentEqual(
|
|
114
|
+
reactTreeAttributedString) &&
|
|
114
115
|
state.layoutManager == textLayoutManager_) {
|
|
115
116
|
return;
|
|
116
117
|
}
|
|
@@ -22,7 +22,7 @@ NSString *const RCTTextAttributesAccessibilityRoleAttributeName = @"Accessibilit
|
|
|
22
22
|
/*
|
|
23
23
|
* Creates `NSTextAttributes` from given `facebook::react::TextAttributes`
|
|
24
24
|
*/
|
|
25
|
-
|
|
25
|
+
NSMutableDictionary<NSAttributedStringKey, id> *RCTNSTextAttributesFromTextAttributes(
|
|
26
26
|
const facebook::react::TextAttributes &textAttributes);
|
|
27
27
|
|
|
28
28
|
/*
|
|
@@ -41,6 +41,17 @@ NSString *RCTNSStringFromStringApplyingTextTransform(NSString *string, facebook:
|
|
|
41
41
|
|
|
42
42
|
void RCTApplyBaselineOffset(NSMutableAttributedString *attributedText);
|
|
43
43
|
|
|
44
|
+
/*
|
|
45
|
+
* Whether two `NSAttributedString` lead to the same underlying displayed text, even if they are not strictly equal.
|
|
46
|
+
* I.e. is one string substitutable for the other when backing a control (which may have some ignorable attributes
|
|
47
|
+
* provided).
|
|
48
|
+
*/
|
|
49
|
+
BOOL RCTIsAttributedStringEffectivelySame(
|
|
50
|
+
NSAttributedString *text1,
|
|
51
|
+
NSAttributedString *text2,
|
|
52
|
+
NSDictionary<NSAttributedStringKey, id> *insensitiveAttributes,
|
|
53
|
+
const facebook::react::TextAttributes &baseTextAttributes);
|
|
54
|
+
|
|
44
55
|
@interface RCTWeakEventEmitterWrapper : NSObject
|
|
45
56
|
@property (nonatomic, assign) facebook::react::SharedEventEmitter eventEmitter;
|
|
46
57
|
@end
|
|
@@ -35,6 +35,24 @@ using namespace facebook::react;
|
|
|
35
35
|
_weakEventEmitter.reset();
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
- (BOOL)isEqual:(id)object
|
|
39
|
+
{
|
|
40
|
+
// We consider the underlying EventEmitter as the identity
|
|
41
|
+
if (![object isKindOfClass:[self class]]) {
|
|
42
|
+
return NO;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
auto thisEventEmitter = [self eventEmitter];
|
|
46
|
+
auto otherEventEmitter = [((RCTWeakEventEmitterWrapper *)object) eventEmitter];
|
|
47
|
+
return thisEventEmitter == otherEventEmitter;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
- (NSUInteger)hash
|
|
51
|
+
{
|
|
52
|
+
// We consider the underlying EventEmitter as the identity
|
|
53
|
+
return (NSUInteger)_weakEventEmitter.lock().get();
|
|
54
|
+
}
|
|
55
|
+
|
|
38
56
|
@end
|
|
39
57
|
|
|
40
58
|
inline static UIFontWeight RCTUIFontWeightFromInteger(NSInteger fontWeight)
|
|
@@ -182,7 +200,8 @@ inline static UIColor *RCTEffectiveBackgroundColorFromTextAttributes(const TextA
|
|
|
182
200
|
return effectiveBackgroundColor ?: [UIColor clearColor];
|
|
183
201
|
}
|
|
184
202
|
|
|
185
|
-
|
|
203
|
+
NSMutableDictionary<NSAttributedStringKey, id> *RCTNSTextAttributesFromTextAttributes(
|
|
204
|
+
const TextAttributes &textAttributes)
|
|
186
205
|
{
|
|
187
206
|
NSMutableDictionary<NSAttributedStringKey, id> *attributes = [NSMutableDictionary dictionaryWithCapacity:10];
|
|
188
207
|
|
|
@@ -306,7 +325,7 @@ NSDictionary<NSAttributedStringKey, id> *RCTNSTextAttributesFromTextAttributes(c
|
|
|
306
325
|
attributes[RCTTextAttributesAccessibilityRoleAttributeName] = [NSString stringWithUTF8String:roleStr.c_str()];
|
|
307
326
|
}
|
|
308
327
|
|
|
309
|
-
return
|
|
328
|
+
return attributes;
|
|
310
329
|
}
|
|
311
330
|
|
|
312
331
|
void RCTApplyBaselineOffset(NSMutableAttributedString *attributedText)
|
|
@@ -470,3 +489,147 @@ NSString *RCTNSStringFromStringApplyingTextTransform(NSString *string, TextTrans
|
|
|
470
489
|
return string;
|
|
471
490
|
}
|
|
472
491
|
}
|
|
492
|
+
|
|
493
|
+
static BOOL RCTIsParagraphStyleEffectivelySame(
|
|
494
|
+
NSParagraphStyle *style1,
|
|
495
|
+
NSParagraphStyle *style2,
|
|
496
|
+
const TextAttributes &baseTextAttributes)
|
|
497
|
+
{
|
|
498
|
+
if (style1 == nil || style2 == nil) {
|
|
499
|
+
return style1 == nil && style2 == nil;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// The NSParagraphStyle included as part of typingAttributes may eventually resolve "natural" directions to
|
|
503
|
+
// physical direction, so we should compare resolved directions
|
|
504
|
+
auto naturalAlignment =
|
|
505
|
+
baseTextAttributes.layoutDirection.value_or(LayoutDirection::LeftToRight) == LayoutDirection::LeftToRight
|
|
506
|
+
? NSTextAlignmentLeft
|
|
507
|
+
: NSTextAlignmentRight;
|
|
508
|
+
|
|
509
|
+
NSWritingDirection naturalBaseWritingDirection = baseTextAttributes.baseWritingDirection.has_value()
|
|
510
|
+
? RCTNSWritingDirectionFromWritingDirection(baseTextAttributes.baseWritingDirection.value())
|
|
511
|
+
: [NSParagraphStyle defaultWritingDirectionForLanguage:nil];
|
|
512
|
+
|
|
513
|
+
if (style1.alignment == NSTextAlignmentNatural || style1.baseWritingDirection == NSWritingDirectionNatural) {
|
|
514
|
+
NSMutableParagraphStyle *mutableStyle1 = [style1 mutableCopy];
|
|
515
|
+
style1 = mutableStyle1;
|
|
516
|
+
|
|
517
|
+
if (mutableStyle1.alignment == NSTextAlignmentNatural) {
|
|
518
|
+
mutableStyle1.alignment = naturalAlignment;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (mutableStyle1.baseWritingDirection == NSWritingDirectionNatural) {
|
|
522
|
+
mutableStyle1.baseWritingDirection = naturalBaseWritingDirection;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (style2.alignment == NSTextAlignmentNatural || style2.baseWritingDirection == NSWritingDirectionNatural) {
|
|
527
|
+
NSMutableParagraphStyle *mutableStyle2 = [style2 mutableCopy];
|
|
528
|
+
style2 = mutableStyle2;
|
|
529
|
+
|
|
530
|
+
if (mutableStyle2.alignment == NSTextAlignmentNatural) {
|
|
531
|
+
mutableStyle2.alignment = naturalAlignment;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (mutableStyle2.baseWritingDirection == NSWritingDirectionNatural) {
|
|
535
|
+
mutableStyle2.baseWritingDirection = naturalBaseWritingDirection;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
return [style1 isEqual:style2];
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
static BOOL RCTIsAttributeEffectivelySame(
|
|
543
|
+
NSAttributedStringKey attributeKey,
|
|
544
|
+
NSDictionary<NSAttributedStringKey, id> *attributes1,
|
|
545
|
+
NSDictionary<NSAttributedStringKey, id> *attributes2,
|
|
546
|
+
NSDictionary<NSAttributedStringKey, id> *insensitiveAttributes,
|
|
547
|
+
const TextAttributes &baseTextAttributes)
|
|
548
|
+
{
|
|
549
|
+
id attribute1 = attributes1[attributeKey] ?: insensitiveAttributes[attributeKey];
|
|
550
|
+
id attribute2 = attributes2[attributeKey] ?: insensitiveAttributes[attributeKey];
|
|
551
|
+
|
|
552
|
+
// Normalize attributes which can inexact but still effectively the same
|
|
553
|
+
if ([attributeKey isEqualToString:NSParagraphStyleAttributeName]) {
|
|
554
|
+
return RCTIsParagraphStyleEffectivelySame(attribute1, attribute2, baseTextAttributes);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Otherwise rely on built-in comparison
|
|
558
|
+
return [attribute1 isEqual:attribute2];
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
BOOL RCTIsAttributedStringEffectivelySame(
|
|
562
|
+
NSAttributedString *text1,
|
|
563
|
+
NSAttributedString *text2,
|
|
564
|
+
NSDictionary<NSAttributedStringKey, id> *insensitiveAttributes,
|
|
565
|
+
const TextAttributes &baseTextAttributes)
|
|
566
|
+
{
|
|
567
|
+
if (![text1.string isEqualToString:text2.string]) {
|
|
568
|
+
return NO;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// We check that for every fragment in the old string
|
|
572
|
+
// 1. The new string's fragment overlapping the first spans the same characters
|
|
573
|
+
// 2. The attributes of each matching fragment are the same, ignoring those which match insensitive attibutes
|
|
574
|
+
__block BOOL areAttributesSame = YES;
|
|
575
|
+
[text1 enumerateAttributesInRange:NSMakeRange(0, text1.length)
|
|
576
|
+
options:0
|
|
577
|
+
usingBlock:^(
|
|
578
|
+
NSDictionary<NSAttributedStringKey, id> *text1Attributes,
|
|
579
|
+
NSRange text1Range,
|
|
580
|
+
BOOL *text1Stop) {
|
|
581
|
+
[text2 enumerateAttributesInRange:text1Range
|
|
582
|
+
options:0
|
|
583
|
+
usingBlock:^(
|
|
584
|
+
NSDictionary<NSAttributedStringKey, id> *text2Attributes,
|
|
585
|
+
NSRange text2Range,
|
|
586
|
+
BOOL *text2Stop) {
|
|
587
|
+
if (!NSEqualRanges(text1Range, text2Range)) {
|
|
588
|
+
areAttributesSame = NO;
|
|
589
|
+
*text1Stop = YES;
|
|
590
|
+
*text2Stop = YES;
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Compare every attribute in text1 to the corresponding attribute
|
|
595
|
+
// in text2, or the set of insensitive attributes if not present
|
|
596
|
+
for (NSAttributedStringKey key in text1Attributes) {
|
|
597
|
+
if (!RCTIsAttributeEffectivelySame(
|
|
598
|
+
key,
|
|
599
|
+
text1Attributes,
|
|
600
|
+
text2Attributes,
|
|
601
|
+
insensitiveAttributes,
|
|
602
|
+
baseTextAttributes)) {
|
|
603
|
+
areAttributesSame = NO;
|
|
604
|
+
*text1Stop = YES;
|
|
605
|
+
*text2Stop = YES;
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
for (NSAttributedStringKey key in text2Attributes) {
|
|
611
|
+
// We have already compared this attribute if it is present in
|
|
612
|
+
// both
|
|
613
|
+
if (text1Attributes[key] != nil) {
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// But we still need to compare attributes if it is only present
|
|
618
|
+
// in text 2, to compare against insensitive attributes
|
|
619
|
+
if (!RCTIsAttributeEffectivelySame(
|
|
620
|
+
key,
|
|
621
|
+
text1Attributes,
|
|
622
|
+
text2Attributes,
|
|
623
|
+
insensitiveAttributes,
|
|
624
|
+
baseTextAttributes)) {
|
|
625
|
+
areAttributesSame = NO;
|
|
626
|
+
*text1Stop = YES;
|
|
627
|
+
*text2Stop = YES;
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}];
|
|
632
|
+
}];
|
|
633
|
+
|
|
634
|
+
return areAttributesSame;
|
|
635
|
+
}
|
package/cli.js
CHANGED
package/index.js
CHANGED
|
@@ -89,7 +89,6 @@ import typeof DevSettings from './Libraries/Utilities/DevSettings';
|
|
|
89
89
|
import typeof Dimensions from './Libraries/Utilities/Dimensions';
|
|
90
90
|
import typeof PixelRatio from './Libraries/Utilities/PixelRatio';
|
|
91
91
|
import typeof Platform from './Libraries/Utilities/Platform';
|
|
92
|
-
// import typeof TabBarIOS from './Libraries/Components/TabBarIOS/TabBarIOS';
|
|
93
92
|
import typeof TVEventHandler from './Libraries/Components/TV/TVEventHandler';
|
|
94
93
|
import typeof TVFocusGuideView from './Libraries/Components/TV/TVFocusGuideView';
|
|
95
94
|
import typeof TVEventControl from './Libraries/Components/TV/TVEventControl';
|
|
@@ -176,9 +175,6 @@ module.exports = {
|
|
|
176
175
|
get Switch(): Switch {
|
|
177
176
|
return require('./Libraries/Components/Switch/Switch').default;
|
|
178
177
|
},
|
|
179
|
-
get TabBarIOS(): any {
|
|
180
|
-
return require('./Libraries/Components/TabBarIOS/TabBarIOS');
|
|
181
|
-
},
|
|
182
178
|
get Text(): Text {
|
|
183
179
|
return require('./Libraries/Text/Text');
|
|
184
180
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-tvos",
|
|
3
|
-
"version": "0.76.
|
|
3
|
+
"version": "0.76.2-0",
|
|
4
4
|
"description": "A framework for building native apps using React",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -110,13 +110,13 @@
|
|
|
110
110
|
},
|
|
111
111
|
"dependencies": {
|
|
112
112
|
"@jest/create-cache-key-function": "^29.6.3",
|
|
113
|
-
"@react-native/assets-registry": "0.76.
|
|
114
|
-
"@react-native/codegen": "0.76.
|
|
115
|
-
"@react-native/community-cli-plugin": "0.76.
|
|
116
|
-
"@react-native/gradle-plugin": "0.76.
|
|
117
|
-
"@react-native/js-polyfills": "0.76.
|
|
118
|
-
"@react-native/normalize-colors": "0.76.
|
|
119
|
-
"@react-native-tvos/virtualized-lists": "0.76.
|
|
113
|
+
"@react-native/assets-registry": "0.76.2",
|
|
114
|
+
"@react-native/codegen": "0.76.2",
|
|
115
|
+
"@react-native/community-cli-plugin": "0.76.2",
|
|
116
|
+
"@react-native/gradle-plugin": "0.76.2",
|
|
117
|
+
"@react-native/js-polyfills": "0.76.2",
|
|
118
|
+
"@react-native/normalize-colors": "0.76.2",
|
|
119
|
+
"@react-native-tvos/virtualized-lists": "0.76.2-0",
|
|
120
120
|
"abort-controller": "^3.0.0",
|
|
121
121
|
"anser": "^1.4.9",
|
|
122
122
|
"ansi-regex": "^5.0.0",
|
|
@@ -493,7 +493,7 @@ function rootCodegenTargetNeedsThirdPartyComponentProvider(pkgJson, platform) {
|
|
|
493
493
|
function dependencyNeedsThirdPartyComponentProvider(
|
|
494
494
|
schemaInfo,
|
|
495
495
|
platform,
|
|
496
|
-
|
|
496
|
+
appCodegenConfigSpec,
|
|
497
497
|
) {
|
|
498
498
|
// Filter the react native core library out.
|
|
499
499
|
// In the future, core library and third party library should
|
|
@@ -504,7 +504,7 @@ function dependencyNeedsThirdPartyComponentProvider(
|
|
|
504
504
|
// the symbols defined in the app.
|
|
505
505
|
return (
|
|
506
506
|
!isReactNativeCoreLibrary(schemaInfo.library.config.name, platform) &&
|
|
507
|
-
schemaInfo.library.config.name !==
|
|
507
|
+
schemaInfo.library.config.name !== appCodegenConfigSpec
|
|
508
508
|
);
|
|
509
509
|
}
|
|
510
510
|
|
|
@@ -720,7 +720,7 @@ function execute(projectRoot, targetPlatform, baseOutputPath) {
|
|
|
720
720
|
dependencyNeedsThirdPartyComponentProvider(
|
|
721
721
|
schemaInfo,
|
|
722
722
|
platform,
|
|
723
|
-
pkgJson.codegenConfig?.
|
|
723
|
+
pkgJson.codegenConfig?.name,
|
|
724
724
|
),
|
|
725
725
|
);
|
|
726
726
|
const schemas = filteredSchemas.map(schemaInfo => schemaInfo.schema);
|