react-native-tvos 0.71.10-0 → 0.71.11-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.
@@ -1062,6 +1062,18 @@ const emptyFunctionThatReturnsTrue = () => true;
1062
1062
  *
1063
1063
  */
1064
1064
  function InternalTextInput(props: Props): React.Node {
1065
+ const {
1066
+ 'aria-busy': ariaBusy,
1067
+ 'aria-checked': ariaChecked,
1068
+ 'aria-disabled': ariaDisabled,
1069
+ 'aria-expanded': ariaExpanded,
1070
+ 'aria-selected': ariaSelected,
1071
+ accessibilityState,
1072
+ id,
1073
+ tabIndex,
1074
+ ...otherProps
1075
+ } = props;
1076
+
1065
1077
  const inputRef = useRef<null | React.ElementRef<HostComponent<mixed>>>(null);
1066
1078
 
1067
1079
  // Android sends a "onTextChanged" event followed by a "onSelectionChanged" event, for
@@ -1382,13 +1394,25 @@ function InternalTextInput(props: Props): React.Node {
1382
1394
  // so omitting onBlur and onFocus pressability handlers here.
1383
1395
  const {onBlur, onFocus, ...eventHandlers} = usePressability(config) || {};
1384
1396
 
1385
- const _accessibilityState = {
1386
- busy: props['aria-busy'] ?? props.accessibilityState?.busy,
1387
- checked: props['aria-checked'] ?? props.accessibilityState?.checked,
1388
- disabled: props['aria-disabled'] ?? props.accessibilityState?.disabled,
1389
- expanded: props['aria-expanded'] ?? props.accessibilityState?.expanded,
1390
- selected: props['aria-selected'] ?? props.accessibilityState?.selected,
1391
- };
1397
+ let _accessibilityState;
1398
+ if (
1399
+ accessibilityState != null ||
1400
+ ariaBusy != null ||
1401
+ ariaChecked != null ||
1402
+ ariaDisabled != null ||
1403
+ ariaExpanded != null ||
1404
+ ariaSelected != null
1405
+ ) {
1406
+ _accessibilityState = {
1407
+ busy: ariaBusy ?? accessibilityState?.busy,
1408
+ checked: ariaChecked ?? accessibilityState?.checked,
1409
+ disabled: ariaDisabled ?? accessibilityState?.disabled,
1410
+ expanded: ariaExpanded ?? accessibilityState?.expanded,
1411
+ selected: ariaSelected ?? accessibilityState?.selected,
1412
+ };
1413
+ }
1414
+
1415
+ let style = flattenStyle(props.style);
1392
1416
 
1393
1417
  if (Platform.OS === 'ios') {
1394
1418
  const RCTTextInputView =
@@ -1396,10 +1420,7 @@ function InternalTextInput(props: Props): React.Node {
1396
1420
  ? RCTMultilineTextInputView
1397
1421
  : RCTSinglelineTextInputView;
1398
1422
 
1399
- const style =
1400
- props.multiline === true
1401
- ? StyleSheet.flatten([styles.multilineInput, props.style])
1402
- : props.style;
1423
+ style = props.multiline === true ? [styles.multilineInput, style] : style;
1403
1424
 
1404
1425
  const useOnChangeSync =
1405
1426
  (props.unstable_onChangeSync || props.unstable_onChangeTextSync) &&
@@ -1415,15 +1436,16 @@ function InternalTextInput(props: Props): React.Node {
1415
1436
  textInput = (
1416
1437
  <RCTTextInputView
1417
1438
  ref={_setNativeRef}
1418
- {...props}
1439
+ {...otherProps}
1419
1440
  {...eventHandlers}
1420
- accessible={accessible}
1421
1441
  accessibilityState={_accessibilityState}
1442
+ accessible={accessible}
1422
1443
  submitBehavior={submitBehavior}
1423
1444
  caretHidden={caretHidden}
1424
1445
  dataDetectorTypes={props.dataDetectorTypes}
1425
- focusable={focusable}
1446
+ focusable={tabIndex !== undefined ? !tabIndex : focusable}
1426
1447
  mostRecentEventCount={mostRecentEventCount}
1448
+ nativeID={id ?? props.nativeID}
1427
1449
  onBlur={_onBlur}
1428
1450
  onKeyPressSync={props.unstable_onKeyPressSync}
1429
1451
  onChange={_onChange}
@@ -1439,7 +1461,6 @@ function InternalTextInput(props: Props): React.Node {
1439
1461
  />
1440
1462
  );
1441
1463
  } else if (Platform.OS === 'android') {
1442
- const style = [props.style];
1443
1464
  const autoCapitalize = props.autoCapitalize || 'sentences';
1444
1465
  const _accessibilityLabelledBy =
1445
1466
  props?.['aria-labelledby'] ?? props?.accessibilityLabelledBy;
@@ -1465,18 +1486,19 @@ function InternalTextInput(props: Props): React.Node {
1465
1486
  * fixed */
1466
1487
  <AndroidTextInput
1467
1488
  ref={_setNativeRef}
1468
- {...props}
1489
+ {...otherProps}
1469
1490
  {...eventHandlers}
1470
- accessible={accessible}
1471
1491
  accessibilityState={_accessibilityState}
1472
1492
  accessibilityLabelledBy={_accessibilityLabelledBy}
1493
+ accessible={accessible}
1473
1494
  autoCapitalize={autoCapitalize}
1474
1495
  submitBehavior={submitBehavior}
1475
1496
  caretHidden={caretHidden}
1476
1497
  children={children}
1477
1498
  disableFullscreenUI={props.disableFullscreenUI}
1478
- focusable={focusable}
1499
+ focusable={tabIndex !== undefined ? !tabIndex : focusable}
1479
1500
  mostRecentEventCount={mostRecentEventCount}
1501
+ nativeID={id ?? props.nativeID}
1480
1502
  numberOfLines={props.rows ?? props.numberOfLines}
1481
1503
  onBlur={_onBlur}
1482
1504
  onChange={_onChange}
@@ -1606,11 +1628,12 @@ const ExportedForwardRef: React.AbstractComponent<
1606
1628
  React.ElementRef<HostComponent<mixed>> & ImperativeMethods,
1607
1629
  >,
1608
1630
  ) {
1609
- const style = flattenStyle(restProps.style);
1631
+ let style = flattenStyle(restProps.style);
1610
1632
 
1611
1633
  if (style?.verticalAlign != null) {
1612
1634
  style.textAlignVertical =
1613
1635
  verticalAlignToTextAlignVerticalMap[style.verticalAlign];
1636
+ delete style.verticalAlign;
1614
1637
  }
1615
1638
 
1616
1639
  return (
@@ -1651,6 +1674,8 @@ const ExportedForwardRef: React.AbstractComponent<
1651
1674
  );
1652
1675
  });
1653
1676
 
1677
+ ExportedForwardRef.displayName = 'TextInput';
1678
+
1654
1679
  /**
1655
1680
  * Switch to `deprecated-react-native-prop-types` for compatibility with future
1656
1681
  * releases. This is deprecated and will be removed in the future.
@@ -64,7 +64,6 @@ const View: React.AbstractComponent<
64
64
  nativeID,
65
65
  pointerEvents,
66
66
  role,
67
- style,
68
67
  tabIndex,
69
68
  ...otherProps
70
69
  }: ViewProps,
@@ -73,23 +72,42 @@ const View: React.AbstractComponent<
73
72
  const _accessibilityLabelledBy =
74
73
  ariaLabelledBy?.split(/\s*,\s*/g) ?? accessibilityLabelledBy;
75
74
 
76
- const _accessibilityState = {
77
- busy: ariaBusy ?? accessibilityState?.busy,
78
- checked: ariaChecked ?? accessibilityState?.checked,
79
- disabled: ariaDisabled ?? accessibilityState?.disabled,
80
- expanded: ariaExpanded ?? accessibilityState?.expanded,
81
- selected: ariaSelected ?? accessibilityState?.selected,
82
- };
75
+ let _accessibilityState;
76
+ if (
77
+ accessibilityState != null ||
78
+ ariaBusy != null ||
79
+ ariaChecked != null ||
80
+ ariaDisabled != null ||
81
+ ariaExpanded != null ||
82
+ ariaSelected != null
83
+ ) {
84
+ _accessibilityState = {
85
+ busy: ariaBusy ?? accessibilityState?.busy,
86
+ checked: ariaChecked ?? accessibilityState?.checked,
87
+ disabled: ariaDisabled ?? accessibilityState?.disabled,
88
+ expanded: ariaExpanded ?? accessibilityState?.expanded,
89
+ selected: ariaSelected ?? accessibilityState?.selected,
90
+ };
91
+ }
92
+ let _accessibilityValue;
93
+ if (
94
+ accessibilityValue != null ||
95
+ ariaValueMax != null ||
96
+ ariaValueMin != null ||
97
+ ariaValueNow != null ||
98
+ ariaValueText != null
99
+ ) {
100
+ _accessibilityValue = {
101
+ max: ariaValueMax ?? accessibilityValue?.max,
102
+ min: ariaValueMin ?? accessibilityValue?.min,
103
+ now: ariaValueNow ?? accessibilityValue?.now,
104
+ text: ariaValueText ?? accessibilityValue?.text,
105
+ };
106
+ }
83
107
 
84
- const _accessibilityValue = {
85
- max: ariaValueMax ?? accessibilityValue?.max,
86
- min: ariaValueMin ?? accessibilityValue?.min,
87
- now: ariaValueNow ?? accessibilityValue?.now,
88
- text: ariaValueText ?? accessibilityValue?.text,
89
- };
108
+ let style = flattenStyle(otherProps.style);
90
109
 
91
- const flattenedStyle = flattenStyle(style);
92
- const newPointerEvents = flattenedStyle?.pointerEvents || pointerEvents;
110
+ const newPointerEvents = style?.pointerEvents || pointerEvents;
93
111
 
94
112
  const viewRef = React.useRef<?React.ElementRef<typeof View>>(null);
95
113
 
@@ -12,6 +12,6 @@
12
12
  exports.version = {
13
13
  major: 0,
14
14
  minor: 71,
15
- patch: 10,
15
+ patch: 11,
16
16
  prerelease: '0',
17
17
  };
@@ -158,13 +158,13 @@ const BaseImage = (props: ImagePropsType, forwardedRef) => {
158
158
  const {width = props.width, height = props.height, uri} = source;
159
159
  style = flattenStyle([{width, height}, styles.base, props.style]);
160
160
  sources = [source];
161
-
162
161
  if (uri === '') {
163
162
  console.warn('source.uri should not be an empty string');
164
163
  }
165
164
  }
166
165
 
167
166
  const {height, width, ...restProps} = props;
167
+
168
168
  const {onLoadStart, onLoad, onLoadEnd, onError} = props;
169
169
  const nativeProps = {
170
170
  ...restProps,
@@ -9,17 +9,16 @@
9
9
  */
10
10
 
11
11
  import type {PressEvent} from '../Types/CoreEventTypes';
12
+ import type {TextProps} from './TextProps';
12
13
 
13
14
  import * as PressabilityDebug from '../Pressability/PressabilityDebug';
14
15
  import usePressability from '../Pressability/usePressability';
15
16
  import flattenStyle from '../StyleSheet/flattenStyle';
16
17
  import processColor from '../StyleSheet/processColor';
17
- import StyleSheet from '../StyleSheet/StyleSheet';
18
18
  import {getAccessibilityRoleFromRole} from '../Utilities/AcessibilityMapping';
19
19
  import Platform from '../Utilities/Platform';
20
20
  import TextAncestor from './TextAncestor';
21
21
  import {NativeText, NativeVirtualText} from './TextNativeComponent';
22
- import {type TextProps} from './TextProps';
23
22
  import * as React from 'react';
24
23
  import {useContext, useMemo, useState} from 'react';
25
24
 
@@ -36,6 +35,7 @@ const Text: React.AbstractComponent<
36
35
  accessible,
37
36
  accessibilityLabel,
38
37
  accessibilityRole,
38
+ accessibilityState,
39
39
  allowFontScaling,
40
40
  'aria-busy': ariaBusy,
41
41
  'aria-checked': ariaChecked,
@@ -64,13 +64,23 @@ const Text: React.AbstractComponent<
64
64
 
65
65
  const [isHighlighted, setHighlighted] = useState(false);
66
66
 
67
- const _accessibilityState = {
68
- busy: ariaBusy ?? props.accessibilityState?.busy,
69
- checked: ariaChecked ?? props.accessibilityState?.checked,
70
- disabled: ariaDisabled ?? props.accessibilityState?.disabled,
71
- expanded: ariaExpanded ?? props.accessibilityState?.expanded,
72
- selected: ariaSelected ?? props.accessibilityState?.selected,
73
- };
67
+ let _accessibilityState;
68
+ if (
69
+ accessibilityState != null ||
70
+ ariaBusy != null ||
71
+ ariaChecked != null ||
72
+ ariaDisabled != null ||
73
+ ariaExpanded != null ||
74
+ ariaSelected != null
75
+ ) {
76
+ _accessibilityState = {
77
+ busy: ariaBusy ?? accessibilityState?.busy,
78
+ checked: ariaChecked ?? accessibilityState?.checked,
79
+ disabled: ariaDisabled ?? accessibilityState?.disabled,
80
+ expanded: ariaExpanded ?? accessibilityState?.expanded,
81
+ selected: ariaSelected ?? accessibilityState?.selected,
82
+ };
83
+ }
74
84
 
75
85
  const _disabled =
76
86
  restProps.disabled != null
@@ -174,25 +184,11 @@ const Text: React.AbstractComponent<
174
184
  ? null
175
185
  : processColor(restProps.selectionColor);
176
186
 
177
- let style = flattenStyle(restProps.style);
178
-
179
- let _selectable = restProps.selectable;
180
- if (style?.userSelect != null) {
181
- _selectable = userSelectToSelectableMap[style.userSelect];
182
- }
183
-
184
- if (style?.verticalAlign != null) {
185
- style = StyleSheet.compose(style, {
186
- textAlignVertical:
187
- verticalAlignToTextAlignVerticalMap[style.verticalAlign],
188
- });
189
- }
187
+ let style = restProps.style;
190
188
 
191
189
  if (__DEV__) {
192
190
  if (PressabilityDebug.isEnabled() && onPress != null) {
193
- style = StyleSheet.compose(restProps.style, {
194
- color: 'magenta',
195
- });
191
+ style = [restProps.style, {color: 'magenta'}];
196
192
  }
197
193
  }
198
194
 
@@ -211,10 +207,22 @@ const Text: React.AbstractComponent<
211
207
  default: accessible,
212
208
  });
213
209
 
214
- let flattenedStyle = flattenStyle(style);
210
+ style = flattenStyle(style);
211
+
212
+ if (typeof style?.fontWeight === 'number') {
213
+ style.fontWeight = style?.fontWeight.toString();
214
+ }
215
215
 
216
- if (typeof flattenedStyle?.fontWeight === 'number') {
217
- flattenedStyle.fontWeight = flattenedStyle?.fontWeight.toString();
216
+ let _selectable = restProps.selectable;
217
+ if (style?.userSelect != null) {
218
+ _selectable = userSelectToSelectableMap[style.userSelect];
219
+ delete style.userSelect;
220
+ }
221
+
222
+ if (style?.verticalAlign != null) {
223
+ style.textAlignVertical =
224
+ verticalAlignToTextAlignVerticalMap[style.verticalAlign];
225
+ delete style.verticalAlign;
218
226
  }
219
227
 
220
228
  const _hasOnPressOrOnLongPress =
@@ -223,20 +231,20 @@ const Text: React.AbstractComponent<
223
231
  return hasTextAncestor ? (
224
232
  <NativeVirtualText
225
233
  {...restProps}
226
- accessibilityState={_accessibilityState}
227
234
  {...eventHandlersForText}
228
235
  accessibilityLabel={ariaLabel ?? accessibilityLabel}
229
236
  accessibilityRole={
230
237
  role ? getAccessibilityRoleFromRole(role) : accessibilityRole
231
238
  }
239
+ accessibilityState={_accessibilityState}
232
240
  isHighlighted={isHighlighted}
233
241
  isPressable={isPressable}
234
- selectable={_selectable}
235
242
  nativeID={id ?? nativeID}
236
243
  numberOfLines={numberOfLines}
237
- selectionColor={selectionColor}
238
- style={flattenedStyle}
239
244
  ref={forwardedRef}
245
+ selectable={_selectable}
246
+ selectionColor={selectionColor}
247
+ style={style}
240
248
  />
241
249
  ) : (
242
250
  <TextAncestor.Provider value={true}>
@@ -257,13 +265,15 @@ const Text: React.AbstractComponent<
257
265
  role ? getAccessibilityRoleFromRole(role) : accessibilityRole
258
266
  }
259
267
  allowFontScaling={allowFontScaling !== false}
268
+ disabled={_disabled}
260
269
  ellipsizeMode={ellipsizeMode ?? 'tail'}
261
270
  isHighlighted={isHighlighted}
262
271
  nativeID={id ?? nativeID}
263
272
  numberOfLines={numberOfLines}
264
- selectionColor={selectionColor}
265
- style={flattenedStyle}
266
273
  ref={forwardedRef}
274
+ selectable={_selectable}
275
+ selectionColor={selectionColor}
276
+ style={style}
267
277
  />
268
278
  </TextAncestor.Provider>
269
279
  );
package/README.md CHANGED
@@ -77,7 +77,8 @@ react-native init TestApp --template=react-native-tvos@latest
77
77
  cd TestApp && react-native run-ios --simulator "Apple TV" --scheme "TestApp-tvOS"
78
78
  ```
79
79
 
80
- (_Note_: As of now, `npx react-native run-ios` will no longer run Apple TV targets. A fix for this has been merged (https://github.com/react-native-community/cli/pull/1929) and will be released shortly. To run Apple TV (and Android TV) targets from the command line, it is now possible to use the Expo CLI, using the following steps:
80
+ To run Apple TV (and Android TV) targets from the command line, it is now possible to use the Expo CLI, using the following steps:
81
+
81
82
  - In your app, install the required Expo modules: `yarn add expo`
82
83
  - Add a file `react-native.config.js` at the top level of your app directory, with [these contents](https://github.com/byCedric/custom-prebuild-example/blob/main/app/react-native.config.js).
83
84
  - Then an Apple TV target can be run: `npx expo run:ios --scheme MyApp-tvOS --device "Apple TV"`
@@ -209,9 +210,3 @@ More information on the focus handling improvements above can be found in [this
209
210
 
210
211
  - _TVTextScrollView_: On Apple TV, a ScrollView will not scroll unless there are focusable items inside it or above/below it. This component wraps ScrollView and uses tvOS-specific native code to allow scrolling using swipe gestures from the remote control.
211
212
 
212
- - _Known issues_:
213
-
214
- - The Hermes engine has not yet been ported to Apple TV, so it should be disabled in application Podfiles targeting TV.
215
- - There are known issues with the TabBarIOS component, due to changes that Apple made in UITabBar for tvOS 13.
216
-
217
-
@@ -312,7 +312,17 @@ static void attemptAsynchronousLoadOfBundleAtURL(
312
312
  return;
313
313
  }
314
314
 
315
- RCTSource *source = RCTSourceCreate(scriptURL, data, data.length);
315
+ // Prefer `Content-Location` as the canonical source URL, if given, or fall back to scriptURL.
316
+ NSURL *sourceURL = scriptURL;
317
+ NSString *contentLocationHeader = headers[@"Content-Location"];
318
+ if (contentLocationHeader) {
319
+ NSURL *contentLocationURL = [NSURL URLWithString:contentLocationHeader relativeToURL:scriptURL];
320
+ if (contentLocationURL) {
321
+ sourceURL = contentLocationURL;
322
+ }
323
+ }
324
+
325
+ RCTSource *source = RCTSourceCreate(sourceURL, data, data.length);
316
326
  parseHeaders(headers, source);
317
327
  onComplete(nil, source);
318
328
  }
@@ -23,7 +23,7 @@ NSDictionary* RCTGetReactNativeVersion(void)
23
23
  __rnVersion = @{
24
24
  RCTVersionMajor: @(0),
25
25
  RCTVersionMinor: @(71),
26
- RCTVersionPatch: @(10),
26
+ RCTVersionPatch: @(11),
27
27
  RCTVersionPrerelease: @"0",
28
28
  };
29
29
  });
@@ -474,6 +474,7 @@ struct RCTInstanceCallback : public InstanceCallback {
474
474
  // Load the source asynchronously, then store it for later execution.
475
475
  dispatch_group_enter(prepareBridge);
476
476
  __block NSData *sourceCode;
477
+ __block NSURL *sourceURL = self.bundleURL;
477
478
 
478
479
  #if (RCT_DEV | RCT_ENABLE_LOADING_VIEW) && __has_include(<React/RCTDevLoadingViewProtocol.h>)
479
480
  {
@@ -489,6 +490,9 @@ struct RCTInstanceCallback : public InstanceCallback {
489
490
  }
490
491
 
491
492
  sourceCode = source.data;
493
+ if (source.url) {
494
+ sourceURL = source.url;
495
+ }
492
496
  dispatch_group_leave(prepareBridge);
493
497
  }
494
498
  onProgress:^(RCTLoadingProgress *progressData) {
@@ -503,7 +507,7 @@ struct RCTInstanceCallback : public InstanceCallback {
503
507
  dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
504
508
  RCTCxxBridge *strongSelf = weakSelf;
505
509
  if (sourceCode && strongSelf.loading) {
506
- [strongSelf executeSourceCode:sourceCode sync:NO];
510
+ [strongSelf executeSourceCode:sourceCode withSourceURL:sourceURL sync:NO];
507
511
  }
508
512
  });
509
513
  RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
@@ -1049,7 +1053,7 @@ struct RCTInstanceCallback : public InstanceCallback {
1049
1053
  [_displayLink registerModuleForFrameUpdates:module withModuleData:moduleData];
1050
1054
  }
1051
1055
 
1052
- - (void)executeSourceCode:(NSData *)sourceCode sync:(BOOL)sync
1056
+ - (void)executeSourceCode:(NSData *)sourceCode withSourceURL:(NSURL *)url sync:(BOOL)sync
1053
1057
  {
1054
1058
  // This will get called from whatever thread was actually executing JS.
1055
1059
  dispatch_block_t completion = ^{
@@ -1074,12 +1078,13 @@ struct RCTInstanceCallback : public InstanceCallback {
1074
1078
  };
1075
1079
 
1076
1080
  if (sync) {
1077
- [self executeApplicationScriptSync:sourceCode url:self.bundleURL];
1081
+ [self executeApplicationScriptSync:sourceCode url:url];
1078
1082
  completion();
1079
1083
  } else {
1080
- [self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:completion];
1084
+ [self enqueueApplicationScript:sourceCode url:url onComplete:completion];
1081
1085
  }
1082
1086
 
1087
+ // Use the original request URL here - HMRClient uses this to derive the /hot URL and entry point.
1083
1088
  [self.devSettings setupHMRClientWithBundleURL:self.bundleURL];
1084
1089
  }
1085
1090
 
@@ -244,6 +244,22 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
244
244
  motionEffectsAdded = NO;
245
245
  }
246
246
 
247
+ - (void)didMoveToSuperview {
248
+ [super didMoveToSuperview];
249
+
250
+ // There's no way for us to understand if the view is actually getting removed or getting detached.
251
+ // We play safe here and set the focusGuide's preferredFocusEnvs to an empty array
252
+ // to break a potential retain cycle (see `handleFocusGuide`).
253
+ if (self.superview == nil && self.focusGuide != nil) {
254
+ self.focusGuide.preferredFocusEnvironments = @[];
255
+ }
256
+
257
+ // We should restore focusGuide's state if the item was only detached and now getting attached again.
258
+ if (self.superview != nil && self.focusGuide != nil) {
259
+ [self handleFocusGuide];
260
+ }
261
+ }
262
+
247
263
  - (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
248
264
  {
249
265
  // This is the `trapFocus*` logic that prevents the focus updates if
@@ -1,4 +1,4 @@
1
- VERSION_NAME=0.71.10-0
1
+ VERSION_NAME=0.71.11-0
2
2
 
3
3
  # GROUP=com.facebook.react
4
4
  # Group for the TV repo
@@ -14,6 +14,7 @@ import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE;
14
14
 
15
15
  import android.app.Activity;
16
16
  import android.content.Context;
17
+ import android.content.ContextWrapper;
17
18
  import android.graphics.Canvas;
18
19
  import android.graphics.Insets;
19
20
  import android.graphics.Point;
@@ -917,6 +918,14 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
917
918
  checkForDeviceDimensionsChanges();
918
919
  }
919
920
 
921
+ private Activity getActivity() {
922
+ Context context = getContext();
923
+ while (!(context instanceof Activity) && context instanceof ContextWrapper) {
924
+ context = ((ContextWrapper) context).getBaseContext();
925
+ }
926
+ return (Activity) context;
927
+ }
928
+
920
929
  @RequiresApi(api = Build.VERSION_CODES.R)
921
930
  private void checkForKeyboardEvents() {
922
931
  getRootView().getWindowVisibleDisplayFrame(mVisibleViewArea);
@@ -934,7 +943,7 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
934
943
  Insets barInsets = rootInsets.getInsets(WindowInsets.Type.systemBars());
935
944
  int height = imeInsets.bottom - barInsets.bottom;
936
945
 
937
- int softInputMode = ((Activity) getContext()).getWindow().getAttributes().softInputMode;
946
+ int softInputMode = getActivity().getWindow().getAttributes().softInputMode;
938
947
  int screenY =
939
948
  softInputMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING
940
949
  ? mVisibleViewArea.bottom - height
@@ -7,9 +7,12 @@
7
7
 
8
8
  package com.facebook.react.animated;
9
9
 
10
+ import com.facebook.common.logging.FLog;
10
11
  import com.facebook.react.bridge.ReadableArray;
11
12
  import com.facebook.react.bridge.ReadableMap;
12
13
  import com.facebook.react.bridge.ReadableType;
14
+ import com.facebook.react.common.ReactConstants;
15
+ import com.facebook.react.common.build.ReactBuildConfig;
13
16
 
14
17
  /**
15
18
  * Implementation of {@link AnimationDriver} which provides a support for simple time-based
@@ -70,7 +73,17 @@ class FrameBasedAnimationDriver extends AnimationDriver {
70
73
  long timeFromStartMillis = (frameTimeNanos - mStartFrameTimeNanos) / 1000000;
71
74
  int frameIndex = (int) Math.round(timeFromStartMillis / FRAME_TIME_MILLIS);
72
75
  if (frameIndex < 0) {
73
- throw new IllegalStateException("Calculated frame index should never be lower than 0");
76
+ String message =
77
+ "Calculated frame index should never be lower than 0. Called with frameTimeNanos "
78
+ + frameTimeNanos
79
+ + " and mStartFrameTimeNanos "
80
+ + mStartFrameTimeNanos;
81
+ if (ReactBuildConfig.DEBUG) {
82
+ throw new IllegalStateException(message);
83
+ } else {
84
+ FLog.w(ReactConstants.TAG, message);
85
+ return;
86
+ }
74
87
  } else if (mHasFinished) {
75
88
  // nothing to do here
76
89
  return;
@@ -17,6 +17,6 @@ public class ReactNativeVersion {
17
17
  public static final Map<String, Object> VERSION = MapBuilder.<String, Object>of(
18
18
  "major", 0,
19
19
  "minor", 71,
20
- "patch", 10,
20
+ "patch", 11,
21
21
  "prerelease", "0");
22
22
  }
@@ -791,7 +791,7 @@ public class ReactViewBackgroundDrawable extends Drawable {
791
791
 
792
792
  /** Compute mInnerTopLeftCorner */
793
793
  mInnerTopLeftCorner.x = mInnerClipTempRectForBorderRadius.left;
794
- mInnerTopLeftCorner.y = mInnerClipTempRectForBorderRadius.top * 2;
794
+ mInnerTopLeftCorner.y = mInnerClipTempRectForBorderRadius.top;
795
795
 
796
796
  getEllipseIntersectionWithLine(
797
797
  // Ellipse Bounds
@@ -817,7 +817,7 @@ public class ReactViewBackgroundDrawable extends Drawable {
817
817
  }
818
818
 
819
819
  mInnerBottomLeftCorner.x = mInnerClipTempRectForBorderRadius.left;
820
- mInnerBottomLeftCorner.y = mInnerClipTempRectForBorderRadius.bottom * -2;
820
+ mInnerBottomLeftCorner.y = mInnerClipTempRectForBorderRadius.bottom;
821
821
 
822
822
  getEllipseIntersectionWithLine(
823
823
  // Ellipse Bounds
@@ -843,7 +843,7 @@ public class ReactViewBackgroundDrawable extends Drawable {
843
843
  }
844
844
 
845
845
  mInnerTopRightCorner.x = mInnerClipTempRectForBorderRadius.right;
846
- mInnerTopRightCorner.y = mInnerClipTempRectForBorderRadius.top * 2;
846
+ mInnerTopRightCorner.y = mInnerClipTempRectForBorderRadius.top;
847
847
 
848
848
  getEllipseIntersectionWithLine(
849
849
  // Ellipse Bounds
@@ -869,7 +869,7 @@ public class ReactViewBackgroundDrawable extends Drawable {
869
869
  }
870
870
 
871
871
  mInnerBottomRightCorner.x = mInnerClipTempRectForBorderRadius.right;
872
- mInnerBottomRightCorner.y = mInnerClipTempRectForBorderRadius.bottom * -2;
872
+ mInnerBottomRightCorner.y = mInnerClipTempRectForBorderRadius.bottom;
873
873
 
874
874
  getEllipseIntersectionWithLine(
875
875
  // Ellipse Bounds
@@ -17,7 +17,7 @@ namespace facebook::react {
17
17
  constexpr struct {
18
18
  int32_t Major = 0;
19
19
  int32_t Minor = 71;
20
- int32_t Patch = 10;
20
+ int32_t Patch = 11;
21
21
  std::string_view Prerelease = "0";
22
22
  } ReactNativeVersion;
23
23
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-tvos",
3
- "version": "0.71.10-0",
3
+ "version": "0.71.11-0",
4
4
  "bin": "./cli.js",
5
5
  "description": "A framework for building native apps using React",
6
6
  "license": "MIT",
@@ -118,9 +118,9 @@
118
118
  },
119
119
  "dependencies": {
120
120
  "@jest/create-cache-key-function": "^29.2.1",
121
- "@react-native-community/cli": "10.2.2",
121
+ "@react-native-community/cli": "10.2.5",
122
122
  "@react-native-community/cli-platform-android": "10.2.0",
123
- "@react-native-community/cli-platform-ios": "10.2.1",
123
+ "@react-native-community/cli-platform-ios": "10.2.5",
124
124
  "@react-native/assets": "1.0.0",
125
125
  "@react-native/normalize-color": "2.1.0",
126
126
  "@react-native/polyfills": "2.0.0",
@@ -133,9 +133,9 @@
133
133
  "jest-environment-node": "^29.2.1",
134
134
  "jsc-android": "^250231.0.0",
135
135
  "memoize-one": "^5.0.0",
136
- "metro-react-native-babel-transformer": "0.73.9",
137
- "metro-runtime": "0.73.9",
138
- "metro-source-map": "0.73.9",
136
+ "metro-react-native-babel-transformer": "0.73.10",
137
+ "metro-runtime": "0.73.10",
138
+ "metro-source-map": "0.73.10",
139
139
  "mkdirp": "^0.5.1",
140
140
  "nullthrows": "^1.1.1",
141
141
  "pretty-format": "^26.5.2",
@@ -191,12 +191,12 @@
191
191
  "jest": "^29.2.1",
192
192
  "jest-junit": "^10.0.0",
193
193
  "jscodeshift": "^0.13.1",
194
- "metro-babel-register": "0.73.9",
195
- "metro-memory-fs": "0.73.9",
194
+ "metro-babel-register": "0.73.10",
195
+ "metro-memory-fs": "0.73.10",
196
196
  "mkdirp": "^0.5.1",
197
197
  "mock-fs": "^5.1.4",
198
198
  "prettier": "^2.4.1",
199
- "react-native-core": "npm:react-native@0.71.10",
199
+ "react-native-core": "npm:react-native@0.71.11",
200
200
  "shelljs": "^0.8.5",
201
201
  "signedsource": "^1.0.0",
202
202
  "typescript": "4.1.3",
@@ -435,6 +435,60 @@ class UtilsTests < Test::Unit::TestCase
435
435
  assert_equal(user_project_mock.save_invocation_count, 1)
436
436
  end
437
437
 
438
+ # ================================= #
439
+ # Test - Apply Xcode 15 Patch #
440
+ # ================================= #
441
+
442
+ def test_applyXcode15Patch_correctlyAppliesNecessaryPatch
443
+ # Arrange
444
+ first_target = prepare_target("FirstTarget")
445
+ second_target = prepare_target("SecondTarget")
446
+ third_target = TargetMock.new("ThirdTarget", [
447
+ BuildConfigurationMock.new("Debug", {
448
+ "GCC_PREPROCESSOR_DEFINITIONS" => '$(inherited) "SomeFlag=1" '
449
+ }),
450
+ BuildConfigurationMock.new("Release", {
451
+ "GCC_PREPROCESSOR_DEFINITIONS" => '$(inherited) "SomeFlag=1" '
452
+ }),
453
+ ], nil)
454
+
455
+ user_project_mock = UserProjectMock.new("a/path", [
456
+ prepare_config("Debug"),
457
+ prepare_config("Release"),
458
+ ],
459
+ :native_targets => [
460
+ first_target,
461
+ second_target
462
+ ]
463
+ )
464
+ pods_projects_mock = PodsProjectMock.new([], {"hermes-engine" => {}}, :native_targets => [
465
+ third_target
466
+ ])
467
+ installer = InstallerMock.new(pods_projects_mock, [
468
+ AggregatedProjectMock.new(user_project_mock)
469
+ ])
470
+
471
+ # Act
472
+ ReactNativePodsUtils.apply_xcode_15_patch(installer)
473
+
474
+ # Assert
475
+ first_target.build_configurations.each do |config|
476
+ assert_equal(config.build_settings["GCC_PREPROCESSOR_DEFINITIONS"].strip,
477
+ '$(inherited) "_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION"'
478
+ )
479
+ end
480
+ second_target.build_configurations.each do |config|
481
+ assert_equal(config.build_settings["GCC_PREPROCESSOR_DEFINITIONS"].strip,
482
+ '$(inherited) "_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION"'
483
+ )
484
+ end
485
+ third_target.build_configurations.each do |config|
486
+ assert_equal(config.build_settings["GCC_PREPROCESSOR_DEFINITIONS"].strip,
487
+ '$(inherited) "SomeFlag=1" "_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION"'
488
+ )
489
+ end
490
+ end
491
+
438
492
  # ==================================== #
439
493
  # Test - Set Node_Modules User Setting #
440
494
  # ==================================== #
@@ -80,8 +80,8 @@ def flipper_post_install(installer)
80
80
  end
81
81
  end
82
82
 
83
- # Enable flipper for React-Core Debug configuration
84
- if target.name == 'React-Core'
83
+ # Enable flipper for React-Core Debug configuration for iOS
84
+ if target.name == 'React-Core-iOS'
85
85
  target.build_configurations.each do |config|
86
86
  if config.debug?
87
87
  config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = ['$(inherited)', 'FB_SONARKIT_ENABLED=1']
@@ -132,6 +132,18 @@ class ReactNativePodsUtils
132
132
  end
133
133
  end
134
134
 
135
+ def self.apply_xcode_15_patch(installer)
136
+ installer.target_installation_results.pod_target_installation_results
137
+ .each do |pod_name, target_installation_result|
138
+ target_installation_result.native_target.build_configurations.each do |config|
139
+ # unary_function and binary_function are no longer provided in C++17 and newer standard modes as part of Xcode 15. They can be re-enabled with setting _LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION
140
+ # Ref: https://developer.apple.com/documentation/xcode-release-notes/xcode-15-release-notes#Deprecations
141
+ config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= '$(inherited) '
142
+ config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << '"_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION" '
143
+ end
144
+ end
145
+ end
146
+
135
147
  private
136
148
 
137
149
  def self.fix_library_search_path(config)
@@ -228,6 +228,7 @@ def react_native_post_install(installer, react_native_path = "../node_modules/re
228
228
  ReactNativePodsUtils.exclude_i386_architecture_while_using_hermes(installer)
229
229
  ReactNativePodsUtils.fix_library_search_paths(installer)
230
230
  ReactNativePodsUtils.set_node_modules_user_settings(installer, react_native_path)
231
+ ReactNativePodsUtils.apply_xcode_15_patch(installer)
231
232
 
232
233
  NewArchitectureHelper.set_clang_cxx_language_standard_if_needed(installer)
233
234
  is_new_arch_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == "1"
Binary file
@@ -15,7 +15,7 @@
15
15
  <key>CFBundlePackageType</key>
16
16
  <string>APPL</string>
17
17
  <key>CFBundleShortVersionString</key>
18
- <string>1.0</string>
18
+ <string>$(MARKETING_VERSION)</string>
19
19
  <key>CFBundleSignature</key>
20
20
  <string>????</string>
21
21
  <key>CFBundleVersion</key>
@@ -734,6 +734,7 @@
734
734
  );
735
735
  INFOPLIST_FILE = HelloWorld/Info.plist;
736
736
  LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
737
+ MARKETING_VERSION = 1.0;
737
738
  OTHER_LDFLAGS = (
738
739
  "$(inherited)",
739
740
  "-ObjC",
@@ -756,6 +757,7 @@
756
757
  CURRENT_PROJECT_VERSION = 1;
757
758
  INFOPLIST_FILE = HelloWorld/Info.plist;
758
759
  LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
760
+ MARKETING_VERSION = 1.0;
759
761
  OTHER_LDFLAGS = (
760
762
  "$(inherited)",
761
763
  "-ObjC",
@@ -784,6 +786,7 @@
784
786
  GCC_NO_COMMON_BLOCKS = YES;
785
787
  INFOPLIST_FILE = "HelloWorld-tvOS/Info.plist";
786
788
  LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
789
+ MARKETING_VERSION = 1.0;
787
790
  OTHER_LDFLAGS = (
788
791
  "$(inherited)",
789
792
  "-ObjC",
@@ -813,6 +816,7 @@
813
816
  GCC_NO_COMMON_BLOCKS = YES;
814
817
  INFOPLIST_FILE = "HelloWorld-tvOS/Info.plist";
815
818
  LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
819
+ MARKETING_VERSION = 1.0;
816
820
  OTHER_LDFLAGS = (
817
821
  "$(inherited)",
818
822
  "-ObjC",
@@ -3,7 +3,6 @@ require_relative '../node_modules/@react-native-community/cli-platform-ios/nativ
3
3
 
4
4
  source 'https://github.com/react-native-tvos/react-native-tvos-podspecs.git'
5
5
  source 'https://cdn.cocoapods.org/'
6
- install! 'cocoapods', :deterministic_uuids => false
7
6
  prepare_react_native_project!
8
7
 
9
8
  production = ENV["PRODUCTION"] == "1"
@@ -17,6 +16,17 @@ target 'HelloWorld' do
17
16
  # Flags change depending on the env values.
18
17
  flags = get_default_flags()
19
18
 
19
+ # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
20
+ # because `react-native-flipper` depends on (FlipperKit,...) that will be excluded
21
+ #
22
+ # To fix this you can also exclude `react-native-flipper` using a `react-native.config.js`
23
+ # ```js
24
+ # module.exports = {
25
+ # dependencies: {
26
+ # ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}),
27
+ # ```
28
+ flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled
29
+
20
30
  use_react_native!(
21
31
  :path => config[:reactNativePath],
22
32
  # Hermes is now enabled by default. Disable by setting this flag to false.
@@ -28,7 +38,7 @@ target 'HelloWorld' do
28
38
  #
29
39
  # Note that if you have use_frameworks! enabled, Flipper will not work and
30
40
  # you should disable the next line.
31
- # :flipper_configuration => flipper_config,
41
+ :flipper_configuration => flipper_config,
32
42
  # An absolute path to your application root.
33
43
  :app_path => "#{Pod::Config.instance.installation_root}/.."
34
44
  )
@@ -11,7 +11,7 @@
11
11
  },
12
12
  "dependencies": {
13
13
  "react": "18.2.0",
14
- "react-native": "npm:react-native-tvos@0.71.10-0"
14
+ "react-native": "npm:react-native-tvos@0.71.11-0"
15
15
  },
16
16
  "devDependencies": {
17
17
  "@babel/core": "^7.20.0",
@@ -25,7 +25,7 @@
25
25
  "babel-jest": "^29.2.1",
26
26
  "eslint": "^8.19.0",
27
27
  "jest": "^29.2.1",
28
- "metro-react-native-babel-preset": "0.73.9",
28
+ "metro-react-native-babel-preset": "0.73.10",
29
29
  "prettier": "^2.4.1",
30
30
  "react-test-renderer": "18.2.0",
31
31
  "typescript": "4.8.4"