diff --git a/.circleci/Dockerfiles/scripts/run-ci-e2e-tests.sh b/.circleci/Dockerfiles/scripts/run-ci-e2e-tests.sh index 07a8779f60..067a2ea9e1 100755 --- a/.circleci/Dockerfiles/scripts/run-ci-e2e-tests.sh +++ b/.circleci/Dockerfiles/scripts/run-ci-e2e-tests.sh @@ -67,6 +67,11 @@ while :; do shift ;; + --tvos) + RUN_IOS=1 + shift + ;; + *) break esac diff --git a/.gitignore b/.gitignore index 31239be195..6ff8f549e6 100644 --- a/.gitignore +++ b/.gitignore @@ -107,7 +107,6 @@ package-lock.json !/packages/rn-tester/Pods/__offline_mirrors__ # react-native-codegen -/React/FBReactNativeSpec/FBReactNativeSpec /packages/react-native-codegen/lib /ReactCommon/react/renderer/components/rncore/ /packages/rn-tester/NativeModuleExample/ScreenshotManagerSpec* diff --git a/Libraries/ActionSheetIOS/React-RCTActionSheet.podspec b/Libraries/ActionSheetIOS/React-RCTActionSheet.podspec index e81353c8b6..99dc134e41 100644 --- a/Libraries/ActionSheetIOS/React-RCTActionSheet.podspec +++ b/Libraries/ActionSheetIOS/React-RCTActionSheet.podspec @@ -24,10 +24,10 @@ Pod::Spec.new do |s| s.documentation_url = "https://reactnative.dev/docs/actionsheetios" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.source = source s.source_files = "*.{m}" - s.preserve_paths = "package.json", "LICENSE", "LICENSE-docs" + s.preserve_paths = "package.json", "LICENSE", "LICENSE-docs" s.header_dir = "RCTActionSheet" s.dependency "React-Core/RCTActionSheetHeaders", version diff --git a/Libraries/Blob/React-RCTBlob.podspec b/Libraries/Blob/React-RCTBlob.podspec index 15a46df823..bd085fefeb 100644 --- a/Libraries/Blob/React-RCTBlob.podspec +++ b/Libraries/Blob/React-RCTBlob.podspec @@ -26,7 +26,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "*.{h,m,mm}" diff --git a/Libraries/Components/AppleTV/NativeTVNavigationEventEmitter.js b/Libraries/Components/AppleTV/NativeTVNavigationEventEmitter.js new file mode 100644 index 0000000000..31445ea0ae --- /dev/null +++ b/Libraries/Components/AppleTV/NativeTVNavigationEventEmitter.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +'use strict'; + +import type {TurboModule} from '../../TurboModule/RCTExport'; +import * as TurboModuleRegistry from '../../TurboModule/TurboModuleRegistry'; + +export interface Spec extends TurboModule { + +addListener: (eventName: string) => void; + +removeListeners: (count: number) => void; +} + +export default (TurboModuleRegistry.get( + 'TVNavigationEventEmitter', +): ?Spec); + diff --git a/Libraries/Components/AppleTV/TVEventHandler.js b/Libraries/Components/AppleTV/TVEventHandler.js new file mode 100644 index 0000000000..d3f475a1fd --- /dev/null +++ b/Libraries/Components/AppleTV/TVEventHandler.js @@ -0,0 +1,51 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +'use strict'; + +import NativeEventEmitter from '../../EventEmitter/NativeEventEmitter'; +import Platform from '../../Utilities/Platform'; +import {type EventSubscription} from '../../vendor/emitter/EventEmitter'; +import NativeTVNavigationEventEmitter from './NativeTVNavigationEventEmitter'; + +class TVEventHandler { + __nativeTVNavigationEventListener: ?EventSubscription = null; + __nativeTVNavigationEventEmitter: ?NativeEventEmitter = null; + + enable(component: ?any, callback: Function): void { + if (Platform.OS === 'ios' && !NativeTVNavigationEventEmitter) { + return; + } + + this.__nativeTVNavigationEventEmitter = new NativeEventEmitter( + NativeTVNavigationEventEmitter, + ); + this.__nativeTVNavigationEventListener = this.__nativeTVNavigationEventEmitter.addListener( + 'onHWKeyEvent', + data => { + if (callback) { + callback(component, data); + } + }, + ); + } + + disable(): void { + if (this.__nativeTVNavigationEventListener) { + this.__nativeTVNavigationEventListener.remove(); + delete this.__nativeTVNavigationEventListener; + } + if (this.__nativeTVNavigationEventEmitter) { + delete this.__nativeTVNavigationEventEmitter; + } + } +} + +module.exports = TVEventHandler; diff --git a/Libraries/Components/AppleTV/TVFocusEventHandler.js b/Libraries/Components/AppleTV/TVFocusEventHandler.js new file mode 100644 index 0000000000..b90153a8dc --- /dev/null +++ b/Libraries/Components/AppleTV/TVFocusEventHandler.js @@ -0,0 +1,51 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +'use strict'; + +import NativeEventEmitter from '../../EventEmitter/NativeEventEmitter'; +import Platform from '../../Utilities/Platform'; +import {type EventSubscription} from '../../vendor/emitter/EventEmitter'; +import NativeTVNavigationEventEmitter from './NativeTVNavigationEventEmitter'; + +class TVFocusEventHandler { + __nativeTVNavigationEventListener: ?EventSubscription = null; + __nativeTVNavigationEventEmitter: ?NativeEventEmitter = null; + __callbackMap: Map = new Map(); + + constructor() { + if (Platform.OS === 'ios' && !NativeTVNavigationEventEmitter) { + return; + } + + this.__nativeTVNavigationEventEmitter = new NativeEventEmitter( + NativeTVNavigationEventEmitter, + ); + this.__nativeTVNavigationEventListener = this.__nativeTVNavigationEventEmitter.addListener( + 'onHWKeyEvent', + data => { + const callback = this.__callbackMap.get(data.tag); + if (callback) { + callback(data); + } + }, + ); + } + + register(componentTag: ?any, callback: Function): void { + this.__callbackMap.set(componentTag, callback); + } + + unregister(componentTag: ?any): void { + this.__callbackMap.delete(componentTag); + } +} + +export const tvFocusEventHandler: TVFocusEventHandler | null = Platform.isTV ? new TVFocusEventHandler() : null; diff --git a/Libraries/Components/AppleTV/TVFocusGuideView.js b/Libraries/Components/AppleTV/TVFocusGuideView.js new file mode 100644 index 0000000000..58fcfbb1c8 --- /dev/null +++ b/Libraries/Components/AppleTV/TVFocusGuideView.js @@ -0,0 +1,79 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +const React = require('react'); +const ReactNative = require('react-native'); +import ReactNativeShims from '../../Renderer/shims/ReactNative'; +const Platform = require('../../Utilities/Platform'); +import {Commands} from '../../Components/View/ViewNativeComponent'; +import type {ViewProps} from '../View/ViewPropTypes'; + +type FocusGuideProps = $ReadOnly<{ + ...ViewProps, + + /** + * The views the focus should go to + */ + destinations: ?(Object[]), +}>; + +const TVFocusGuideView = (props: FocusGuideProps) => { + const focusGuideRef = React.useRef(null); + + React.useEffect(() => { + const nativeDestinations = (props.destinations || []) + .map(d => ReactNative.findNodeHandle(d)) + .filter(c => c !== 0 && c !== null && c !== undefined); + const hostComponentRef = ReactNativeShims.findHostInstance_DEPRECATED(focusGuideRef?.current); + hostComponentRef && Commands.setDestinations(hostComponentRef, nativeDestinations); + }, [props.destinations]); + + return ( + // Container view must have nonzero size + + { + /** + * The client specified layout(using 'style' prop) should be applied the container view ReactNative.View. + * And the focusGuide's layout shoule be overrided to wrap it fully inside the container view. + * For example, if the client specifies 'marginLeft' property in the style prop, + * then the TVFocusGuideView will apply the 'marginLeft' for both the parentView and the focusGuideView. + * and so, the left margin is getting added twice and UI becomes incorrect. + * The same is applicable for other layout properties. + */ + } + {Platform.isTVOS ? ( + + {props.children} + + ) : ( + props.children + )} + + ); + +}; + +const styles = ReactNative.StyleSheet.create({ + focusGuideLayout: { + left: 0, + top: 0, + right: 0, + bottom: 0, + marginLeft: 0, + marginTop: 0, + marginRight: 0, + marginBottom: 0, + }, +}); + +module.exports = TVFocusGuideView; diff --git a/Libraries/Components/AppleTV/TVMenuControl.js b/Libraries/Components/AppleTV/TVMenuControl.js new file mode 100644 index 0000000000..24f0512916 --- /dev/null +++ b/Libraries/Components/AppleTV/TVMenuControl.js @@ -0,0 +1,17 @@ +/** + * @format + * @flow + */ + +'use strict'; + +const TVMenuBridge = require('../../BatchedBridge/NativeModules').TVMenuBridge; + +module.exports = { + enableTVMenuKey: () => { + TVMenuBridge && TVMenuBridge.enableTVMenuKey(); + }, + disableTVMenuKey: () => { + TVMenuBridge && TVMenuBridge.disableTVMenuKey(); + }, +}; diff --git a/Libraries/Components/AppleTV/TVTextScrollView.js b/Libraries/Components/AppleTV/TVTextScrollView.js new file mode 100644 index 0000000000..6d9986bc3d --- /dev/null +++ b/Libraries/Components/AppleTV/TVTextScrollView.js @@ -0,0 +1,104 @@ +/** + * Copyright (c) Douglas Lowder. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow strict-local + */ + +const React = require('react'); +const ScrollView = require('../ScrollView/ScrollView'); +const TVEventHandler = require('./TVEventHandler'); +const findNodeHandle = require('../../Renderer/shims/ReactNative') + .findNodeHandle; + +import typeof Props from '../ScrollView/ScrollView'; + +/** + * Convenience wrapper to create a scroll view that will scroll correctly + * using swipe gestures on tvOS, even if the scroll view has no focusable + * subviews. + * + * The main use case would be when a large scrolling block of text needs + * to be presented to the user. + * + * Props: + * + */ + +class TVTextScrollView extends React.Component<{ + ...Props, + /** + * The duration of the scroll animation when a swipe is detected. + * Default value is 0.3 s + */ + scrollDuration?: number, + /** + * Scrolling distance when a swipe is detected + * Default value is half the visible height (vertical scroller) + * or width (horizontal scroller) + */ + pageSize?: number, + /** + * If true, will scroll to start when focus moves out past the beginning + * of the scroller + * Defaults to true + */ + snapToStart?: boolean, + /** + * If true, will scroll to end when focus moves out past the end of the + * scroller + * Defaults to true + */ + snapToEnd?: boolean, + /** + * Called when the scroller comes into focus (e.g. for highlighting) + */ + onFocus?: (evt: Event) => void, + /** + * Called when the scroller goes out of focus + */ + onBlur?: (evt: Event) => void, +}> { + _tvEventHandler: ?TVEventHandler; + + componentDidMount() { + this._tvEventHandler = new TVEventHandler(); + this._tvEventHandler.enable(this, function(cmp, evt) { + const myTag = findNodeHandle(cmp); + evt.dispatchConfig = {}; + if (myTag === evt.tag) { + if (evt.eventType === 'focus') { + cmp.props.onFocus && cmp.props.onFocus(evt); + } else if (evt.eventType === 'blur') { + cmp.props.onBlur && cmp.props.onBlur(evt); + } + } + }); + } + + componentWillUnmount() { + if (this._tvEventHandler) { + this._tvEventHandler.disable(); + delete this._tvEventHandler; + } + } + + render(): React.Node | React.Element { + const props: $FlowFixMe = { + ...this.props, + tvParallaxProperties: { + pressDuration: this.props.scrollDuration || 0.0, + }, + isTVSelectable: true, + snapToInterval: this.props.pageSize || 0.0, + removeClippedSubviews: false, + automaticallyAdjustContentInsets: false, + }; + return {this.props.children}; + } +} + +module.exports = TVTextScrollView; diff --git a/Libraries/Components/AppleTV/TVViewPropTypes.js b/Libraries/Components/AppleTV/TVViewPropTypes.js new file mode 100644 index 0000000000..2938939d23 --- /dev/null +++ b/Libraries/Components/AppleTV/TVViewPropTypes.js @@ -0,0 +1,81 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +'use strict'; + +export type TVParallaxPropertiesType = $ReadOnly<{| + /** + * If true, parallax effects are enabled. Defaults to true. + */ + enabled?: boolean, + + /** + * Defaults to 2.0. + */ + shiftDistanceX?: number, + + /** + * Defaults to 2.0. + */ + shiftDistanceY?: number, + + /** + * Defaults to 0.05. + */ + tiltAngle?: number, + + /** + * Defaults to 1.0 + */ + magnification?: number, + + /** + * Defaults to 1.0 + */ + pressMagnification?: number, + + /** + * Defaults to 0.3 + */ + pressDuration?: number, + + /** + * Defaults to 0.3 + */ + pressDelay?: number, +|}>; + +/** + * Additional View properties for Apple TV + */ +export type TVViewProps = $ReadOnly<{| + /** + * *(Apple TV only)* When set to true, this view will be focusable + * and navigable using the Apple TV remote. + * + * @platform ios + */ + isTVSelectable?: boolean, + + /** + * *(Apple TV only)* May be set to true to force the Apple TV focus engine to move focus to this view. + * + * @platform ios + */ + hasTVPreferredFocus?: boolean, + + /** + * *(Apple TV only)* Object with properties to control Apple TV parallax effects. + * + * @platform ios + */ + tvParallaxProperties?: TVParallaxPropertiesType, + +|}>; diff --git a/Libraries/Components/AppleTV/useTVEventHandler.js b/Libraries/Components/AppleTV/useTVEventHandler.js new file mode 100644 index 0000000000..56c5e12337 --- /dev/null +++ b/Libraries/Components/AppleTV/useTVEventHandler.js @@ -0,0 +1,17 @@ +// @flow +import React from 'react'; +import TVEventHandler from './TVEventHandler'; + +const useTVEventHandler = (handleEvent: (evt: Event) => void) => { + React.useEffect(() => { + const handler: TVEventHandler = new TVEventHandler(); + handler.enable(null, function(cmp, evt) { + handleEvent(evt); + }); + return () => { + handler.disable(); + }; + }, [handleEvent]); +}; + +module.exports = useTVEventHandler; diff --git a/Libraries/Components/Clipboard/NativeClipboard.js b/Libraries/Components/Clipboard/NativeClipboard.js index 9e4ee4733f..f9a682cfa8 100644 --- a/Libraries/Components/Clipboard/NativeClipboard.js +++ b/Libraries/Components/Clipboard/NativeClipboard.js @@ -11,10 +11,24 @@ import type {TurboModule} from '../../TurboModule/RCTExport'; import * as TurboModuleRegistry from '../../TurboModule/TurboModuleRegistry'; +import Platform from '../../Utilities/Platform'; + export interface Spec extends TurboModule { - +getConstants: () => {||}; + +getConstants: () => {...}; +getString: () => Promise; +setString: (content: string) => void; } -export default (TurboModuleRegistry.getEnforcing('Clipboard'): Spec); +const Placeholder = { + getConstants: () => { + return {}; + }, + getString: () => new Promise((resolve, reject) => resolve('')), + setString: (content: string) => {}, +}; + +const NativeClipboard: Spec = Platform.isTVOS + ? (Placeholder: Spec) + : (TurboModuleRegistry.getEnforcing('Clipboard'): Spec); + +export default NativeClipboard; diff --git a/Libraries/Components/DatePicker/RCTDatePickerNativeComponent.js b/Libraries/Components/DatePicker/RCTDatePickerNativeComponent.js index c7ae082d19..5321617070 100644 --- a/Libraries/Components/DatePicker/RCTDatePickerNativeComponent.js +++ b/Libraries/Components/DatePicker/RCTDatePickerNativeComponent.js @@ -10,14 +10,14 @@ import type {HostComponent} from '../../Renderer/shims/ReactNativeTypes'; import type {ViewProps} from '../View/ViewPropTypes'; -import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands'; -import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; +import codegenNativeCommands from '../../Utilities/codegenNativeCommands'; +import codegenNativeComponent from '../../Utilities/codegenNativeComponent'; import * as React from 'react'; import type { Float, WithDefault, BubblingEventHandler, -} from 'react-native/Libraries/Types/CodegenTypes'; +} from '../../Types/CodegenTypes'; type Event = $ReadOnly<{| timestamp: Float, diff --git a/Libraries/Components/Pressable/Pressable.js b/Libraries/Components/Pressable/Pressable.js index 877595adcf..bee1cb939a 100644 --- a/Libraries/Components/Pressable/Pressable.js +++ b/Libraries/Components/Pressable/Pressable.js @@ -9,7 +9,14 @@ */ import * as React from 'react'; -import {useMemo, useState, useRef, useImperativeHandle} from 'react'; +import { + useMemo, + useState, + useRef, + useImperativeHandle, + useCallback, + useEffect, +} from 'react'; import useAndroidRippleForView, { type RippleConfig, } from './useAndroidRippleForView'; @@ -29,11 +36,28 @@ import type { PressEvent, } from '../../Types/CoreEventTypes'; import View from '../View/View'; +import typeof TVParallaxPropertiesType from '../AppleTV/TVViewPropTypes'; +import Platform from '../../Utilities/Platform'; +import {tvFocusEventHandler} from '../AppleTV/TVFocusEventHandler'; type ViewStyleProp = $ElementType, 'style'>; export type StateCallbackType = $ReadOnly<{| pressed: boolean, + focused: boolean, +|}>; + +type TVProps = $ReadOnly<{| + hasTVPreferredFocus?: boolean, + isTVSelectable?: ?boolean, + tvParallaxProperties?: TVParallaxPropertiesType, + nextFocusDown?: ?number, + nextFocusForward?: ?number, + nextFocusLeft?: ?number, + nextFocusRight?: ?number, + nextFocusUp?: ?number, + onFocus?: ?(event: FocusEvent) => mixed, + onBlur?: ?(event: FocusEvent) => mixed, |}>; type Props = $ReadOnly<{| @@ -163,6 +187,10 @@ type Props = $ReadOnly<{| * Duration to wait after press down before calling `onPressIn`. */ unstable_pressDelay?: ?number, + /** + * Props needed for Apple TV and Android TV + */ + ...TVProps, |}>; /** @@ -183,6 +211,9 @@ function Pressable(props: Props, forwardedRef): React.Node { focusable, onHoverIn, onHoverOut, + isTVSelectable, + onBlur, + onFocus, onLongPress, onPress, onPressIn, @@ -190,6 +221,7 @@ function Pressable(props: Props, forwardedRef): React.Node { pressRetentionOffset, style, testOnly_pressed, + tvParallaxProperties, unstable_pressDelay, ...restProps } = props; @@ -201,6 +233,8 @@ function Pressable(props: Props, forwardedRef): React.Node { const [pressed, setPressed] = usePressState(testOnly_pressed === true); + const [focused, setFocused] = useState(false); + const hitSlop = normalizeRect(props.hitSlop); const accessibilityState = @@ -214,6 +248,7 @@ function Pressable(props: Props, forwardedRef): React.Node { accessible: accessible !== false, accessibilityState, focusable: focusable !== false, + isTVSelectable: isTVSelectable !== false && accessible !== false, hitSlop, }; @@ -230,6 +265,8 @@ function Pressable(props: Props, forwardedRef): React.Node { delayPressIn: unstable_pressDelay, onHoverIn, onHoverOut, + onBlur, + onFocus, onLongPress, onPress, onPressIn(event: PressEvent): void { @@ -263,6 +300,8 @@ function Pressable(props: Props, forwardedRef): React.Node { hitSlop, onHoverIn, onHoverOut, + onBlur, + onFocus, onLongPress, onPress, onPressIn, @@ -274,14 +313,52 @@ function Pressable(props: Props, forwardedRef): React.Node { ); const eventHandlers = usePressability(config); + const pressableTVFocusEventHandler = useCallback( + (evt: Event) => { + if (isTVSelectable !== false || focusable !== false) { + if (evt?.eventType === 'focus') { + setFocused(true); + onFocus && onFocus(evt); + } else if (evt.eventType === 'blur') { + onBlur && onBlur(evt); + setFocused(false); + } + } + // Use these on tvOS only. Android press events go to onClick() so we don't + // need to call onPress() again here + if (Platform.isTVOS) { + if (focused && evt.eventType === 'select') { + onPress && onPress(evt); + } + if (focused && evt.eventType === 'longSelect') { + onLongPress && onLongPress(evt); + } + } + }, + [focused, onBlur, onFocus, onLongPress, onPress, focusable, isTVSelectable], + ); + + useEffect(() => { + if (!tvFocusEventHandler) { + return; + } + const viewTag = viewRef?.current?._nativeTag; + tvFocusEventHandler.register(viewTag, pressableTVFocusEventHandler); + return () => { + tvFocusEventHandler.unregister(viewTag); + }; + }, [pressableTVFocusEventHandler]); + return ( - {typeof children === 'function' ? children({pressed}) : children} + {typeof children === 'function' ? children({pressed, focused}) : children} {__DEV__ ? : null} ); diff --git a/Libraries/Components/RefreshControl/AndroidSwipeRefreshLayoutNativeComponent.js b/Libraries/Components/RefreshControl/AndroidSwipeRefreshLayoutNativeComponent.js index 0ef1fdc208..34ff49636f 100644 --- a/Libraries/Components/RefreshControl/AndroidSwipeRefreshLayoutNativeComponent.js +++ b/Libraries/Components/RefreshControl/AndroidSwipeRefreshLayoutNativeComponent.js @@ -10,7 +10,7 @@ import * as React from 'react'; -import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands'; +import codegenNativeCommands from '../../Utilities/codegenNativeCommands'; import codegenNativeComponent from '../../Utilities/codegenNativeComponent'; import type {HostComponent} from '../../Renderer/shims/ReactNativeTypes'; diff --git a/Libraries/Components/RefreshControl/PullToRefreshViewNativeComponent.js b/Libraries/Components/RefreshControl/PullToRefreshViewNativeComponent.js index 0ebd98469e..50c7edbbc5 100644 --- a/Libraries/Components/RefreshControl/PullToRefreshViewNativeComponent.js +++ b/Libraries/Components/RefreshControl/PullToRefreshViewNativeComponent.js @@ -19,7 +19,7 @@ import * as React from 'react'; import codegenNativeComponent from '../../Utilities/codegenNativeComponent'; import type {HostComponent} from '../../Renderer/shims/ReactNativeTypes'; -import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands'; +import codegenNativeCommands from '../../Utilities/codegenNativeCommands'; type NativeProps = $ReadOnly<{| ...ViewProps, diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js index 7cee521c16..39940d7811 100644 --- a/Libraries/Components/ScrollView/ScrollView.js +++ b/Libraries/Components/ScrollView/ScrollView.js @@ -1771,9 +1771,10 @@ class ScrollView extends React.Component { if (refreshControl) { if (Platform.OS === 'ios') { // On iOS the RefreshControl is a child of the ScrollView. + // tvOS lacks native support for RefreshControl, so don't include it in that case return ( - {refreshControl} + {Platform.isTV ? null : refreshControl} {contentContainer} ); diff --git a/Libraries/Components/Slider/Slider.js b/Libraries/Components/Slider/Slider.js index 15e3387dc1..ce0853d843 100644 --- a/Libraries/Components/Slider/Slider.js +++ b/Libraries/Components/Slider/Slider.js @@ -15,6 +15,7 @@ import StyleSheet, { type ViewStyleProp, type ColorValue, } from '../../StyleSheet/StyleSheet'; +import UnimplementedView from '../UnimplementedViews/UnimplementedView'; import type {ImageSource} from '../../Image/ImageSource'; import type {ViewProps} from '../View/ViewPropTypes'; @@ -241,6 +242,12 @@ const Slider = ( ? {...props.accessibilityState, disabled: true} : props.accessibilityState; + if (Platform.isTVOS) { + return ( + + ); + } + return ( {| +HEIGHT: number, @@ -36,7 +38,9 @@ export interface Spec extends TurboModule { +setHidden: (hidden: boolean, withAnimation: string) => void; } -const NativeModule = TurboModuleRegistry.getEnforcing('StatusBarManager'); +const NativeModule = Platform.isTVOS + ? null + : TurboModuleRegistry.getEnforcing('StatusBarManager'); let constants = null; const NativeStatusBarManager = { @@ -45,26 +49,28 @@ const NativeStatusBarManager = { +DEFAULT_BACKGROUND_COLOR?: number, |} { if (constants == null) { - constants = NativeModule.getConstants(); + constants = NativeModule + ? NativeModule.getConstants() + : {HEIGHT: 0, DEFAULT_BACKGROUND_COLOR: 0}; } return constants; }, // TODO(T47754272) Can we remove this method? getHeight(callback: (result: {|height: number|}) => void): void { - NativeModule.getHeight(callback); + NativeModule ? NativeModule.getHeight(callback) : callback({height: 0}); }, setNetworkActivityIndicatorVisible(visible: boolean): void { - NativeModule.setNetworkActivityIndicatorVisible(visible); + NativeModule && NativeModule.setNetworkActivityIndicatorVisible(visible); }, addListener(eventType: string): void { - NativeModule.addListener(eventType); + NativeModule && NativeModule.addListener(eventType); }, removeListeners(count: number): void { - NativeModule.removeListeners(count); + NativeModule && NativeModule.removeListeners(count); }, /** @@ -74,14 +80,14 @@ const NativeStatusBarManager = { * - 'light-content' */ setStyle(statusBarStyle?: ?string, animated: boolean): void { - NativeModule.setStyle(statusBarStyle, animated); + NativeModule && NativeModule.setStyle(statusBarStyle, animated); }, /** * - withAnimation can be: 'none' | 'fade' | 'slide' */ setHidden(hidden: boolean, withAnimation: string): void { - NativeModule.setHidden(hidden, withAnimation); + NativeModule && NativeModule.setHidden(hidden, withAnimation); }, }; diff --git a/Libraries/Components/Switch/AndroidSwitchNativeComponent.js b/Libraries/Components/Switch/AndroidSwitchNativeComponent.js index 200c5230d6..b4b85fd149 100644 --- a/Libraries/Components/Switch/AndroidSwitchNativeComponent.js +++ b/Libraries/Components/Switch/AndroidSwitchNativeComponent.js @@ -15,9 +15,9 @@ import type { BubblingEventHandler, } from 'react-native/Libraries/Types/CodegenTypes'; -import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands'; -import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; -import type {HostComponent} from 'react-native/Libraries/Renderer/shims/ReactNativeTypes'; +import codegenNativeCommands from '../../Utilities/codegenNativeCommands'; +import codegenNativeComponent from '../../Utilities/codegenNativeComponent'; +import type {HostComponent} from '../../Renderer/shims/ReactNativeTypes'; import type {ColorValue} from '../../StyleSheet/StyleSheet'; import type {ViewProps} from '../View/ViewPropTypes'; diff --git a/Libraries/Components/Switch/Switch.js b/Libraries/Components/Switch/Switch.js index 6c90f3d49c..cb71c80f4d 100644 --- a/Libraries/Components/Switch/Switch.js +++ b/Libraries/Components/Switch/Switch.js @@ -13,6 +13,7 @@ import Platform from '../../Utilities/Platform'; import * as React from 'react'; import StyleSheet from '../../StyleSheet/StyleSheet'; import useMergeRefs from '../../Utilities/useMergeRefs'; +import UnimplementedView from '../UnimplementedViews/UnimplementedView'; import AndroidSwitchNativeComponent, { Commands as AndroidSwitchCommands, @@ -183,6 +184,12 @@ const SwitchWithForwardedRef: React.AbstractComponent< } }, [value, native]); + if (Platform.isTVOS) { + return ( + + ); + } + if (Platform.OS === 'android') { const platformProps = { enabled: disabled !== true, diff --git a/Libraries/Components/Switch/SwitchNativeComponent.js b/Libraries/Components/Switch/SwitchNativeComponent.js index 5187656915..55dbe21824 100644 --- a/Libraries/Components/Switch/SwitchNativeComponent.js +++ b/Libraries/Components/Switch/SwitchNativeComponent.js @@ -14,7 +14,7 @@ import type {ViewProps} from '../View/ViewPropTypes'; import * as React from 'react'; import codegenNativeComponent from '../../Utilities/codegenNativeComponent'; -import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands'; +import codegenNativeCommands from '../../Utilities/codegenNativeCommands'; import type {HostComponent} from '../../Renderer/shims/ReactNativeTypes'; type SwitchChangeEvent = $ReadOnly<{| diff --git a/Libraries/Components/TabBarIOS/RCTTabBarItemNativeComponent.js b/Libraries/Components/TabBarIOS/RCTTabBarItemNativeComponent.js new file mode 100644 index 0000000000..55bd2c8a3b --- /dev/null +++ b/Libraries/Components/TabBarIOS/RCTTabBarItemNativeComponent.js @@ -0,0 +1,99 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @noflow + */ + +'use strict'; + +const requireNativeComponent = require('../../ReactNative/requireNativeComponent'); + +import type {ViewProps} from '../View/ViewPropTypes'; +import type {ColorValue} from '../../StyleSheet/StyleSheetTypes'; +import type {SyntheticEvent} from '../../Types/CoreEventTypes'; +import type {ImageSource} from '../../Image/ImageSource'; + +type TabBarItemEvent = SyntheticEvent; + +export type NativeProps = $ReadOnly< {| + ...ViewProps, + + /** + * Little red bubble that sits at the top right of the icon. + */ + badge?: ?(string | number), + + /** + * Background color for the badge. Available since iOS 10. + */ + badgeColor?: ColorValue, + + /** + * Items comes with a few predefined system icons. Note that if you are + * using them, the title and selectedIcon will be overridden with the + * system ones. + */ + systemIcon?: ?( + | 'bookmarks' + | 'contacts' + | 'downloads' + | 'favorites' + | 'featured' + | 'history' + | 'more' + | 'most-recent' + | 'most-viewed' + | 'recents' + | 'search' + | 'top-rated' + ), + + /** + * A custom icon for the tab. It is ignored when a system icon is defined. + */ + icon?: ?ImageSource, + + /** + * A custom icon when the tab is selected. It is ignored when a system + * icon is defined. If left empty, the icon will be tinted in blue. + */ + selectedIcon?: ?ImageSource, + + /** + * Callback when this tab is being selected, you should change the state of your + * component to set selected={true}. + */ + onPress?: ?(event: TabBarItemEvent) => mixed, + + /** + * If set to true it renders the image as original, + * it defaults to being displayed as a template + */ + renderAsOriginal?: ?boolean, + + /** + * It specifies whether the children are visible or not. If you see a + * blank content, you probably forgot to add a selected one. + */ + selected?: ?boolean, + + /** + * Text that appears under the icon. It is ignored when a system icon + * is defined. + */ + title?: ?string, + + /** + * *(Apple TV only)* When set to true, this view will be focusable + * and navigable using the Apple TV remote. + * + * @platform ios + */ + isTVSelectable?: ?boolean, +|} >; + +module.exports = requireNativeComponent('RCTTabBarItem'); diff --git a/Libraries/Components/TabBarIOS/RCTTabBarNativeComponent.js b/Libraries/Components/TabBarIOS/RCTTabBarNativeComponent.js new file mode 100644 index 0000000000..aec139290e --- /dev/null +++ b/Libraries/Components/TabBarIOS/RCTTabBarNativeComponent.js @@ -0,0 +1,31 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +'use strict'; + +const requireNativeComponent = require('../../ReactNative/requireNativeComponent'); + +import type {ViewProps} from '../View/ViewPropTypes'; +import type {ColorValue} from '../../StyleSheet/StyleSheetTypes'; + +type NativeProps = $ReadOnly<{| + ...ViewProps, + unselectedTintColor?: ColorValue, + tintColor?: ColorValue, + unselectedItemTintColor?: ColorValue, + barTintColor?: ColorValue, + barStyle?: ?('default' | 'black'), + translucent?: ?boolean, + itemPositioning?: ?('fill' | 'center' | 'auto'), +|}>; + +// type RCTTabBarNativeType = Class>; + +module.exports = (requireNativeComponent('RCTTabBar'): any); diff --git a/Libraries/Components/TabBarIOS/TabBarIOS.android.js b/Libraries/Components/TabBarIOS/TabBarIOS.android.js new file mode 100644 index 0000000000..fd0c9743e9 --- /dev/null +++ b/Libraries/Components/TabBarIOS/TabBarIOS.android.js @@ -0,0 +1,49 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +'use strict'; + +import React from 'react'; +const StyleSheet = require('../../StyleSheet/StyleSheet'); +const TabBarItemIOS = require('./TabBarItemIOS'); +const View = require('../View/View'); + +let showedDeprecationWarning = false; + +class DummyTabBarIOS extends React.Component<$FlowFixMeProps> { + static Item = TabBarItemIOS; + + componentDidMount() { + if (!showedDeprecationWarning) { + console.warn( + 'TabBarIOS and TabBarItemIOS are deprecated and will be removed in a future release. ' + + 'Please use react-native-tab-view instead.', + ); + + showedDeprecationWarning = true; + } + } + + render() { + return ( + + {this.props.children} + + ); + } +} + +const styles = StyleSheet.create({ + tabGroup: { + flex: 1, + }, +}); + +module.exports = DummyTabBarIOS; diff --git a/Libraries/Components/TabBarIOS/TabBarIOS.ios.js b/Libraries/Components/TabBarIOS/TabBarIOS.ios.js new file mode 100644 index 0000000000..873b8020fb --- /dev/null +++ b/Libraries/Components/TabBarIOS/TabBarIOS.ios.js @@ -0,0 +1,108 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +'use strict'; + +const React = require('react'); +const StyleSheet = require('../../StyleSheet/StyleSheet'); +const TabBarItemIOS = require('./TabBarItemIOS'); + +const RCTTabBar = require('./RCTTabBarNativeComponent'); + +import type {ViewProps} from '../View/ViewPropTypes'; +import type {ColorValue} from '../../StyleSheet/StyleSheetTypes'; + +type Props = $ReadOnly<{| + ...ViewProps, + + /** + * Color of text on unselected tabs + */ + unselectedTintColor?: ColorValue, + + /** + * Color of the currently selected tab icon + */ + tintColor?: ColorValue, + + /** + * Color of unselected tab icons. Available since iOS 10. + */ + unselectedItemTintColor?: ColorValue, + + /** + * Background color of the tab bar + */ + barTintColor?: ColorValue, + + /** + * The style of the tab bar. Supported values are 'default', 'black'. + * Use 'black' instead of setting `barTintColor` to black. This produces + * a tab bar with the native iOS style with higher translucency. + */ + barStyle?: ?('default' | 'black'), + + /** + * A Boolean value that indicates whether the tab bar is translucent + */ + translucent?: ?boolean, + + /** + * Specifies tab bar item positioning. Available values are: + * - fill - distributes items across the entire width of the tab bar + * - center - centers item in the available tab bar space + * - auto (default) - distributes items dynamically according to the + * user interface idiom. In a horizontally compact environment (e.g. iPhone 5) + * this value defaults to `fill`, in a horizontally regular one (e.g. iPad) + * it defaults to center. + */ + itemPositioning?: ?('fill' | 'center' | 'auto'), +|}>; + +let showedDeprecationWarning = true; + +class TabBarIOS extends React.Component { + static Item: React.Node = TabBarItemIOS; + + componentDidMount() { + if (!showedDeprecationWarning) { + console.warn( + 'TabBarIOS and TabBarItemIOS are deprecated and will be removed in a future release. ' + + 'Please use react-native-tab-view instead.', + ); + + showedDeprecationWarning = true; + } + } + + render(): React.Node { + return ( + + {this.props.children} + + ); + } +} + +const styles = StyleSheet.create({ + tabGroup: { + flex: 1, + }, +}); + +module.exports = TabBarIOS; diff --git a/Libraries/Components/TabBarIOS/TabBarItemIOS.android.js b/Libraries/Components/TabBarIOS/TabBarItemIOS.android.js new file mode 100644 index 0000000000..0ba5bae9a1 --- /dev/null +++ b/Libraries/Components/TabBarIOS/TabBarItemIOS.android.js @@ -0,0 +1,53 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +'use strict'; + +import React from 'react'; +const View = require('../View/View'); +const StyleSheet = require('../../StyleSheet/StyleSheet'); + +let showedDeprecationWarning = false; + +class DummyTab extends React.Component { + componentDidMount() { + if (!showedDeprecationWarning) { + console.warn( + 'TabBarIOS and TabBarItemIOS are deprecated and will be removed in a future release. ' + + 'Please use react-native-tab-view instead.', + ); + + showedDeprecationWarning = true; + } + } + + render() { + if (!this.props.selected) { + return ; + } + return ( + {this.props.children} + ); + } +} + +const styles = StyleSheet.create({ + tab: { + // TODO(5405356): Implement overflow: visible so position: absolute isn't useless + // position: 'absolute', + top: 0, + right: 0, + bottom: 0, + left: 0, + borderColor: 'red', + borderWidth: 1, + }, +}); + +module.exports = DummyTab; diff --git a/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js b/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js new file mode 100644 index 0000000000..f836e7c98e --- /dev/null +++ b/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js @@ -0,0 +1,169 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @noflow + */ + +'use strict'; + +import React from 'react'; +const StaticContainer = require('../StaticContainer.react'); +const StyleSheet = require('../../StyleSheet/StyleSheet'); +const View = require('../View/View'); +const RCTTabBarItemNativeComponent = require('./RCTTabBarItemNativeComponent'); + +import type {ViewProps} from '../View/ViewPropTypes'; +import type {ColorValue} from '../../StyleSheet/StyleSheetTypes'; +import type {SyntheticEvent} from '../../Types/CoreEventTypes'; +import type {ImageSource} from '../../Image/ImageSource'; + +type Props = $ReadOnly<{| + ...ViewProps, + + /** + * Little red bubble that sits at the top right of the icon. + */ + badge?: ?(string | number), + + /** + * Background color for the badge. Available since iOS 10. + */ + badgeColor?: ColorValue, + + /** + * Items comes with a few predefined system icons. Note that if you are + * using them, the title and selectedIcon will be overridden with the + * system ones. + */ + systemIcon?: ?( + | 'bookmarks' + | 'contacts' + | 'downloads' + | 'favorites' + | 'featured' + | 'history' + | 'more' + | 'most-recent' + | 'most-viewed' + | 'recents' + | 'search' + | 'top-rated' + ), + + /** + * A custom icon for the tab. It is ignored when a system icon is defined. + */ + icon?: ?ImageSource, + + /** + * A custom icon when the tab is selected. It is ignored when a system + * icon is defined. If left empty, the icon will be tinted in blue. + */ + selectedIcon?: ?ImageSource, + + /** + * Callback when this tab is being selected, you should change the state of your + * component to set selected={true}. + */ + onPress?: ?(event: SyntheticEvent) => mixed, + + /** + * If set to true it renders the image as original, + * it defaults to being displayed as a template + */ + renderAsOriginal?: ?boolean, + + /** + * It specifies whether the children are visible or not. If you see a + * blank content, you probably forgot to add a selected one. + */ + selected?: ?boolean, + + /** + * Text that appears under the icon. It is ignored when a system icon + * is defined. + */ + title?: ?string, + + /** + * *(Apple TV only)* When set to true, this view will be focusable + * and navigable using the Apple TV remote. + * + * @platform ios + */ + isTVSelectable?: ?boolean, +|}>; + +type State = {| + hasBeenSelected: boolean, +|}; + +let showedDeprecationWarning = true; + +class TabBarItemIOS extends React.Component { + state = { + hasBeenSelected: false, + }; + + UNSAFE_componentWillMount() { + if (this.props.selected) { + this.setState({hasBeenSelected: true}); + } + } + + UNSAFE_componentWillReceiveProps(nextProps: Props) { + if (this.state.hasBeenSelected || nextProps.selected) { + this.setState({hasBeenSelected: true}); + } + } + + componentDidMount() { + if (!showedDeprecationWarning) { + console.warn( + 'TabBarIOS and TabBarItemIOS are deprecated and will be removed in a future release. ' + + 'Please use react-native-tab-view instead.', + ); + + showedDeprecationWarning = true; + } + } + + render() { + const {style, children, ...props} = this.props; + + // if the tab has already been shown once, always continue to show it so we + // preserve state between tab transitions + let tabContents; + if (this.state.hasBeenSelected) { + tabContents = ( + + {children} + + ); + } else { + tabContents = ; + } + + return ( + + {tabContents} + + ); + } +} + +const styles = StyleSheet.create({ + tab: { + position: 'absolute', + top: 0, + right: 0, + bottom: 0, + left: 0, + }, +}); + +module.exports = TabBarItemIOS; diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index d5d167d61e..6d90d5c2a8 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -24,6 +24,7 @@ import nullthrows from 'nullthrows'; import setAndForwardRef from '../../Utilities/setAndForwardRef'; import usePressability from '../../Pressability/usePressability'; +import warnOnce from '../../Utilities/warnOnce'; import type {ViewProps} from '../View/ViewPropTypes'; import type { @@ -1232,6 +1233,13 @@ function InternalTextInput(props: Props): React.Node { (props.unstable_onChangeSync || props.unstable_onChangeTextSync) && !(props.onChange || props.onChangeText); + if (props.multiline && Platform.isTVOS) { + warnOnce( + 'text-input-multiline-tvos', + 'Multiline TextInput not supported on Apple TV. See https://github.com/react-native-tvos/react-native-tvos/issues/109', + ); + } + textInput = ( boolean, + onBlur: (event: BlurEvent) => mixed, + onFocus: (event: FocusEvent) => mixed, + onPress: (event: PressEvent) => mixed, +|}>; + +export default class TVTouchable { + _tvEventHandler: TVEventHandler; + + constructor(component: any, config: TVTouchableConfig) { + invariant(Platform.isTV, 'TVTouchable: Requires `Platform.isTV`.'); + this._tvEventHandler = new TVEventHandler(); + this._tvEventHandler.enable(component, (_, tvData) => { + tvData.dispatchConfig = {}; + if (ReactNative.findNodeHandle(component) === tvData.tag) { + if (tvData.eventType === 'focus') { + config.onFocus(tvData); + } else if (tvData.eventType === 'blur') { + config.onBlur(tvData); + } else if (tvData.eventType === 'select') { + if (!config.getDisabled()) { + config.onPress(tvData); + } + } + } + }); + } + + destroy(): void { + this._tvEventHandler.disable(); + } +} diff --git a/Libraries/Components/Touchable/Touchable.js b/Libraries/Components/Touchable/Touchable.js index 47fdfa892d..f663e131cf 100644 --- a/Libraries/Components/Touchable/Touchable.js +++ b/Libraries/Components/Touchable/Touchable.js @@ -9,11 +9,13 @@ */ import * as React from 'react'; +import ReactNative from '../../Renderer/shims/ReactNative'; import BoundingDimensions from './BoundingDimensions'; import Platform from '../../Utilities/Platform'; import Position from './Position'; import UIManager from '../../ReactNative/UIManager'; import SoundManager from '../Sound/SoundManager'; +import {tvFocusEventHandler} from '../AppleTV/TVFocusEventHandler'; import {PressabilityDebugView} from '../../Pressability/PressabilityDebug'; @@ -365,12 +367,32 @@ const TouchableMixin = { if (!Platform.isTV) { return; } + if (!tvFocusEventHandler) { + return; + } + + this._componentTag = ReactNative.findNodeHandle(this); + tvFocusEventHandler.register(this._componentTag, evt => { + evt.dispatchConfig = {}; + if (evt.eventType === 'focus') { + this.touchableHandleFocus(evt); + } else if (evt.eventType === 'blur') { + this.touchableHandleBlur(evt); + } else if (evt.eventType === 'select' && Platform.OS !== 'android') { + this.touchableHandlePress && + !this.props.disabled && + this.touchableHandlePress(evt); + } + }); }, /** * Clear all timeouts on unmount */ componentWillUnmount: function () { + if (tvFocusEventHandler) { + tvFocusEventHandler.unregister(this._componentTag); + } this.touchableDelayTimeout && clearTimeout(this.touchableDelayTimeout); this.longPressDelayTimeout && clearTimeout(this.longPressDelayTimeout); this.pressOutDelayTimeout && clearTimeout(this.pressOutDelayTimeout); diff --git a/Libraries/Components/Touchable/TouchableBounce.js b/Libraries/Components/Touchable/TouchableBounce.js index 60f4050e77..4d044f8076 100644 --- a/Libraries/Components/Touchable/TouchableBounce.js +++ b/Libraries/Components/Touchable/TouchableBounce.js @@ -13,6 +13,7 @@ import Pressability, { } from '../../Pressability/Pressability'; import {PressabilityDebugView} from '../../Pressability/PressabilityDebug'; import type {ViewStyleProp} from '../../StyleSheet/StyleSheet'; +import TVTouchable from './TVTouchable'; import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback'; import {Animated, Platform} from 'react-native'; import * as React from 'react'; @@ -35,6 +36,8 @@ type State = $ReadOnly<{| |}>; class TouchableBounce extends React.Component { + _tvTouchable: ?TVTouchable; + state: State = { pressability: new Pressability(this._createPressabilityConfig()), scale: new Animated.Value(1), @@ -164,11 +167,39 @@ class TouchableBounce extends React.Component { ); } + componentDidMount(): void { + if (Platform.isTV) { + this._tvTouchable = new TVTouchable(this, { + getDisabled: () => this.props.disabled === true, + onBlur: event => { + if (this.props.onBlur != null) { + this.props.onBlur(event); + } + }, + onFocus: event => { + if (this.props.onFocus != null) { + this.props.onFocus(event); + } + }, + onPress: event => { + if (this.props.onPress != null) { + this.props.onPress(event); + } + }, + }); + } + } + componentDidUpdate(prevProps: Props, prevState: State) { this.state.pressability.configure(this._createPressabilityConfig()); } componentWillUnmount(): void { + if (Platform.isTV) { + if (this._tvTouchable != null) { + this._tvTouchable.destroy(); + } + } this.state.pressability.reset(); } } diff --git a/Libraries/Components/Touchable/TouchableHighlight.js b/Libraries/Components/Touchable/TouchableHighlight.js index 3c6a3fe2bd..b558be8844 100644 --- a/Libraries/Components/Touchable/TouchableHighlight.js +++ b/Libraries/Components/Touchable/TouchableHighlight.js @@ -14,9 +14,11 @@ import Pressability, { import {PressabilityDebugView} from '../../Pressability/PressabilityDebug'; import StyleSheet, {type ViewStyleProp} from '../../StyleSheet/StyleSheet'; import type {ColorValue} from '../../StyleSheet/StyleSheet'; +import TVTouchable from './TVTouchable'; import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback'; import Platform from '../../Utilities/Platform'; import View from '../../Components/View/View'; +import type {ViewProps} from '../../Components/View/ViewPropTypes'; import * as React from 'react'; type AndroidProps = $ReadOnly<{| @@ -27,14 +29,10 @@ type AndroidProps = $ReadOnly<{| nextFocusUp?: ?number, |}>; -type IOSProps = $ReadOnly<{| - hasTVPreferredFocus?: ?boolean, -|}>; - type Props = $ReadOnly<{| ...React.ElementConfig, ...AndroidProps, - ...IOSProps, + ...ViewProps, activeOpacity?: ?number, underlayColor?: ?ColorValue, @@ -155,6 +153,7 @@ type State = $ReadOnly<{| class TouchableHighlight extends React.Component { _hideTimeout: ?TimeoutID; _isMounted: boolean = false; + _tvTouchable: ?TVTouchable; state: State = { pressability: new Pressability(this._createPressabilityConfig()), @@ -311,7 +310,9 @@ class TouchableHighlight extends React.Component { )} onLayout={this.props.onLayout} hitSlop={this.props.hitSlop} - hasTVPreferredFocus={this.props.hasTVPreferredFocus} + hasTVPreferredFocus={this.props.hasTVPreferredFocus === true} + isTVSelectable={this.props.isTVSelectable !== false && this.props.accessible !== false} + tvParallaxProperties={this.props.tvParallaxProperties} nextFocusDown={this.props.nextFocusDown} nextFocusForward={this.props.nextFocusForward} nextFocusLeft={this.props.nextFocusLeft} @@ -339,6 +340,32 @@ class TouchableHighlight extends React.Component { componentDidMount(): void { this._isMounted = true; + if (Platform.isTV) { + this._tvTouchable = new TVTouchable(this, { + getDisabled: () => this.props.disabled === true, + onBlur: event => { + if (Platform.isTV) { + this._hideUnderlay(); + } + if (this.props.onBlur != null) { + this.props.onBlur(event); + } + }, + onFocus: event => { + if (Platform.isTV) { + this._showUnderlay(); + } + if (this.props.onFocus != null) { + this.props.onFocus(event); + } + }, + onPress: event => { + if (this.props.onPress != null && Platform.OS !== 'android') { + this.props.onPress(event); + } + }, + }); + } } componentDidUpdate(prevProps: Props, prevState: State) { @@ -350,6 +377,11 @@ class TouchableHighlight extends React.Component { if (this._hideTimeout != null) { clearTimeout(this._hideTimeout); } + if (Platform.isTV) { + if (this._tvTouchable != null) { + this._tvTouchable.destroy(); + } + } this.state.pressability.reset(); } } diff --git a/Libraries/Components/Touchable/TouchableNativeFeedback.js b/Libraries/Components/Touchable/TouchableNativeFeedback.js index 1ae3119eba..a470f47e05 100644 --- a/Libraries/Components/Touchable/TouchableNativeFeedback.js +++ b/Libraries/Components/Touchable/TouchableNativeFeedback.js @@ -12,10 +12,11 @@ import Pressability, { type PressabilityConfig, } from '../../Pressability/Pressability'; import {PressabilityDebugView} from '../../Pressability/PressabilityDebug'; +import TVTouchable from './TVTouchable'; import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback'; -import {Commands} from 'react-native/Libraries/Components/View/ViewNativeComponent'; -import ReactNative from 'react-native/Libraries/Renderer/shims/ReactNative'; -import type {PressEvent} from 'react-native/Libraries/Types/CoreEventTypes'; +import {Commands} from '../../Components/View/ViewNativeComponent'; +import ReactNative from '../../Renderer/shims/ReactNative'; +import type {PressEvent} from '../../Types/CoreEventTypes'; import Platform from '../../Utilities/Platform'; import View from '../../Components/View/View'; import processColor from '../../StyleSheet/processColor'; @@ -157,6 +158,8 @@ class TouchableNativeFeedback extends React.Component { static canUseNativeForeground: () => boolean = () => Platform.OS === 'android' && Platform.Version >= 23; + _tvTouchable: ?TVTouchable; + state: State = { pressability: new Pressability(this._createPressabilityConfig()), }; @@ -300,11 +303,39 @@ class TouchableNativeFeedback extends React.Component { ); } + componentDidMount(): void { + if (Platform.isTV) { + this._tvTouchable = new TVTouchable(this, { + getDisabled: () => this.props.disabled === true, + onBlur: event => { + if (this.props.onBlur != null) { + this.props.onBlur(event); + } + }, + onFocus: event => { + if (this.props.onFocus != null) { + this.props.onFocus(event); + } + }, + onPress: event => { + if (this.props.onPress != null) { + this.props.onPress(event); + } + }, + }); + } + } + componentDidUpdate(prevProps: Props, prevState: State) { this.state.pressability.configure(this._createPressabilityConfig()); } componentWillUnmount(): void { + if (Platform.isTV) { + if (this._tvTouchable != null) { + this._tvTouchable.destroy(); + } + } this.state.pressability.reset(); } } diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index 089c50c008..7ee459e0d8 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -12,16 +12,21 @@ import Pressability, { type PressabilityConfig, } from '../../Pressability/Pressability'; import {PressabilityDebugView} from '../../Pressability/PressabilityDebug'; +import TVTouchable from './TVTouchable'; import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback'; -import Animated from 'react-native/Libraries/Animated/Animated'; -import Easing from 'react-native/Libraries/Animated/Easing'; -import type {ViewStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet'; -import flattenStyle from 'react-native/Libraries/StyleSheet/flattenStyle'; +import Animated from '../../Animated/Animated'; +import Easing from '../../Animated/Easing'; +import type {ViewStyleProp} from '../../StyleSheet/StyleSheet'; +import flattenStyle from '../../StyleSheet/flattenStyle'; import Platform from '../../Utilities/Platform'; +import typeof TVParallaxPropertiesType from '../AppleTV/TVViewPropTypes'; + import * as React from 'react'; type TVProps = $ReadOnly<{| hasTVPreferredFocus?: ?boolean, + isTVSelectable?: ?boolean, + tvParallaxProperties?: ?TVParallaxPropertiesType, nextFocusDown?: ?number, nextFocusForward?: ?number, nextFocusLeft?: ?number, @@ -129,6 +134,8 @@ type State = $ReadOnly<{| * */ class TouchableOpacity extends React.Component { + _tvTouchable: ?TVTouchable; + state: State = { anim: new Animated.Value(this._getChildStyleOpacityWithDefault()), pressability: new Pressability(this._createPressabilityConfig()), @@ -243,7 +250,9 @@ class TouchableOpacity extends React.Component { nextFocusLeft={this.props.nextFocusLeft} nextFocusRight={this.props.nextFocusRight} nextFocusUp={this.props.nextFocusUp} - hasTVPreferredFocus={this.props.hasTVPreferredFocus} + hasTVPreferredFocus={this.props.hasTVPreferredFocus === true} + isTVSelectable={this.props.isTVSelectable !== false && this.props.accessible !== false} + tvParallaxProperties={this.props.tvParallaxProperties} hitSlop={this.props.hitSlop} focusable={ this.props.focusable !== false && this.props.onPress !== undefined @@ -258,6 +267,31 @@ class TouchableOpacity extends React.Component { ); } + componentDidMount(): void { + if (Platform.isTV) { + this._tvTouchable = new TVTouchable(this, { + getDisabled: () => this.props.disabled === true, + onBlur: event => { + this._opacityInactive(250); + if (this.props.onBlur != null) { + this.props.onBlur(event); + } + }, + onFocus: event => { + this._opacityActive(150); + if (this.props.onFocus != null) { + this.props.onFocus(event); + } + }, + onPress: event => { + if (this.props.onPress != null && Platform.OS !== "android") { + this.props.onPress(event); + } + }, + }); + } + } + componentDidUpdate(prevProps: Props, prevState: State) { this.state.pressability.configure(this._createPressabilityConfig()); if (this.props.disabled !== prevProps.disabled) { @@ -266,6 +300,11 @@ class TouchableOpacity extends React.Component { } componentWillUnmount(): void { + if (Platform.isTV) { + if (this._tvTouchable != null) { + this._tvTouchable.destroy(); + } + } this.state.pressability.reset(); } } diff --git a/Libraries/Components/Touchable/TouchableWithoutFeedback.js b/Libraries/Components/Touchable/TouchableWithoutFeedback.js index b1bb99c28a..b1b2d1e957 100755 --- a/Libraries/Components/Touchable/TouchableWithoutFeedback.js +++ b/Libraries/Components/Touchable/TouchableWithoutFeedback.js @@ -12,6 +12,7 @@ import Pressability, { type PressabilityConfig, } from '../../Pressability/Pressability'; import {PressabilityDebugView} from '../../Pressability/PressabilityDebug'; +import TVTouchable from './TVTouchable'; import type { AccessibilityActionEvent, AccessibilityActionInfo, @@ -26,6 +27,7 @@ import type { LayoutEvent, PressEvent, } from '../../Types/CoreEventTypes'; +import Platform from '../../Utilities/Platform'; import View from '../../Components/View/View'; import * as React from 'react'; @@ -89,6 +91,8 @@ const PASSTHROUGH_PROPS = [ ]; class TouchableWithoutFeedback extends React.Component { + _tvTouchable: ?TVTouchable; + state: State = { pressability: new Pressability(createPressabilityConfig(this.props)), }; @@ -131,11 +135,39 @@ class TouchableWithoutFeedback extends React.Component { return React.cloneElement(element, elementProps, ...children); } + componentDidMount(): void { + if (Platform.isTV) { + this._tvTouchable = new TVTouchable(this, { + getDisabled: () => this.props.disabled === true, + onBlur: event => { + if (this.props.onBlur != null) { + this.props.onBlur(event); + } + }, + onFocus: event => { + if (this.props.onFocus != null) { + this.props.onFocus(event); + } + }, + onPress: event => { + if (this.props.onPress != null) { + this.props.onPress(event); + } + }, + }); + } + } + componentDidUpdate(): void { this.state.pressability.configure(createPressabilityConfig(this.props)); } componentWillUnmount(): void { + if (Platform.isTV) { + if (this._tvTouchable != null) { + this._tvTouchable.destroy(); + } + } this.state.pressability.reset(); } } diff --git a/Libraries/Components/View/ReactNativeViewViewConfig.js b/Libraries/Components/View/ReactNativeViewViewConfig.js index e56eb0dfe3..ede5678c84 100644 --- a/Libraries/Components/View/ReactNativeViewViewConfig.js +++ b/Libraries/Components/View/ReactNativeViewViewConfig.js @@ -10,6 +10,7 @@ import type {ViewConfig} from '../../Renderer/shims/ReactNativeTypes'; import ReactNativeViewViewConfigAndroid from './ReactNativeViewViewConfigAndroid'; +import ReactNativeViewViewConfigAppleTV from './ReactNativeViewViewConfigAppleTV'; import {Platform} from 'react-native'; const ReactNativeViewConfig: ViewConfig = { @@ -124,6 +125,7 @@ const ReactNativeViewConfig: ViewConfig = { }, validAttributes: { ...ReactNativeViewViewConfigAndroid.validAttributes, + ...ReactNativeViewViewConfigAppleTV.validAttributes, accessibilityActions: true, accessibilityElementsHidden: true, accessibilityHint: true, diff --git a/Libraries/Components/View/ReactNativeViewViewConfigAppleTV.js b/Libraries/Components/View/ReactNativeViewViewConfigAppleTV.js new file mode 100644 index 0000000000..b637710438 --- /dev/null +++ b/Libraries/Components/View/ReactNativeViewViewConfigAppleTV.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +'use strict'; + +const ReactNativeViewViewConfigAppleTV = { + validAttributes: { + isTVSelectable: true, + tvParallaxProperties: true, + }, +}; + +module.exports = ReactNativeViewViewConfigAppleTV; diff --git a/Libraries/Components/View/ViewNativeComponent.js b/Libraries/Components/View/ViewNativeComponent.js index 9d6db6e23c..5b1db3c674 100644 --- a/Libraries/Components/View/ViewNativeComponent.js +++ b/Libraries/Components/View/ViewNativeComponent.js @@ -33,10 +33,15 @@ interface NativeCommands { viewRef: React.ElementRef>, pressed: boolean, ) => void; + // Apple TV focus guide API + +setDestinations: ( + viewRef: React.ElementRef>, + destinations: Array>>, + ) => void; } export const Commands: NativeCommands = codegenNativeCommands({ - supportedCommands: ['hotspotUpdate', 'setPressed'], + supportedCommands: ['hotspotUpdate', 'setPressed', 'setDestinations'], }); export default ViewNativeComponent; diff --git a/Libraries/Components/View/ViewPropTypes.js b/Libraries/Components/View/ViewPropTypes.js index 0e6f739470..1cea7f6493 100644 --- a/Libraries/Components/View/ViewPropTypes.js +++ b/Libraries/Components/View/ViewPropTypes.js @@ -21,6 +21,7 @@ import type { import type {EdgeInsetsProp} from '../../StyleSheet/EdgeInsetsPropType'; import type {Node} from 'react'; import type {ViewStyleProp} from '../../StyleSheet/StyleSheet'; +import type {TVViewProps} from '../AppleTV/TVViewPropTypes'; import type { AccessibilityRole, AccessibilityState, @@ -280,20 +281,6 @@ type AndroidViewProps = $ReadOnly<{| */ importantForAccessibility?: ?('auto' | 'yes' | 'no' | 'no-hide-descendants'), - /** - * Whether to force the Android TV focus engine to move focus to this view. - * - * @platform android - */ - hasTVPreferredFocus?: ?boolean, - - /** - * TV next focus down (see documentation for the View component). - * - * @platform android - */ - nextFocusDown?: ?number, - /** * TV next focus forward (see documentation for the View component). * @@ -301,27 +288,6 @@ type AndroidViewProps = $ReadOnly<{| */ nextFocusForward?: ?number, - /** - * TV next focus left (see documentation for the View component). - * - * @platform android - */ - nextFocusLeft?: ?number, - - /** - * TV next focus right (see documentation for the View component). - * - * @platform android - */ - nextFocusRight?: ?number, - - /** - * TV next focus up (see documentation for the View component). - * - * @platform android - */ - nextFocusUp?: ?number, - /** * Whether this `View` should be focusable with a non-touch input device, eg. receive focus with a hardware keyboard. * @@ -376,6 +342,32 @@ type IOSViewProps = $ReadOnly<{| shouldRasterizeIOS?: ?boolean, |}>; +type NextFocusProps = $ReadOnly<{| + /** + * TV next focus down (see documentation for the View component). + * + */ + nextFocusDown?: ?number, + + /** + * TV next focus left (see documentation for the View component). + * + */ + nextFocusLeft?: ?number, + + /** + * TV next focus right (see documentation for the View component). + * + */ + nextFocusRight?: ?number, + + /** + * TV next focus up (see documentation for the View component). + * + */ + nextFocusUp?: ?number, +|}>; + export type ViewProps = $ReadOnly<{| ...BubblingEventProps, ...DirectEventProps, @@ -384,6 +376,8 @@ export type ViewProps = $ReadOnly<{| ...TouchEventProps, ...AndroidViewProps, ...IOSViewProps, + ...TVViewProps, + ...NextFocusProps, children?: Node, style?: ?ViewStyleProp, diff --git a/Libraries/Core/ReactNativeVersion.js b/Libraries/Core/ReactNativeVersion.js index 4cdef62f72..a34d3ce94b 100644 --- a/Libraries/Core/ReactNativeVersion.js +++ b/Libraries/Core/ReactNativeVersion.js @@ -1,7 +1,13 @@ /** +<<<<<<< HEAD * @generated by scripts/set-rn-version.js * * Copyright (c) Meta Platforms, Inc. and affiliates. +======= + * @generated by scripts/bump-oss-version.js + * + * Copyright (c) Facebook, Inc. and its affiliates. +>>>>>>> tvos-v0.66.3 * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. @@ -13,5 +19,5 @@ exports.version = { major: 0, minor: 68, patch: 0, - prerelease: 'rc.2', + prerelease: null, }; diff --git a/Libraries/DeprecatedPropTypes/DeprecatedTVViewPropTypes.js b/Libraries/DeprecatedPropTypes/DeprecatedTVViewPropTypes.js new file mode 100644 index 0000000000..22628f0e46 --- /dev/null +++ b/Libraries/DeprecatedPropTypes/DeprecatedTVViewPropTypes.js @@ -0,0 +1,23 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow strict-local + */ + +'use strict'; + +const PropTypes = require('prop-types'); + +const DeprecatedTVViewPropTypes = { + hasTVPreferredFocus: PropTypes.bool, + tvParallaxShiftDistanceX: PropTypes.number, + tvParallaxShiftDistanceY: PropTypes.number, + tvParallaxTiltAngle: PropTypes.number, + tvParallaxMagnification: PropTypes.number, +}; + +module.exports = DeprecatedTVViewPropTypes; diff --git a/Libraries/FBLazyVector/FBLazyVector.podspec b/Libraries/FBLazyVector/FBLazyVector.podspec index c4e844ece0..42c0a01e1d 100644 --- a/Libraries/FBLazyVector/FBLazyVector.podspec +++ b/Libraries/FBLazyVector/FBLazyVector.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.source = source s.source_files = "**/*.{c,h,m,mm,cpp}" s.header_dir = "FBLazyVector" diff --git a/Libraries/Image/RCTImageView.mm b/Libraries/Image/RCTImageView.mm index c807372d19..46c22617c3 100644 --- a/Libraries/Image/RCTImageView.mm +++ b/Libraries/Image/RCTImageView.mm @@ -101,7 +101,7 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge name:UIApplicationDidEnterBackgroundNotification object:nil]; #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 - if (@available(iOS 13.0, *)) { + if (@available(iOS 13.0, tvOS 13.0, *)) { [center addObserver:self selector:@selector(clearImageIfDetached) diff --git a/Libraries/Image/React-RCTImage.podspec b/Libraries/Image/React-RCTImage.podspec index bdbd33ddc9..4eb689fb9d 100644 --- a/Libraries/Image/React-RCTImage.podspec +++ b/Libraries/Image/React-RCTImage.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.documentation_url = "https://reactnative.dev/docs/image" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "*.{m,mm}" diff --git a/Libraries/LinkingIOS/React-RCTLinking.podspec b/Libraries/LinkingIOS/React-RCTLinking.podspec index a50ce131f6..6c2037dcf6 100644 --- a/Libraries/LinkingIOS/React-RCTLinking.podspec +++ b/Libraries/LinkingIOS/React-RCTLinking.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.documentation_url = "https://reactnative.dev/docs/linking" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "*.{m,mm}" diff --git a/Libraries/LogBox/LogBoxInspectorContainer.js b/Libraries/LogBox/LogBoxInspectorContainer.js index c163ba7733..30257dbdb6 100644 --- a/Libraries/LogBox/LogBoxInspectorContainer.js +++ b/Libraries/LogBox/LogBoxInspectorContainer.js @@ -9,7 +9,8 @@ */ import * as React from 'react'; -import {View, StyleSheet} from 'react-native'; +import View from '../Components/View/View'; +import StyleSheet from '../StyleSheet/StyleSheet'; import * as LogBoxData from './Data/LogBoxData'; import LogBoxInspector from './UI/LogBoxInspector'; import type LogBoxLog from './Data/LogBoxLog'; diff --git a/Libraries/LogBox/LogBoxNotificationContainer.js b/Libraries/LogBox/LogBoxNotificationContainer.js index a1291e5f01..f61bc60af7 100644 --- a/Libraries/LogBox/LogBoxNotificationContainer.js +++ b/Libraries/LogBox/LogBoxNotificationContainer.js @@ -11,6 +11,7 @@ import * as React from 'react'; import StyleSheet from '../StyleSheet/StyleSheet'; import View from '../Components/View/View'; +import Platform from '../Utilities/Platform'; import * as LogBoxData from './Data/LogBoxData'; import LogBoxLog from './Data/LogBoxLog'; import LogBoxLogNotification from './UI/LogBoxNotification'; @@ -54,7 +55,7 @@ export function _LogBoxNotificationContainer(props: Props): React.Node { log => log.level === 'error' || log.level === 'fatal', ); return ( - + {warnings.length > 0 && ( setPressed(true)} onPressOut={() => setPressed(false)}> {content} - + ); } diff --git a/Libraries/LogBox/UI/LogBoxInspectorFooter.js b/Libraries/LogBox/UI/LogBoxInspectorFooter.js index 5e1d29e63e..eebd9e41bd 100644 --- a/Libraries/LogBox/UI/LogBoxInspectorFooter.js +++ b/Libraries/LogBox/UI/LogBoxInspectorFooter.js @@ -69,6 +69,8 @@ function FooterButton(props: ButtonProps): React.Node { const buttonStyles = StyleSheet.create({ safeArea: { flex: 1, + marginLeft: 10, + marginRight: 10, paddingBottom: DeviceInfo.getConstants().isIPhoneX_deprecated ? 30 : 0, }, content: { @@ -87,6 +89,7 @@ const buttonStyles = StyleSheet.create({ const styles = StyleSheet.create({ root: { backgroundColor: LogBoxStyle.getBackgroundColor(1), + minHeight: 30, shadowColor: '#000', shadowOffset: {width: 0, height: -2}, shadowRadius: 2, diff --git a/Libraries/LogBox/UI/LogBoxNotification.js b/Libraries/LogBox/UI/LogBoxNotification.js index 91376168dd..151114df17 100644 --- a/Libraries/LogBox/UI/LogBoxNotification.js +++ b/Libraries/LogBox/UI/LogBoxNotification.js @@ -11,6 +11,7 @@ import type {Message as MessageType} from '../Data/parseLogBoxLog'; import * as React from 'react'; import Image from '../../Image/Image'; +import Platform from '../../Utilities/Platform'; import StyleSheet from '../../StyleSheet/StyleSheet'; import Text from '../../Text/Text'; import View from '../../Components/View/View'; @@ -32,9 +33,12 @@ function LogBoxLogNotification(props: Props): React.Node { const {totalLogCount, level, log} = props; // Eagerly symbolicate so the stack is available when pressing to inspect. - React.useEffect(() => { - LogBoxData.symbolicateLogLazy(log); - }, [log]); + React.useEffect( + () => { + LogBoxData.symbolicateLogLazy(log); + }, + [log], + ); return ( @@ -48,9 +52,25 @@ function LogBoxLogNotification(props: Props): React.Node { - + {Platform.isTV ? null : ( + + )} + {Platform.isTV ? ( + + + + + + ) : null} ); } @@ -151,6 +171,7 @@ const countStyles = StyleSheet.create({ const messageStyles = StyleSheet.create({ container: { alignSelf: 'stretch', + alignItems: 'center', flexGrow: 1, flexShrink: 1, flexBasis: 'auto', @@ -195,7 +216,7 @@ const dismissStyles = StyleSheet.create({ const toastStyles = StyleSheet.create({ container: { - height: 48, + height: (Platform.isTV ? 96 : 48), position: 'relative', width: '100%', justifyContent: 'center', @@ -218,6 +239,16 @@ const toastStyles = StyleSheet.create({ flexShrink: 0, flexBasis: 'auto', }, + tvDismissContainer: { + alignItems: 'center', + flexDirection: 'row', + borderRadius: 8, + flexGrow: 0, + flexShrink: 0, + flexBasis: 'auto', + height: 30, + justifyContent: 'center', + } }); export default LogBoxLogNotification; diff --git a/Libraries/NativeAnimation/React-RCTAnimation.podspec b/Libraries/NativeAnimation/React-RCTAnimation.podspec index 94af813946..c925adb784 100644 --- a/Libraries/NativeAnimation/React-RCTAnimation.podspec +++ b/Libraries/NativeAnimation/React-RCTAnimation.podspec @@ -26,7 +26,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "{Drivers/*,Nodes/*,*}.{m,mm}" diff --git a/Libraries/NativeModules/specs/NativeLogBox.js b/Libraries/NativeModules/specs/NativeLogBox.js index 65a9f5242e..281ff85bde 100644 --- a/Libraries/NativeModules/specs/NativeLogBox.js +++ b/Libraries/NativeModules/specs/NativeLogBox.js @@ -8,8 +8,8 @@ * @format */ -import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport'; -import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry'; +import type {TurboModule} from '../../TurboModule/RCTExport'; +import * as TurboModuleRegistry from '../../TurboModule/TurboModuleRegistry'; export interface Spec extends TurboModule { +show: () => void; diff --git a/Libraries/Network/React-RCTNetwork.podspec b/Libraries/Network/React-RCTNetwork.podspec index 06c3f16869..e8f54109c4 100644 --- a/Libraries/Network/React-RCTNetwork.podspec +++ b/Libraries/Network/React-RCTNetwork.podspec @@ -26,7 +26,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "*.{m,mm}" diff --git a/Libraries/NewAppScreen/components/DebugInstructions.js b/Libraries/NewAppScreen/components/DebugInstructions.js index 35bb4a80da..2aee0e58b9 100644 --- a/Libraries/NewAppScreen/components/DebugInstructions.js +++ b/Libraries/NewAppScreen/components/DebugInstructions.js @@ -20,11 +20,20 @@ const styles = StyleSheet.create({ const DebugInstructions: () => Node = Platform.select({ ios: () => ( - - Press Cmd + D in the simulator or{' '} - Shake your device to open the React - Native debug menu. - + Platform.isTVOS ? ( + + Press Cmd + D in the simulator or{' '} + long press the play/pause button on + the Apple TV remote to open the React + Native debug menu. + + ) : ( + + Press Cmd + D in the simulator or{' '} + Shake your device to open the React + Native debug menu. + + ) ), default: () => ( diff --git a/Libraries/NewAppScreen/components/HermesBadge.js b/Libraries/NewAppScreen/components/HermesBadge.js index 9ead0caf91..e2644ee60e 100644 --- a/Libraries/NewAppScreen/components/HermesBadge.js +++ b/Libraries/NewAppScreen/components/HermesBadge.js @@ -10,27 +10,54 @@ import React from 'react'; import type {Node} from 'react'; -import {StyleSheet, Text, useColorScheme, View} from 'react-native'; +import { + Platform, + StyleSheet, + Text, + useColorScheme, + useTVEventHandler, + View, +} from 'react-native'; import Colors from './Colors'; const HermesBadge = (): Node => { + const [lastEventType, setLastEventType] = React.useState(''); + const myTVEventHandler = evt => { + setLastEventType(evt.eventType); + }; + if (Platform.isTV) { + useTVEventHandler(myTVEventHandler); + } const isDarkMode = useColorScheme() === 'dark'; const version = global.HermesInternal?.getRuntimeProperties?.()['OSS Release Version'] ?? ''; - return global.HermesInternal ? ( + return ( - - {`Engine: Hermes ${version}`} - + {global.HermesInternal ? ( + + {`Engine: Hermes ${version}`} + + ) : null} + {Platform.isTV ? ( + + TVEvent: {lastEventType} + + ) : null} - ) : null; + ); }; const styles = StyleSheet.create({ diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.h b/Libraries/PushNotificationIOS/RCTPushNotificationManager.h index cbd581536e..cc6ba6e6ee 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.h +++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.h @@ -13,7 +13,7 @@ extern NSString *const RCTRemoteNotificationReceived; typedef void (^RCTRemoteNotificationCallback)(UIBackgroundFetchResult result); -#if !TARGET_OS_UIKITFORMAC +#if !TARGET_OS_TV && !TARGET_OS_UIKITFORMAC + (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings; + (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken; + (void)didReceiveRemoteNotification:(NSDictionary *)notification; diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm b/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm index a56a88edf7..a612ff2343 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm +++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm @@ -24,7 +24,7 @@ static NSString *const kErrorUnableToRequestPermissions = @"E_UNABLE_TO_REQUEST_PERMISSIONS"; -#if !TARGET_OS_UIKITFORMAC +#if !TARGET_OS_TV @implementation RCTConvert (NSCalendarUnit) RCT_ENUM_CONVERTER(NSCalendarUnit, @@ -76,13 +76,13 @@ + (UILocalNotification *)UILocalNotification:(id)json @end #else -@interface RCTPushNotificationManager () +@interface RCTPushNotificationManager () @end -#endif // TARGET_OS_UIKITFORMAC +#endif //TARGET_OS_TV / TARGET_OS_UIKITFORMAC @implementation RCTPushNotificationManager -#if !TARGET_OS_UIKITFORMAC +#if !TARGET_OS_TV && !TARGET_OS_UIKITFORMAC static NSDictionary *RCTFormatLocalNotification(UILocalNotification *notification) { @@ -126,7 +126,7 @@ @implementation RCTPushNotificationManager return formattedNotification; } -#endif // TARGET_OS_UIKITFORMAC +#endif //TARGET_OS_TV / TARGET_OS_UIKITFORMAC RCT_EXPORT_MODULE() @@ -135,7 +135,7 @@ - (dispatch_queue_t)methodQueue return dispatch_get_main_queue(); } -#if !TARGET_OS_UIKITFORMAC +#if !TARGET_OS_TV && !TARGET_OS_UIKITFORMAC - (void)startObserving { [[NSNotificationCenter defaultCenter] addObserver:self @@ -478,7 +478,7 @@ - (void)handleRemoteNotificationRegistrationError:(NSNotification *)notification }]; } -#else // TARGET_OS_UIKITFORMAC +#else //TARGET_OS_TV / TARGET_OS_UIKITFORMAC RCT_EXPORT_METHOD(onFinishRemoteNotification:(NSString *)notificationId fetchResult:(NSString *)fetchResult) { @@ -568,7 +568,7 @@ - (void)handleRemoteNotificationRegistrationError:(NSNotification *)notification return @[]; } -#endif // TARGET_OS_UIKITFORMAC +#endif //TARGET_OS_TV / TARGET_OS_UIKITFORMAC - (std::shared_ptr)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params { diff --git a/Libraries/PushNotificationIOS/React-RCTPushNotification.podspec b/Libraries/PushNotificationIOS/React-RCTPushNotification.podspec index a23c9a9c02..1fb1c09dd0 100644 --- a/Libraries/PushNotificationIOS/React-RCTPushNotification.podspec +++ b/Libraries/PushNotificationIOS/React-RCTPushNotification.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.documentation_url = "https://reactnative.dev/docs/pushnotificationios" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "*.{m,mm}" diff --git a/Libraries/RCTRequired/RCTRequired.podspec b/Libraries/RCTRequired/RCTRequired.podspec index 7d657a0b54..19d2df553e 100644 --- a/Libraries/RCTRequired/RCTRequired.podspec +++ b/Libraries/RCTRequired/RCTRequired.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.source = source s.source_files = "**/*.{c,h,m,mm,cpp}" s.header_dir = "RCTRequired" diff --git a/Libraries/Settings/React-RCTSettings.podspec b/Libraries/Settings/React-RCTSettings.podspec index 2715eb92cd..b0c556f5de 100644 --- a/Libraries/Settings/React-RCTSettings.podspec +++ b/Libraries/Settings/React-RCTSettings.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.documentation_url = "https://reactnative.dev/docs/settings" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "*.{m,mm}" diff --git a/Libraries/Text/React-RCTText.podspec b/Libraries/Text/React-RCTText.podspec index cec2fef17e..6bdd8a6f08 100644 --- a/Libraries/Text/React-RCTText.podspec +++ b/Libraries/Text/React-RCTText.podspec @@ -24,7 +24,7 @@ Pod::Spec.new do |s| s.documentation_url = "https://reactnative.dev/docs/text" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.source = source s.source_files = "**/*.{h,m}" s.preserve_paths = "package.json", "LICENSE", "LICENSE-docs" diff --git a/Libraries/Text/Text/RCTTextView.m b/Libraries/Text/Text/RCTTextView.m index 6726e12dea..99b5163d98 100644 --- a/Libraries/Text/Text/RCTTextView.m +++ b/Libraries/Text/Text/RCTTextView.m @@ -227,7 +227,7 @@ - (void)disableContextMenu - (void)handleLongPress:(UILongPressGestureRecognizer *)gesture { // TODO: Adopt showMenuFromRect (necessary for UIKitForMac) -#if !TARGET_OS_UIKITFORMAC +#if !TARGET_OS_TV && !TARGET_OS_UIKITFORMAC UIMenuController *menuController = [UIMenuController sharedMenuController]; if (menuController.isMenuVisible) { @@ -259,6 +259,7 @@ - (BOOL)canPerformAction:(SEL)action withSender:(id)sender - (void)copy:(id)sender { +#if !TARGET_OS_TV NSAttributedString *attributedText = _textStorage; NSMutableDictionary *item = [NSMutableDictionary new]; @@ -275,6 +276,7 @@ - (void)copy:(id)sender UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; pasteboard.items = @[item]; +#endif } @end diff --git a/Libraries/Text/TextInput/Multiline/RCTMultilineTextInputViewManager.m b/Libraries/Text/TextInput/Multiline/RCTMultilineTextInputViewManager.m index 6eb74da39e..7ac86f808f 100644 --- a/Libraries/Text/TextInput/Multiline/RCTMultilineTextInputViewManager.m +++ b/Libraries/Text/TextInput/Multiline/RCTMultilineTextInputViewManager.m @@ -19,6 +19,8 @@ - (UIView *)view #pragma mark - Multiline (aka TextView) specific properties +#if !TARGET_OS_TV RCT_REMAP_VIEW_PROPERTY(dataDetectorTypes, backedTextInputView.dataDetectorTypes, UIDataDetectorTypes) +#endif @end diff --git a/Libraries/Text/TextInput/Multiline/RCTUITextView.m b/Libraries/Text/TextInput/Multiline/RCTUITextView.m index 92371bc8bf..65bc9f40f6 100644 --- a/Libraries/Text/TextInput/Multiline/RCTUITextView.m +++ b/Libraries/Text/TextInput/Multiline/RCTUITextView.m @@ -51,7 +51,9 @@ - (instancetype)initWithFrame:(CGRect)frame self.textColor = [UIColor blackColor]; // This line actually removes 5pt (default value) left and right padding in UITextView. self.textContainer.lineFragmentPadding = 0; +#if !TARGET_OS_TV self.scrollsToTop = NO; +#endif self.scrollEnabled = YES; } diff --git a/Libraries/Text/TextInput/RCTBaseTextInputView.m b/Libraries/Text/TextInput/RCTBaseTextInputView.m index a4924923f1..8b0d99a4fa 100644 --- a/Libraries/Text/TextInput/RCTBaseTextInputView.m +++ b/Libraries/Text/TextInput/RCTBaseTextInputView.m @@ -122,6 +122,10 @@ - (BOOL)textOf:(NSAttributedString*)newText equals:(NSAttributedString*)oldText{ self.backedTextInputView.isSecureTextEntry || fontHasBeenUpdatedBySystem; +#if TARGET_OS_TV + shouldFallbackToBareTextComparison = YES; +#endif + if (shouldFallbackToBareTextComparison) { return ([newText.string isEqualToString:oldText.string]); } else { @@ -249,7 +253,7 @@ - (void)setTextContentType:(NSString *)type }; #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 120000 /* __IPHONE_12_0 */ - if (@available(iOS 12.0, *)) { + if (@available(iOS 12.0, tvOS 12.0, *)) { NSDictionary * iOS12extras = @{@"newPassword": UITextContentTypeNewPassword, @"oneTimeCode": UITextContentTypeOneTimeCode}; @@ -572,6 +576,7 @@ - (void)didSetProps:(NSArray *)changedProps - (void)setCustomInputAccessoryViewWithNativeID:(NSString *)nativeID { + #if !TARGET_OS_TV __weak RCTBaseTextInputView *weakSelf = self; [_bridge.uiManager rootViewForReactTag:self.reactTag withCompletion:^(UIView *rootView) { RCTBaseTextInputView *strongSelf = weakSelf; @@ -584,10 +589,12 @@ - (void)setCustomInputAccessoryViewWithNativeID:(NSString *)nativeID } } }]; + #endif /* !TARGET_OS_TV */ } - (void)setDefaultInputAccessoryView { + #if !TARGET_OS_TV UIView *textInputView = self.backedTextInputView; UIKeyboardType keyboardType = textInputView.keyboardType; @@ -626,6 +633,7 @@ - (void)setDefaultInputAccessoryView textInputView.inputAccessoryView = nil; } [self reloadInputViewsIfNecessary]; + #endif /* !TARGET_OS_TV */ } - (void)reloadInputViewsIfNecessary diff --git a/Libraries/TypeSafety/RCTTypeSafety.podspec b/Libraries/TypeSafety/RCTTypeSafety.podspec index 198a0f09b4..9c6b2ad0e5 100644 --- a/Libraries/TypeSafety/RCTTypeSafety.podspec +++ b/Libraries/TypeSafety/RCTTypeSafety.podspec @@ -26,7 +26,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.compiler_flags = folly_compiler_flags s.source = source s.source_files = "**/*.{c,h,m,mm,cpp}" diff --git a/Libraries/Utilities/BackHandler.android.js b/Libraries/Utilities/BackHandler.android.js index 8162e8c7cd..93183917e0 100644 --- a/Libraries/Utilities/BackHandler.android.js +++ b/Libraries/Utilities/BackHandler.android.js @@ -33,6 +33,10 @@ RCTDeviceEventEmitter.addListener(DEVICE_BACK_EVENT, function () { * Android: Detect hardware back button presses, and programmatically invoke the default back button * functionality to exit the app if there are no listeners or if none of the listeners return true. * + * tvOS: Detect presses of the menu button on the TV remote. (Still to be implemented: + * programmatically disable menu button handling + * functionality to exit the app if there are no listeners or if none of the listeners return true.) + * * iOS: Not applicable. * * The event subscriptions are called in reverse order (i.e. last registered subscription first), @@ -76,7 +80,8 @@ const BackHandler: TBackHandler = { /** * Adds an event handler. Supported events: * - * - `hardwareBackPress`: Fires when the Android hardware back button is pressed. + * - `hardwareBackPress`: Fires when the Android hardware back button is pressed or when the + * tvOS menu button is pressed. */ addEventListener: function ( eventName: BackPressEventName, diff --git a/Libraries/Utilities/BackHandler.ios.js b/Libraries/Utilities/BackHandler.ios.js index 3cd0370646..98063e8044 100644 --- a/Libraries/Utilities/BackHandler.ios.js +++ b/Libraries/Utilities/BackHandler.ios.js @@ -4,18 +4,52 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * + * @flow strict-local * @format - * @flow */ +// On Apple TV, this implements back navigation using the TV remote's menu button. +// On iOS, this just implements a stub. + 'use strict'; -module.exports = require('../Components/UnimplementedViews/UnimplementedView'); +const Platform = require('./Platform'); +const TVEventHandler = require('../Components/AppleTV/TVEventHandler'); type BackPressEventName = 'backPress' | 'hardwareBackPress'; function emptyFunction(): void {} +/** + * Detect hardware button presses for back navigation. + * + * Android: Detect hardware back button presses, and programmatically invoke the default back button + * functionality to exit the app if there are no listeners or if none of the listeners return true. + * + * tvOS: Detect presses of the menu button on the TV remote. (Still to be implemented: + * programmatically disable menu button handling + * functionality to exit the app if there are no listeners or if none of the listeners return true.) + * + * iOS: Not applicable. + * + * The event subscriptions are called in reverse order (i.e. last registered subscription first), + * and if one subscription returns true then subscriptions registered earlier will not be called. + * + * Example: + * + * ```javascript + * BackHandler.addEventListener('hardwareBackPress', function() { + * // this.onMainScreen and this.goBack are just examples, you need to use your own implementation here + * // Typically you would use the navigator here to go to the last state. + * + * if (!this.onMainScreen()) { + * this.goBack(); + * return true; + * } + * return false; + * }); + * ``` + */ type TBackHandler = {| +exitApp: () => void, +addEventListener: ( @@ -28,14 +62,65 @@ type TBackHandler = {| ) => void, |}; -let BackHandler: TBackHandler = { - exitApp: emptyFunction, - addEventListener(_eventName: BackPressEventName, _handler: Function) { - return { - remove: emptyFunction, - }; - }, - removeEventListener(_eventName: BackPressEventName, _handler: Function) {}, -}; +let BackHandler: TBackHandler; + +if (Platform.isTV) { + const _tvEventHandler = new TVEventHandler(); + const _backPressSubscriptions = new Set(); + + _tvEventHandler.enable(this, function(cmp, evt) { + if (evt && evt.eventType === 'menu') { + let invokeDefault = true; + const subscriptions = Array.from( + _backPressSubscriptions.values(), + ).reverse(); + + for (let i = 0; i < subscriptions.length; ++i) { + if (subscriptions[i]()) { + invokeDefault = false; + break; + } + } + + if (invokeDefault) { + BackHandler.exitApp(); + } + } + }); + + BackHandler = { + exitApp: emptyFunction, + + addEventListener: function( + eventName: BackPressEventName, + handler: () => ?boolean, + ): {remove: () => void, ...} { + _backPressSubscriptions.add(handler); + return { + remove: () => BackHandler.removeEventListener(eventName, handler), + }; + }, + + removeEventListener: function( + eventName: BackPressEventName, + handler: () => ?boolean, + ): void { + _backPressSubscriptions.delete(handler); + }, + }; +} else { + BackHandler = { + exitApp: emptyFunction, + addEventListener(_eventName: BackPressEventName, _handler: () => ?boolean) { + return { + remove: emptyFunction, + }; + }, + removeEventListener( + _eventName: BackPressEventName, + _handler: () => ?boolean, + ) {}, + }; +} module.exports = BackHandler; diff --git a/Libraries/Vibration/NativeVibration.js b/Libraries/Vibration/NativeVibration.js index d2ce739c11..8917d27897 100644 --- a/Libraries/Vibration/NativeVibration.js +++ b/Libraries/Vibration/NativeVibration.js @@ -10,9 +10,10 @@ import type {TurboModule} from '../TurboModule/RCTExport'; import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; +import Platform from '../Utilities/Platform'; export interface Spec extends TurboModule { - +getConstants: () => {||}; + +getConstants: () => {...}; +vibrate: (pattern: number) => void; // Android only @@ -20,4 +21,17 @@ export interface Spec extends TurboModule { +cancel: () => void; } -export default (TurboModuleRegistry.getEnforcing('Vibration'): Spec); +const Placeholder = { + getConstants: () => { + return {}; + }, + vibrate: (pattern: number) => {}, + vibrateByPattern: (pattern: Array, repeat: number) => {}, + cancel: () => {}, +}; + +const NativeVibration: Spec = Platform.isTVOS + ? (Placeholder: Spec) + : (TurboModuleRegistry.getEnforcing('Vibration'): Spec); + +export default NativeVibration; diff --git a/Libraries/Vibration/React-RCTVibration.podspec b/Libraries/Vibration/React-RCTVibration.podspec index 775a4f177c..1041086808 100644 --- a/Libraries/Vibration/React-RCTVibration.podspec +++ b/Libraries/Vibration/React-RCTVibration.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.documentation_url = "https://reactnative.dev/docs/vibration" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "*.{m,mm}" diff --git a/README-core.md b/README-core.md new file mode 100644 index 0000000000..01fafa6e36 --- /dev/null +++ b/README-core.md @@ -0,0 +1,147 @@ +

+ + React Native + +

+ +

+ Learn once, write anywhere:
+ Build mobile apps with React. +

+ +

+ + React Native is released under the MIT license. + + + Current CircleCI build status. + + + Current npm package version. + + + PRs welcome! + + + Follow @reactnative + +

+ +

+ Getting Started + · + Learn the Basics + · + Showcase + · + Contribute + · + Community + · + Support +

+ +React Native brings [**React**'s][r] declarative UI framework to iOS and Android. With React Native, you use native UI controls and have full access to the native platform. + +- **Declarative.** React makes it painless to create interactive UIs. Declarative views make your code more predictable and easier to debug. +- **Component-Based.** Build encapsulated components that manage their state, then compose them to make complex UIs. +- **Developer Velocity.** See local changes in seconds. Changes to JavaScript code can be live reloaded without rebuilding the native app. +- **Portability.** Reuse code across iOS, Android, and [other platforms][p]. + +React Native is developed and supported by many companies and individual core contributors. Find out more in our [ecosystem overview][e]. + +[r]: https://reactjs.org/ +[p]: https://reactnative.dev/docs/out-of-tree-platforms +[e]: https://github.com/facebook/react-native/blob/HEAD/ECOSYSTEM.md + +## Contents + +- [Requirements](#-requirements) +- [Building your first React Native app](#-building-your-first-react-native-app) +- [Documentation](#-documentation) +- [Upgrading](#-upgrading) +- [How to Contribute](#-how-to-contribute) +- [Code of Conduct](#code-of-conduct) +- [License](#-license) + + +## 📋 Requirements + +React Native apps may target iOS 11.0 and Android 5.0 (API 21) or newer. You may use Windows, macOS, or Linux as your development operating system, though building and running iOS apps is limited to macOS. Tools like [Expo](https://expo.io) can be used to work around this. + +## 🎉 Building your first React Native app + +Follow the [Getting Started guide](https://reactnative.dev/docs/getting-started). The recommended way to install React Native depends on your project. Here you can find short guides for the most common scenarios: + +- [Trying out React Native][hello-world] +- [Creating a New Application][new-app] +- [Adding React Native to an Existing Application][existing] + +[hello-world]: https://snack.expo.io/@hramos/hello,-world! +[new-app]: https://reactnative.dev/docs/getting-started +[existing]: https://reactnative.dev/docs/integration-with-existing-apps + +## 📖 Documentation + +The full documentation for React Native can be found on our [website][docs]. + +The React Native documentation discusses components, APIs, and topics that are specific to React Native. For further documentation on the React API that is shared between React Native and React DOM, refer to the [React documentation][r-docs]. + +The source for the React Native documentation and website is hosted on a separate repo, [**@facebook/react-native-website**][repo-website]. + +[docs]: https://reactnative.dev/docs/getting-started +[r-docs]: https://reactjs.org/docs/getting-started.html +[repo-website]: https://github.com/facebook/react-native-website + +## 🚀 Upgrading + +Upgrading to new versions of React Native may give you access to more APIs, views, developer tools, and other goodies. See the [Upgrading Guide][u] for instructions. + +React Native releases are discussed in the React Native Community, [**@react-native-community/react-native-releases**][repo-releases]. + +[u]: https://reactnative.dev/docs/upgrading +[repo-releases]: https://github.com/react-native-community/react-native-releases + +## 👏 How to Contribute + +The main purpose of this repository is to continue evolving React Native core. We want to make contributing to this project as easy and transparent as possible, and we are grateful to the community for contributing bug fixes and improvements. Read below to learn how you can take part in improving React Native. + +### [Code of Conduct][code] + +Facebook has adopted a Code of Conduct that we expect project participants to adhere to. +Please read the [full text][code] so that you can understand what actions will and will not be tolerated. + +[code]: https://code.fb.com/codeofconduct/ + +### [Contributing Guide][contribute] + +Read our [**Contributing Guide**][contribute] to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to React Native. + +[contribute]: https://reactnative.dev/docs/contributing + +### [Open Source Roadmap][roadmap] + +You can learn more about our vision for React Native in the [**Roadmap**][roadmap]. + +[roadmap]: https://github.com/facebook/react-native/wiki/Roadmap + +### Good First Issues + +We have a list of [good first issues][gfi] that contain bugs which have a relatively limited scope. This is a great place to get started, gain experience, and get familiar with our contribution process. + +[gfi]: https://github.com/facebook/react-native/labels/good%20first%20issue + +### Discussions + +Larger discussions and proposals are discussed in [**@react-native-community/discussions-and-proposals**][repo-meta]. + +[repo-meta]: https://github.com/react-native-community/discussions-and-proposals + +## 📄 License + +React Native is MIT licensed, as found in the [LICENSE][l] file. + +React Native documentation is Creative Commons licensed, as found in the [LICENSE-docs][ld] file. + +[l]: https://github.com/facebook/react-native/blob/HEAD/LICENSE +[ld]: https://github.com/facebook/react-native/blob/HEAD/LICENSE-docs diff --git a/README.md b/README.md index a99e21ad4b..365ebb3aea 100644 --- a/README.md +++ b/README.md @@ -1,147 +1,199 @@ -

- - React Native - -

+## react-native-tvos -

- Learn once, write anywhere:
- Build mobile apps with React. -

+Going forward, Apple TV support for React Native will be maintained here and in the corresponding `react-native-tvos` NPM package, and not in the [core repo](https://github.com/facebook/react-native/). This is a full fork of the main repository, with only the changes needed to support Apple TV. -

- - React Native is released under the MIT license. - - - Current CircleCI build status. - - - Current npm package version. - - - PRs welcome! - - - Follow @reactnative - -

+Releases of `react-native-tvos` will be based on a public release of `react-native`; e.g. the 0.66.3-0 release of this package will be derived from the 0.66.3 release of `react-native`. All releases of this repo will follow the 0.xx.x-y format, where x digits are from a specific RN core release, and y represents the additional versioning from this repo. -

- Getting Started - · - Learn the Basics - · - Showcase - · - Contribute - · - Community - · - Support -

+Releases will be published on npmjs.org and you may find the latest release version here: https://www.npmjs.com/package/react-native-tvos?activeTab=versions or use the tag `@latest` -React Native brings [**React**'s][r] declarative UI framework to iOS and Android. With React Native, you use native UI controls and have full access to the native platform. +You will find the relevant tvOS support and maintenance within the branches marked `tvos-v0.xx.x`; -- **Declarative.** React makes it painless to create interactive UIs. Declarative views make your code more predictable and easier to debug. -- **Component-Based.** Build encapsulated components that manage their state, then compose them to make complex UIs. -- **Developer Velocity.** See local changes in seconds. Changes to JavaScript code can be live reloaded without rebuilding the native app. -- **Portability.** Reuse code across iOS, Android, and [other platforms][p]. +To build your project for Apple TV, you should change your `package.json` imports to import `react-native` as follows, so that this package is used instead of the core react-native package. -React Native is developed and supported by many companies and individual core contributors. Find out more in our [ecosystem overview][e]. +```js +"react-native": "npm:react-native-tvos@latest", +``` -[r]: https://reactjs.org/ -[p]: https://reactnative.dev/docs/out-of-tree-platforms -[e]: https://github.com/facebook/react-native/blob/HEAD/ECOSYSTEM.md +You cannot use this package and the core react-native package simultaneously in a project. -## Contents +### Typescript -- [Requirements](#-requirements) -- [Building your first React Native app](#-building-your-first-react-native-app) -- [Documentation](#-documentation) -- [Upgrading](#-upgrading) -- [How to Contribute](#-how-to-contribute) -- [Code of Conduct](#code-of-conduct) -- [License](#-license) +Due to the nature of the typing resolution, the current solution to include types is to: +- install `@types/react-native` as a dev dependency +- put `import 'react-native/tvos-types.d'` in any of your `.ts` files (root suggested) -## 📋 Requirements +See the "Build Changes" section below for how to start a new project that will automatically use Typescript. -React Native apps may target iOS 11.0 and Android 5.0 (API 21) or newer. You may use Windows, macOS, or Linux as your development operating system, though building and running iOS apps is limited to macOS. Tools like [Expo](https://expo.io) can be used to work around this. +## General support for Apple TV -## 🎉 Building your first React Native app +TV devices support has been implemented with the intention of making existing React Native applications "just work" on Apple TV, with few or no changes needed in the JavaScript code for the applications. -Follow the [Getting Started guide](https://reactnative.dev/docs/getting-started). The recommended way to install React Native depends on your project. Here you can find short guides for the most common scenarios: +The RNTester app supports Apple TV. In this repo, `RNTester/Podfile` and `RNTester/RNTesterPods.xcodeproj` have been modified to work for tvOS. Run `pod install`, then open `RNTesterPods.xcworkspace` and build. -- [Trying out React Native][hello-world] -- [Creating a New Application][new-app] -- [Adding React Native to an Existing Application][existing] +## Pitfall -[hello-world]: https://snack.expo.io/@hramos/hello,-world! -[new-app]: https://reactnative.dev/docs/getting-started -[existing]: https://reactnative.dev/docs/integration-with-existing-apps +Make sure you do not globally install `react-native` or `react-native-tvos`. You should only install `react-native-cli` to use the commands below. If you have done this the wrong way, you may get error messages like: -## 📖 Documentation +``` +ld: library not found for -lPods-TestApp-tvOS +``` -The full documentation for React Native can be found on our [website][docs]. +You should also install `yarn` globally, as it should be used instead of npm for working in React Native projects. -The React Native documentation discusses components, APIs, and topics that are specific to React Native. For further documentation on the React API that is shared between React Native and React DOM, refer to the [React documentation][r-docs]. +## Build changes -The source for the React Native documentation and website is hosted on a separate repo, [**@facebook/react-native-website**][repo-website]. +- _Native layer_: React Native Xcode projects all now have Apple TV build targets, with names ending in the string '-tvOS'. -[docs]: https://reactnative.dev/docs/getting-started -[r-docs]: https://reactjs.org/docs/getting-started.html -[repo-website]: https://github.com/facebook/react-native-website +- _react-native init_: Creating a new project that uses this package is done using the react-native CLI. New projects created this way will automatically have properly configured Apple TV targets created in their XCode projects. -## 🚀 Upgrading +To use this NPM package in a new project, you can reference it as in the following example using the older `react-native-cli` package: -Upgrading to new versions of React Native may give you access to more APIs, views, developer tools, and other goodies. See the [Upgrading Guide][u] for instructions. +```sh +# Make sure you have the CLI installed globally (this only needs to be done once on your system) +npm install -g react-native-cli +# Init an app called 'TestApp', note that you must not be in a node module (directory with node_modules sub-directory) for this to work +react-native init TestApp --version=react-native@npm:react-native-tvos@latest +# Now start the app in the tvOS Simulator - this will only work on a macOS machine +cd TestApp && react-native run-ios --simulator "Apple TV" --scheme "TestApp-tvOS" +``` -React Native releases are discussed [in this discussion repo](https://github.com/reactwg/react-native-releases/discussions). +If you are using the newer `@react-native-community/cli` package, the syntax is slightly different: -[u]: https://reactnative.dev/docs/upgrading -[repo-releases]: https://github.com/react-native-community/react-native-releases +```sh +# Make sure you have the CLI installed globally (this only needs to be done once on your system) +npm install -g @react-native-community/cli +# Init an app called 'TestApp', note that you must not be in a node module (directory with node_modules sub-directory) for this to work +react-native init TestApp --template=react-native-tvos@latest +# Now start the app in the tvOS Simulator - this will only work on a macOS machine +cd TestApp && react-native run-ios --simulator "Apple TV" --scheme "TestApp-tvOS" +``` -## 👏 How to Contribute +A minimal Typescript starter template can be used to start a new project using the community react-native CLI. The process is the same as above except for the template: -The main purpose of this repository is to continue evolving React Native core. We want to make contributing to this project as easy and transparent as possible, and we are grateful to the community for contributing bug fixes and improvements. Read below to learn how you can take part in improving React Native. +```sh +react-native init TestApp --template=react-native-template-typescript-tv +``` -### [Code of Conduct][code] +- _JavaScript layer_: Support for Apple TV has been added to `Platform.ios.js`. You can check whether code is running on AppleTV by doing -Facebook has adopted a Code of Conduct that we expect project participants to adhere to. -Please read the [full text][code] so that you can understand what actions will and will not be tolerated. +```javascript +var Platform = require('Platform'); +var running_on_tv = Platform.isTV; -[code]: https://code.fb.com/codeofconduct/ +// If you want to be more specific and only detect devices running tvOS +// (but no Android TV devices) you can use: +var running_on_apple_tv = Platform.isTVOS; +``` -### [Contributing Guide][contribute] +## Code changes -Read our [**Contributing Guide**][contribute] to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to React Native. +- _General support for tvOS_: Apple TV specific changes in native code are all wrapped by the TARGET_OS_TV define. These include changes to suppress APIs that are not supported on tvOS (e.g. web views, sliders, switches, status bar, etc.), and changes to support user input from the TV remote or keyboard. -[contribute]: https://reactnative.dev/docs/contributing +- _Common codebase_: Since tvOS and iOS share most Objective-C and JavaScript code in common, most documentation for iOS applies equally to tvOS. -### [Open Source Roadmap][roadmap] +- _Access to touchable controls_: When running on Apple TV, the native view class is `RCTTVView`, which has additional methods to make use of the tvOS focus engine. The `Touchable` mixin has code added to detect focus changes and use existing methods to style the components properly and initiate the proper actions when the view is selected using the TV remote, so `TouchableWithoutFeedback`, `TouchableHighlight` and `TouchableOpacity` will "just work". In particular: -You can learn more about our vision for React Native in the [**Roadmap**][roadmap]. + - `onFocus` will be executed when the touchable view goes into focus + - `onBlur` will be executed when the touchable view goes out of focus + - `onPress` will be executed when the touchable view is actually selected by pressing the "select" button on the TV remote. -[roadmap]: https://github.com/facebook/react-native/wiki/Roadmap +- _TV remote/keyboard input_: A native class, `RCTTVRemoteHandler`, sets up gesture recognizers for TV remote events. When TV remote events occur, this class fires notifications that are picked up by `RCTTVNavigationEventEmitter` (a subclass of `RCTEventEmitter`), that fires a JS event. This event will be picked up by instances of the `TVEventHandler` JavaScript object. Application code that needs to implement custom handling of TV remote events can create an instance of `TVEventHandler` and listen for these events. In 0.63.1-1, we have added `useTVEventHandler`, which wraps `useEffect` to make this more convenient and simpler for use with functional components. In 0.64.2-2, we added a TV event display to the new app template using `useTVEventHandler`. -### Good First Issues +```javascript -We have a list of [good first issues][gfi] that contain bugs which have a relatively limited scope. This is a great place to get started, gain experience, and get familiar with our contribution process. +import { TVEventHandler, useTVEventHandler } from 'react-native'; -[gfi]: https://github.com/facebook/react-native/labels/good%20first%20issue +// Functional component -### Discussions +const TVEventHandlerView: () => React.Node = () => { + const [lastEventType, setLastEventType] = React.useState(''); -Larger discussions and proposals are discussed in [**@react-native-community/discussions-and-proposals**][repo-meta]. + const myTVEventHandler = evt => { + setLastEventType(evt.eventType); + }; -[repo-meta]: https://github.com/react-native-community/discussions-and-proposals + useTVEventHandler(myTVEventHandler); -## 📄 License + return ( + + {}}> + + This example enables an instance of TVEventHandler to show the last + event detected from the Apple TV Siri remote or from a keyboard. + + + {lastEventType} + + ); -React Native is MIT licensed, as found in the [LICENSE][l] file. +}; + +// Class based component + +class Game2048 extends React.Component { + _tvEventHandler: any; + + _enableTVEventHandler() { + this._tvEventHandler = new TVEventHandler(); + this._tvEventHandler.enable(this, function(cmp, evt) { + if (evt && evt.eventType === 'right') { + cmp.setState({board: cmp.state.board.move(2)}); + } else if(evt && evt.eventType === 'up') { + cmp.setState({board: cmp.state.board.move(1)}); + } else if(evt && evt.eventType === 'left') { + cmp.setState({board: cmp.state.board.move(0)}); + } else if(evt && evt.eventType === 'down') { + cmp.setState({board: cmp.state.board.move(3)}); + } else if(evt && evt.eventType === 'playPause') { + cmp.restartGame(); + } + }); + } + + _disableTVEventHandler() { + if (this._tvEventHandler) { + this._tvEventHandler.disable(); + delete this._tvEventHandler; + } + } + + componentDidMount() { + this._enableTVEventHandler(); + } + + componentWillUnmount() { + this._disableTVEventHandler(); + } +``` + +- _Turbomodules_: Working as of the 0.61.2-0 release. + +- _Flipper_: Working in the 0.62.2-x releases. Working in the 0.63.x releases; however, tvOS requires the Flipper pods from 0.62.2-x. `scripts/react_native_pods.rb` contains macros for both versions. The new project template Podfile is correctly set up to provide the older Flipper for both iOS and tvOS targets. In 0.64.x, Flipper support has been temporarily removed until issues can be resolved with newer Xcode versions. + +- _Hermes for tvOS_: RN core added support for the Hermes JS engine on iOS in 0.64. tvOS does not yet have this, as it will require significant additions to the Hermes build structure. + +- _LogBox_: The new LogBox error/warning display (which replaced YellowBox in 0.63) is working as expected in tvOS, after a few adjustments to make the controls accessible to the focus engine. + +- _Pressable_: The new `Pressable` API for React Native 0.63 works with TV. Additional `onFocus` and `onBlur` props are provided to allow you to customize behavior when a Pressable enters or leaves focus. Similar to the `pressed` state that is true while a user is pressing the component on a touchscreen, the `focused` state will be true when it is focused on TV. `PressableExample` in RNTester has been modified appropriately. + +- _Dev Menu support_: On the simulator, cmd-D will bring up the developer menu, just like on iOS. To bring it up on a real Apple TV device, make a long press on the play/pause button on the remote. (Please do not shake the Apple TV device, that will not work :) ) + +- _TV remote animations_: `RCTTVView` native code implements Apple-recommended parallax animations to help guide the eye as the user navigates through views. The animations can be disabled or adjusted with new optional view properties. + +- _Back navigation with the TV remote menu button_: The `BackHandler` component, originally written to support the Android back button, now also supports back navigation on the Apple TV using the menu button on the TV remote. + +- _TVMenuControl_: This module provides methods to enable and disable navigation using the menu key on the TV remote. This is required in order to fix an issue with Apple's guidelines for menu key navigation (see https://github.com/facebook/react-native/issues/18930). The `RNTester` app uses this new module to implement correct menu key behavior. + +- _TVFocusGuideView_: This component provides support for Apple's `UIFocusGuide` API, to help ensure that focusable controls can be navigated to, even if they are not directly in line with other controls. An example is provided in `RNTester` that shows two different ways of using this component. + +- _Next Focus Direction_: the props `nextFocus*` on `View` should work as expected on iOS too (previously android only). One caveat is that if there is no focusable in the `nextFocusable*` direction next to the starting view, iOS doesn't check if we want to override the destination. + +- _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. + +- _Known issues_: + + - As of the 0.61.2-0 release, Fabric code does not compile or run. Issue is under investigation. + - There are known issues with the TabBarIOS component, due to changes that Apple made in UITabBar for tvOS 13. -React Native documentation is Creative Commons licensed, as found in the [LICENSE-docs][ld] file. -[l]: https://github.com/facebook/react-native/blob/HEAD/LICENSE -[ld]: https://github.com/facebook/react-native/blob/HEAD/LICENSE-docs diff --git a/React-Core.podspec b/React-Core.podspec index 375bd9e1ea..1c19caebaa 100644 --- a/React-Core.podspec +++ b/React-Core.podspec @@ -41,7 +41,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.source = source s.resource_bundle = { "AccessibilityResources" => ["React/AccessibilityResources/*.lproj"]} s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags @@ -59,6 +59,13 @@ Pod::Spec.new do |s| "React/FBReactNativeSpec/**/*", "React/Tests/**/*", "React/Inspector/**/*" + ss.ios.exclude_files = "React/**/RCTTV*.*" + ss.tvos.exclude_files = "React/Modules/RCTClipboard*", + "React/Views/RCTDatePicker*", + "React/Views/RCTPicker*", + "React/Views/RefreshControl/*", + "React/Views/RCTSlider*", + "React/Views/RCTSwitch*" ss.private_header_files = "React/Cxx*/*.h" end diff --git a/React.podspec b/React.podspec index 9c44e3101c..5aa3b941b1 100644 --- a/React.podspec +++ b/React.podspec @@ -36,7 +36,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.source = source s.preserve_paths = "package.json", "LICENSE", "LICENSE-docs" s.cocoapods_version = ">= 1.10.1" @@ -44,7 +44,8 @@ Pod::Spec.new do |s| s.dependency "React-Core", version s.dependency "React-Core/DevSupport", version s.dependency "React-Core/RCTWebSocket", version - s.dependency "React-RCTActionSheet", version + # Not for tvOS + # s.dependency "React-RCTActionSheet", version s.dependency "React-RCTAnimation", version s.dependency "React-RCTBlob", version s.dependency "React-RCTImage", version @@ -52,5 +53,6 @@ Pod::Spec.new do |s| s.dependency "React-RCTNetwork", version s.dependency "React-RCTSettings", version s.dependency "React-RCTText", version - s.dependency "React-RCTVibration", version + # Not for tvOS + # s.dependency "React-RCTVibration", version end diff --git a/React/Base/RCTConvert.h b/React/Base/RCTConvert.h index c4913f60c7..e137a923c1 100644 --- a/React/Base/RCTConvert.h +++ b/React/Base/RCTConvert.h @@ -15,7 +15,7 @@ #import #import #import -#if TARGET_OS_IPHONE +#if TARGET_OS_IPHONE && !TARGET_OS_TV #import #endif @@ -73,8 +73,8 @@ typedef NSURL RCTFileURL; + (UIDataDetectorTypes)UIDataDetectorTypes:(id)json; #endif -#if TARGET_OS_IPHONE -+ (WKDataDetectorTypes)WKDataDetectorTypes:(id)json; +#if TARGET_OS_IPHONE && !TARGET_OS_TV ++ (WKDataDetectorTypes)WKDataDetectorTypes:(id)json API_AVAILABLE(ios(10.0)); #endif + (UIViewContentMode)UIViewContentMode:(id)json; diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index c21ef072ac..782091d6a6 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -416,6 +416,7 @@ + (UIKeyboardType)UIKeyboardType:(id)json RCT_DYNAMIC return type; } +#if !TARGET_OS_TV RCT_MULTI_ENUM_CONVERTER( UIDataDetectorTypes, (@{ @@ -445,6 +446,8 @@ + (UIKeyboardType)UIKeyboardType:(id)json RCT_DYNAMIC WKDataDetectorTypePhoneNumber, unsignedLongLongValue) +#endif // !TARGET_OS_TV + RCT_ENUM_CONVERTER( UIKeyboardAppearance, (@{ @@ -497,6 +500,7 @@ + (UIKeyboardType)UIKeyboardType:(id)json RCT_DYNAMIC UIViewContentModeScaleAspectFill, integerValue) +#if !TARGET_OS_TV RCT_ENUM_CONVERTER( UIBarStyle, (@{ @@ -507,6 +511,7 @@ + (UIKeyboardType)UIKeyboardType:(id)json RCT_DYNAMIC }), UIBarStyleDefault, integerValue) +#endif static void convertCGStruct(const char *type, NSArray *fields, CGFloat *result, id json) { @@ -910,7 +915,7 @@ + (UIColor *)UIColor:(id)json UIColor *highContrastDarkColor = [RCTConvert UIColor:highContrastDark]; if (lightColor != nil && darkColor != nil) { #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 - if (@available(iOS 13.0, *)) { + if (@available(iOS 13.0, tvOS 13.0, *)) { UIColor *color = [UIColor colorWithDynamicProvider:^UIColor *_Nonnull( UITraitCollection *_Nonnull collection) { if (collection.userInterfaceStyle == UIUserInterfaceStyleDark) { diff --git a/React/Base/RCTRootView.h b/React/Base/RCTRootView.h index 32eeb7ab00..50c53c811d 100644 --- a/React/Base/RCTRootView.h +++ b/React/Base/RCTRootView.h @@ -11,6 +11,10 @@ #import #import +extern NSString *const RCTTVEnableMenuKeyNotification; +extern NSString *const RCTTVDisableMenuKeyNotification; + + @protocol RCTRootViewDelegate; /** diff --git a/React/Base/RCTRootView.m b/React/Base/RCTRootView.m index 122ed87322..a59e8289bf 100644 --- a/React/Base/RCTRootView.m +++ b/React/Base/RCTRootView.m @@ -28,8 +28,22 @@ #import "RCTView.h" #import "UIView+React.h" +#if TARGET_OS_TV +#import "RCTTVNavigationEventEmitter.h" +#import "RCTTVRemoteHandler.h" +#endif + +#if RCT_DEV +#import "RCTDevMenu.h" +#endif + + NSString *const RCTContentDidAppearNotification = @"RCTContentDidAppearNotification"; +NSString *const RCTTVEnableMenuKeyNotification = @"RCTTVEnableMenuKeyNotification"; +NSString *const RCTTVDisableMenuKeyNotification = @"RCTTVDisableMenuKeyNotification"; + + @interface RCTUIManager (RCTRootView) - (NSNumber *)allocateRootTag; @@ -84,6 +98,18 @@ - (instancetype)initWithFrame:(CGRect)frame name:RCTContentDidAppearNotification object:self]; +#if TARGET_OS_TV + +#if RCT_DEV + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(showDevMenu) + name:@"RCTShowDevMenuNotification" + object:nil]; +#endif + + self.tvRemoteHandler = [[RCTTVRemoteHandler alloc] initWithView:self]; +#endif + [self showLoadingView]; // Immediately schedule the application to be started. @@ -103,6 +129,14 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge return [self initWithFrame:CGRectZero bridge:bridge moduleName:moduleName initialProperties:initialProperties]; } +#if TARGET_OS_TV && RCT_DEV +- (void)showDevMenu { + dispatch_async(dispatch_get_main_queue(), ^{ + [[self->_bridge devMenu] show]; + }); +} +#endif // TARGET_OS_TV + - (instancetype)initWithBundleURL:(NSURL *)bundleURL moduleName:(NSString *)moduleName initialProperties:(NSDictionary *)initialProperties @@ -131,6 +165,16 @@ - (RCTModuleRegistry *)moduleRegistry return [self.moduleRegistry moduleForName:"EventDispatcher"]; } +#if TARGET_OS_TV +- (NSArray> *)preferredFocusEnvironments +{ + if (self.reactPreferredFocusedView) { + return @[self.reactPreferredFocusedView]; + } + return [super preferredFocusEnvironments]; +} +#endif + #pragma mark - passThroughTouches - (BOOL)passThroughTouches @@ -389,6 +433,9 @@ - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection - (void)dealloc { +#if TARGET_OS_TV + self.tvRemoteHandler = nil; +#endif [_contentView invalidate]; } diff --git a/React/Base/RCTRootViewInternal.h b/React/Base/RCTRootViewInternal.h index bfff4e0893..92199379c8 100644 --- a/React/Base/RCTRootViewInternal.h +++ b/React/Base/RCTRootViewInternal.h @@ -7,6 +7,8 @@ #import +@class RCTTVRemoteHandler; + /** * The interface provides a set of functions that allow other internal framework * classes to change the RCTRootViews's internal state. @@ -19,6 +21,14 @@ */ @property (readwrite, nonatomic, assign) CGSize intrinsicContentSize; +/** + * TV remote gesture recognizers + */ +#if TARGET_OS_TV +@property (nonatomic, strong) RCTTVRemoteHandler *tvRemoteHandler; +@property (nonatomic, strong) UIView *reactPreferredFocusedView; +#endif + - (void)contentViewInvalidated; @end diff --git a/React/Base/RCTTVRemoteHandler.h b/React/Base/RCTTVRemoteHandler.h new file mode 100644 index 0000000000..5b71c56af7 --- /dev/null +++ b/React/Base/RCTTVRemoteHandler.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import + +extern NSString *const RCTTVRemoteEventMenu; +extern NSString *const RCTTVRemoteEventPlayPause; +extern NSString *const RCTTVRemoteEventSelect; + +extern NSString *const RCTTVRemoteEventLongPlayPause; +extern NSString *const RCTTVRemoteEventLongSelect; + +extern NSString *const RCTTVRemoteEventLeft; +extern NSString *const RCTTVRemoteEventRight; +extern NSString *const RCTTVRemoteEventUp; +extern NSString *const RCTTVRemoteEventDown; + +extern NSString *const RCTTVRemoteEventPageUp; +extern NSString *const RCTTVRemoteEventPageDown; + +extern NSString *const RCTTVRemoteEventSwipeLeft; +extern NSString *const RCTTVRemoteEventSwipeRight; +extern NSString *const RCTTVRemoteEventSwipeUp; +extern NSString *const RCTTVRemoteEventSwipeDown; + +@interface RCTTVRemoteHandler : NSObject + +- (instancetype _Nonnull )initWithView:(UIView * _Nonnull)view; +- (instancetype _Nonnull )init __attribute__((unavailable("init not available, use initWithView:"))); + ++ (BOOL)useMenuKey; ++ (void)setUseMenuKey:(BOOL)useMenuKey; + +@end diff --git a/React/Base/RCTTVRemoteHandler.m b/React/Base/RCTTVRemoteHandler.m new file mode 100644 index 0000000000..ea9c957c23 --- /dev/null +++ b/React/Base/RCTTVRemoteHandler.m @@ -0,0 +1,354 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTTVRemoteHandler.h" + +#import + +#import "RCTAssert.h" +#import "RCTBridge.h" +#import "RCTEventDispatcher.h" +#import "RCTLog.h" +#import "RCTRootView.h" +#import "RCTTVNavigationEventEmitter.h" +#import "RCTUIManager.h" +#import "RCTUtils.h" +#import "RCTView.h" +#import "UIView+React.h" + +NSString *const RCTTVRemoteEventMenu = @"menu"; +NSString *const RCTTVRemoteEventPlayPause = @"playPause"; +NSString *const RCTTVRemoteEventSelect = @"select"; + +NSString *const RCTTVRemoteEventLongPlayPause = @"longPlayPause"; +NSString *const RCTTVRemoteEventLongSelect = @"longSelect"; + +NSString *const RCTTVRemoteEventLeft = @"left"; +NSString *const RCTTVRemoteEventRight = @"right"; +NSString *const RCTTVRemoteEventUp = @"up"; +NSString *const RCTTVRemoteEventDown = @"down"; + +NSString *const RCTTVRemoteEventPageUp = @"pageUp"; +NSString *const RCTTVRemoteEventPageDown = @"pageDown"; + +NSString *const RCTTVRemoteEventSwipeLeft = @"swipeLeft"; +NSString *const RCTTVRemoteEventSwipeRight = @"swipeRight"; +NSString *const RCTTVRemoteEventSwipeUp = @"swipeUp"; +NSString *const RCTTVRemoteEventSwipeDown = @"swipeDown"; + +@interface RCTTVRemoteHandler() + +@property (nonatomic, copy, readonly) NSDictionary *tvRemoteGestureRecognizers; +@property (nonatomic, strong) UITapGestureRecognizer *tvMenuKeyRecognizer; +@property (nonatomic, weak) UIView *view; + +@end + +@implementation RCTTVRemoteHandler { + NSMutableDictionary *_tvRemoteGestureRecognizers; +} + +#pragma mark - +#pragma mark Static setting for using menu key + +static __volatile BOOL __useMenuKey = NO; + ++ (BOOL)useMenuKey +{ + return __useMenuKey; +} + ++ (void)setUseMenuKey:(BOOL)useMenuKey +{ + __useMenuKey = useMenuKey; +} + +#pragma mark - +#pragma mark Public methods + +- (instancetype)initWithView:(UIView *)view +{ + if ((self = [super init])) { + _view = view; + [self setUpGestureRecognizers]; + [self attachToView]; + } + return self; +} + +- (void)dealloc +{ + [self detachFromView]; +} + +#pragma mark - +#pragma mark Private methods + +- (void)setUpGestureRecognizers +{ + _tvRemoteGestureRecognizers = [NSMutableDictionary dictionary]; + // Recognizers for Apple TV remote buttons + // Menu recognizer + self.tvMenuKeyRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(menuPressed:)]; + self.tvMenuKeyRecognizer.allowedPressTypes = @[@(UIPressTypeMenu)]; + + // Play/Pause + [self addTapGestureRecognizerWithSelector:@selector(playPausePressed:) + pressType:UIPressTypePlayPause + name:RCTTVRemoteEventPlayPause]; + + // Select + [self addTapGestureRecognizerWithSelector:@selector(selectPressed:) + pressType:UIPressTypeSelect + name:RCTTVRemoteEventSelect]; + + // Page Up/Down + if (@available(tvOS 14.3, *)) { + [self addTapGestureRecognizerWithSelector:@selector(tappedPageUp:) + pressType:UIPressTypePageUp + name:RCTTVRemoteEventPageUp]; + + [self addTapGestureRecognizerWithSelector:@selector(tappedPageDown:) + pressType:UIPressTypePageDown + name:RCTTVRemoteEventPageDown]; + } + + // Up + [self addTapGestureRecognizerWithSelector:@selector(tappedUp:) + pressType:UIPressTypeUpArrow + name:RCTTVRemoteEventUp]; + + // Down + [self addTapGestureRecognizerWithSelector:@selector(tappedDown:) + pressType:UIPressTypeDownArrow + name:RCTTVRemoteEventDown]; + + // Left + [self addTapGestureRecognizerWithSelector:@selector(tappedLeft:) + pressType:UIPressTypeLeftArrow + name:RCTTVRemoteEventLeft]; + + // Right + [self addTapGestureRecognizerWithSelector:@selector(tappedRight:) + pressType:UIPressTypeRightArrow + name:RCTTVRemoteEventRight]; + + // Recognizers for long button presses + // We don't intercept long menu press -- that's used by the system to go to the home screen + + [self addLongPressGestureRecognizerWithSelector:@selector(longPlayPausePressed:) + pressType:UIPressTypePlayPause + name:RCTTVRemoteEventLongPlayPause]; + + [self addLongPressGestureRecognizerWithSelector:@selector(longSelectPressed:) + pressType:UIPressTypeSelect + name:RCTTVRemoteEventLongSelect]; + + // Recognizers for Apple TV remote trackpad swipes + + // Up + [self addSwipeGestureRecognizerWithSelector:@selector(swipedUp:) + direction:UISwipeGestureRecognizerDirectionUp + name:RCTTVRemoteEventSwipeUp]; + + // Down + [self addSwipeGestureRecognizerWithSelector:@selector(swipedDown:) + direction:UISwipeGestureRecognizerDirectionDown + name:RCTTVRemoteEventSwipeDown]; + + // Left + [self addSwipeGestureRecognizerWithSelector:@selector(swipedLeft:) + direction:UISwipeGestureRecognizerDirectionLeft + name:RCTTVRemoteEventSwipeLeft]; + + // Right + [self addSwipeGestureRecognizerWithSelector:@selector(swipedRight:) + direction:UISwipeGestureRecognizerDirectionRight + name:RCTTVRemoteEventSwipeRight]; + +} + +- (void)attachToView +{ + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(enableTVMenuKey) + name:RCTTVEnableMenuKeyNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(disableTVMenuKey) + name:RCTTVDisableMenuKeyNotification + object:nil]; + for (NSString *key in [self.tvRemoteGestureRecognizers allKeys]) { + [_view addGestureRecognizer:self.tvRemoteGestureRecognizers[key]]; + } + if ([RCTTVRemoteHandler useMenuKey]) { + [self enableTVMenuKey]; + } else { + [self disableTVMenuKey]; + } +} + +- (void)detachFromView +{ + if ([[self.view gestureRecognizers] containsObject:self.tvMenuKeyRecognizer]) { + [self.view removeGestureRecognizer:self.tvMenuKeyRecognizer]; + } + for (NSString *key in [self.tvRemoteGestureRecognizers allKeys]) { + [_view removeGestureRecognizer:self.tvRemoteGestureRecognizers[key]]; + } + [[NSNotificationCenter defaultCenter] removeObserver:self + name:RCTTVEnableMenuKeyNotification + object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self + name:RCTTVDisableMenuKeyNotification + object:nil]; + +} + +# pragma mark - +# pragma mark Notification handlers + +- (void)enableTVMenuKey +{ + dispatch_async(dispatch_get_main_queue(), ^{ + if (![[self.view gestureRecognizers] containsObject:self.tvMenuKeyRecognizer]) { + [self.view addGestureRecognizer:self.tvMenuKeyRecognizer]; + } + }); +} + +- (void)disableTVMenuKey +{ + dispatch_async(dispatch_get_main_queue(), ^{ + if ([[self.view gestureRecognizers] containsObject:self.tvMenuKeyRecognizer]) { + [self.view removeGestureRecognizer:self.tvMenuKeyRecognizer]; + } + }); +} + +# pragma mark - +# pragma mark Gesture handlers + +- (void)playPausePressed:(UIGestureRecognizer *)r +{ + [self sendAppleTVEvent:RCTTVRemoteEventPlayPause toView:r.view]; +} + +- (void)menuPressed:(UIGestureRecognizer *)r +{ + [self sendAppleTVEvent:RCTTVRemoteEventMenu toView:r.view]; +} + +- (void)selectPressed:(UIGestureRecognizer *)r +{ + [self sendAppleTVEvent:RCTTVRemoteEventSelect toView:r.view]; +} + +- (void)longPlayPausePressed:(UIGestureRecognizer *)r +{ + [self sendAppleTVEvent:RCTTVRemoteEventLongPlayPause toView:r.view]; + +#if RCT_DEV + // If shake to show is enabled on device, use long play/pause event to show dev menu + [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTShowDevMenuNotification" object:nil]; +#endif +} + +- (void)longSelectPressed:(UIGestureRecognizer *)r +{ + [self sendAppleTVEvent:RCTTVRemoteEventLongSelect toView:r.view]; +} + +- (void)swipedUp:(UIGestureRecognizer *)r +{ + [self sendAppleTVEvent:RCTTVRemoteEventSwipeUp toView:r.view]; +} + +- (void)swipedDown:(UIGestureRecognizer *)r +{ + [self sendAppleTVEvent:RCTTVRemoteEventSwipeDown toView:r.view]; +} + +- (void)swipedLeft:(UIGestureRecognizer *)r +{ + [self sendAppleTVEvent:RCTTVRemoteEventSwipeLeft toView:r.view]; +} + +- (void)swipedRight:(UIGestureRecognizer *)r +{ + [self sendAppleTVEvent:RCTTVRemoteEventSwipeRight toView:r.view]; +} + +- (void)tappedUp:(UIGestureRecognizer *)r +{ + [self sendAppleTVEvent:RCTTVRemoteEventUp toView:r.view]; +} + +- (void)tappedDown:(UIGestureRecognizer *)r +{ + [self sendAppleTVEvent:RCTTVRemoteEventDown toView:r.view]; +} + +- (void)tappedPageUp:(UIGestureRecognizer *)r +{ + [self sendAppleTVEvent:RCTTVRemoteEventPageUp toView:r.view]; +} + +- (void)tappedPageDown:(UIGestureRecognizer *)r +{ + [self sendAppleTVEvent:RCTTVRemoteEventPageDown toView:r.view]; +} + +- (void)tappedLeft:(UIGestureRecognizer *)r +{ + [self sendAppleTVEvent:RCTTVRemoteEventLeft toView:r.view]; +} + +- (void)tappedRight:(UIGestureRecognizer *)r +{ + [self sendAppleTVEvent:RCTTVRemoteEventRight toView:r.view]; +} + +#pragma mark - +#pragma mark Convenience methods for adding gesture recognizers + +- (void)addLongPressGestureRecognizerWithSelector:(nonnull SEL)selector + pressType:(UIPressType)pressType + name:(NSString *)name +{ + UILongPressGestureRecognizer *recognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:selector]; + recognizer.allowedPressTypes = @[ @(pressType) ]; + + _tvRemoteGestureRecognizers[name] = recognizer; +} + +- (void)addTapGestureRecognizerWithSelector:(nonnull SEL)selector pressType:(UIPressType)pressType name:(NSString *)name +{ + UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:selector]; + recognizer.allowedPressTypes = @[ @(pressType) ]; + + _tvRemoteGestureRecognizers[name] = recognizer; +} + +- (void)addSwipeGestureRecognizerWithSelector:(nonnull SEL)selector + direction:(UISwipeGestureRecognizerDirection)direction + name:(NSString *)name +{ + UISwipeGestureRecognizer *recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:selector]; + recognizer.direction = direction; + + _tvRemoteGestureRecognizers[name] = recognizer; +} + +- (void)sendAppleTVEvent:(NSString *)eventType toView:(__unused UIView *)v +{ + [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTTVNavigationEventNotification" + object:@{@"eventType" : eventType}]; +} + +@end diff --git a/React/Base/RCTVersion.m b/React/Base/RCTVersion.m index de0fa2a856..8117244d44 100644 --- a/React/Base/RCTVersion.m +++ b/React/Base/RCTVersion.m @@ -24,7 +24,7 @@ RCTVersionMajor: @(0), RCTVersionMinor: @(68), RCTVersionPatch: @(0), - RCTVersionPrerelease: @"rc.2", + RCTVersionPrerelease: [NSNull null], }; }); return __rnVersion; diff --git a/React/CoreModules/BUCK b/React/CoreModules/BUCK index 130dd99cf7..d5a42389b9 100644 --- a/React/CoreModules/BUCK +++ b/React/CoreModules/BUCK @@ -106,6 +106,9 @@ rn_apple_library( ) + react_module_plugin_providers( name = "LogBox", native_class_func = "RCTLogBoxCls", + ) + react_module_plugin_providers( + name = "TVNavigationEventEmitter", + native_class_func = "RCTTVNavigationEventEmitterCls", ) + react_module_plugin_providers( name = "WebSocketExecutor", native_class_func = "RCTWebSocketExecutorCls", diff --git a/React/CoreModules/CoreModulesPlugins.h b/React/CoreModules/CoreModulesPlugins.h index 55015a55c1..342c24dc57 100644 --- a/React/CoreModules/CoreModulesPlugins.h +++ b/React/CoreModules/CoreModulesPlugins.h @@ -49,6 +49,7 @@ Class RCTDevMenuCls(void) __attribute__((used)); Class RCTDevSettingsCls(void) __attribute__((used)); Class RCTRedBoxCls(void) __attribute__((used)); Class RCTLogBoxCls(void) __attribute__((used)); +Class RCTTVNavigationEventEmitterCls(void) __attribute__((used)); Class RCTWebSocketExecutorCls(void) __attribute__((used)); Class RCTWebSocketModuleCls(void) __attribute__((used)); Class RCTDevLoadingViewCls(void) __attribute__((used)); diff --git a/React/CoreModules/CoreModulesPlugins.mm b/React/CoreModules/CoreModulesPlugins.mm index e74ddb8ee0..daaaab8b12 100644 --- a/React/CoreModules/CoreModulesPlugins.mm +++ b/React/CoreModules/CoreModulesPlugins.mm @@ -27,7 +27,9 @@ Class RCTCoreModulesClassProvider(const char *name) { {"Clipboard", RCTClipboardCls}, {"I18nManager", RCTI18nManagerCls}, {"SourceCode", RCTSourceCodeCls}, +#if !TARGET_OS_TV {"ActionSheetManager", RCTActionSheetManagerCls}, +#endif {"AlertManager", RCTAlertManagerCls}, {"AsyncLocalStorage", RCTAsyncLocalStorageCls}, {"Timing", RCTTimingCls}, @@ -39,6 +41,7 @@ Class RCTCoreModulesClassProvider(const char *name) { {"DevSettings", RCTDevSettingsCls}, {"RedBox", RCTRedBoxCls}, {"LogBox", RCTLogBoxCls}, + {"TVNavigationEventEmitter", RCTTVNavigationEventEmitterCls}, {"WebSocketExecutor", RCTWebSocketExecutorCls}, {"WebSocketModule", RCTWebSocketModuleCls}, {"DevLoadingView", RCTDevLoadingViewCls}, diff --git a/React/CoreModules/RCTActionSheetManager.mm b/React/CoreModules/RCTActionSheetManager.mm index ee3c7b69b4..d600ee61a6 100644 --- a/React/CoreModules/RCTActionSheetManager.mm +++ b/React/CoreModules/RCTActionSheetManager.mm @@ -166,7 +166,7 @@ - (void)presentViewController:(UIViewController *)alertController alertController.view.tintColor = tintColor; #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \ __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 - if (@available(iOS 13.0, *)) { + if (@available(iOS 13.0, tvOS 13.0, *)) { NSString *userInterfaceStyle = [RCTConvert NSString:options.userInterfaceStyle()]; if (userInterfaceStyle == nil || [userInterfaceStyle isEqualToString:@""]) { @@ -247,7 +247,7 @@ - (void)presentViewController:(UIViewController *)alertController #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \ __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 - if (@available(iOS 13.0, *)) { + if (@available(iOS 13.0, tvOS 13.0, *)) { NSString *userInterfaceStyle = [RCTConvert NSString:options.userInterfaceStyle()]; if (userInterfaceStyle == nil || [userInterfaceStyle isEqualToString:@""]) { diff --git a/React/CoreModules/RCTAppearance.mm b/React/CoreModules/RCTAppearance.mm index 71259d4a98..16bdcea30a 100644 --- a/React/CoreModules/RCTAppearance.mm +++ b/React/CoreModules/RCTAppearance.mm @@ -39,7 +39,7 @@ void RCTOverrideAppearancePreference(NSString *const colorSchemeOverride) { #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \ __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 - if (@available(iOS 13.0, *)) { + if (@available(iOS 13.0, tvOS 13.0, *)) { static NSDictionary *appearances; static dispatch_once_t onceToken; @@ -123,7 +123,7 @@ - (void)appearanceChanged:(NSNotification *)notification - (void)startObserving { - if (@available(iOS 13.0, *)) { + if (@available(iOS 13.0, tvOS 13.0, *)) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appearanceChanged:) name:RCTUserInterfaceStyleDidChangeNotification @@ -133,7 +133,7 @@ - (void)startObserving - (void)stopObserving { - if (@available(iOS 13.0, *)) { + if (@available(iOS 13.0, tvOS 13.0, *)) { [[NSNotificationCenter defaultCenter] removeObserver:self]; } } diff --git a/React/CoreModules/RCTAsyncLocalStorage.mm b/React/CoreModules/RCTAsyncLocalStorage.mm index 326ea78f8d..943e0f684f 100644 --- a/React/CoreModules/RCTAsyncLocalStorage.mm +++ b/React/CoreModules/RCTAsyncLocalStorage.mm @@ -76,7 +76,11 @@ static void RCTAppendError(NSDictionary *error, NSMutableArray * static NSString *storageDirectory = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ +#if TARGET_OS_TV + storageDirectory = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject; +#else storageDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; +#endif storageDirectory = [storageDirectory stringByAppendingPathComponent:RCTStorageDirectory]; }); return storageDirectory; diff --git a/React/CoreModules/RCTClipboard.mm b/React/CoreModules/RCTClipboard.mm index 2dc098a473..e6ff1c271a 100644 --- a/React/CoreModules/RCTClipboard.mm +++ b/React/CoreModules/RCTClipboard.mm @@ -28,14 +28,20 @@ - (dispatch_queue_t)methodQueue RCT_EXPORT_METHOD(setString : (NSString *)content) { +#if !TARGET_OS_TV UIPasteboard *clipboard = [UIPasteboard generalPasteboard]; - clipboard.string = (content ?: @""); + clipboard.string = (content ? : @""); +#endif } RCT_EXPORT_METHOD(getString : (RCTPromiseResolveBlock)resolve reject : (__unused RCTPromiseRejectBlock)reject) { +#if TARGET_OS_TV + resolve(@""); +#else UIPasteboard *clipboard = [UIPasteboard generalPasteboard]; - resolve((clipboard.string ?: @"")); + resolve((clipboard.string ? : @"")); +#endif } - (std::shared_ptr)getTurboModule:(const ObjCTurboModule::InitParams &)params diff --git a/React/CoreModules/RCTDevLoadingView.mm b/React/CoreModules/RCTDevLoadingView.mm index 316c248920..c49e8ac829 100644 --- a/React/CoreModules/RCTDevLoadingView.mm +++ b/React/CoreModules/RCTDevLoadingView.mm @@ -121,8 +121,12 @@ - (void)showMessage:(NSString *)message color:(UIColor *)color backgroundColor:( [[UILabel alloc] initWithFrame:CGRectMake(0, window.safeAreaInsets.top - 10, screenSize.width, 20)]; [self->_window addSubview:self->_label]; +#if TARGET_OS_TV + self->_window.windowLevel = UIWindowLevelNormal + 1; +#else self->_window.windowLevel = UIWindowLevelStatusBar + 1; - // set a root VC so rotation is supported +#endif + // set a root VC so rotation is supported self->_window.rootViewController = [UIViewController new]; self->_label.font = [UIFont monospacedDigitSystemFontOfSize:12.0 weight:UIFontWeightRegular]; @@ -137,7 +141,7 @@ - (void)showMessage:(NSString *)message color:(UIColor *)color backgroundColor:( #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \ __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 - if (@available(iOS 13.0, *)) { + if (@available(iOS 13.0, tvOS 13.0, *)) { UIWindowScene *scene = (UIWindowScene *)RCTSharedApplication().connectedScenes.anyObject; self->_window.windowScene = scene; } diff --git a/React/CoreModules/RCTDeviceInfo.mm b/React/CoreModules/RCTDeviceInfo.mm index b948e8e479..3f2198c8fa 100644 --- a/React/CoreModules/RCTDeviceInfo.mm +++ b/React/CoreModules/RCTDeviceInfo.mm @@ -24,8 +24,10 @@ @interface RCTDeviceInfo () @end @implementation RCTDeviceInfo { +#if !TARGET_OS_TV UIInterfaceOrientation _currentInterfaceOrientation; NSDictionary *_currentInterfaceDimensions; +#endif } @synthesize moduleRegistry = _moduleRegistry; @@ -49,6 +51,7 @@ - (void)initialize name:RCTAccessibilityManagerDidUpdateMultiplierNotification object:[_moduleRegistry moduleForName:"AccessibilityManager"]]; +#if !TARGET_OS_TV _currentInterfaceOrientation = [RCTSharedApplication() statusBarOrientation]; [[NSNotificationCenter defaultCenter] addObserver:self @@ -67,6 +70,8 @@ - (void)initialize selector:@selector(interfaceFrameDidChange) name:RCTUserInterfaceStyleDidChangeNotification object:nil]; + +#endif } static BOOL RCTIsIPhoneX() @@ -158,6 +163,8 @@ - (void)didReceiveNewContentSizeMultiplier }); } +#if !TARGET_OS_TV + - (void)interfaceOrientationDidChange { __weak __typeof(self) weakSelf = self; @@ -208,6 +215,8 @@ - (void)_interfaceFrameDidChange _currentInterfaceDimensions = nextInterfaceDimensions; } +#endif // TARGET_OS_TV + - (std::shared_ptr)getTurboModule:(const ObjCTurboModule::InitParams &)params { return std::make_shared(params); diff --git a/React/CoreModules/RCTKeyboardObserver.mm b/React/CoreModules/RCTKeyboardObserver.mm index ce483ddceb..c5ee945e18 100644 --- a/React/CoreModules/RCTKeyboardObserver.mm +++ b/React/CoreModules/RCTKeyboardObserver.mm @@ -23,6 +23,8 @@ @implementation RCTKeyboardObserver - (void)startObserving { +#if !TARGET_OS_TV + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; #define ADD_KEYBOARD_HANDLER(NAME, SELECTOR) [nc addObserver:self selector:@selector(SELECTOR:) name:NAME object:nil] @@ -35,6 +37,8 @@ - (void)startObserving ADD_KEYBOARD_HANDLER(UIKeyboardDidChangeFrameNotification, keyboardDidChangeFrame); #undef ADD_KEYBOARD_HANDLER + +#endif } - (NSArray *)supportedEvents @@ -109,6 +113,9 @@ -(void)EVENT : (NSNotification *)notification static NSDictionary *RCTParseKeyboardNotification(NSNotification *notification) { +#if TARGET_OS_TV + return @{}; +#else NSDictionary *userInfo = notification.userInfo; CGRect beginFrame = [userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue]; CGRect endFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; @@ -124,6 +131,7 @@ -(void)EVENT : (NSNotification *)notification @"easing" : RCTAnimationNameForCurve(curve), @"isEventFromThisApp" : isLocalUserInfoKey == 1 ? @YES : @NO, }; +#endif } Class RCTKeyboardObserverCls(void) diff --git a/React/CoreModules/RCTLogBoxView.mm b/React/CoreModules/RCTLogBoxView.mm index c61b8c5ae2..202023057e 100644 --- a/React/CoreModules/RCTLogBoxView.mm +++ b/React/CoreModules/RCTLogBoxView.mm @@ -17,8 +17,12 @@ @implementation RCTLogBoxView { - (instancetype)initWithFrame:(CGRect)frame { if ((self = [super initWithFrame:frame])) { - self.windowLevel = UIWindowLevelStatusBar - 1; - self.backgroundColor = [UIColor clearColor]; +#if TARGET_OS_TV + self.windowLevel = UIWindowLevelNormal; +#else + self.windowLevel = UIWindowLevelStatusBar - 1; +#endif + self.backgroundColor = [UIColor clearColor]; } return self; } @@ -35,7 +39,11 @@ - (void)createRootViewController:(UIView *)view - (instancetype)initWithFrame:(CGRect)frame bridge:(RCTBridge *)bridge { if ((self = [super initWithFrame:frame])) { +#if TARGET_OS_TV + self.windowLevel = UIWindowLevelNormal; +#else self.windowLevel = UIWindowLevelStatusBar - 1; +#endif self.backgroundColor = [UIColor clearColor]; _surface = [[RCTSurface alloc] initWithBridge:bridge moduleName:@"LogBox" initialProperties:@{}]; diff --git a/React/CoreModules/RCTPerfMonitor.mm b/React/CoreModules/RCTPerfMonitor.mm index 832cd32c3c..39b9be55fd 100644 --- a/React/CoreModules/RCTPerfMonitor.mm +++ b/React/CoreModules/RCTPerfMonitor.mm @@ -52,7 +52,7 @@ static BOOL RCTJSCSetOption(const char *option) // options at runtime. The options are protected and will cause an // exception when you try to change them after the VM has been initialized. // https://github.com/facebook/react-native/issues/28414 - if (@available(iOS 13.4, *)) { + if (@available(iOS 13.4, tvOS 13.4, *)) { return NO; } @@ -219,10 +219,18 @@ - (UIView *)container _container.backgroundColor = [UIColor whiteColor]; #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \ - __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 + __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 && !TARGET_OS_TV if (@available(iOS 13.0, *)) { _container.backgroundColor = [UIColor systemBackgroundColor]; } +#endif +#if TARGET_OS_TV && defined(__TV_OS_VERSION_MAX_ALLOWED) && defined(__TVOS_10_0) && \ + __TV_OS_VERSION_MAX_ALLOWED >= __TVOS_10_0 + if (@available(tvOS 13.0, *)) { + _container.backgroundColor = [UITraitCollection.currentTraitCollection userInterfaceStyle] == UIUserInterfaceStyleDark + ? [UIColor blackColor] + : [UIColor whiteColor]; + } #endif } diff --git a/React/CoreModules/RCTRedBox.mm b/React/CoreModules/RCTRedBox.mm index 4d86309b06..421a613a0b 100644 --- a/React/CoreModules/RCTRedBox.mm +++ b/React/CoreModules/RCTRedBox.mm @@ -104,8 +104,10 @@ - (instancetype)initWithFrame:(CGRect)frame _stackTraceTableView.delegate = self; _stackTraceTableView.dataSource = self; _stackTraceTableView.backgroundColor = [UIColor clearColor]; +#if !TARGET_OS_TV _stackTraceTableView.separatorColor = [UIColor colorWithWhite:1 alpha:0.3]; _stackTraceTableView.separatorStyle = UITableViewCellSeparatorStyleNone; +#endif _stackTraceTableView.indicatorStyle = UIScrollViewIndicatorStyleWhite; [rootView addSubview:_stackTraceTableView]; @@ -283,8 +285,10 @@ - (void)copyStack [fullStackTrace appendFormat:@" %@\n", [self formatFrameSource:stackFrame]]; } } +#if !TARGET_OS_TV UIPasteboard *pb = [UIPasteboard generalPasteboard]; [pb setString:fullStackTrace]; +#endif } - (NSString *)formatFrameSource:(RCTJSStackFrame *)stackFrame diff --git a/React/CoreModules/RCTStatusBarManager.h b/React/CoreModules/RCTStatusBarManager.h index ff1e02e0d6..ab7fcdb862 100644 --- a/React/CoreModules/RCTStatusBarManager.h +++ b/React/CoreModules/RCTStatusBarManager.h @@ -12,8 +12,10 @@ @interface RCTConvert (UIStatusBar) +#if !TARGET_OS_TV + (UIStatusBarStyle)UIStatusBarStyle:(id)json; + (UIStatusBarAnimation)UIStatusBarAnimation:(id)json; +#endif @end diff --git a/React/CoreModules/RCTStatusBarManager.mm b/React/CoreModules/RCTStatusBarManager.mm index 19d1d8f89b..a6f2d4dfa3 100644 --- a/React/CoreModules/RCTStatusBarManager.mm +++ b/React/CoreModules/RCTStatusBarManager.mm @@ -12,6 +12,7 @@ #import #import +#if !TARGET_OS_TV #import @implementation RCTConvert (UIStatusBar) @@ -57,10 +58,15 @@ + (UIStatusBarStyle)UIStatusBarStyle:(id)json RCT_DYNAMIC integerValue); @end +#endif + +#if !TARGET_OS_TV @interface RCTStatusBarManager () @end +#endif + @implementation RCTStatusBarManager static BOOL RCTViewControllerBasedStatusBarAppearance() @@ -88,6 +94,17 @@ + (BOOL)requiresMainQueueSetup return @[ @"statusBarFrameDidChange", @"statusBarFrameWillChange" ]; } +#if TARGET_OS_TV + +RCT_EXPORT_METHOD(getHeight : (RCTResponseSenderBlock)callback) +{ + callback(@[ @{ + @"height" : @(0), + } ]); +} + +#else + - (void)startObserving { NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; @@ -199,6 +216,8 @@ - (void)applicationWillChangeStatusBarFrame:(NSNotification *)notification return std::make_shared(params); } +#endif // TARGET_OS_TV + @end Class RCTStatusBarManagerCls(void) diff --git a/React/CoreModules/RCTTVNavigationEventEmitter.h b/React/CoreModules/RCTTVNavigationEventEmitter.h new file mode 100644 index 0000000000..0696999150 --- /dev/null +++ b/React/CoreModules/RCTTVNavigationEventEmitter.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import + +@protocol NativeTVNavigationEventEmitterSpec + +- (void)addListener:(NSString *)eventName; +- (void)removeListeners:(double)count; + +@end + +@interface RCTTVNavigationEventEmitter : RCTEventEmitter + +@end diff --git a/React/CoreModules/RCTTVNavigationEventEmitter.mm b/React/CoreModules/RCTTVNavigationEventEmitter.mm new file mode 100644 index 0000000000..980db9f673 --- /dev/null +++ b/React/CoreModules/RCTTVNavigationEventEmitter.mm @@ -0,0 +1,61 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTTVNavigationEventEmitter.h" + +#import +#import "CoreModulesPlugins.h" + +static NSString *const TVNavigationEventName = @"onHWKeyEvent"; + +@interface RCTTVNavigationEventEmitter () +@end + +@implementation RCTTVNavigationEventEmitter + +RCT_EXPORT_MODULE() + ++ (BOOL)requiresMainQueueSetup +{ + return NO; +} + +- (instancetype)init +{ + if (self = [super init]) { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleTVNavigationEventNotification:) + name:@"RCTTVNavigationEventNotification" + object:nil]; + } + return self; +} + +- (NSArray *)supportedEvents +{ + return @[ TVNavigationEventName ]; +} + +- (void)handleTVNavigationEventNotification:(NSNotification *)notif +{ + if (self.bridge) { + [self sendEventWithName:TVNavigationEventName body:notif.object]; + } +} + +- (std::shared_ptr)getTurboModule: + (const facebook::react::ObjCTurboModule::InitParams &)params +{ + return std::make_shared(params); +} + +@end + +Class RCTTVNavigationEventEmitterCls(void) +{ + return RCTTVNavigationEventEmitter.class; +} diff --git a/React/CoreModules/React-CoreModules.podspec b/React/CoreModules/React-CoreModules.podspec index e3c55c06f1..a7cdb0d919 100644 --- a/React/CoreModules/React-CoreModules.podspec +++ b/React/CoreModules/React-CoreModules.podspec @@ -26,10 +26,12 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "**/*.{c,m,mm,cpp}" + s.tvos.exclude_files = "**/RCTActionSheet*.*" + s.tvos.source_files = "**/RCTTV*.*" s.header_dir = "CoreModules" s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES", diff --git a/React/FBReactNativeSpec/FBReactNativeSpec.podspec b/React/FBReactNativeSpec/FBReactNativeSpec.podspec index 16079fdb16..5783f72740 100644 --- a/React/FBReactNativeSpec/FBReactNativeSpec.podspec +++ b/React/FBReactNativeSpec/FBReactNativeSpec.podspec @@ -29,7 +29,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source # This podspec is used to trigger the codegen, and built files are generated in a different location. diff --git a/React/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm b/React/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm new file mode 100644 index 0000000000..b33fb1466a --- /dev/null +++ b/React/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm @@ -0,0 +1,2119 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @generated by codegen project: GenerateModuleObjCpp + * + * We create an umbrella header (and corresponding implementation) here since + * Cxx compilation in BUCK has a limitation: source-code producing genrule()s + * must have a single output. More files => more genrule()s => slower builds. + */ + +#import "FBReactNativeSpec.h" + + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeAccessibilityInfoSpecJSI_isReduceMotionEnabled(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "isReduceMotionEnabled", @selector(isReduceMotionEnabled:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAccessibilityInfoSpecJSI_isTouchExplorationEnabled(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "isTouchExplorationEnabled", @selector(isTouchExplorationEnabled:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAccessibilityInfoSpecJSI_setAccessibilityFocus(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setAccessibilityFocus", @selector(setAccessibilityFocus:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAccessibilityInfoSpecJSI_announceForAccessibility(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "announceForAccessibility", @selector(announceForAccessibility:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAccessibilityInfoSpecJSI_getRecommendedTimeoutMillis(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getRecommendedTimeoutMillis", @selector(getRecommendedTimeoutMillis:onSuccess:), args, count); + } + + NativeAccessibilityInfoSpecJSI::NativeAccessibilityInfoSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["isReduceMotionEnabled"] = MethodMetadata {1, __hostFunction_NativeAccessibilityInfoSpecJSI_isReduceMotionEnabled}; + + + methodMap_["isTouchExplorationEnabled"] = MethodMetadata {1, __hostFunction_NativeAccessibilityInfoSpecJSI_isTouchExplorationEnabled}; + + + methodMap_["setAccessibilityFocus"] = MethodMetadata {1, __hostFunction_NativeAccessibilityInfoSpecJSI_setAccessibilityFocus}; + + + methodMap_["announceForAccessibility"] = MethodMetadata {1, __hostFunction_NativeAccessibilityInfoSpecJSI_announceForAccessibility}; + + + methodMap_["getRecommendedTimeoutMillis"] = MethodMetadata {2, __hostFunction_NativeAccessibilityInfoSpecJSI_getRecommendedTimeoutMillis}; + + } + } // namespace react +} // namespace facebook +@implementation RCTCxxConvert (NativeAccessibilityManager_SpecSetAccessibilityContentSizeMultipliersJSMultipliers) ++ (RCTManagedPointer *)JS_NativeAccessibilityManager_SpecSetAccessibilityContentSizeMultipliersJSMultipliers:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeAccessibilityManagerSpecJSI_getCurrentBoldTextState(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getCurrentBoldTextState", @selector(getCurrentBoldTextState:onError:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAccessibilityManagerSpecJSI_getCurrentGrayscaleState(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getCurrentGrayscaleState", @selector(getCurrentGrayscaleState:onError:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAccessibilityManagerSpecJSI_getCurrentInvertColorsState(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getCurrentInvertColorsState", @selector(getCurrentInvertColorsState:onError:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAccessibilityManagerSpecJSI_getCurrentReduceMotionState(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getCurrentReduceMotionState", @selector(getCurrentReduceMotionState:onError:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAccessibilityManagerSpecJSI_getCurrentReduceTransparencyState(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getCurrentReduceTransparencyState", @selector(getCurrentReduceTransparencyState:onError:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAccessibilityManagerSpecJSI_getCurrentVoiceOverState(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getCurrentVoiceOverState", @selector(getCurrentVoiceOverState:onError:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAccessibilityManagerSpecJSI_setAccessibilityContentSizeMultipliers(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setAccessibilityContentSizeMultipliers", @selector(setAccessibilityContentSizeMultipliers:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAccessibilityManagerSpecJSI_setAccessibilityFocus(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setAccessibilityFocus", @selector(setAccessibilityFocus:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAccessibilityManagerSpecJSI_announceForAccessibility(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "announceForAccessibility", @selector(announceForAccessibility:), args, count); + } + + NativeAccessibilityManagerSpecJSI::NativeAccessibilityManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["getCurrentBoldTextState"] = MethodMetadata {2, __hostFunction_NativeAccessibilityManagerSpecJSI_getCurrentBoldTextState}; + + + methodMap_["getCurrentGrayscaleState"] = MethodMetadata {2, __hostFunction_NativeAccessibilityManagerSpecJSI_getCurrentGrayscaleState}; + + + methodMap_["getCurrentInvertColorsState"] = MethodMetadata {2, __hostFunction_NativeAccessibilityManagerSpecJSI_getCurrentInvertColorsState}; + + + methodMap_["getCurrentReduceMotionState"] = MethodMetadata {2, __hostFunction_NativeAccessibilityManagerSpecJSI_getCurrentReduceMotionState}; + + + methodMap_["getCurrentReduceTransparencyState"] = MethodMetadata {2, __hostFunction_NativeAccessibilityManagerSpecJSI_getCurrentReduceTransparencyState}; + + + methodMap_["getCurrentVoiceOverState"] = MethodMetadata {2, __hostFunction_NativeAccessibilityManagerSpecJSI_getCurrentVoiceOverState}; + + + methodMap_["setAccessibilityContentSizeMultipliers"] = MethodMetadata {1, __hostFunction_NativeAccessibilityManagerSpecJSI_setAccessibilityContentSizeMultipliers}; + setMethodArgConversionSelector(@"setAccessibilityContentSizeMultipliers", 0, @"JS_NativeAccessibilityManager_SpecSetAccessibilityContentSizeMultipliersJSMultipliers:"); + + methodMap_["setAccessibilityFocus"] = MethodMetadata {1, __hostFunction_NativeAccessibilityManagerSpecJSI_setAccessibilityFocus}; + + + methodMap_["announceForAccessibility"] = MethodMetadata {1, __hostFunction_NativeAccessibilityManagerSpecJSI_announceForAccessibility}; + + } + } // namespace react +} // namespace facebook +@implementation RCTCxxConvert (NativeActionSheetManager_SpecShowActionSheetWithOptionsOptions) ++ (RCTManagedPointer *)JS_NativeActionSheetManager_SpecShowActionSheetWithOptionsOptions:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +@implementation RCTCxxConvert (NativeActionSheetManager_SpecShowShareActionSheetWithOptionsOptions) ++ (RCTManagedPointer *)JS_NativeActionSheetManager_SpecShowShareActionSheetWithOptionsOptions:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeActionSheetManagerSpecJSI_showActionSheetWithOptions(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "showActionSheetWithOptions", @selector(showActionSheetWithOptions:callback:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeActionSheetManagerSpecJSI_showShareActionSheetWithOptions(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "showShareActionSheetWithOptions", @selector(showShareActionSheetWithOptions:failureCallback:successCallback:), args, count); + } + + NativeActionSheetManagerSpecJSI::NativeActionSheetManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["showActionSheetWithOptions"] = MethodMetadata {2, __hostFunction_NativeActionSheetManagerSpecJSI_showActionSheetWithOptions}; + setMethodArgConversionSelector(@"showActionSheetWithOptions", 0, @"JS_NativeActionSheetManager_SpecShowActionSheetWithOptionsOptions:"); + + methodMap_["showShareActionSheetWithOptions"] = MethodMetadata {3, __hostFunction_NativeActionSheetManagerSpecJSI_showShareActionSheetWithOptions}; + setMethodArgConversionSelector(@"showShareActionSheetWithOptions", 0, @"JS_NativeActionSheetManager_SpecShowShareActionSheetWithOptionsOptions:"); + } + } // namespace react +} // namespace facebook +@implementation RCTCxxConvert (NativeAlertManager_Args) ++ (RCTManagedPointer *)JS_NativeAlertManager_Args:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeAlertManagerSpecJSI_alertWithArgs(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "alertWithArgs", @selector(alertWithArgs:callback:), args, count); + } + + NativeAlertManagerSpecJSI::NativeAlertManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["alertWithArgs"] = MethodMetadata {2, __hostFunction_NativeAlertManagerSpecJSI_alertWithArgs}; + setMethodArgConversionSelector(@"alertWithArgs", 0, @"JS_NativeAlertManager_Args:"); + } + } // namespace react +} // namespace facebook +@implementation RCTCxxConvert (NativeAnimatedModule_EventMapping) ++ (RCTManagedPointer *)JS_NativeAnimatedModule_EventMapping:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_startOperationBatch(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "startOperationBatch", @selector(startOperationBatch), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_finishOperationBatch(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "finishOperationBatch", @selector(finishOperationBatch), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_createAnimatedNode(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "createAnimatedNode", @selector(createAnimatedNode:config:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_getValue(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getValue", @selector(getValue:saveValueCallback:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_startListeningToAnimatedNodeValue(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "startListeningToAnimatedNodeValue", @selector(startListeningToAnimatedNodeValue:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_stopListeningToAnimatedNodeValue(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "stopListeningToAnimatedNodeValue", @selector(stopListeningToAnimatedNodeValue:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_connectAnimatedNodes(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "connectAnimatedNodes", @selector(connectAnimatedNodes:childTag:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_disconnectAnimatedNodes(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "disconnectAnimatedNodes", @selector(disconnectAnimatedNodes:childTag:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_startAnimatingNode(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "startAnimatingNode", @selector(startAnimatingNode:nodeTag:config:endCallback:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_stopAnimation(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "stopAnimation", @selector(stopAnimation:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_setAnimatedNodeValue(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setAnimatedNodeValue", @selector(setAnimatedNodeValue:value:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_setAnimatedNodeOffset(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setAnimatedNodeOffset", @selector(setAnimatedNodeOffset:offset:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_flattenAnimatedNodeOffset(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "flattenAnimatedNodeOffset", @selector(flattenAnimatedNodeOffset:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_extractAnimatedNodeOffset(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "extractAnimatedNodeOffset", @selector(extractAnimatedNodeOffset:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_connectAnimatedNodeToView(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "connectAnimatedNodeToView", @selector(connectAnimatedNodeToView:viewTag:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_disconnectAnimatedNodeFromView(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "disconnectAnimatedNodeFromView", @selector(disconnectAnimatedNodeFromView:viewTag:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_restoreDefaultValues(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "restoreDefaultValues", @selector(restoreDefaultValues:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_dropAnimatedNode(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "dropAnimatedNode", @selector(dropAnimatedNode:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_addAnimatedEventToView(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addAnimatedEventToView", @selector(addAnimatedEventToView:eventName:eventMapping:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_removeAnimatedEventFromView(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeAnimatedEventFromView", @selector(removeAnimatedEventFromView:eventName:animatedNodeTag:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_addListener(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addListener", @selector(addListener:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_removeListeners(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeListeners", @selector(removeListeners:), args, count); + } + + NativeAnimatedModuleSpecJSI::NativeAnimatedModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["startOperationBatch"] = MethodMetadata {0, __hostFunction_NativeAnimatedModuleSpecJSI_startOperationBatch}; + + + methodMap_["finishOperationBatch"] = MethodMetadata {0, __hostFunction_NativeAnimatedModuleSpecJSI_finishOperationBatch}; + + + methodMap_["createAnimatedNode"] = MethodMetadata {2, __hostFunction_NativeAnimatedModuleSpecJSI_createAnimatedNode}; + + + methodMap_["getValue"] = MethodMetadata {2, __hostFunction_NativeAnimatedModuleSpecJSI_getValue}; + + + methodMap_["startListeningToAnimatedNodeValue"] = MethodMetadata {1, __hostFunction_NativeAnimatedModuleSpecJSI_startListeningToAnimatedNodeValue}; + + + methodMap_["stopListeningToAnimatedNodeValue"] = MethodMetadata {1, __hostFunction_NativeAnimatedModuleSpecJSI_stopListeningToAnimatedNodeValue}; + + + methodMap_["connectAnimatedNodes"] = MethodMetadata {2, __hostFunction_NativeAnimatedModuleSpecJSI_connectAnimatedNodes}; + + + methodMap_["disconnectAnimatedNodes"] = MethodMetadata {2, __hostFunction_NativeAnimatedModuleSpecJSI_disconnectAnimatedNodes}; + + + methodMap_["startAnimatingNode"] = MethodMetadata {4, __hostFunction_NativeAnimatedModuleSpecJSI_startAnimatingNode}; + + + methodMap_["stopAnimation"] = MethodMetadata {1, __hostFunction_NativeAnimatedModuleSpecJSI_stopAnimation}; + + + methodMap_["setAnimatedNodeValue"] = MethodMetadata {2, __hostFunction_NativeAnimatedModuleSpecJSI_setAnimatedNodeValue}; + + + methodMap_["setAnimatedNodeOffset"] = MethodMetadata {2, __hostFunction_NativeAnimatedModuleSpecJSI_setAnimatedNodeOffset}; + + + methodMap_["flattenAnimatedNodeOffset"] = MethodMetadata {1, __hostFunction_NativeAnimatedModuleSpecJSI_flattenAnimatedNodeOffset}; + + + methodMap_["extractAnimatedNodeOffset"] = MethodMetadata {1, __hostFunction_NativeAnimatedModuleSpecJSI_extractAnimatedNodeOffset}; + + + methodMap_["connectAnimatedNodeToView"] = MethodMetadata {2, __hostFunction_NativeAnimatedModuleSpecJSI_connectAnimatedNodeToView}; + + + methodMap_["disconnectAnimatedNodeFromView"] = MethodMetadata {2, __hostFunction_NativeAnimatedModuleSpecJSI_disconnectAnimatedNodeFromView}; + + + methodMap_["restoreDefaultValues"] = MethodMetadata {1, __hostFunction_NativeAnimatedModuleSpecJSI_restoreDefaultValues}; + + + methodMap_["dropAnimatedNode"] = MethodMetadata {1, __hostFunction_NativeAnimatedModuleSpecJSI_dropAnimatedNode}; + + + methodMap_["addAnimatedEventToView"] = MethodMetadata {3, __hostFunction_NativeAnimatedModuleSpecJSI_addAnimatedEventToView}; + setMethodArgConversionSelector(@"addAnimatedEventToView", 2, @"JS_NativeAnimatedModule_EventMapping:"); + + methodMap_["removeAnimatedEventFromView"] = MethodMetadata {3, __hostFunction_NativeAnimatedModuleSpecJSI_removeAnimatedEventFromView}; + + + methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeAnimatedModuleSpecJSI_addListener}; + + + methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeAnimatedModuleSpecJSI_removeListeners}; + + } + } // namespace react +} // namespace facebook +@implementation RCTCxxConvert (NativeAnimatedTurboModule_EventMapping) ++ (RCTManagedPointer *)JS_NativeAnimatedTurboModule_EventMapping:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_startOperationBatch(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "startOperationBatch", @selector(startOperationBatch), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_finishOperationBatch(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "finishOperationBatch", @selector(finishOperationBatch), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_createAnimatedNode(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "createAnimatedNode", @selector(createAnimatedNode:config:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_getValue(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getValue", @selector(getValue:saveValueCallback:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_startListeningToAnimatedNodeValue(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "startListeningToAnimatedNodeValue", @selector(startListeningToAnimatedNodeValue:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_stopListeningToAnimatedNodeValue(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "stopListeningToAnimatedNodeValue", @selector(stopListeningToAnimatedNodeValue:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_connectAnimatedNodes(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "connectAnimatedNodes", @selector(connectAnimatedNodes:childTag:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_disconnectAnimatedNodes(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "disconnectAnimatedNodes", @selector(disconnectAnimatedNodes:childTag:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_startAnimatingNode(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "startAnimatingNode", @selector(startAnimatingNode:nodeTag:config:endCallback:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_stopAnimation(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "stopAnimation", @selector(stopAnimation:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_setAnimatedNodeValue(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setAnimatedNodeValue", @selector(setAnimatedNodeValue:value:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_setAnimatedNodeOffset(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setAnimatedNodeOffset", @selector(setAnimatedNodeOffset:offset:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_flattenAnimatedNodeOffset(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "flattenAnimatedNodeOffset", @selector(flattenAnimatedNodeOffset:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_extractAnimatedNodeOffset(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "extractAnimatedNodeOffset", @selector(extractAnimatedNodeOffset:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_connectAnimatedNodeToView(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "connectAnimatedNodeToView", @selector(connectAnimatedNodeToView:viewTag:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_disconnectAnimatedNodeFromView(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "disconnectAnimatedNodeFromView", @selector(disconnectAnimatedNodeFromView:viewTag:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_restoreDefaultValues(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "restoreDefaultValues", @selector(restoreDefaultValues:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_dropAnimatedNode(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "dropAnimatedNode", @selector(dropAnimatedNode:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_addAnimatedEventToView(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addAnimatedEventToView", @selector(addAnimatedEventToView:eventName:eventMapping:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_removeAnimatedEventFromView(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeAnimatedEventFromView", @selector(removeAnimatedEventFromView:eventName:animatedNodeTag:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_addListener(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addListener", @selector(addListener:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimatedTurboModuleSpecJSI_removeListeners(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeListeners", @selector(removeListeners:), args, count); + } + + NativeAnimatedTurboModuleSpecJSI::NativeAnimatedTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["startOperationBatch"] = MethodMetadata {0, __hostFunction_NativeAnimatedTurboModuleSpecJSI_startOperationBatch}; + + + methodMap_["finishOperationBatch"] = MethodMetadata {0, __hostFunction_NativeAnimatedTurboModuleSpecJSI_finishOperationBatch}; + + + methodMap_["createAnimatedNode"] = MethodMetadata {2, __hostFunction_NativeAnimatedTurboModuleSpecJSI_createAnimatedNode}; + + + methodMap_["getValue"] = MethodMetadata {2, __hostFunction_NativeAnimatedTurboModuleSpecJSI_getValue}; + + + methodMap_["startListeningToAnimatedNodeValue"] = MethodMetadata {1, __hostFunction_NativeAnimatedTurboModuleSpecJSI_startListeningToAnimatedNodeValue}; + + + methodMap_["stopListeningToAnimatedNodeValue"] = MethodMetadata {1, __hostFunction_NativeAnimatedTurboModuleSpecJSI_stopListeningToAnimatedNodeValue}; + + + methodMap_["connectAnimatedNodes"] = MethodMetadata {2, __hostFunction_NativeAnimatedTurboModuleSpecJSI_connectAnimatedNodes}; + + + methodMap_["disconnectAnimatedNodes"] = MethodMetadata {2, __hostFunction_NativeAnimatedTurboModuleSpecJSI_disconnectAnimatedNodes}; + + + methodMap_["startAnimatingNode"] = MethodMetadata {4, __hostFunction_NativeAnimatedTurboModuleSpecJSI_startAnimatingNode}; + + + methodMap_["stopAnimation"] = MethodMetadata {1, __hostFunction_NativeAnimatedTurboModuleSpecJSI_stopAnimation}; + + + methodMap_["setAnimatedNodeValue"] = MethodMetadata {2, __hostFunction_NativeAnimatedTurboModuleSpecJSI_setAnimatedNodeValue}; + + + methodMap_["setAnimatedNodeOffset"] = MethodMetadata {2, __hostFunction_NativeAnimatedTurboModuleSpecJSI_setAnimatedNodeOffset}; + + + methodMap_["flattenAnimatedNodeOffset"] = MethodMetadata {1, __hostFunction_NativeAnimatedTurboModuleSpecJSI_flattenAnimatedNodeOffset}; + + + methodMap_["extractAnimatedNodeOffset"] = MethodMetadata {1, __hostFunction_NativeAnimatedTurboModuleSpecJSI_extractAnimatedNodeOffset}; + + + methodMap_["connectAnimatedNodeToView"] = MethodMetadata {2, __hostFunction_NativeAnimatedTurboModuleSpecJSI_connectAnimatedNodeToView}; + + + methodMap_["disconnectAnimatedNodeFromView"] = MethodMetadata {2, __hostFunction_NativeAnimatedTurboModuleSpecJSI_disconnectAnimatedNodeFromView}; + + + methodMap_["restoreDefaultValues"] = MethodMetadata {1, __hostFunction_NativeAnimatedTurboModuleSpecJSI_restoreDefaultValues}; + + + methodMap_["dropAnimatedNode"] = MethodMetadata {1, __hostFunction_NativeAnimatedTurboModuleSpecJSI_dropAnimatedNode}; + + + methodMap_["addAnimatedEventToView"] = MethodMetadata {3, __hostFunction_NativeAnimatedTurboModuleSpecJSI_addAnimatedEventToView}; + setMethodArgConversionSelector(@"addAnimatedEventToView", 2, @"JS_NativeAnimatedTurboModule_EventMapping:"); + + methodMap_["removeAnimatedEventFromView"] = MethodMetadata {3, __hostFunction_NativeAnimatedTurboModuleSpecJSI_removeAnimatedEventFromView}; + + + methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeAnimatedTurboModuleSpecJSI_addListener}; + + + methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeAnimatedTurboModuleSpecJSI_removeListeners}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeAnimationsDebugModuleSpecJSI_startRecordingFps(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "startRecordingFps", @selector(startRecordingFps), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAnimationsDebugModuleSpecJSI_stopRecordingFps(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "stopRecordingFps", @selector(stopRecordingFps:), args, count); + } + + NativeAnimationsDebugModuleSpecJSI::NativeAnimationsDebugModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["startRecordingFps"] = MethodMetadata {0, __hostFunction_NativeAnimationsDebugModuleSpecJSI_startRecordingFps}; + + + methodMap_["stopRecordingFps"] = MethodMetadata {1, __hostFunction_NativeAnimationsDebugModuleSpecJSI_stopRecordingFps}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeAppStateSpecJSI_getCurrentAppState(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getCurrentAppState", @selector(getCurrentAppState:error:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAppStateSpecJSI_addListener(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addListener", @selector(addListener:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAppStateSpecJSI_removeListeners(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeListeners", @selector(removeListeners:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAppStateSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, "getConstants", @selector(getConstants), args, count); + } + + NativeAppStateSpecJSI::NativeAppStateSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["getCurrentAppState"] = MethodMetadata {2, __hostFunction_NativeAppStateSpecJSI_getCurrentAppState}; + + + methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeAppStateSpecJSI_addListener}; + + + methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeAppStateSpecJSI_removeListeners}; + + + methodMap_["getConstants"] = MethodMetadata {0, __hostFunction_NativeAppStateSpecJSI_getConstants}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeAppearanceSpecJSI_getColorScheme(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, StringKind, "getColorScheme", @selector(getColorScheme), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAppearanceSpecJSI_addListener(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addListener", @selector(addListener:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAppearanceSpecJSI_removeListeners(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeListeners", @selector(removeListeners:), args, count); + } + + NativeAppearanceSpecJSI::NativeAppearanceSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["getColorScheme"] = MethodMetadata {0, __hostFunction_NativeAppearanceSpecJSI_getColorScheme}; + + + methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeAppearanceSpecJSI_addListener}; + + + methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeAppearanceSpecJSI_removeListeners}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeAsyncLocalStorageSpecJSI_multiGet(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "multiGet", @selector(multiGet:callback:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAsyncLocalStorageSpecJSI_multiSet(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "multiSet", @selector(multiSet:callback:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAsyncLocalStorageSpecJSI_multiMerge(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "multiMerge", @selector(multiMerge:callback:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAsyncLocalStorageSpecJSI_multiRemove(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "multiRemove", @selector(multiRemove:callback:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAsyncLocalStorageSpecJSI_clear(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "clear", @selector(clear:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAsyncLocalStorageSpecJSI_getAllKeys(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getAllKeys", @selector(getAllKeys:), args, count); + } + + NativeAsyncLocalStorageSpecJSI::NativeAsyncLocalStorageSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["multiGet"] = MethodMetadata {2, __hostFunction_NativeAsyncLocalStorageSpecJSI_multiGet}; + + + methodMap_["multiSet"] = MethodMetadata {2, __hostFunction_NativeAsyncLocalStorageSpecJSI_multiSet}; + + + methodMap_["multiMerge"] = MethodMetadata {2, __hostFunction_NativeAsyncLocalStorageSpecJSI_multiMerge}; + + + methodMap_["multiRemove"] = MethodMetadata {2, __hostFunction_NativeAsyncLocalStorageSpecJSI_multiRemove}; + + + methodMap_["clear"] = MethodMetadata {1, __hostFunction_NativeAsyncLocalStorageSpecJSI_clear}; + + + methodMap_["getAllKeys"] = MethodMetadata {1, __hostFunction_NativeAsyncLocalStorageSpecJSI_getAllKeys}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeAsyncSQLiteDBStorageSpecJSI_multiGet(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "multiGet", @selector(multiGet:callback:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAsyncSQLiteDBStorageSpecJSI_multiSet(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "multiSet", @selector(multiSet:callback:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAsyncSQLiteDBStorageSpecJSI_multiMerge(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "multiMerge", @selector(multiMerge:callback:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAsyncSQLiteDBStorageSpecJSI_multiRemove(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "multiRemove", @selector(multiRemove:callback:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAsyncSQLiteDBStorageSpecJSI_clear(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "clear", @selector(clear:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeAsyncSQLiteDBStorageSpecJSI_getAllKeys(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getAllKeys", @selector(getAllKeys:), args, count); + } + + NativeAsyncSQLiteDBStorageSpecJSI::NativeAsyncSQLiteDBStorageSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["multiGet"] = MethodMetadata {2, __hostFunction_NativeAsyncSQLiteDBStorageSpecJSI_multiGet}; + + + methodMap_["multiSet"] = MethodMetadata {2, __hostFunction_NativeAsyncSQLiteDBStorageSpecJSI_multiSet}; + + + methodMap_["multiMerge"] = MethodMetadata {2, __hostFunction_NativeAsyncSQLiteDBStorageSpecJSI_multiMerge}; + + + methodMap_["multiRemove"] = MethodMetadata {2, __hostFunction_NativeAsyncSQLiteDBStorageSpecJSI_multiRemove}; + + + methodMap_["clear"] = MethodMetadata {1, __hostFunction_NativeAsyncSQLiteDBStorageSpecJSI_clear}; + + + methodMap_["getAllKeys"] = MethodMetadata {1, __hostFunction_NativeAsyncSQLiteDBStorageSpecJSI_getAllKeys}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeBlobModuleSpecJSI_addNetworkingHandler(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addNetworkingHandler", @selector(addNetworkingHandler), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeBlobModuleSpecJSI_addWebSocketHandler(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addWebSocketHandler", @selector(addWebSocketHandler:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeBlobModuleSpecJSI_removeWebSocketHandler(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeWebSocketHandler", @selector(removeWebSocketHandler:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeBlobModuleSpecJSI_sendOverSocket(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "sendOverSocket", @selector(sendOverSocket:socketID:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeBlobModuleSpecJSI_createFromParts(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "createFromParts", @selector(createFromParts:withId:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeBlobModuleSpecJSI_release(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "release", @selector(release:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeBlobModuleSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, "getConstants", @selector(getConstants), args, count); + } + + NativeBlobModuleSpecJSI::NativeBlobModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["addNetworkingHandler"] = MethodMetadata {0, __hostFunction_NativeBlobModuleSpecJSI_addNetworkingHandler}; + + + methodMap_["addWebSocketHandler"] = MethodMetadata {1, __hostFunction_NativeBlobModuleSpecJSI_addWebSocketHandler}; + + + methodMap_["removeWebSocketHandler"] = MethodMetadata {1, __hostFunction_NativeBlobModuleSpecJSI_removeWebSocketHandler}; + + + methodMap_["sendOverSocket"] = MethodMetadata {2, __hostFunction_NativeBlobModuleSpecJSI_sendOverSocket}; + + + methodMap_["createFromParts"] = MethodMetadata {2, __hostFunction_NativeBlobModuleSpecJSI_createFromParts}; + + + methodMap_["release"] = MethodMetadata {1, __hostFunction_NativeBlobModuleSpecJSI_release}; + + + methodMap_["getConstants"] = MethodMetadata {0, __hostFunction_NativeBlobModuleSpecJSI_getConstants}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeBugReportingSpecJSI_startReportAProblemFlow(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "startReportAProblemFlow", @selector(startReportAProblemFlow), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeBugReportingSpecJSI_setExtraData(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setExtraData", @selector(setExtraData:extraFiles:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeBugReportingSpecJSI_setCategoryID(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setCategoryID", @selector(setCategoryID:), args, count); + } + + NativeBugReportingSpecJSI::NativeBugReportingSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["startReportAProblemFlow"] = MethodMetadata {0, __hostFunction_NativeBugReportingSpecJSI_startReportAProblemFlow}; + + + methodMap_["setExtraData"] = MethodMetadata {2, __hostFunction_NativeBugReportingSpecJSI_setExtraData}; + + + methodMap_["setCategoryID"] = MethodMetadata {1, __hostFunction_NativeBugReportingSpecJSI_setCategoryID}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeClipboardSpecJSI_getString(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "getString", @selector(getString:reject:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeClipboardSpecJSI_setString(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setString", @selector(setString:), args, count); + } + + NativeClipboardSpecJSI::NativeClipboardSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["getString"] = MethodMetadata {0, __hostFunction_NativeClipboardSpecJSI_getString}; + + + methodMap_["setString"] = MethodMetadata {1, __hostFunction_NativeClipboardSpecJSI_setString}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeDevLoadingViewSpecJSI_showMessage(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "showMessage", @selector(showMessage:withColor:withBackgroundColor:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeDevLoadingViewSpecJSI_hide(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "hide", @selector(hide), args, count); + } + + NativeDevLoadingViewSpecJSI::NativeDevLoadingViewSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["showMessage"] = MethodMetadata {3, __hostFunction_NativeDevLoadingViewSpecJSI_showMessage}; + + + methodMap_["hide"] = MethodMetadata {0, __hostFunction_NativeDevLoadingViewSpecJSI_hide}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeDevMenuSpecJSI_show(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "show", @selector(show), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeDevMenuSpecJSI_reload(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "reload", @selector(reload), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeDevMenuSpecJSI_debugRemotely(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "debugRemotely", @selector(debugRemotely:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeDevMenuSpecJSI_setProfilingEnabled(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setProfilingEnabled", @selector(setProfilingEnabled:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeDevMenuSpecJSI_setHotLoadingEnabled(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setHotLoadingEnabled", @selector(setHotLoadingEnabled:), args, count); + } + + NativeDevMenuSpecJSI::NativeDevMenuSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["show"] = MethodMetadata {0, __hostFunction_NativeDevMenuSpecJSI_show}; + + + methodMap_["reload"] = MethodMetadata {0, __hostFunction_NativeDevMenuSpecJSI_reload}; + + + methodMap_["debugRemotely"] = MethodMetadata {1, __hostFunction_NativeDevMenuSpecJSI_debugRemotely}; + + + methodMap_["setProfilingEnabled"] = MethodMetadata {1, __hostFunction_NativeDevMenuSpecJSI_setProfilingEnabled}; + + + methodMap_["setHotLoadingEnabled"] = MethodMetadata {1, __hostFunction_NativeDevMenuSpecJSI_setHotLoadingEnabled}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeDevSettingsSpecJSI_reload(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "reload", @selector(reload), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeDevSettingsSpecJSI_reloadWithReason(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "reloadWithReason", @selector(reloadWithReason:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeDevSettingsSpecJSI_onFastRefresh(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "onFastRefresh", @selector(onFastRefresh), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeDevSettingsSpecJSI_setHotLoadingEnabled(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setHotLoadingEnabled", @selector(setHotLoadingEnabled:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeDevSettingsSpecJSI_setIsDebuggingRemotely(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setIsDebuggingRemotely", @selector(setIsDebuggingRemotely:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeDevSettingsSpecJSI_setProfilingEnabled(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setProfilingEnabled", @selector(setProfilingEnabled:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeDevSettingsSpecJSI_toggleElementInspector(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "toggleElementInspector", @selector(toggleElementInspector), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeDevSettingsSpecJSI_addMenuItem(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addMenuItem", @selector(addMenuItem:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeDevSettingsSpecJSI_addListener(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addListener", @selector(addListener:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeDevSettingsSpecJSI_removeListeners(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeListeners", @selector(removeListeners:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeDevSettingsSpecJSI_setIsShakeToShowDevMenuEnabled(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setIsShakeToShowDevMenuEnabled", @selector(setIsShakeToShowDevMenuEnabled:), args, count); + } + + NativeDevSettingsSpecJSI::NativeDevSettingsSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["reload"] = MethodMetadata {0, __hostFunction_NativeDevSettingsSpecJSI_reload}; + + + methodMap_["reloadWithReason"] = MethodMetadata {1, __hostFunction_NativeDevSettingsSpecJSI_reloadWithReason}; + + + methodMap_["onFastRefresh"] = MethodMetadata {0, __hostFunction_NativeDevSettingsSpecJSI_onFastRefresh}; + + + methodMap_["setHotLoadingEnabled"] = MethodMetadata {1, __hostFunction_NativeDevSettingsSpecJSI_setHotLoadingEnabled}; + + + methodMap_["setIsDebuggingRemotely"] = MethodMetadata {1, __hostFunction_NativeDevSettingsSpecJSI_setIsDebuggingRemotely}; + + + methodMap_["setProfilingEnabled"] = MethodMetadata {1, __hostFunction_NativeDevSettingsSpecJSI_setProfilingEnabled}; + + + methodMap_["toggleElementInspector"] = MethodMetadata {0, __hostFunction_NativeDevSettingsSpecJSI_toggleElementInspector}; + + + methodMap_["addMenuItem"] = MethodMetadata {1, __hostFunction_NativeDevSettingsSpecJSI_addMenuItem}; + + + methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeDevSettingsSpecJSI_addListener}; + + + methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeDevSettingsSpecJSI_removeListeners}; + + + methodMap_["setIsShakeToShowDevMenuEnabled"] = MethodMetadata {1, __hostFunction_NativeDevSettingsSpecJSI_setIsShakeToShowDevMenuEnabled}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeDevSplitBundleLoaderSpecJSI_loadBundle(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "loadBundle", @selector(loadBundle:resolve:reject:), args, count); + } + + NativeDevSplitBundleLoaderSpecJSI::NativeDevSplitBundleLoaderSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["loadBundle"] = MethodMetadata {1, __hostFunction_NativeDevSplitBundleLoaderSpecJSI_loadBundle}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeDeviceEventManagerSpecJSI_invokeDefaultBackPressHandler(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "invokeDefaultBackPressHandler", @selector(invokeDefaultBackPressHandler), args, count); + } + + NativeDeviceEventManagerSpecJSI::NativeDeviceEventManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["invokeDefaultBackPressHandler"] = MethodMetadata {0, __hostFunction_NativeDeviceEventManagerSpecJSI_invokeDefaultBackPressHandler}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeDeviceInfoSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, "getConstants", @selector(getConstants), args, count); + } + + NativeDeviceInfoSpecJSI::NativeDeviceInfoSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["getConstants"] = MethodMetadata {0, __hostFunction_NativeDeviceInfoSpecJSI_getConstants}; + + } + } // namespace react +} // namespace facebook +@implementation RCTCxxConvert (NativeExceptionsManager_StackFrame) ++ (RCTManagedPointer *)JS_NativeExceptionsManager_StackFrame:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +@implementation RCTCxxConvert (NativeExceptionsManager_ExceptionData) ++ (RCTManagedPointer *)JS_NativeExceptionsManager_ExceptionData:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeExceptionsManagerSpecJSI_reportFatalException(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "reportFatalException", @selector(reportFatalException:stack:exceptionId:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeExceptionsManagerSpecJSI_reportSoftException(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "reportSoftException", @selector(reportSoftException:stack:exceptionId:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeExceptionsManagerSpecJSI_reportException(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "reportException", @selector(reportException:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeExceptionsManagerSpecJSI_updateExceptionMessage(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "updateExceptionMessage", @selector(updateExceptionMessage:stack:exceptionId:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeExceptionsManagerSpecJSI_dismissRedbox(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "dismissRedbox", @selector(dismissRedbox), args, count); + } + + NativeExceptionsManagerSpecJSI::NativeExceptionsManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["reportFatalException"] = MethodMetadata {3, __hostFunction_NativeExceptionsManagerSpecJSI_reportFatalException}; + + + methodMap_["reportSoftException"] = MethodMetadata {3, __hostFunction_NativeExceptionsManagerSpecJSI_reportSoftException}; + + + methodMap_["reportException"] = MethodMetadata {1, __hostFunction_NativeExceptionsManagerSpecJSI_reportException}; + setMethodArgConversionSelector(@"reportException", 0, @"JS_NativeExceptionsManager_ExceptionData:"); + + methodMap_["updateExceptionMessage"] = MethodMetadata {3, __hostFunction_NativeExceptionsManagerSpecJSI_updateExceptionMessage}; + + + methodMap_["dismissRedbox"] = MethodMetadata {0, __hostFunction_NativeExceptionsManagerSpecJSI_dismissRedbox}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeFileReaderModuleSpecJSI_readAsDataURL(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "readAsDataURL", @selector(readAsDataURL:resolve:reject:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeFileReaderModuleSpecJSI_readAsText(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "readAsText", @selector(readAsText:encoding:resolve:reject:), args, count); + } + + NativeFileReaderModuleSpecJSI::NativeFileReaderModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["readAsDataURL"] = MethodMetadata {1, __hostFunction_NativeFileReaderModuleSpecJSI_readAsDataURL}; + + + methodMap_["readAsText"] = MethodMetadata {2, __hostFunction_NativeFileReaderModuleSpecJSI_readAsText}; + + } + } // namespace react +} // namespace facebook +@implementation RCTCxxConvert (NativeFrameRateLogger_SpecSetGlobalOptionsOptions) ++ (RCTManagedPointer *)JS_NativeFrameRateLogger_SpecSetGlobalOptionsOptions:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeFrameRateLoggerSpecJSI_setGlobalOptions(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setGlobalOptions", @selector(setGlobalOptions:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeFrameRateLoggerSpecJSI_setContext(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setContext", @selector(setContext:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeFrameRateLoggerSpecJSI_beginScroll(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "beginScroll", @selector(beginScroll), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeFrameRateLoggerSpecJSI_endScroll(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "endScroll", @selector(endScroll), args, count); + } + + NativeFrameRateLoggerSpecJSI::NativeFrameRateLoggerSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["setGlobalOptions"] = MethodMetadata {1, __hostFunction_NativeFrameRateLoggerSpecJSI_setGlobalOptions}; + setMethodArgConversionSelector(@"setGlobalOptions", 0, @"JS_NativeFrameRateLogger_SpecSetGlobalOptionsOptions:"); + + methodMap_["setContext"] = MethodMetadata {1, __hostFunction_NativeFrameRateLoggerSpecJSI_setContext}; + + + methodMap_["beginScroll"] = MethodMetadata {0, __hostFunction_NativeFrameRateLoggerSpecJSI_beginScroll}; + + + methodMap_["endScroll"] = MethodMetadata {0, __hostFunction_NativeFrameRateLoggerSpecJSI_endScroll}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeHeadlessJsTaskSupportSpecJSI_notifyTaskFinished(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "notifyTaskFinished", @selector(notifyTaskFinished:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeHeadlessJsTaskSupportSpecJSI_notifyTaskRetry(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "notifyTaskRetry", @selector(notifyTaskRetry:resolve:reject:), args, count); + } + + NativeHeadlessJsTaskSupportSpecJSI::NativeHeadlessJsTaskSupportSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["notifyTaskFinished"] = MethodMetadata {1, __hostFunction_NativeHeadlessJsTaskSupportSpecJSI_notifyTaskFinished}; + + + methodMap_["notifyTaskRetry"] = MethodMetadata {1, __hostFunction_NativeHeadlessJsTaskSupportSpecJSI_notifyTaskRetry}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeI18nManagerSpecJSI_allowRTL(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "allowRTL", @selector(allowRTL:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeI18nManagerSpecJSI_forceRTL(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "forceRTL", @selector(forceRTL:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeI18nManagerSpecJSI_swapLeftAndRightInRTL(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "swapLeftAndRightInRTL", @selector(swapLeftAndRightInRTL:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeI18nManagerSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, "getConstants", @selector(getConstants), args, count); + } + + NativeI18nManagerSpecJSI::NativeI18nManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["allowRTL"] = MethodMetadata {1, __hostFunction_NativeI18nManagerSpecJSI_allowRTL}; + + + methodMap_["forceRTL"] = MethodMetadata {1, __hostFunction_NativeI18nManagerSpecJSI_forceRTL}; + + + methodMap_["swapLeftAndRightInRTL"] = MethodMetadata {1, __hostFunction_NativeI18nManagerSpecJSI_swapLeftAndRightInRTL}; + + + methodMap_["getConstants"] = MethodMetadata {0, __hostFunction_NativeI18nManagerSpecJSI_getConstants}; + + } + } // namespace react +} // namespace facebook +@implementation RCTCxxConvert (NativeImageEditor_OptionsOffset) ++ (RCTManagedPointer *)JS_NativeImageEditor_OptionsOffset:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +@implementation RCTCxxConvert (NativeImageEditor_OptionsSize) ++ (RCTManagedPointer *)JS_NativeImageEditor_OptionsSize:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +@implementation RCTCxxConvert (NativeImageEditor_OptionsDisplaySize) ++ (RCTManagedPointer *)JS_NativeImageEditor_OptionsDisplaySize:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +@implementation RCTCxxConvert (NativeImageEditor_Options) ++ (RCTManagedPointer *)JS_NativeImageEditor_Options:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeImageEditorSpecJSI_cropImage(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "cropImage", @selector(cropImage:cropData:successCallback:errorCallback:), args, count); + } + + NativeImageEditorSpecJSI::NativeImageEditorSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["cropImage"] = MethodMetadata {4, __hostFunction_NativeImageEditorSpecJSI_cropImage}; + setMethodArgConversionSelector(@"cropImage", 1, @"JS_NativeImageEditor_Options:"); + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeImageLoaderIOSSpecJSI_getSize(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "getSize", @selector(getSize:resolve:reject:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeImageLoaderIOSSpecJSI_getSizeWithHeaders(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "getSizeWithHeaders", @selector(getSizeWithHeaders:headers:resolve:reject:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeImageLoaderIOSSpecJSI_prefetchImage(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "prefetchImage", @selector(prefetchImage:resolve:reject:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeImageLoaderIOSSpecJSI_prefetchImageWithMetadata(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "prefetchImageWithMetadata", @selector(prefetchImageWithMetadata:queryRootName:rootTag:resolve:reject:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeImageLoaderIOSSpecJSI_queryCache(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "queryCache", @selector(queryCache:resolve:reject:), args, count); + } + + NativeImageLoaderIOSSpecJSI::NativeImageLoaderIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["getSize"] = MethodMetadata {1, __hostFunction_NativeImageLoaderIOSSpecJSI_getSize}; + + + methodMap_["getSizeWithHeaders"] = MethodMetadata {2, __hostFunction_NativeImageLoaderIOSSpecJSI_getSizeWithHeaders}; + + + methodMap_["prefetchImage"] = MethodMetadata {1, __hostFunction_NativeImageLoaderIOSSpecJSI_prefetchImage}; + + + methodMap_["prefetchImageWithMetadata"] = MethodMetadata {3, __hostFunction_NativeImageLoaderIOSSpecJSI_prefetchImageWithMetadata}; + + + methodMap_["queryCache"] = MethodMetadata {1, __hostFunction_NativeImageLoaderIOSSpecJSI_queryCache}; + + } + } // namespace react +} // namespace facebook +@implementation RCTCxxConvert (NativeImagePickerIOS_SpecOpenCameraDialogConfig) ++ (RCTManagedPointer *)JS_NativeImagePickerIOS_SpecOpenCameraDialogConfig:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +@implementation RCTCxxConvert (NativeImagePickerIOS_SpecOpenSelectDialogConfig) ++ (RCTManagedPointer *)JS_NativeImagePickerIOS_SpecOpenSelectDialogConfig:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeImagePickerIOSSpecJSI_canRecordVideos(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "canRecordVideos", @selector(canRecordVideos:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeImagePickerIOSSpecJSI_canUseCamera(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "canUseCamera", @selector(canUseCamera:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeImagePickerIOSSpecJSI_openCameraDialog(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "openCameraDialog", @selector(openCameraDialog:successCallback:cancelCallback:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeImagePickerIOSSpecJSI_openSelectDialog(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "openSelectDialog", @selector(openSelectDialog:successCallback:cancelCallback:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeImagePickerIOSSpecJSI_clearAllPendingVideos(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "clearAllPendingVideos", @selector(clearAllPendingVideos), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeImagePickerIOSSpecJSI_removePendingVideo(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removePendingVideo", @selector(removePendingVideo:), args, count); + } + + NativeImagePickerIOSSpecJSI::NativeImagePickerIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["canRecordVideos"] = MethodMetadata {1, __hostFunction_NativeImagePickerIOSSpecJSI_canRecordVideos}; + + + methodMap_["canUseCamera"] = MethodMetadata {1, __hostFunction_NativeImagePickerIOSSpecJSI_canUseCamera}; + + + methodMap_["openCameraDialog"] = MethodMetadata {3, __hostFunction_NativeImagePickerIOSSpecJSI_openCameraDialog}; + setMethodArgConversionSelector(@"openCameraDialog", 0, @"JS_NativeImagePickerIOS_SpecOpenCameraDialogConfig:"); + + methodMap_["openSelectDialog"] = MethodMetadata {3, __hostFunction_NativeImagePickerIOSSpecJSI_openSelectDialog}; + setMethodArgConversionSelector(@"openSelectDialog", 0, @"JS_NativeImagePickerIOS_SpecOpenSelectDialogConfig:"); + + methodMap_["clearAllPendingVideos"] = MethodMetadata {0, __hostFunction_NativeImagePickerIOSSpecJSI_clearAllPendingVideos}; + + + methodMap_["removePendingVideo"] = MethodMetadata {1, __hostFunction_NativeImagePickerIOSSpecJSI_removePendingVideo}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeImageStoreIOSSpecJSI_getBase64ForTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getBase64ForTag", @selector(getBase64ForTag:successCallback:errorCallback:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeImageStoreIOSSpecJSI_hasImageForTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "hasImageForTag", @selector(hasImageForTag:callback:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeImageStoreIOSSpecJSI_removeImageForTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeImageForTag", @selector(removeImageForTag:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeImageStoreIOSSpecJSI_addImageFromBase64(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addImageFromBase64", @selector(addImageFromBase64:successCallback:errorCallback:), args, count); + } + + NativeImageStoreIOSSpecJSI::NativeImageStoreIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["getBase64ForTag"] = MethodMetadata {3, __hostFunction_NativeImageStoreIOSSpecJSI_getBase64ForTag}; + + + methodMap_["hasImageForTag"] = MethodMetadata {2, __hostFunction_NativeImageStoreIOSSpecJSI_hasImageForTag}; + + + methodMap_["removeImageForTag"] = MethodMetadata {1, __hostFunction_NativeImageStoreIOSSpecJSI_removeImageForTag}; + + + methodMap_["addImageFromBase64"] = MethodMetadata {3, __hostFunction_NativeImageStoreIOSSpecJSI_addImageFromBase64}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeJSCHeapCaptureSpecJSI_captureComplete(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "captureComplete", @selector(captureComplete:error:), args, count); + } + + NativeJSCHeapCaptureSpecJSI::NativeJSCHeapCaptureSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["captureComplete"] = MethodMetadata {2, __hostFunction_NativeJSCHeapCaptureSpecJSI_captureComplete}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeJSCSamplingProfilerSpecJSI_operationComplete(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "operationComplete", @selector(operationComplete:result:error:), args, count); + } + + NativeJSCSamplingProfilerSpecJSI::NativeJSCSamplingProfilerSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["operationComplete"] = MethodMetadata {3, __hostFunction_NativeJSCSamplingProfilerSpecJSI_operationComplete}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeJSDevSupportSpecJSI_onSuccess(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "onSuccess", @selector(onSuccess:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeJSDevSupportSpecJSI_onFailure(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "onFailure", @selector(onFailure:error:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeJSDevSupportSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, "getConstants", @selector(getConstants), args, count); + } + + NativeJSDevSupportSpecJSI::NativeJSDevSupportSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["onSuccess"] = MethodMetadata {1, __hostFunction_NativeJSDevSupportSpecJSI_onSuccess}; + + + methodMap_["onFailure"] = MethodMetadata {2, __hostFunction_NativeJSDevSupportSpecJSI_onFailure}; + + + methodMap_["getConstants"] = MethodMetadata {0, __hostFunction_NativeJSDevSupportSpecJSI_getConstants}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeKeyboardObserverSpecJSI_addListener(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addListener", @selector(addListener:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeKeyboardObserverSpecJSI_removeListeners(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeListeners", @selector(removeListeners:), args, count); + } + + NativeKeyboardObserverSpecJSI::NativeKeyboardObserverSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeKeyboardObserverSpecJSI_addListener}; + + + methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeKeyboardObserverSpecJSI_removeListeners}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeLinkingManagerSpecJSI_getInitialURL(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "getInitialURL", @selector(getInitialURL:reject:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeLinkingManagerSpecJSI_canOpenURL(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "canOpenURL", @selector(canOpenURL:resolve:reject:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeLinkingManagerSpecJSI_openURL(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "openURL", @selector(openURL:resolve:reject:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeLinkingManagerSpecJSI_openSettings(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "openSettings", @selector(openSettings:reject:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeLinkingManagerSpecJSI_addListener(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addListener", @selector(addListener:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeLinkingManagerSpecJSI_removeListeners(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeListeners", @selector(removeListeners:), args, count); + } + + NativeLinkingManagerSpecJSI::NativeLinkingManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["getInitialURL"] = MethodMetadata {0, __hostFunction_NativeLinkingManagerSpecJSI_getInitialURL}; + + + methodMap_["canOpenURL"] = MethodMetadata {1, __hostFunction_NativeLinkingManagerSpecJSI_canOpenURL}; + + + methodMap_["openURL"] = MethodMetadata {1, __hostFunction_NativeLinkingManagerSpecJSI_openURL}; + + + methodMap_["openSettings"] = MethodMetadata {0, __hostFunction_NativeLinkingManagerSpecJSI_openSettings}; + + + methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeLinkingManagerSpecJSI_addListener}; + + + methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeLinkingManagerSpecJSI_removeListeners}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeLogBoxSpecJSI_show(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "show", @selector(show), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeLogBoxSpecJSI_hide(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "hide", @selector(hide), args, count); + } + + NativeLogBoxSpecJSI::NativeLogBoxSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["show"] = MethodMetadata {0, __hostFunction_NativeLogBoxSpecJSI_show}; + + + methodMap_["hide"] = MethodMetadata {0, __hostFunction_NativeLogBoxSpecJSI_hide}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeModalManagerSpecJSI_addListener(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addListener", @selector(addListener:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeModalManagerSpecJSI_removeListeners(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeListeners", @selector(removeListeners:), args, count); + } + + NativeModalManagerSpecJSI::NativeModalManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeModalManagerSpecJSI_addListener}; + + + methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeModalManagerSpecJSI_removeListeners}; + + } + } // namespace react +} // namespace facebook +@implementation RCTCxxConvert (NativeNetworkingIOS_SpecSendRequestQuery) ++ (RCTManagedPointer *)JS_NativeNetworkingIOS_SpecSendRequestQuery:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeNetworkingIOSSpecJSI_sendRequest(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "sendRequest", @selector(sendRequest:callback:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeNetworkingIOSSpecJSI_abortRequest(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "abortRequest", @selector(abortRequest:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeNetworkingIOSSpecJSI_clearCookies(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "clearCookies", @selector(clearCookies:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeNetworkingIOSSpecJSI_addListener(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addListener", @selector(addListener:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeNetworkingIOSSpecJSI_removeListeners(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeListeners", @selector(removeListeners:), args, count); + } + + NativeNetworkingIOSSpecJSI::NativeNetworkingIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["sendRequest"] = MethodMetadata {2, __hostFunction_NativeNetworkingIOSSpecJSI_sendRequest}; + setMethodArgConversionSelector(@"sendRequest", 0, @"JS_NativeNetworkingIOS_SpecSendRequestQuery:"); + + methodMap_["abortRequest"] = MethodMetadata {1, __hostFunction_NativeNetworkingIOSSpecJSI_abortRequest}; + + + methodMap_["clearCookies"] = MethodMetadata {1, __hostFunction_NativeNetworkingIOSSpecJSI_clearCookies}; + + + methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeNetworkingIOSSpecJSI_addListener}; + + + methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeNetworkingIOSSpecJSI_removeListeners}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativePlatformConstantsIOSSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, "getConstants", @selector(getConstants), args, count); + } + + NativePlatformConstantsIOSSpecJSI::NativePlatformConstantsIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["getConstants"] = MethodMetadata {0, __hostFunction_NativePlatformConstantsIOSSpecJSI_getConstants}; + + } + } // namespace react +} // namespace facebook +@implementation RCTCxxConvert (NativePushNotificationManagerIOS_SpecRequestPermissionsPermission) ++ (RCTManagedPointer *)JS_NativePushNotificationManagerIOS_SpecRequestPermissionsPermission:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +@implementation RCTCxxConvert (NativePushNotificationManagerIOS_Notification) ++ (RCTManagedPointer *)JS_NativePushNotificationManagerIOS_Notification:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativePushNotificationManagerIOSSpecJSI_onFinishRemoteNotification(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "onFinishRemoteNotification", @selector(onFinishRemoteNotification:fetchResult:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativePushNotificationManagerIOSSpecJSI_setApplicationIconBadgeNumber(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setApplicationIconBadgeNumber", @selector(setApplicationIconBadgeNumber:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativePushNotificationManagerIOSSpecJSI_getApplicationIconBadgeNumber(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getApplicationIconBadgeNumber", @selector(getApplicationIconBadgeNumber:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativePushNotificationManagerIOSSpecJSI_requestPermissions(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "requestPermissions", @selector(requestPermissions:resolve:reject:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativePushNotificationManagerIOSSpecJSI_abandonPermissions(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "abandonPermissions", @selector(abandonPermissions), args, count); + } + + static facebook::jsi::Value __hostFunction_NativePushNotificationManagerIOSSpecJSI_checkPermissions(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "checkPermissions", @selector(checkPermissions:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativePushNotificationManagerIOSSpecJSI_presentLocalNotification(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "presentLocalNotification", @selector(presentLocalNotification:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativePushNotificationManagerIOSSpecJSI_scheduleLocalNotification(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "scheduleLocalNotification", @selector(scheduleLocalNotification:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativePushNotificationManagerIOSSpecJSI_cancelAllLocalNotifications(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "cancelAllLocalNotifications", @selector(cancelAllLocalNotifications), args, count); + } + + static facebook::jsi::Value __hostFunction_NativePushNotificationManagerIOSSpecJSI_cancelLocalNotifications(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "cancelLocalNotifications", @selector(cancelLocalNotifications:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativePushNotificationManagerIOSSpecJSI_getInitialNotification(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "getInitialNotification", @selector(getInitialNotification:reject:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativePushNotificationManagerIOSSpecJSI_getScheduledLocalNotifications(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getScheduledLocalNotifications", @selector(getScheduledLocalNotifications:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativePushNotificationManagerIOSSpecJSI_removeAllDeliveredNotifications(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeAllDeliveredNotifications", @selector(removeAllDeliveredNotifications), args, count); + } + + static facebook::jsi::Value __hostFunction_NativePushNotificationManagerIOSSpecJSI_removeDeliveredNotifications(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeDeliveredNotifications", @selector(removeDeliveredNotifications:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativePushNotificationManagerIOSSpecJSI_getDeliveredNotifications(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getDeliveredNotifications", @selector(getDeliveredNotifications:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativePushNotificationManagerIOSSpecJSI_getAuthorizationStatus(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getAuthorizationStatus", @selector(getAuthorizationStatus:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativePushNotificationManagerIOSSpecJSI_addListener(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addListener", @selector(addListener:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativePushNotificationManagerIOSSpecJSI_removeListeners(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeListeners", @selector(removeListeners:), args, count); + } + + NativePushNotificationManagerIOSSpecJSI::NativePushNotificationManagerIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["onFinishRemoteNotification"] = MethodMetadata {2, __hostFunction_NativePushNotificationManagerIOSSpecJSI_onFinishRemoteNotification}; + + + methodMap_["setApplicationIconBadgeNumber"] = MethodMetadata {1, __hostFunction_NativePushNotificationManagerIOSSpecJSI_setApplicationIconBadgeNumber}; + + + methodMap_["getApplicationIconBadgeNumber"] = MethodMetadata {1, __hostFunction_NativePushNotificationManagerIOSSpecJSI_getApplicationIconBadgeNumber}; + + + methodMap_["requestPermissions"] = MethodMetadata {1, __hostFunction_NativePushNotificationManagerIOSSpecJSI_requestPermissions}; + setMethodArgConversionSelector(@"requestPermissions", 0, @"JS_NativePushNotificationManagerIOS_SpecRequestPermissionsPermission:"); + + methodMap_["abandonPermissions"] = MethodMetadata {0, __hostFunction_NativePushNotificationManagerIOSSpecJSI_abandonPermissions}; + + + methodMap_["checkPermissions"] = MethodMetadata {1, __hostFunction_NativePushNotificationManagerIOSSpecJSI_checkPermissions}; + + + methodMap_["presentLocalNotification"] = MethodMetadata {1, __hostFunction_NativePushNotificationManagerIOSSpecJSI_presentLocalNotification}; + setMethodArgConversionSelector(@"presentLocalNotification", 0, @"JS_NativePushNotificationManagerIOS_Notification:"); + + methodMap_["scheduleLocalNotification"] = MethodMetadata {1, __hostFunction_NativePushNotificationManagerIOSSpecJSI_scheduleLocalNotification}; + setMethodArgConversionSelector(@"scheduleLocalNotification", 0, @"JS_NativePushNotificationManagerIOS_Notification:"); + + methodMap_["cancelAllLocalNotifications"] = MethodMetadata {0, __hostFunction_NativePushNotificationManagerIOSSpecJSI_cancelAllLocalNotifications}; + + + methodMap_["cancelLocalNotifications"] = MethodMetadata {1, __hostFunction_NativePushNotificationManagerIOSSpecJSI_cancelLocalNotifications}; + + + methodMap_["getInitialNotification"] = MethodMetadata {0, __hostFunction_NativePushNotificationManagerIOSSpecJSI_getInitialNotification}; + + + methodMap_["getScheduledLocalNotifications"] = MethodMetadata {1, __hostFunction_NativePushNotificationManagerIOSSpecJSI_getScheduledLocalNotifications}; + + + methodMap_["removeAllDeliveredNotifications"] = MethodMetadata {0, __hostFunction_NativePushNotificationManagerIOSSpecJSI_removeAllDeliveredNotifications}; + + + methodMap_["removeDeliveredNotifications"] = MethodMetadata {1, __hostFunction_NativePushNotificationManagerIOSSpecJSI_removeDeliveredNotifications}; + + + methodMap_["getDeliveredNotifications"] = MethodMetadata {1, __hostFunction_NativePushNotificationManagerIOSSpecJSI_getDeliveredNotifications}; + + + methodMap_["getAuthorizationStatus"] = MethodMetadata {1, __hostFunction_NativePushNotificationManagerIOSSpecJSI_getAuthorizationStatus}; + + + methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativePushNotificationManagerIOSSpecJSI_addListener}; + + + methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativePushNotificationManagerIOSSpecJSI_removeListeners}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeRedBoxSpecJSI_setExtraData(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setExtraData", @selector(setExtraData:forIdentifier:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeRedBoxSpecJSI_dismiss(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "dismiss", @selector(dismiss), args, count); + } + + NativeRedBoxSpecJSI::NativeRedBoxSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["setExtraData"] = MethodMetadata {2, __hostFunction_NativeRedBoxSpecJSI_setExtraData}; + + + methodMap_["dismiss"] = MethodMetadata {0, __hostFunction_NativeRedBoxSpecJSI_dismiss}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeSegmentFetcherSpecJSI_fetchSegment(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "fetchSegment", @selector(fetchSegment:options:callback:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeSegmentFetcherSpecJSI_getSegment(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getSegment", @selector(getSegment:options:callback:), args, count); + } + + NativeSegmentFetcherSpecJSI::NativeSegmentFetcherSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["fetchSegment"] = MethodMetadata {3, __hostFunction_NativeSegmentFetcherSpecJSI_fetchSegment}; + + + methodMap_["getSegment"] = MethodMetadata {3, __hostFunction_NativeSegmentFetcherSpecJSI_getSegment}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeSettingsManagerSpecJSI_setValues(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setValues", @selector(setValues:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeSettingsManagerSpecJSI_deleteValues(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "deleteValues", @selector(deleteValues:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeSettingsManagerSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, "getConstants", @selector(getConstants), args, count); + } + + NativeSettingsManagerSpecJSI::NativeSettingsManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["setValues"] = MethodMetadata {1, __hostFunction_NativeSettingsManagerSpecJSI_setValues}; + + + methodMap_["deleteValues"] = MethodMetadata {1, __hostFunction_NativeSettingsManagerSpecJSI_deleteValues}; + + + methodMap_["getConstants"] = MethodMetadata {0, __hostFunction_NativeSettingsManagerSpecJSI_getConstants}; + + } + } // namespace react +} // namespace facebook +@implementation RCTCxxConvert (NativeShareModule_SpecShareContent) ++ (RCTManagedPointer *)JS_NativeShareModule_SpecShareContent:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeShareModuleSpecJSI_share(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "share", @selector(share:dialogTitle:resolve:reject:), args, count); + } + + NativeShareModuleSpecJSI::NativeShareModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["share"] = MethodMetadata {2, __hostFunction_NativeShareModuleSpecJSI_share}; + setMethodArgConversionSelector(@"share", 0, @"JS_NativeShareModule_SpecShareContent:"); + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeSoundManagerSpecJSI_playTouchSound(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "playTouchSound", @selector(playTouchSound), args, count); + } + + NativeSoundManagerSpecJSI::NativeSoundManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["playTouchSound"] = MethodMetadata {0, __hostFunction_NativeSoundManagerSpecJSI_playTouchSound}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeSourceCodeSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, "getConstants", @selector(getConstants), args, count); + } + + NativeSourceCodeSpecJSI::NativeSourceCodeSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["getConstants"] = MethodMetadata {0, __hostFunction_NativeSourceCodeSpecJSI_getConstants}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeStatusBarManagerIOSSpecJSI_getHeight(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getHeight", @selector(getHeight:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeStatusBarManagerIOSSpecJSI_setNetworkActivityIndicatorVisible(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setNetworkActivityIndicatorVisible", @selector(setNetworkActivityIndicatorVisible:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeStatusBarManagerIOSSpecJSI_addListener(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addListener", @selector(addListener:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeStatusBarManagerIOSSpecJSI_removeListeners(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeListeners", @selector(removeListeners:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeStatusBarManagerIOSSpecJSI_setStyle(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setStyle", @selector(setStyle:animated:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeStatusBarManagerIOSSpecJSI_setHidden(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setHidden", @selector(setHidden:withAnimation:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeStatusBarManagerIOSSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, "getConstants", @selector(getConstants), args, count); + } + + NativeStatusBarManagerIOSSpecJSI::NativeStatusBarManagerIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["getHeight"] = MethodMetadata {1, __hostFunction_NativeStatusBarManagerIOSSpecJSI_getHeight}; + + + methodMap_["setNetworkActivityIndicatorVisible"] = MethodMetadata {1, __hostFunction_NativeStatusBarManagerIOSSpecJSI_setNetworkActivityIndicatorVisible}; + + + methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeStatusBarManagerIOSSpecJSI_addListener}; + + + methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeStatusBarManagerIOSSpecJSI_removeListeners}; + + + methodMap_["setStyle"] = MethodMetadata {2, __hostFunction_NativeStatusBarManagerIOSSpecJSI_setStyle}; + + + methodMap_["setHidden"] = MethodMetadata {2, __hostFunction_NativeStatusBarManagerIOSSpecJSI_setHidden}; + + + methodMap_["getConstants"] = MethodMetadata {0, __hostFunction_NativeStatusBarManagerIOSSpecJSI_getConstants}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeTVNavigationEventEmitterSpecJSI_addListener(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addListener", @selector(addListener:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeTVNavigationEventEmitterSpecJSI_removeListeners(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeListeners", @selector(removeListeners:), args, count); + } + + NativeTVNavigationEventEmitterSpecJSI::NativeTVNavigationEventEmitterSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeTVNavigationEventEmitterSpecJSI_addListener}; + + + methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeTVNavigationEventEmitterSpecJSI_removeListeners}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeTimingSpecJSI_createTimer(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "createTimer", @selector(createTimer:duration:jsSchedulingTime:repeats:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeTimingSpecJSI_deleteTimer(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "deleteTimer", @selector(deleteTimer:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeTimingSpecJSI_setSendIdleEvents(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setSendIdleEvents", @selector(setSendIdleEvents:), args, count); + } + + NativeTimingSpecJSI::NativeTimingSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["createTimer"] = MethodMetadata {4, __hostFunction_NativeTimingSpecJSI_createTimer}; + + + methodMap_["deleteTimer"] = MethodMetadata {1, __hostFunction_NativeTimingSpecJSI_deleteTimer}; + + + methodMap_["setSendIdleEvents"] = MethodMetadata {1, __hostFunction_NativeTimingSpecJSI_setSendIdleEvents}; + + } + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeVibrationSpecJSI_vibrate(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "vibrate", @selector(vibrate:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeVibrationSpecJSI_vibrateByPattern(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "vibrateByPattern", @selector(vibrateByPattern:repeat:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeVibrationSpecJSI_cancel(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "cancel", @selector(cancel), args, count); + } + + NativeVibrationSpecJSI::NativeVibrationSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["vibrate"] = MethodMetadata {1, __hostFunction_NativeVibrationSpecJSI_vibrate}; + + + methodMap_["vibrateByPattern"] = MethodMetadata {2, __hostFunction_NativeVibrationSpecJSI_vibrateByPattern}; + + + methodMap_["cancel"] = MethodMetadata {0, __hostFunction_NativeVibrationSpecJSI_cancel}; + + } + } // namespace react +} // namespace facebook +@implementation RCTCxxConvert (NativeWebSocketModule_SpecConnectOptions) ++ (RCTManagedPointer *)JS_NativeWebSocketModule_SpecConnectOptions:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeWebSocketModuleSpecJSI_connect(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "connect", @selector(connect:protocols:options:socketID:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeWebSocketModuleSpecJSI_send(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "send", @selector(send:forSocketID:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeWebSocketModuleSpecJSI_sendBinary(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "sendBinary", @selector(sendBinary:forSocketID:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeWebSocketModuleSpecJSI_ping(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "ping", @selector(ping:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeWebSocketModuleSpecJSI_close(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "close", @selector(close:reason:socketID:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeWebSocketModuleSpecJSI_addListener(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addListener", @selector(addListener:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeWebSocketModuleSpecJSI_removeListeners(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeListeners", @selector(removeListeners:), args, count); + } + + NativeWebSocketModuleSpecJSI::NativeWebSocketModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["connect"] = MethodMetadata {4, __hostFunction_NativeWebSocketModuleSpecJSI_connect}; + setMethodArgConversionSelector(@"connect", 2, @"JS_NativeWebSocketModule_SpecConnectOptions:"); + + methodMap_["send"] = MethodMetadata {2, __hostFunction_NativeWebSocketModuleSpecJSI_send}; + + + methodMap_["sendBinary"] = MethodMetadata {2, __hostFunction_NativeWebSocketModuleSpecJSI_sendBinary}; + + + methodMap_["ping"] = MethodMetadata {1, __hostFunction_NativeWebSocketModuleSpecJSI_ping}; + + + methodMap_["close"] = MethodMetadata {3, __hostFunction_NativeWebSocketModuleSpecJSI_close}; + + + methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeWebSocketModuleSpecJSI_addListener}; + + + methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeWebSocketModuleSpecJSI_removeListeners}; + + } + } // namespace react +} // namespace facebook diff --git a/React/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h b/React/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h new file mode 100644 index 0000000000..2139179f5c --- /dev/null +++ b/React/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h @@ -0,0 +1,2573 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @generated by codegen project: GenerateModuleObjCpp + * + * We create an umbrella header (and corresponding implementation) here since + * Cxx compilation in BUCK has a limitation: source-code producing genrule()s + * must have a single output. More files => more genrule()s => slower builds. + */ + +#ifndef __cplusplus +#error This file must be compiled as Obj-C++. If you are importing it, you must change your file extension to .mm. +#endif +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + + +@protocol NativeAccessibilityInfoSpec + +- (void)isReduceMotionEnabled:(RCTResponseSenderBlock)onSuccess; +- (void)isTouchExplorationEnabled:(RCTResponseSenderBlock)onSuccess; +- (void)setAccessibilityFocus:(double)reactTag; +- (void)announceForAccessibility:(NSString *)announcement; +- (void)getRecommendedTimeoutMillis:(double)mSec + onSuccess:(RCTResponseSenderBlock)onSuccess; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeAccessibilityInfo' + */ + class JSI_EXPORT NativeAccessibilityInfoSpecJSI : public ObjCTurboModule { + public: + NativeAccessibilityInfoSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeAccessibilityManager { + struct SpecSetAccessibilityContentSizeMultipliersJSMultipliers { + folly::Optional extraSmall() const; + folly::Optional small() const; + folly::Optional medium() const; + folly::Optional large() const; + folly::Optional extraLarge() const; + folly::Optional extraExtraLarge() const; + folly::Optional extraExtraExtraLarge() const; + folly::Optional accessibilityMedium() const; + folly::Optional accessibilityLarge() const; + folly::Optional accessibilityExtraLarge() const; + folly::Optional accessibilityExtraExtraLarge() const; + folly::Optional accessibilityExtraExtraExtraLarge() const; + + SpecSetAccessibilityContentSizeMultipliersJSMultipliers(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeAccessibilityManager_SpecSetAccessibilityContentSizeMultipliersJSMultipliers) ++ (RCTManagedPointer *)JS_NativeAccessibilityManager_SpecSetAccessibilityContentSizeMultipliersJSMultipliers:(id)json; +@end +@protocol NativeAccessibilityManagerSpec + +- (void)getCurrentBoldTextState:(RCTResponseSenderBlock)onSuccess + onError:(RCTResponseSenderBlock)onError; +- (void)getCurrentGrayscaleState:(RCTResponseSenderBlock)onSuccess + onError:(RCTResponseSenderBlock)onError; +- (void)getCurrentInvertColorsState:(RCTResponseSenderBlock)onSuccess + onError:(RCTResponseSenderBlock)onError; +- (void)getCurrentReduceMotionState:(RCTResponseSenderBlock)onSuccess + onError:(RCTResponseSenderBlock)onError; +- (void)getCurrentReduceTransparencyState:(RCTResponseSenderBlock)onSuccess + onError:(RCTResponseSenderBlock)onError; +- (void)getCurrentVoiceOverState:(RCTResponseSenderBlock)onSuccess + onError:(RCTResponseSenderBlock)onError; +- (void)setAccessibilityContentSizeMultipliers:(JS::NativeAccessibilityManager::SpecSetAccessibilityContentSizeMultipliersJSMultipliers &)JSMultipliers; +- (void)setAccessibilityFocus:(double)reactTag; +- (void)announceForAccessibility:(NSString *)announcement; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeAccessibilityManager' + */ + class JSI_EXPORT NativeAccessibilityManagerSpecJSI : public ObjCTurboModule { + public: + NativeAccessibilityManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeActionSheetManager { + struct SpecShowActionSheetWithOptionsOptions { + NSString *title() const; + NSString *message() const; + folly::Optional> options() const; + folly::Optional> destructiveButtonIndices() const; + folly::Optional cancelButtonIndex() const; + folly::Optional anchor() const; + folly::Optional tintColor() const; + NSString *userInterfaceStyle() const; + folly::Optional> disabledButtonIndices() const; + + SpecShowActionSheetWithOptionsOptions(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeActionSheetManager_SpecShowActionSheetWithOptionsOptions) ++ (RCTManagedPointer *)JS_NativeActionSheetManager_SpecShowActionSheetWithOptionsOptions:(id)json; +@end +namespace JS { + namespace NativeActionSheetManager { + struct SpecShowShareActionSheetWithOptionsOptions { + NSString *message() const; + NSString *url() const; + NSString *subject() const; + folly::Optional anchor() const; + folly::Optional tintColor() const; + folly::Optional> excludedActivityTypes() const; + NSString *userInterfaceStyle() const; + + SpecShowShareActionSheetWithOptionsOptions(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeActionSheetManager_SpecShowShareActionSheetWithOptionsOptions) ++ (RCTManagedPointer *)JS_NativeActionSheetManager_SpecShowShareActionSheetWithOptionsOptions:(id)json; +@end +@protocol NativeActionSheetManagerSpec + +- (void)showActionSheetWithOptions:(JS::NativeActionSheetManager::SpecShowActionSheetWithOptionsOptions &)options + callback:(RCTResponseSenderBlock)callback; +- (void)showShareActionSheetWithOptions:(JS::NativeActionSheetManager::SpecShowShareActionSheetWithOptionsOptions &)options + failureCallback:(RCTResponseSenderBlock)failureCallback + successCallback:(RCTResponseSenderBlock)successCallback; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeActionSheetManager' + */ + class JSI_EXPORT NativeActionSheetManagerSpecJSI : public ObjCTurboModule { + public: + NativeActionSheetManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeAlertManager { + struct Args { + NSString *title() const; + NSString *message() const; + folly::Optional >> buttons() const; + NSString *type() const; + NSString *defaultValue() const; + NSString *cancelButtonKey() const; + NSString *destructiveButtonKey() const; + NSString *keyboardType() const; + + Args(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeAlertManager_Args) ++ (RCTManagedPointer *)JS_NativeAlertManager_Args:(id)json; +@end +@protocol NativeAlertManagerSpec + +- (void)alertWithArgs:(JS::NativeAlertManager::Args &)args + callback:(RCTResponseSenderBlock)callback; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeAlertManager' + */ + class JSI_EXPORT NativeAlertManagerSpecJSI : public ObjCTurboModule { + public: + NativeAlertManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeAnimatedModule { + struct EventMapping { + facebook::react::LazyVector nativeEventPath() const; + folly::Optional animatedValueTag() const; + + EventMapping(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeAnimatedModule_EventMapping) ++ (RCTManagedPointer *)JS_NativeAnimatedModule_EventMapping:(id)json; +@end +@protocol NativeAnimatedModuleSpec + +- (void)startOperationBatch; +- (void)finishOperationBatch; +- (void)createAnimatedNode:(double)tag + config:(NSDictionary *)config; +- (void)getValue:(double)tag +saveValueCallback:(RCTResponseSenderBlock)saveValueCallback; +- (void)startListeningToAnimatedNodeValue:(double)tag; +- (void)stopListeningToAnimatedNodeValue:(double)tag; +- (void)connectAnimatedNodes:(double)parentTag + childTag:(double)childTag; +- (void)disconnectAnimatedNodes:(double)parentTag + childTag:(double)childTag; +- (void)startAnimatingNode:(double)animationId + nodeTag:(double)nodeTag + config:(NSDictionary *)config + endCallback:(RCTResponseSenderBlock)endCallback; +- (void)stopAnimation:(double)animationId; +- (void)setAnimatedNodeValue:(double)nodeTag + value:(double)value; +- (void)setAnimatedNodeOffset:(double)nodeTag + offset:(double)offset; +- (void)flattenAnimatedNodeOffset:(double)nodeTag; +- (void)extractAnimatedNodeOffset:(double)nodeTag; +- (void)connectAnimatedNodeToView:(double)nodeTag + viewTag:(double)viewTag; +- (void)disconnectAnimatedNodeFromView:(double)nodeTag + viewTag:(double)viewTag; +- (void)restoreDefaultValues:(double)nodeTag; +- (void)dropAnimatedNode:(double)tag; +- (void)addAnimatedEventToView:(double)viewTag + eventName:(NSString *)eventName + eventMapping:(JS::NativeAnimatedModule::EventMapping &)eventMapping; +- (void)removeAnimatedEventFromView:(double)viewTag + eventName:(NSString *)eventName + animatedNodeTag:(double)animatedNodeTag; +- (void)addListener:(NSString *)eventName; +- (void)removeListeners:(double)count; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeAnimatedModule' + */ + class JSI_EXPORT NativeAnimatedModuleSpecJSI : public ObjCTurboModule { + public: + NativeAnimatedModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeAnimatedTurboModule { + struct EventMapping { + facebook::react::LazyVector nativeEventPath() const; + folly::Optional animatedValueTag() const; + + EventMapping(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeAnimatedTurboModule_EventMapping) ++ (RCTManagedPointer *)JS_NativeAnimatedTurboModule_EventMapping:(id)json; +@end +@protocol NativeAnimatedTurboModuleSpec + +- (void)startOperationBatch; +- (void)finishOperationBatch; +- (void)createAnimatedNode:(double)tag + config:(NSDictionary *)config; +- (void)getValue:(double)tag +saveValueCallback:(RCTResponseSenderBlock)saveValueCallback; +- (void)startListeningToAnimatedNodeValue:(double)tag; +- (void)stopListeningToAnimatedNodeValue:(double)tag; +- (void)connectAnimatedNodes:(double)parentTag + childTag:(double)childTag; +- (void)disconnectAnimatedNodes:(double)parentTag + childTag:(double)childTag; +- (void)startAnimatingNode:(double)animationId + nodeTag:(double)nodeTag + config:(NSDictionary *)config + endCallback:(RCTResponseSenderBlock)endCallback; +- (void)stopAnimation:(double)animationId; +- (void)setAnimatedNodeValue:(double)nodeTag + value:(double)value; +- (void)setAnimatedNodeOffset:(double)nodeTag + offset:(double)offset; +- (void)flattenAnimatedNodeOffset:(double)nodeTag; +- (void)extractAnimatedNodeOffset:(double)nodeTag; +- (void)connectAnimatedNodeToView:(double)nodeTag + viewTag:(double)viewTag; +- (void)disconnectAnimatedNodeFromView:(double)nodeTag + viewTag:(double)viewTag; +- (void)restoreDefaultValues:(double)nodeTag; +- (void)dropAnimatedNode:(double)tag; +- (void)addAnimatedEventToView:(double)viewTag + eventName:(NSString *)eventName + eventMapping:(JS::NativeAnimatedTurboModule::EventMapping &)eventMapping; +- (void)removeAnimatedEventFromView:(double)viewTag + eventName:(NSString *)eventName + animatedNodeTag:(double)animatedNodeTag; +- (void)addListener:(NSString *)eventName; +- (void)removeListeners:(double)count; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeAnimatedTurboModule' + */ + class JSI_EXPORT NativeAnimatedTurboModuleSpecJSI : public ObjCTurboModule { + public: + NativeAnimatedTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeAnimationsDebugModuleSpec + +- (void)startRecordingFps; +- (void)stopRecordingFps:(double)animationStopTimeMs; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeAnimationsDebugModule' + */ + class JSI_EXPORT NativeAnimationsDebugModuleSpecJSI : public ObjCTurboModule { + public: + NativeAnimationsDebugModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeAppState { + struct Constants { + + struct Builder { + struct Input { + RCTRequired initialAppState; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing Constants */ + Builder(Constants i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static Constants fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + Constants(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +@protocol NativeAppStateSpec + +- (void)getCurrentAppState:(RCTResponseSenderBlock)success + error:(RCTResponseSenderBlock)error; +- (void)addListener:(NSString *)eventName; +- (void)removeListeners:(double)count; +- (facebook::react::ModuleConstants)constantsToExport; +- (facebook::react::ModuleConstants)getConstants; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeAppState' + */ + class JSI_EXPORT NativeAppStateSpecJSI : public ObjCTurboModule { + public: + NativeAppStateSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeAppearanceSpec + +- (NSString * _Nullable)getColorScheme; +- (void)addListener:(NSString *)eventName; +- (void)removeListeners:(double)count; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeAppearance' + */ + class JSI_EXPORT NativeAppearanceSpecJSI : public ObjCTurboModule { + public: + NativeAppearanceSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeAsyncLocalStorageSpec + +- (void)multiGet:(NSArray *)keys + callback:(RCTResponseSenderBlock)callback; +- (void)multiSet:(NSArray *)kvPairs + callback:(RCTResponseSenderBlock)callback; +- (void)multiMerge:(NSArray *)kvPairs + callback:(RCTResponseSenderBlock)callback; +- (void)multiRemove:(NSArray *)keys + callback:(RCTResponseSenderBlock)callback; +- (void)clear:(RCTResponseSenderBlock)callback; +- (void)getAllKeys:(RCTResponseSenderBlock)callback; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeAsyncLocalStorage' + */ + class JSI_EXPORT NativeAsyncLocalStorageSpecJSI : public ObjCTurboModule { + public: + NativeAsyncLocalStorageSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeAsyncSQLiteDBStorageSpec + +- (void)multiGet:(NSArray *)keys + callback:(RCTResponseSenderBlock)callback; +- (void)multiSet:(NSArray *)kvPairs + callback:(RCTResponseSenderBlock)callback; +- (void)multiMerge:(NSArray *)kvPairs + callback:(RCTResponseSenderBlock)callback; +- (void)multiRemove:(NSArray *)keys + callback:(RCTResponseSenderBlock)callback; +- (void)clear:(RCTResponseSenderBlock)callback; +- (void)getAllKeys:(RCTResponseSenderBlock)callback; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeAsyncSQLiteDBStorage' + */ + class JSI_EXPORT NativeAsyncSQLiteDBStorageSpecJSI : public ObjCTurboModule { + public: + NativeAsyncSQLiteDBStorageSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeBlobModule { + struct Constants { + + struct Builder { + struct Input { + RCTRequired BLOB_URI_SCHEME; + RCTRequired BLOB_URI_HOST; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing Constants */ + Builder(Constants i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static Constants fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + Constants(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +@protocol NativeBlobModuleSpec + +- (void)addNetworkingHandler; +- (void)addWebSocketHandler:(double)id; +- (void)removeWebSocketHandler:(double)id; +- (void)sendOverSocket:(NSDictionary *)blob + socketID:(double)socketID; +- (void)createFromParts:(NSArray *)parts + withId:(NSString *)withId; +- (void)release:(NSString *)blobId; +- (facebook::react::ModuleConstants)constantsToExport; +- (facebook::react::ModuleConstants)getConstants; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeBlobModule' + */ + class JSI_EXPORT NativeBlobModuleSpecJSI : public ObjCTurboModule { + public: + NativeBlobModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeBugReportingSpec + +- (void)startReportAProblemFlow; +- (void)setExtraData:(NSDictionary *)extraData + extraFiles:(NSDictionary *)extraFiles; +- (void)setCategoryID:(NSString *)categoryID; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeBugReporting' + */ + class JSI_EXPORT NativeBugReportingSpecJSI : public ObjCTurboModule { + public: + NativeBugReportingSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeClipboardSpec + +- (void)getString:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (void)setString:(NSString *)content; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeClipboard' + */ + class JSI_EXPORT NativeClipboardSpecJSI : public ObjCTurboModule { + public: + NativeClipboardSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeDevLoadingViewSpec + +- (void)showMessage:(NSString *)message + withColor:(NSNumber *)withColor +withBackgroundColor:(NSNumber *)withBackgroundColor; +- (void)hide; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeDevLoadingView' + */ + class JSI_EXPORT NativeDevLoadingViewSpecJSI : public ObjCTurboModule { + public: + NativeDevLoadingViewSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeDevMenuSpec + +- (void)show; +- (void)reload; +- (void)debugRemotely:(BOOL)enableDebug; +- (void)setProfilingEnabled:(BOOL)enabled; +- (void)setHotLoadingEnabled:(BOOL)enabled; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeDevMenu' + */ + class JSI_EXPORT NativeDevMenuSpecJSI : public ObjCTurboModule { + public: + NativeDevMenuSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeDevSettingsSpec + +- (void)reload; +- (void)reloadWithReason:(NSString *)reason; +- (void)onFastRefresh; +- (void)setHotLoadingEnabled:(BOOL)isHotLoadingEnabled; +- (void)setIsDebuggingRemotely:(BOOL)isDebuggingRemotelyEnabled; +- (void)setProfilingEnabled:(BOOL)isProfilingEnabled; +- (void)toggleElementInspector; +- (void)addMenuItem:(NSString *)title; +- (void)addListener:(NSString *)eventName; +- (void)removeListeners:(double)count; +- (void)setIsShakeToShowDevMenuEnabled:(BOOL)enabled; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeDevSettings' + */ + class JSI_EXPORT NativeDevSettingsSpecJSI : public ObjCTurboModule { + public: + NativeDevSettingsSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeDevSplitBundleLoaderSpec + +- (void)loadBundle:(NSString *)bundlePath + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeDevSplitBundleLoader' + */ + class JSI_EXPORT NativeDevSplitBundleLoaderSpecJSI : public ObjCTurboModule { + public: + NativeDevSplitBundleLoaderSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeDeviceEventManagerSpec + +- (void)invokeDefaultBackPressHandler; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeDeviceEventManager' + */ + class JSI_EXPORT NativeDeviceEventManagerSpecJSI : public ObjCTurboModule { + public: + NativeDeviceEventManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeDeviceInfo { + struct DisplayMetrics { + + struct Builder { + struct Input { + RCTRequired width; + RCTRequired height; + RCTRequired scale; + RCTRequired fontScale; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing DisplayMetrics */ + Builder(DisplayMetrics i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static DisplayMetrics fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + DisplayMetrics(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +namespace JS { + namespace NativeDeviceInfo { + struct DisplayMetricsAndroid { + + struct Builder { + struct Input { + RCTRequired width; + RCTRequired height; + RCTRequired scale; + RCTRequired fontScale; + RCTRequired densityDpi; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing DisplayMetricsAndroid */ + Builder(DisplayMetricsAndroid i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static DisplayMetricsAndroid fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + DisplayMetricsAndroid(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +namespace JS { + namespace NativeDeviceInfo { + struct DimensionsPayload { + + struct Builder { + struct Input { + folly::Optional window; + folly::Optional screen; + folly::Optional windowPhysicalPixels; + folly::Optional screenPhysicalPixels; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing DimensionsPayload */ + Builder(DimensionsPayload i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static DimensionsPayload fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + DimensionsPayload(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +namespace JS { + namespace NativeDeviceInfo { + struct Constants { + + struct Builder { + struct Input { + RCTRequired Dimensions; + folly::Optional isIPhoneX_deprecated; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing Constants */ + Builder(Constants i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static Constants fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + Constants(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +@protocol NativeDeviceInfoSpec + +- (facebook::react::ModuleConstants)constantsToExport; +- (facebook::react::ModuleConstants)getConstants; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeDeviceInfo' + */ + class JSI_EXPORT NativeDeviceInfoSpecJSI : public ObjCTurboModule { + public: + NativeDeviceInfoSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeExceptionsManager { + struct StackFrame { + folly::Optional column() const; + NSString *file() const; + folly::Optional lineNumber() const; + NSString *methodName() const; + folly::Optional collapse() const; + + StackFrame(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeExceptionsManager_StackFrame) ++ (RCTManagedPointer *)JS_NativeExceptionsManager_StackFrame:(id)json; +@end +namespace JS { + namespace NativeExceptionsManager { + struct ExceptionData { + NSString *message() const; + NSString *originalMessage() const; + NSString *name() const; + NSString *componentStack() const; + facebook::react::LazyVector stack() const; + double id_() const; + bool isFatal() const; + id _Nullable extraData() const; + + ExceptionData(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeExceptionsManager_ExceptionData) ++ (RCTManagedPointer *)JS_NativeExceptionsManager_ExceptionData:(id)json; +@end +@protocol NativeExceptionsManagerSpec + +- (void)reportFatalException:(NSString *)message + stack:(NSArray *)stack + exceptionId:(double)exceptionId; +- (void)reportSoftException:(NSString *)message + stack:(NSArray *)stack + exceptionId:(double)exceptionId; +- (void)reportException:(JS::NativeExceptionsManager::ExceptionData &)data; +- (void)updateExceptionMessage:(NSString *)message + stack:(NSArray *)stack + exceptionId:(double)exceptionId; +- (void)dismissRedbox; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeExceptionsManager' + */ + class JSI_EXPORT NativeExceptionsManagerSpecJSI : public ObjCTurboModule { + public: + NativeExceptionsManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeFileReaderModuleSpec + +- (void)readAsDataURL:(NSDictionary *)data + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (void)readAsText:(NSDictionary *)data + encoding:(NSString *)encoding + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeFileReaderModule' + */ + class JSI_EXPORT NativeFileReaderModuleSpecJSI : public ObjCTurboModule { + public: + NativeFileReaderModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeFrameRateLogger { + struct SpecSetGlobalOptionsOptions { + folly::Optional debug() const; + folly::Optional reportStackTraces() const; + + SpecSetGlobalOptionsOptions(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeFrameRateLogger_SpecSetGlobalOptionsOptions) ++ (RCTManagedPointer *)JS_NativeFrameRateLogger_SpecSetGlobalOptionsOptions:(id)json; +@end +@protocol NativeFrameRateLoggerSpec + +- (void)setGlobalOptions:(JS::NativeFrameRateLogger::SpecSetGlobalOptionsOptions &)options; +- (void)setContext:(NSString *)context; +- (void)beginScroll; +- (void)endScroll; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeFrameRateLogger' + */ + class JSI_EXPORT NativeFrameRateLoggerSpecJSI : public ObjCTurboModule { + public: + NativeFrameRateLoggerSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeHeadlessJsTaskSupportSpec + +- (void)notifyTaskFinished:(double)taskId; +- (void)notifyTaskRetry:(double)taskId + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeHeadlessJsTaskSupport' + */ + class JSI_EXPORT NativeHeadlessJsTaskSupportSpecJSI : public ObjCTurboModule { + public: + NativeHeadlessJsTaskSupportSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeI18nManager { + struct Constants { + + struct Builder { + struct Input { + RCTRequired isRTL; + RCTRequired doLeftAndRightSwapInRTL; + RCTRequired localeIdentifier; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing Constants */ + Builder(Constants i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static Constants fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + Constants(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +@protocol NativeI18nManagerSpec + +- (void)allowRTL:(BOOL)allowRTL; +- (void)forceRTL:(BOOL)forceRTL; +- (void)swapLeftAndRightInRTL:(BOOL)flipStyles; +- (facebook::react::ModuleConstants)constantsToExport; +- (facebook::react::ModuleConstants)getConstants; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeI18nManager' + */ + class JSI_EXPORT NativeI18nManagerSpecJSI : public ObjCTurboModule { + public: + NativeI18nManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeImageEditor { + struct OptionsOffset { + double x() const; + double y() const; + + OptionsOffset(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeImageEditor_OptionsOffset) ++ (RCTManagedPointer *)JS_NativeImageEditor_OptionsOffset:(id)json; +@end +namespace JS { + namespace NativeImageEditor { + struct OptionsSize { + double width() const; + double height() const; + + OptionsSize(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeImageEditor_OptionsSize) ++ (RCTManagedPointer *)JS_NativeImageEditor_OptionsSize:(id)json; +@end +namespace JS { + namespace NativeImageEditor { + struct OptionsDisplaySize { + double width() const; + double height() const; + + OptionsDisplaySize(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeImageEditor_OptionsDisplaySize) ++ (RCTManagedPointer *)JS_NativeImageEditor_OptionsDisplaySize:(id)json; +@end +namespace JS { + namespace NativeImageEditor { + struct Options { + JS::NativeImageEditor::OptionsOffset offset() const; + JS::NativeImageEditor::OptionsSize size() const; + folly::Optional displaySize() const; + NSString *resizeMode() const; + folly::Optional allowExternalStorage() const; + + Options(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeImageEditor_Options) ++ (RCTManagedPointer *)JS_NativeImageEditor_Options:(id)json; +@end +@protocol NativeImageEditorSpec + +- (void)cropImage:(NSString *)uri + cropData:(JS::NativeImageEditor::Options &)cropData + successCallback:(RCTResponseSenderBlock)successCallback + errorCallback:(RCTResponseSenderBlock)errorCallback; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeImageEditor' + */ + class JSI_EXPORT NativeImageEditorSpecJSI : public ObjCTurboModule { + public: + NativeImageEditorSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeImageLoaderIOSSpec + +- (void)getSize:(NSString *)uri + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (void)getSizeWithHeaders:(NSString *)uri + headers:(NSDictionary *)headers + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (void)prefetchImage:(NSString *)uri + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (void)prefetchImageWithMetadata:(NSString *)uri + queryRootName:(NSString *)queryRootName + rootTag:(double)rootTag + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (void)queryCache:(NSArray *)uris + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeImageLoaderIOS' + */ + class JSI_EXPORT NativeImageLoaderIOSSpecJSI : public ObjCTurboModule { + public: + NativeImageLoaderIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeImagePickerIOS { + struct SpecOpenCameraDialogConfig { + bool unmirrorFrontFacingCamera() const; + bool videoMode() const; + + SpecOpenCameraDialogConfig(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeImagePickerIOS_SpecOpenCameraDialogConfig) ++ (RCTManagedPointer *)JS_NativeImagePickerIOS_SpecOpenCameraDialogConfig:(id)json; +@end +namespace JS { + namespace NativeImagePickerIOS { + struct SpecOpenSelectDialogConfig { + bool showImages() const; + bool showVideos() const; + + SpecOpenSelectDialogConfig(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeImagePickerIOS_SpecOpenSelectDialogConfig) ++ (RCTManagedPointer *)JS_NativeImagePickerIOS_SpecOpenSelectDialogConfig:(id)json; +@end +@protocol NativeImagePickerIOSSpec + +- (void)canRecordVideos:(RCTResponseSenderBlock)callback; +- (void)canUseCamera:(RCTResponseSenderBlock)callback; +- (void)openCameraDialog:(JS::NativeImagePickerIOS::SpecOpenCameraDialogConfig &)config + successCallback:(RCTResponseSenderBlock)successCallback + cancelCallback:(RCTResponseSenderBlock)cancelCallback; +- (void)openSelectDialog:(JS::NativeImagePickerIOS::SpecOpenSelectDialogConfig &)config + successCallback:(RCTResponseSenderBlock)successCallback + cancelCallback:(RCTResponseSenderBlock)cancelCallback; +- (void)clearAllPendingVideos; +- (void)removePendingVideo:(NSString *)url; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeImagePickerIOS' + */ + class JSI_EXPORT NativeImagePickerIOSSpecJSI : public ObjCTurboModule { + public: + NativeImagePickerIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeImageStoreIOSSpec + +- (void)getBase64ForTag:(NSString *)uri + successCallback:(RCTResponseSenderBlock)successCallback + errorCallback:(RCTResponseSenderBlock)errorCallback; +- (void)hasImageForTag:(NSString *)uri + callback:(RCTResponseSenderBlock)callback; +- (void)removeImageForTag:(NSString *)uri; +- (void)addImageFromBase64:(NSString *)base64ImageData + successCallback:(RCTResponseSenderBlock)successCallback + errorCallback:(RCTResponseSenderBlock)errorCallback; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeImageStoreIOS' + */ + class JSI_EXPORT NativeImageStoreIOSSpecJSI : public ObjCTurboModule { + public: + NativeImageStoreIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeJSCHeapCaptureSpec + +- (void)captureComplete:(NSString *)path + error:(NSString * _Nullable)error; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeJSCHeapCapture' + */ + class JSI_EXPORT NativeJSCHeapCaptureSpecJSI : public ObjCTurboModule { + public: + NativeJSCHeapCaptureSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeJSCSamplingProfilerSpec + +- (void)operationComplete:(double)token + result:(NSString * _Nullable)result + error:(NSString * _Nullable)error; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeJSCSamplingProfiler' + */ + class JSI_EXPORT NativeJSCSamplingProfilerSpecJSI : public ObjCTurboModule { + public: + NativeJSCSamplingProfilerSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeJSDevSupport { + struct Constants { + + struct Builder { + struct Input { + RCTRequired ERROR_CODE_EXCEPTION; + RCTRequired ERROR_CODE_VIEW_NOT_FOUND; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing Constants */ + Builder(Constants i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static Constants fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + Constants(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +@protocol NativeJSDevSupportSpec + +- (void)onSuccess:(NSString *)data; +- (void)onFailure:(double)errorCode + error:(NSString *)error; +- (facebook::react::ModuleConstants)constantsToExport; +- (facebook::react::ModuleConstants)getConstants; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeJSDevSupport' + */ + class JSI_EXPORT NativeJSDevSupportSpecJSI : public ObjCTurboModule { + public: + NativeJSDevSupportSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeKeyboardObserverSpec + +- (void)addListener:(NSString *)eventName; +- (void)removeListeners:(double)count; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeKeyboardObserver' + */ + class JSI_EXPORT NativeKeyboardObserverSpecJSI : public ObjCTurboModule { + public: + NativeKeyboardObserverSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeLinkingManagerSpec + +- (void)getInitialURL:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (void)canOpenURL:(NSString *)url + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (void)openURL:(NSString *)url + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (void)openSettings:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (void)addListener:(NSString *)eventName; +- (void)removeListeners:(double)count; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeLinkingManager' + */ + class JSI_EXPORT NativeLinkingManagerSpecJSI : public ObjCTurboModule { + public: + NativeLinkingManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeLogBoxSpec + +- (void)show; +- (void)hide; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeLogBox' + */ + class JSI_EXPORT NativeLogBoxSpecJSI : public ObjCTurboModule { + public: + NativeLogBoxSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeModalManagerSpec + +- (void)addListener:(NSString *)eventName; +- (void)removeListeners:(double)count; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeModalManager' + */ + class JSI_EXPORT NativeModalManagerSpecJSI : public ObjCTurboModule { + public: + NativeModalManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeNetworkingIOS { + struct SpecSendRequestQuery { + NSString *method() const; + NSString *url() const; + id data() const; + id headers() const; + NSString *responseType() const; + bool incrementalUpdates() const; + double timeout() const; + bool withCredentials() const; + + SpecSendRequestQuery(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeNetworkingIOS_SpecSendRequestQuery) ++ (RCTManagedPointer *)JS_NativeNetworkingIOS_SpecSendRequestQuery:(id)json; +@end +@protocol NativeNetworkingIOSSpec + +- (void)sendRequest:(JS::NativeNetworkingIOS::SpecSendRequestQuery &)query + callback:(RCTResponseSenderBlock)callback; +- (void)abortRequest:(double)requestId; +- (void)clearCookies:(RCTResponseSenderBlock)callback; +- (void)addListener:(NSString *)eventName; +- (void)removeListeners:(double)count; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeNetworkingIOS' + */ + class JSI_EXPORT NativeNetworkingIOSSpecJSI : public ObjCTurboModule { + public: + NativeNetworkingIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativePlatformConstantsIOS { + struct ConstantsReactNativeVersion { + + struct Builder { + struct Input { + RCTRequired major; + RCTRequired minor; + RCTRequired patch; + RCTRequired> prerelease; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing ConstantsReactNativeVersion */ + Builder(ConstantsReactNativeVersion i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static ConstantsReactNativeVersion fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + ConstantsReactNativeVersion(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +namespace JS { + namespace NativePlatformConstantsIOS { + struct Constants { + + struct Builder { + struct Input { + RCTRequired isTesting; + RCTRequired reactNativeVersion; + RCTRequired forceTouchAvailable; + RCTRequired osVersion; + RCTRequired systemName; + RCTRequired interfaceIdiom; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing Constants */ + Builder(Constants i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static Constants fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + Constants(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +@protocol NativePlatformConstantsIOSSpec + +- (facebook::react::ModuleConstants)constantsToExport; +- (facebook::react::ModuleConstants)getConstants; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativePlatformConstantsIOS' + */ + class JSI_EXPORT NativePlatformConstantsIOSSpecJSI : public ObjCTurboModule { + public: + NativePlatformConstantsIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativePushNotificationManagerIOS { + struct SpecRequestPermissionsPermission { + bool alert() const; + bool badge() const; + bool sound() const; + + SpecRequestPermissionsPermission(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativePushNotificationManagerIOS_SpecRequestPermissionsPermission) ++ (RCTManagedPointer *)JS_NativePushNotificationManagerIOS_SpecRequestPermissionsPermission:(id)json; +@end +namespace JS { + namespace NativePushNotificationManagerIOS { + struct Notification { + NSString *alertTitle() const; + folly::Optional fireDate() const; + NSString *alertBody() const; + NSString *alertAction() const; + id _Nullable userInfo() const; + NSString *category() const; + NSString *repeatInterval() const; + folly::Optional applicationIconBadgeNumber() const; + folly::Optional isSilent() const; + + Notification(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativePushNotificationManagerIOS_Notification) ++ (RCTManagedPointer *)JS_NativePushNotificationManagerIOS_Notification:(id)json; +@end +@protocol NativePushNotificationManagerIOSSpec + +- (void)onFinishRemoteNotification:(NSString *)notificationId + fetchResult:(NSString *)fetchResult; +- (void)setApplicationIconBadgeNumber:(double)num; +- (void)getApplicationIconBadgeNumber:(RCTResponseSenderBlock)callback; +- (void)requestPermissions:(JS::NativePushNotificationManagerIOS::SpecRequestPermissionsPermission &)permission + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (void)abandonPermissions; +- (void)checkPermissions:(RCTResponseSenderBlock)callback; +- (void)presentLocalNotification:(JS::NativePushNotificationManagerIOS::Notification &)notification; +- (void)scheduleLocalNotification:(JS::NativePushNotificationManagerIOS::Notification &)notification; +- (void)cancelAllLocalNotifications; +- (void)cancelLocalNotifications:(NSDictionary *)userInfo; +- (void)getInitialNotification:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (void)getScheduledLocalNotifications:(RCTResponseSenderBlock)callback; +- (void)removeAllDeliveredNotifications; +- (void)removeDeliveredNotifications:(NSArray *)identifiers; +- (void)getDeliveredNotifications:(RCTResponseSenderBlock)callback; +- (void)getAuthorizationStatus:(RCTResponseSenderBlock)callback; +- (void)addListener:(NSString *)eventType; +- (void)removeListeners:(double)count; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativePushNotificationManagerIOS' + */ + class JSI_EXPORT NativePushNotificationManagerIOSSpecJSI : public ObjCTurboModule { + public: + NativePushNotificationManagerIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeRedBoxSpec + +- (void)setExtraData:(NSDictionary *)extraData + forIdentifier:(NSString *)forIdentifier; +- (void)dismiss; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeRedBox' + */ + class JSI_EXPORT NativeRedBoxSpecJSI : public ObjCTurboModule { + public: + NativeRedBoxSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeSegmentFetcherSpec + +- (void)fetchSegment:(double)segmentId + options:(NSDictionary *)options + callback:(RCTResponseSenderBlock)callback; +- (void)getSegment:(double)segmentId + options:(NSDictionary *)options + callback:(RCTResponseSenderBlock)callback; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeSegmentFetcher' + */ + class JSI_EXPORT NativeSegmentFetcherSpecJSI : public ObjCTurboModule { + public: + NativeSegmentFetcherSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeSettingsManager { + struct Constants { + + struct Builder { + struct Input { + RCTRequired > settings; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing Constants */ + Builder(Constants i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static Constants fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + Constants(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +@protocol NativeSettingsManagerSpec + +- (void)setValues:(NSDictionary *)values; +- (void)deleteValues:(NSArray *)values; +- (facebook::react::ModuleConstants)constantsToExport; +- (facebook::react::ModuleConstants)getConstants; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeSettingsManager' + */ + class JSI_EXPORT NativeSettingsManagerSpecJSI : public ObjCTurboModule { + public: + NativeSettingsManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeShareModule { + struct SpecShareContent { + NSString *title() const; + NSString *message() const; + + SpecShareContent(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeShareModule_SpecShareContent) ++ (RCTManagedPointer *)JS_NativeShareModule_SpecShareContent:(id)json; +@end +@protocol NativeShareModuleSpec + +- (void)share:(JS::NativeShareModule::SpecShareContent &)content + dialogTitle:(NSString *)dialogTitle + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeShareModule' + */ + class JSI_EXPORT NativeShareModuleSpecJSI : public ObjCTurboModule { + public: + NativeShareModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeSoundManagerSpec + +- (void)playTouchSound; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeSoundManager' + */ + class JSI_EXPORT NativeSoundManagerSpecJSI : public ObjCTurboModule { + public: + NativeSoundManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeSourceCode { + struct Constants { + + struct Builder { + struct Input { + RCTRequired scriptURL; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing Constants */ + Builder(Constants i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static Constants fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + Constants(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +@protocol NativeSourceCodeSpec + +- (facebook::react::ModuleConstants)constantsToExport; +- (facebook::react::ModuleConstants)getConstants; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeSourceCode' + */ + class JSI_EXPORT NativeSourceCodeSpecJSI : public ObjCTurboModule { + public: + NativeSourceCodeSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeStatusBarManagerIOS { + struct Constants { + + struct Builder { + struct Input { + RCTRequired HEIGHT; + folly::Optional DEFAULT_BACKGROUND_COLOR; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing Constants */ + Builder(Constants i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static Constants fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + Constants(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +@protocol NativeStatusBarManagerIOSSpec + +- (void)getHeight:(RCTResponseSenderBlock)callback; +- (void)setNetworkActivityIndicatorVisible:(BOOL)visible; +- (void)addListener:(NSString *)eventType; +- (void)removeListeners:(double)count; +- (void)setStyle:(NSString * _Nullable)statusBarStyle + animated:(BOOL)animated; +- (void)setHidden:(BOOL)hidden + withAnimation:(NSString *)withAnimation; +- (facebook::react::ModuleConstants)constantsToExport; +- (facebook::react::ModuleConstants)getConstants; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeStatusBarManagerIOS' + */ + class JSI_EXPORT NativeStatusBarManagerIOSSpecJSI : public ObjCTurboModule { + public: + NativeStatusBarManagerIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeTVNavigationEventEmitterSpec + +- (void)addListener:(NSString *)eventName; +- (void)removeListeners:(double)count; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeTVNavigationEventEmitter' + */ + class JSI_EXPORT NativeTVNavigationEventEmitterSpecJSI : public ObjCTurboModule { + public: + NativeTVNavigationEventEmitterSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeTimingSpec + +- (void)createTimer:(double)callbackID + duration:(double)duration + jsSchedulingTime:(double)jsSchedulingTime + repeats:(BOOL)repeats; +- (void)deleteTimer:(double)timerID; +- (void)setSendIdleEvents:(BOOL)sendIdleEvents; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeTiming' + */ + class JSI_EXPORT NativeTimingSpecJSI : public ObjCTurboModule { + public: + NativeTimingSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +@protocol NativeVibrationSpec + +- (void)vibrate:(double)pattern; +- (void)vibrateByPattern:(NSArray *)pattern + repeat:(double)repeat; +- (void)cancel; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeVibration' + */ + class JSI_EXPORT NativeVibrationSpecJSI : public ObjCTurboModule { + public: + NativeVibrationSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +namespace JS { + namespace NativeWebSocketModule { + struct SpecConnectOptions { + id _Nullable headers() const; + + SpecConnectOptions(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeWebSocketModule_SpecConnectOptions) ++ (RCTManagedPointer *)JS_NativeWebSocketModule_SpecConnectOptions:(id)json; +@end +@protocol NativeWebSocketModuleSpec + +- (void)connect:(NSString *)url + protocols:(NSArray * _Nullable)protocols + options:(JS::NativeWebSocketModule::SpecConnectOptions &)options + socketID:(double)socketID; +- (void)send:(NSString *)message + forSocketID:(double)forSocketID; +- (void)sendBinary:(NSString *)base64String + forSocketID:(double)forSocketID; +- (void)ping:(double)socketID; +- (void)close:(double)code + reason:(NSString *)reason + socketID:(double)socketID; +- (void)addListener:(NSString *)eventName; +- (void)removeListeners:(double)count; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NativeWebSocketModule' + */ + class JSI_EXPORT NativeWebSocketModuleSpecJSI : public ObjCTurboModule { + public: + NativeWebSocketModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + +inline folly::Optional JS::NativeAccessibilityManager::SpecSetAccessibilityContentSizeMultipliersJSMultipliers::extraSmall() const +{ + id const p = _v[@"extraSmall"]; + return RCTBridgingToOptionalDouble(p); +} +inline folly::Optional JS::NativeAccessibilityManager::SpecSetAccessibilityContentSizeMultipliersJSMultipliers::small() const +{ + id const p = _v[@"small"]; + return RCTBridgingToOptionalDouble(p); +} +inline folly::Optional JS::NativeAccessibilityManager::SpecSetAccessibilityContentSizeMultipliersJSMultipliers::medium() const +{ + id const p = _v[@"medium"]; + return RCTBridgingToOptionalDouble(p); +} +inline folly::Optional JS::NativeAccessibilityManager::SpecSetAccessibilityContentSizeMultipliersJSMultipliers::large() const +{ + id const p = _v[@"large"]; + return RCTBridgingToOptionalDouble(p); +} +inline folly::Optional JS::NativeAccessibilityManager::SpecSetAccessibilityContentSizeMultipliersJSMultipliers::extraLarge() const +{ + id const p = _v[@"extraLarge"]; + return RCTBridgingToOptionalDouble(p); +} +inline folly::Optional JS::NativeAccessibilityManager::SpecSetAccessibilityContentSizeMultipliersJSMultipliers::extraExtraLarge() const +{ + id const p = _v[@"extraExtraLarge"]; + return RCTBridgingToOptionalDouble(p); +} +inline folly::Optional JS::NativeAccessibilityManager::SpecSetAccessibilityContentSizeMultipliersJSMultipliers::extraExtraExtraLarge() const +{ + id const p = _v[@"extraExtraExtraLarge"]; + return RCTBridgingToOptionalDouble(p); +} +inline folly::Optional JS::NativeAccessibilityManager::SpecSetAccessibilityContentSizeMultipliersJSMultipliers::accessibilityMedium() const +{ + id const p = _v[@"accessibilityMedium"]; + return RCTBridgingToOptionalDouble(p); +} +inline folly::Optional JS::NativeAccessibilityManager::SpecSetAccessibilityContentSizeMultipliersJSMultipliers::accessibilityLarge() const +{ + id const p = _v[@"accessibilityLarge"]; + return RCTBridgingToOptionalDouble(p); +} +inline folly::Optional JS::NativeAccessibilityManager::SpecSetAccessibilityContentSizeMultipliersJSMultipliers::accessibilityExtraLarge() const +{ + id const p = _v[@"accessibilityExtraLarge"]; + return RCTBridgingToOptionalDouble(p); +} +inline folly::Optional JS::NativeAccessibilityManager::SpecSetAccessibilityContentSizeMultipliersJSMultipliers::accessibilityExtraExtraLarge() const +{ + id const p = _v[@"accessibilityExtraExtraLarge"]; + return RCTBridgingToOptionalDouble(p); +} +inline folly::Optional JS::NativeAccessibilityManager::SpecSetAccessibilityContentSizeMultipliersJSMultipliers::accessibilityExtraExtraExtraLarge() const +{ + id const p = _v[@"accessibilityExtraExtraExtraLarge"]; + return RCTBridgingToOptionalDouble(p); +} +inline NSString *JS::NativeActionSheetManager::SpecShowActionSheetWithOptionsOptions::title() const +{ + id const p = _v[@"title"]; + return RCTBridgingToOptionalString(p); +} +inline NSString *JS::NativeActionSheetManager::SpecShowActionSheetWithOptionsOptions::message() const +{ + id const p = _v[@"message"]; + return RCTBridgingToOptionalString(p); +} +inline folly::Optional> JS::NativeActionSheetManager::SpecShowActionSheetWithOptionsOptions::options() const +{ + id const p = _v[@"options"]; + return RCTBridgingToOptionalVec(p, ^NSString *(id itemValue_0) { return RCTBridgingToString(itemValue_0); }); +} +inline folly::Optional> JS::NativeActionSheetManager::SpecShowActionSheetWithOptionsOptions::destructiveButtonIndices() const +{ + id const p = _v[@"destructiveButtonIndices"]; + return RCTBridgingToOptionalVec(p, ^double(id itemValue_0) { return RCTBridgingToDouble(itemValue_0); }); +} +inline folly::Optional JS::NativeActionSheetManager::SpecShowActionSheetWithOptionsOptions::cancelButtonIndex() const +{ + id const p = _v[@"cancelButtonIndex"]; + return RCTBridgingToOptionalDouble(p); +} +inline folly::Optional JS::NativeActionSheetManager::SpecShowActionSheetWithOptionsOptions::anchor() const +{ + id const p = _v[@"anchor"]; + return RCTBridgingToOptionalDouble(p); +} +inline folly::Optional JS::NativeActionSheetManager::SpecShowActionSheetWithOptionsOptions::tintColor() const +{ + id const p = _v[@"tintColor"]; + return RCTBridgingToOptionalDouble(p); +} +inline NSString *JS::NativeActionSheetManager::SpecShowActionSheetWithOptionsOptions::userInterfaceStyle() const +{ + id const p = _v[@"userInterfaceStyle"]; + return RCTBridgingToOptionalString(p); +} +inline folly::Optional> JS::NativeActionSheetManager::SpecShowActionSheetWithOptionsOptions::disabledButtonIndices() const +{ + id const p = _v[@"disabledButtonIndices"]; + return RCTBridgingToOptionalVec(p, ^double(id itemValue_0) { return RCTBridgingToDouble(itemValue_0); }); +} +inline NSString *JS::NativeActionSheetManager::SpecShowShareActionSheetWithOptionsOptions::message() const +{ + id const p = _v[@"message"]; + return RCTBridgingToOptionalString(p); +} +inline NSString *JS::NativeActionSheetManager::SpecShowShareActionSheetWithOptionsOptions::url() const +{ + id const p = _v[@"url"]; + return RCTBridgingToOptionalString(p); +} +inline NSString *JS::NativeActionSheetManager::SpecShowShareActionSheetWithOptionsOptions::subject() const +{ + id const p = _v[@"subject"]; + return RCTBridgingToOptionalString(p); +} +inline folly::Optional JS::NativeActionSheetManager::SpecShowShareActionSheetWithOptionsOptions::anchor() const +{ + id const p = _v[@"anchor"]; + return RCTBridgingToOptionalDouble(p); +} +inline folly::Optional JS::NativeActionSheetManager::SpecShowShareActionSheetWithOptionsOptions::tintColor() const +{ + id const p = _v[@"tintColor"]; + return RCTBridgingToOptionalDouble(p); +} +inline folly::Optional> JS::NativeActionSheetManager::SpecShowShareActionSheetWithOptionsOptions::excludedActivityTypes() const +{ + id const p = _v[@"excludedActivityTypes"]; + return RCTBridgingToOptionalVec(p, ^NSString *(id itemValue_0) { return RCTBridgingToString(itemValue_0); }); +} +inline NSString *JS::NativeActionSheetManager::SpecShowShareActionSheetWithOptionsOptions::userInterfaceStyle() const +{ + id const p = _v[@"userInterfaceStyle"]; + return RCTBridgingToOptionalString(p); +} +inline NSString *JS::NativeAlertManager::Args::title() const +{ + id const p = _v[@"title"]; + return RCTBridgingToOptionalString(p); +} +inline NSString *JS::NativeAlertManager::Args::message() const +{ + id const p = _v[@"message"]; + return RCTBridgingToOptionalString(p); +} +inline folly::Optional >> JS::NativeAlertManager::Args::buttons() const +{ + id const p = _v[@"buttons"]; + return RCTBridgingToOptionalVec(p, ^id (id itemValue_0) { return itemValue_0; }); +} +inline NSString *JS::NativeAlertManager::Args::type() const +{ + id const p = _v[@"type"]; + return RCTBridgingToOptionalString(p); +} +inline NSString *JS::NativeAlertManager::Args::defaultValue() const +{ + id const p = _v[@"defaultValue"]; + return RCTBridgingToOptionalString(p); +} +inline NSString *JS::NativeAlertManager::Args::cancelButtonKey() const +{ + id const p = _v[@"cancelButtonKey"]; + return RCTBridgingToOptionalString(p); +} +inline NSString *JS::NativeAlertManager::Args::destructiveButtonKey() const +{ + id const p = _v[@"destructiveButtonKey"]; + return RCTBridgingToOptionalString(p); +} +inline NSString *JS::NativeAlertManager::Args::keyboardType() const +{ + id const p = _v[@"keyboardType"]; + return RCTBridgingToOptionalString(p); +} +inline facebook::react::LazyVector JS::NativeAnimatedModule::EventMapping::nativeEventPath() const +{ + id const p = _v[@"nativeEventPath"]; + return RCTBridgingToVec(p, ^NSString *(id itemValue_0) { return RCTBridgingToString(itemValue_0); }); +} +inline folly::Optional JS::NativeAnimatedModule::EventMapping::animatedValueTag() const +{ + id const p = _v[@"animatedValueTag"]; + return RCTBridgingToOptionalDouble(p); +} +inline facebook::react::LazyVector JS::NativeAnimatedTurboModule::EventMapping::nativeEventPath() const +{ + id const p = _v[@"nativeEventPath"]; + return RCTBridgingToVec(p, ^NSString *(id itemValue_0) { return RCTBridgingToString(itemValue_0); }); +} +inline folly::Optional JS::NativeAnimatedTurboModule::EventMapping::animatedValueTag() const +{ + id const p = _v[@"animatedValueTag"]; + return RCTBridgingToOptionalDouble(p); +} + +inline JS::NativeAppState::Constants::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto initialAppState = i.initialAppState.get(); + d[@"initialAppState"] = initialAppState; + return d; +}) {} +inline JS::NativeAppState::Constants::Builder::Builder(Constants i) : _factory(^{ + return i.unsafeRawValue(); +}) {} + + + +inline JS::NativeBlobModule::Constants::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto BLOB_URI_SCHEME = i.BLOB_URI_SCHEME.get(); + d[@"BLOB_URI_SCHEME"] = BLOB_URI_SCHEME; + auto BLOB_URI_HOST = i.BLOB_URI_HOST.get(); + d[@"BLOB_URI_HOST"] = BLOB_URI_HOST; + return d; +}) {} +inline JS::NativeBlobModule::Constants::Builder::Builder(Constants i) : _factory(^{ + return i.unsafeRawValue(); +}) {} + + + + + + + +inline JS::NativeDeviceInfo::DisplayMetrics::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto width = i.width.get(); + d[@"width"] = @(width); + auto height = i.height.get(); + d[@"height"] = @(height); + auto scale = i.scale.get(); + d[@"scale"] = @(scale); + auto fontScale = i.fontScale.get(); + d[@"fontScale"] = @(fontScale); + return d; +}) {} +inline JS::NativeDeviceInfo::DisplayMetrics::Builder::Builder(DisplayMetrics i) : _factory(^{ + return i.unsafeRawValue(); +}) {} +inline JS::NativeDeviceInfo::DisplayMetricsAndroid::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto width = i.width.get(); + d[@"width"] = @(width); + auto height = i.height.get(); + d[@"height"] = @(height); + auto scale = i.scale.get(); + d[@"scale"] = @(scale); + auto fontScale = i.fontScale.get(); + d[@"fontScale"] = @(fontScale); + auto densityDpi = i.densityDpi.get(); + d[@"densityDpi"] = @(densityDpi); + return d; +}) {} +inline JS::NativeDeviceInfo::DisplayMetricsAndroid::Builder::Builder(DisplayMetricsAndroid i) : _factory(^{ + return i.unsafeRawValue(); +}) {} +inline JS::NativeDeviceInfo::DimensionsPayload::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto window = i.window; + d[@"window"] = window.hasValue() ? window.value().buildUnsafeRawValue() : nil; + auto screen = i.screen; + d[@"screen"] = screen.hasValue() ? screen.value().buildUnsafeRawValue() : nil; + auto windowPhysicalPixels = i.windowPhysicalPixels; + d[@"windowPhysicalPixels"] = windowPhysicalPixels.hasValue() ? windowPhysicalPixels.value().buildUnsafeRawValue() : nil; + auto screenPhysicalPixels = i.screenPhysicalPixels; + d[@"screenPhysicalPixels"] = screenPhysicalPixels.hasValue() ? screenPhysicalPixels.value().buildUnsafeRawValue() : nil; + return d; +}) {} +inline JS::NativeDeviceInfo::DimensionsPayload::Builder::Builder(DimensionsPayload i) : _factory(^{ + return i.unsafeRawValue(); +}) {} +inline JS::NativeDeviceInfo::Constants::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto Dimensions = i.Dimensions.get(); + d[@"Dimensions"] = Dimensions.buildUnsafeRawValue(); + auto isIPhoneX_deprecated = i.isIPhoneX_deprecated; + d[@"isIPhoneX_deprecated"] = isIPhoneX_deprecated.hasValue() ? @((BOOL)isIPhoneX_deprecated.value()) : nil; + return d; +}) {} +inline JS::NativeDeviceInfo::Constants::Builder::Builder(Constants i) : _factory(^{ + return i.unsafeRawValue(); +}) {} +inline folly::Optional JS::NativeExceptionsManager::StackFrame::column() const +{ + id const p = _v[@"column"]; + return RCTBridgingToOptionalDouble(p); +} +inline NSString *JS::NativeExceptionsManager::StackFrame::file() const +{ + id const p = _v[@"file"]; + return RCTBridgingToOptionalString(p); +} +inline folly::Optional JS::NativeExceptionsManager::StackFrame::lineNumber() const +{ + id const p = _v[@"lineNumber"]; + return RCTBridgingToOptionalDouble(p); +} +inline NSString *JS::NativeExceptionsManager::StackFrame::methodName() const +{ + id const p = _v[@"methodName"]; + return RCTBridgingToString(p); +} +inline folly::Optional JS::NativeExceptionsManager::StackFrame::collapse() const +{ + id const p = _v[@"collapse"]; + return RCTBridgingToOptionalBool(p); +} +inline NSString *JS::NativeExceptionsManager::ExceptionData::message() const +{ + id const p = _v[@"message"]; + return RCTBridgingToString(p); +} +inline NSString *JS::NativeExceptionsManager::ExceptionData::originalMessage() const +{ + id const p = _v[@"originalMessage"]; + return RCTBridgingToOptionalString(p); +} +inline NSString *JS::NativeExceptionsManager::ExceptionData::name() const +{ + id const p = _v[@"name"]; + return RCTBridgingToOptionalString(p); +} +inline NSString *JS::NativeExceptionsManager::ExceptionData::componentStack() const +{ + id const p = _v[@"componentStack"]; + return RCTBridgingToOptionalString(p); +} +inline facebook::react::LazyVector JS::NativeExceptionsManager::ExceptionData::stack() const +{ + id const p = _v[@"stack"]; + return RCTBridgingToVec(p, ^JS::NativeExceptionsManager::StackFrame(id itemValue_0) { return JS::NativeExceptionsManager::StackFrame(itemValue_0); }); +} +inline double JS::NativeExceptionsManager::ExceptionData::id_() const +{ + id const p = _v[@"id"]; + return RCTBridgingToDouble(p); +} +inline bool JS::NativeExceptionsManager::ExceptionData::isFatal() const +{ + id const p = _v[@"isFatal"]; + return RCTBridgingToBool(p); +} +inline id _Nullable JS::NativeExceptionsManager::ExceptionData::extraData() const +{ + id const p = _v[@"extraData"]; + return p; +} + +inline folly::Optional JS::NativeFrameRateLogger::SpecSetGlobalOptionsOptions::debug() const +{ + id const p = _v[@"debug"]; + return RCTBridgingToOptionalBool(p); +} +inline folly::Optional JS::NativeFrameRateLogger::SpecSetGlobalOptionsOptions::reportStackTraces() const +{ + id const p = _v[@"reportStackTraces"]; + return RCTBridgingToOptionalBool(p); +} + +inline JS::NativeI18nManager::Constants::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto isRTL = i.isRTL.get(); + d[@"isRTL"] = @(isRTL); + auto doLeftAndRightSwapInRTL = i.doLeftAndRightSwapInRTL.get(); + d[@"doLeftAndRightSwapInRTL"] = @(doLeftAndRightSwapInRTL); + auto localeIdentifier = i.localeIdentifier.get(); + d[@"localeIdentifier"] = localeIdentifier; + return d; +}) {} +inline JS::NativeI18nManager::Constants::Builder::Builder(Constants i) : _factory(^{ + return i.unsafeRawValue(); +}) {} +inline double JS::NativeImageEditor::OptionsOffset::x() const +{ + id const p = _v[@"x"]; + return RCTBridgingToDouble(p); +} +inline double JS::NativeImageEditor::OptionsOffset::y() const +{ + id const p = _v[@"y"]; + return RCTBridgingToDouble(p); +} +inline double JS::NativeImageEditor::OptionsSize::width() const +{ + id const p = _v[@"width"]; + return RCTBridgingToDouble(p); +} +inline double JS::NativeImageEditor::OptionsSize::height() const +{ + id const p = _v[@"height"]; + return RCTBridgingToDouble(p); +} +inline double JS::NativeImageEditor::OptionsDisplaySize::width() const +{ + id const p = _v[@"width"]; + return RCTBridgingToDouble(p); +} +inline double JS::NativeImageEditor::OptionsDisplaySize::height() const +{ + id const p = _v[@"height"]; + return RCTBridgingToDouble(p); +} +inline JS::NativeImageEditor::OptionsOffset JS::NativeImageEditor::Options::offset() const +{ + id const p = _v[@"offset"]; + return JS::NativeImageEditor::OptionsOffset(p); +} +inline JS::NativeImageEditor::OptionsSize JS::NativeImageEditor::Options::size() const +{ + id const p = _v[@"size"]; + return JS::NativeImageEditor::OptionsSize(p); +} +inline folly::Optional JS::NativeImageEditor::Options::displaySize() const +{ + id const p = _v[@"displaySize"]; + return (p == nil ? folly::none : folly::make_optional(JS::NativeImageEditor::OptionsDisplaySize(p))); +} +inline NSString *JS::NativeImageEditor::Options::resizeMode() const +{ + id const p = _v[@"resizeMode"]; + return RCTBridgingToOptionalString(p); +} +inline folly::Optional JS::NativeImageEditor::Options::allowExternalStorage() const +{ + id const p = _v[@"allowExternalStorage"]; + return RCTBridgingToOptionalBool(p); +} + +inline bool JS::NativeImagePickerIOS::SpecOpenCameraDialogConfig::unmirrorFrontFacingCamera() const +{ + id const p = _v[@"unmirrorFrontFacingCamera"]; + return RCTBridgingToBool(p); +} +inline bool JS::NativeImagePickerIOS::SpecOpenCameraDialogConfig::videoMode() const +{ + id const p = _v[@"videoMode"]; + return RCTBridgingToBool(p); +} +inline bool JS::NativeImagePickerIOS::SpecOpenSelectDialogConfig::showImages() const +{ + id const p = _v[@"showImages"]; + return RCTBridgingToBool(p); +} +inline bool JS::NativeImagePickerIOS::SpecOpenSelectDialogConfig::showVideos() const +{ + id const p = _v[@"showVideos"]; + return RCTBridgingToBool(p); +} + + + +inline JS::NativeJSDevSupport::Constants::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto ERROR_CODE_EXCEPTION = i.ERROR_CODE_EXCEPTION.get(); + d[@"ERROR_CODE_EXCEPTION"] = @(ERROR_CODE_EXCEPTION); + auto ERROR_CODE_VIEW_NOT_FOUND = i.ERROR_CODE_VIEW_NOT_FOUND.get(); + d[@"ERROR_CODE_VIEW_NOT_FOUND"] = @(ERROR_CODE_VIEW_NOT_FOUND); + return d; +}) {} +inline JS::NativeJSDevSupport::Constants::Builder::Builder(Constants i) : _factory(^{ + return i.unsafeRawValue(); +}) {} + + + + +inline NSString *JS::NativeNetworkingIOS::SpecSendRequestQuery::method() const +{ + id const p = _v[@"method"]; + return RCTBridgingToString(p); +} +inline NSString *JS::NativeNetworkingIOS::SpecSendRequestQuery::url() const +{ + id const p = _v[@"url"]; + return RCTBridgingToString(p); +} +inline id JS::NativeNetworkingIOS::SpecSendRequestQuery::data() const +{ + id const p = _v[@"data"]; + return p; +} +inline id JS::NativeNetworkingIOS::SpecSendRequestQuery::headers() const +{ + id const p = _v[@"headers"]; + return p; +} +inline NSString *JS::NativeNetworkingIOS::SpecSendRequestQuery::responseType() const +{ + id const p = _v[@"responseType"]; + return RCTBridgingToString(p); +} +inline bool JS::NativeNetworkingIOS::SpecSendRequestQuery::incrementalUpdates() const +{ + id const p = _v[@"incrementalUpdates"]; + return RCTBridgingToBool(p); +} +inline double JS::NativeNetworkingIOS::SpecSendRequestQuery::timeout() const +{ + id const p = _v[@"timeout"]; + return RCTBridgingToDouble(p); +} +inline bool JS::NativeNetworkingIOS::SpecSendRequestQuery::withCredentials() const +{ + id const p = _v[@"withCredentials"]; + return RCTBridgingToBool(p); +} +inline JS::NativePlatformConstantsIOS::ConstantsReactNativeVersion::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto major = i.major.get(); + d[@"major"] = @(major); + auto minor = i.minor.get(); + d[@"minor"] = @(minor); + auto patch = i.patch.get(); + d[@"patch"] = @(patch); + auto prerelease = i.prerelease.get(); + d[@"prerelease"] = prerelease.hasValue() ? @((double)prerelease.value()) : nil; + return d; +}) {} +inline JS::NativePlatformConstantsIOS::ConstantsReactNativeVersion::Builder::Builder(ConstantsReactNativeVersion i) : _factory(^{ + return i.unsafeRawValue(); +}) {} +inline JS::NativePlatformConstantsIOS::Constants::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto isTesting = i.isTesting.get(); + d[@"isTesting"] = @(isTesting); + auto reactNativeVersion = i.reactNativeVersion.get(); + d[@"reactNativeVersion"] = reactNativeVersion.buildUnsafeRawValue(); + auto forceTouchAvailable = i.forceTouchAvailable.get(); + d[@"forceTouchAvailable"] = @(forceTouchAvailable); + auto osVersion = i.osVersion.get(); + d[@"osVersion"] = osVersion; + auto systemName = i.systemName.get(); + d[@"systemName"] = systemName; + auto interfaceIdiom = i.interfaceIdiom.get(); + d[@"interfaceIdiom"] = interfaceIdiom; + return d; +}) {} +inline JS::NativePlatformConstantsIOS::Constants::Builder::Builder(Constants i) : _factory(^{ + return i.unsafeRawValue(); +}) {} +inline bool JS::NativePushNotificationManagerIOS::SpecRequestPermissionsPermission::alert() const +{ + id const p = _v[@"alert"]; + return RCTBridgingToBool(p); +} +inline bool JS::NativePushNotificationManagerIOS::SpecRequestPermissionsPermission::badge() const +{ + id const p = _v[@"badge"]; + return RCTBridgingToBool(p); +} +inline bool JS::NativePushNotificationManagerIOS::SpecRequestPermissionsPermission::sound() const +{ + id const p = _v[@"sound"]; + return RCTBridgingToBool(p); +} +inline NSString *JS::NativePushNotificationManagerIOS::Notification::alertTitle() const +{ + id const p = _v[@"alertTitle"]; + return RCTBridgingToOptionalString(p); +} +inline folly::Optional JS::NativePushNotificationManagerIOS::Notification::fireDate() const +{ + id const p = _v[@"fireDate"]; + return RCTBridgingToOptionalDouble(p); +} +inline NSString *JS::NativePushNotificationManagerIOS::Notification::alertBody() const +{ + id const p = _v[@"alertBody"]; + return RCTBridgingToOptionalString(p); +} +inline NSString *JS::NativePushNotificationManagerIOS::Notification::alertAction() const +{ + id const p = _v[@"alertAction"]; + return RCTBridgingToOptionalString(p); +} +inline id _Nullable JS::NativePushNotificationManagerIOS::Notification::userInfo() const +{ + id const p = _v[@"userInfo"]; + return p; +} +inline NSString *JS::NativePushNotificationManagerIOS::Notification::category() const +{ + id const p = _v[@"category"]; + return RCTBridgingToOptionalString(p); +} +inline NSString *JS::NativePushNotificationManagerIOS::Notification::repeatInterval() const +{ + id const p = _v[@"repeatInterval"]; + return RCTBridgingToOptionalString(p); +} +inline folly::Optional JS::NativePushNotificationManagerIOS::Notification::applicationIconBadgeNumber() const +{ + id const p = _v[@"applicationIconBadgeNumber"]; + return RCTBridgingToOptionalDouble(p); +} +inline folly::Optional JS::NativePushNotificationManagerIOS::Notification::isSilent() const +{ + id const p = _v[@"isSilent"]; + return RCTBridgingToOptionalBool(p); +} + + +inline JS::NativeSettingsManager::Constants::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto settings = i.settings.get(); + d[@"settings"] = settings; + return d; +}) {} +inline JS::NativeSettingsManager::Constants::Builder::Builder(Constants i) : _factory(^{ + return i.unsafeRawValue(); +}) {} +inline NSString *JS::NativeShareModule::SpecShareContent::title() const +{ + id const p = _v[@"title"]; + return RCTBridgingToOptionalString(p); +} +inline NSString *JS::NativeShareModule::SpecShareContent::message() const +{ + id const p = _v[@"message"]; + return RCTBridgingToOptionalString(p); +} + +inline JS::NativeSourceCode::Constants::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto scriptURL = i.scriptURL.get(); + d[@"scriptURL"] = scriptURL; + return d; +}) {} +inline JS::NativeSourceCode::Constants::Builder::Builder(Constants i) : _factory(^{ + return i.unsafeRawValue(); +}) {} +inline JS::NativeStatusBarManagerIOS::Constants::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto HEIGHT = i.HEIGHT.get(); + d[@"HEIGHT"] = @(HEIGHT); + auto DEFAULT_BACKGROUND_COLOR = i.DEFAULT_BACKGROUND_COLOR; + d[@"DEFAULT_BACKGROUND_COLOR"] = DEFAULT_BACKGROUND_COLOR.hasValue() ? @((double)DEFAULT_BACKGROUND_COLOR.value()) : nil; + return d; +}) {} +inline JS::NativeStatusBarManagerIOS::Constants::Builder::Builder(Constants i) : _factory(^{ + return i.unsafeRawValue(); +}) {} + + + +inline id _Nullable JS::NativeWebSocketModule::SpecConnectOptions::headers() const +{ + id const p = _v[@"headers"]; + return p; +} diff --git a/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.h b/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.h index 149a9788e2..e79a9605a6 100644 --- a/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.h +++ b/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.h @@ -15,6 +15,8 @@ @property (nonatomic, weak) id delegate; +#if !TARGET_OS_TV @property (nonatomic, assign) UIInterfaceOrientationMask supportedInterfaceOrientations; +#endif @end diff --git a/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.mm b/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.mm index f15c2ecafb..3c84047f23 100644 --- a/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.mm +++ b/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.mm @@ -24,7 +24,7 @@ - (instancetype)init #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \ __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 - if (@available(iOS 13.0, *)) { + if (@available(iOS 13.0, tvOS 13.0, *)) { self.modalInPresentation = YES; } #endif @@ -47,6 +47,7 @@ - (void)loadView [_touchHandler attachToView:self.view]; } +#if !TARGET_OS_TV - (UIStatusBarStyle)preferredStatusBarStyle { return [RCTSharedApplication() statusBarStyle]; @@ -81,5 +82,6 @@ - (UIInterfaceOrientationMask)supportedInterfaceOrientations return _supportedInterfaceOrientations; } #endif // RCT_DEV +#endif // !TARGET_OS_TV @end diff --git a/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.mm index 6cbe904eec..6ce20a0165 100644 --- a/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.mm @@ -21,6 +21,7 @@ using namespace facebook::react; +#if !TARGET_OS_TV static UIInterfaceOrientationMask supportedOrientationsMask(ModalHostViewSupportedOrientationsMask mask) { UIInterfaceOrientationMask supportedOrientations = 0; @@ -55,6 +56,7 @@ static UIInterfaceOrientationMask supportedOrientationsMask(ModalHostViewSupport return supportedOrientations; } +#endif static std::tuple animationConfiguration(ModalHostViewAnimationType const animation) { @@ -77,9 +79,17 @@ static UIModalPresentationStyle presentationConfiguration(ModalHostViewProps con case ModalHostViewPresentationStyle::FullScreen: return UIModalPresentationFullScreen; case ModalHostViewPresentationStyle::PageSheet: +#if TARGET_OS_TV + return UIModalPresentationOverFullScreen; +#else return UIModalPresentationPageSheet; +#endif case ModalHostViewPresentationStyle::FormSheet: +#if TARGET_OS_TV + return UIModalPresentationOverFullScreen; +#else return UIModalPresentationFormSheet; +#endif case ModalHostViewPresentationStyle::OverFullScreen: return UIModalPresentationOverFullScreen; } diff --git a/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.h b/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.h index a2cc0aa756..099dc90841 100644 --- a/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.h +++ b/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.h @@ -32,14 +32,20 @@ Class RCTFabricComponentsProvider(const char *name); // Lookup functions Class RCTSafeAreaViewCls(void) __attribute__((used)); Class RCTScrollViewCls(void) __attribute__((used)); +#if !TARGET_OS_TV Class RCTPullToRefreshViewCls(void) __attribute__((used)); +#endif Class RCTActivityIndicatorViewCls(void) __attribute__((used)); +#if !TARGET_OS_TV Class RCTSliderCls(void) __attribute__((used)); Class RCTSwitchCls(void) __attribute__((used)); +#endif Class RCTUnimplementedNativeViewCls(void) __attribute__((used)); Class RCTParagraphCls(void) __attribute__((used)); Class RCTTextInputCls(void) __attribute__((used)); +#if !TARGET_OS_TV Class RCTInputAccessoryCls(void) __attribute__((used)); +#endif Class RCTViewCls(void) __attribute__((used)); Class RCTImageCls(void) __attribute__((used)); diff --git a/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.mm b/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.mm index 84cbe8d55b..1d982b9117 100644 --- a/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.mm +++ b/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.mm @@ -20,14 +20,20 @@ static std::unordered_map sFabricComponentsClassMap = { {"SafeAreaView", RCTSafeAreaViewCls}, {"ScrollView", RCTScrollViewCls}, +#if !TARGET_OS_TV {"PullToRefreshView", RCTPullToRefreshViewCls}, +#endif {"ActivityIndicatorView", RCTActivityIndicatorViewCls}, +#if !TARGET_OS_TV {"Slider", RCTSliderCls}, {"Switch", RCTSwitchCls}, +#endif {"UnimplementedNativeView", RCTUnimplementedNativeViewCls}, {"Paragraph", RCTParagraphCls}, {"TextInput", RCTTextInputCls}, +#if !TARGET_OS_TV {"InputAccessoryView", RCTInputAccessoryCls}, +#endif {"View", RCTViewCls}, {"Image", RCTImageCls}, }; diff --git a/React/Fabric/Mounting/ComponentViews/Root/RCTRootComponentView.h b/React/Fabric/Mounting/ComponentViews/Root/RCTRootComponentView.h index cce48d6ffc..790739151c 100644 --- a/React/Fabric/Mounting/ComponentViews/Root/RCTRootComponentView.h +++ b/React/Fabric/Mounting/ComponentViews/Root/RCTRootComponentView.h @@ -9,6 +9,10 @@ #import +#if TARGET_OS_TV +#import "RCTTVRemoteHandler.h" +#endif + NS_ASSUME_NONNULL_BEGIN /** @@ -16,6 +20,11 @@ NS_ASSUME_NONNULL_BEGIN */ @interface RCTRootComponentView : RCTViewComponentView +#if TARGET_OS_TV +@property (nonatomic, strong, nullable) RCTTVRemoteHandler *tvRemoteHandler; +@property (nonatomic, weak, nullable) UIView *reactPreferredFocusedView; +#endif + @end NS_ASSUME_NONNULL_END diff --git a/React/Fabric/Mounting/ComponentViews/Root/RCTRootComponentView.mm b/React/Fabric/Mounting/ComponentViews/Root/RCTRootComponentView.mm index 4efa8b34b4..343348eff1 100644 --- a/React/Fabric/Mounting/ComponentViews/Root/RCTRootComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/Root/RCTRootComponentView.mm @@ -20,11 +20,29 @@ - (instancetype)initWithFrame:(CGRect)frame if (self = [super initWithFrame:frame]) { static const auto defaultProps = std::make_shared(); _props = defaultProps; +#if TARGET_OS_TV + self.tvRemoteHandler = [[RCTTVRemoteHandler alloc] initWithView:self]; +#endif } return self; } +#if TARGET_OS_TV +- (void)dealloc +{ + self.tvRemoteHandler = nil; +} + +- (NSArray> *)preferredFocusEnvironments { + if (self.reactPreferredFocusedView) { + return @[self.reactPreferredFocusedView]; + } + return [super preferredFocusEnvironments]; +} +#endif + + #pragma mark - RCTComponentViewProtocol + (ComponentDescriptorProvider)componentDescriptorProvider diff --git a/React/Fabric/Mounting/ComponentViews/SafeAreaView/RCTSafeAreaViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/SafeAreaView/RCTSafeAreaViewComponentView.mm index 31ae9ecd96..86c2f89829 100644 --- a/React/Fabric/Mounting/ComponentViews/SafeAreaView/RCTSafeAreaViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/SafeAreaView/RCTSafeAreaViewComponentView.mm @@ -32,7 +32,7 @@ - (instancetype)initWithFrame:(CGRect)frame - (UIEdgeInsets)_safeAreaInsets { - if (@available(iOS 11.0, *)) { + if (@available(iOS 11.0, tvOS 11.0, *)) { return self.safeAreaInsets; } diff --git a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm index 79f3cf315e..f6a6dcf532 100644 --- a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm @@ -190,9 +190,11 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & MAP_SCROLL_VIEW_PROP(maximumZoomScale); MAP_SCROLL_VIEW_PROP(minimumZoomScale); MAP_SCROLL_VIEW_PROP(scrollEnabled); +#if !TARGET_OS_TV MAP_SCROLL_VIEW_PROP(pagingEnabled); MAP_SCROLL_VIEW_PROP(pinchGestureEnabled); MAP_SCROLL_VIEW_PROP(scrollsToTop); +#endif MAP_SCROLL_VIEW_PROP(showsHorizontalScrollIndicator); MAP_SCROLL_VIEW_PROP(showsVerticalScrollIndicator); diff --git a/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm b/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm index 048d1b3dd9..0fe841eaef 100644 --- a/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm @@ -233,7 +233,7 @@ - (void)disableContextMenu - (void)handleLongPress:(UILongPressGestureRecognizer *)gesture { // TODO: Adopt showMenuFromRect (necessary for UIKitForMac) -#if !TARGET_OS_UIKITFORMAC +#if !TARGET_OS_UIKITFORMAC && !TARGET_OS_TV UIMenuController *menuController = [UIMenuController sharedMenuController]; if (menuController.isMenuVisible) { @@ -268,6 +268,7 @@ - (BOOL)canPerformAction:(SEL)action withSender:(id)sender - (void)copy:(id)sender { +#if !TARGET_OS_TV NSAttributedString *attributedText = self.attributedText; NSMutableDictionary *item = [NSMutableDictionary new]; @@ -284,6 +285,7 @@ - (void)copy:(id)sender UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; pasteboard.items = @[ item ]; +#endif } @end diff --git a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm index 1c3b916163..b4752c4000 100644 --- a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm @@ -486,6 +486,7 @@ - (void)setDefaultInputAccessoryView return; } +#if !TARGET_OS_TV if (shouldHaveInputAccesoryView) { UIToolbar *toolbarView = [UIToolbar new]; [toolbarView sizeToFit]; @@ -497,7 +498,9 @@ - (void)setDefaultInputAccessoryView action:@selector(handleInputAccessoryDoneButton)]; toolbarView.items = @[ flexibleSpace, doneButton ]; _backedTextInputView.inputAccessoryView = toolbarView; - } else { + } else +#endif + { _backedTextInputView.inputAccessoryView = nil; } diff --git a/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.h b/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.h index 48298f3fc9..19abcafa1c 100644 --- a/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.h +++ b/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.h @@ -69,6 +69,10 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, assign) UIEdgeInsets hitTestEdgeInsets; +#if TARGET_OS_TV +@property(nonatomic, nullable) UIFocusGuide *focusGuide; +#endif + /** * Flag indicating if subview clipping is enabled for the view. * If subview clipping is enabled, subviews that are outside of the viewport may be removed from the view hierachy. diff --git a/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index 5c25be6021..92fe9a11a2 100644 --- a/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -13,10 +13,26 @@ #import #import #import +#import +#import + +#import + #import #import #import +typedef struct { + BOOL enabled; + float shiftDistanceX; + float shiftDistanceY; + float tiltAngle; + float magnification; + float pressMagnification; + float pressDuration; + float pressDelay; +} ParallaxProperties; + using namespace facebook::react; @implementation RCTViewComponentView { @@ -26,7 +42,16 @@ @implementation RCTViewComponentView { BOOL _isJSResponder; BOOL _removeClippedSubviews; NSMutableArray *_reactSubviews; + BOOL _motionEffectsAdded; + UITapGestureRecognizer *_selectRecognizer; NSSet *_Nullable _propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN; + ParallaxProperties _tvParallaxProperties; + BOOL _hasTVPreferredFocus; + UIView *_nextFocusUp; + UIView *_nextFocusDown; + UIView *_nextFocusLeft; + UIView *_nextFocusRight; + UIView *_nextFocusActiveTarget; } @synthesize removeClippedSubviews = _removeClippedSubviews; @@ -37,7 +62,20 @@ - (instancetype)initWithFrame:(CGRect)frame static auto const defaultProps = std::make_shared(); _props = defaultProps; _reactSubviews = [NSMutableArray new]; +#if TARGET_OS_TV + _tvParallaxProperties.enabled = NO; + _tvParallaxProperties.shiftDistanceX = 0.0f; + _tvParallaxProperties.shiftDistanceY = 0.0f; + _tvParallaxProperties.tiltAngle = 0.0f; + _tvParallaxProperties.magnification = 0.0f; + _tvParallaxProperties.pressMagnification = 0.0f; + _tvParallaxProperties.pressDuration = 0.0f; + _tvParallaxProperties.pressDelay = 0.0f; + self.focusGuide = [UIFocusGuide new]; + [self addLayoutGuide:self.focusGuide]; +#else self.multipleTouchEnabled = YES; +#endif } return self; } @@ -60,7 +98,6 @@ - (void)setContentView:(UIView *)contentView _contentView.frame = RCTCGRectFromRect(_layoutMetrics.getContentFrame()); } } - - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { if (UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero)) { @@ -80,6 +117,290 @@ - (void)setBackgroundColor:(UIColor *)backgroundColor _backgroundColor = backgroundColor; } +#if TARGET_OS_TV + +#pragma mark - Apple TV methods + +- (RCTRootComponentView *)containingRootView +{ + UIView *rootview = self; + if ([rootview class] == [RCTRootComponentView class]) { + return (RCTRootComponentView *)rootview; + } + do { + rootview = [rootview superview]; + } while (([rootview class] != [RCTRootComponentView class]) && rootview != nil); + return (RCTRootComponentView *)rootview; +} + +- (void)addFocusDestinations:(NSArray*)destinations { + + self.focusGuide.preferredFocusEnvironments = destinations; +} + +- (void)sendFocusNotification:(__unused UIFocusUpdateContext *)context +{ + [self sendNotificationWithEventType:@"focus"]; +} + +- (void)sendBlurNotification:(__unused UIFocusUpdateContext *)context +{ + [self sendNotificationWithEventType:@"blur"]; +} + +- (void)sendSelectNotification:(UIGestureRecognizer *)recognizer +{ + [self sendNotificationWithEventType:@"select"]; +} + +- (void)sendNotificationWithEventType:(NSString * __nonnull)eventType +{ + [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTTVNavigationEventNotification" + object:@{ + @"eventType":eventType, + @"tag":@([self tag]), + @"target":@([self tag]) + }]; +} + +- (void)handleSelect:(__unused UIGestureRecognizer *)r +{ + if (_tvParallaxProperties.enabled == YES) { + float magnification = _tvParallaxProperties.magnification; + float pressMagnification = _tvParallaxProperties.pressMagnification; + + // Duration of press animation + float pressDuration = _tvParallaxProperties.pressDuration; + + // Delay of press animation + float pressDelay = _tvParallaxProperties.pressDelay; + + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:pressDelay]]; + + [UIView animateWithDuration:(pressDuration/2) + animations:^{ + self.transform = CGAffineTransformMakeScale(pressMagnification, pressMagnification); + } + completion:^(__unused BOOL finished1){ + [UIView animateWithDuration:(pressDuration/2) + animations:^{ + self.transform = CGAffineTransformMakeScale(magnification, magnification); + } + completion:^(__unused BOOL finished2) { + [self sendSelectNotification:r]; + }]; + }]; + + } else { + [self sendSelectNotification:r]; + } +} + +- (void)addParallaxMotionEffects +{ + if(!_tvParallaxProperties.enabled) { + return; + } + + if(_motionEffectsAdded == YES) { + return; + } + + // Size of shift movements + CGFloat const shiftDistanceX = _tvParallaxProperties.shiftDistanceX; + CGFloat const shiftDistanceY = _tvParallaxProperties.shiftDistanceY; + + // Make horizontal movements shift the centre left and right + UIInterpolatingMotionEffect *xShift = + [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" + type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis]; + xShift.minimumRelativeValue = @(shiftDistanceX * -1.0f); + xShift.maximumRelativeValue = @(shiftDistanceX); + + // Make vertical movements shift the centre up and down + UIInterpolatingMotionEffect *yShift = + [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" + type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis]; + yShift.minimumRelativeValue = @(shiftDistanceY * -1.0f); + yShift.maximumRelativeValue = @(shiftDistanceY); + + // Size of tilt movements + CGFloat const tiltAngle = _tvParallaxProperties.tiltAngle; + + // Now make horizontal movements effect a rotation about the Y axis for side-to-side rotation. + UIInterpolatingMotionEffect *xTilt = + [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"layer.transform" + type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis]; + + // CATransform3D value for minimumRelativeValue + CATransform3D transMinimumTiltAboutY = CATransform3DIdentity; + transMinimumTiltAboutY.m34 = 1.0 / 500; + transMinimumTiltAboutY = CATransform3DRotate(transMinimumTiltAboutY, tiltAngle * -1.0, 0, 1, 0); + + // CATransform3D value for minimumRelativeValue + CATransform3D transMaximumTiltAboutY = CATransform3DIdentity; + transMaximumTiltAboutY.m34 = 1.0 / 500; + transMaximumTiltAboutY = CATransform3DRotate(transMaximumTiltAboutY, tiltAngle, 0, 1, 0); + + // Set the transform property boundaries for the interpolation + xTilt.minimumRelativeValue = [NSValue valueWithCATransform3D:transMinimumTiltAboutY]; + xTilt.maximumRelativeValue = [NSValue valueWithCATransform3D:transMaximumTiltAboutY]; + + // Now make vertical movements effect a rotation about the X axis for up and down rotation. + UIInterpolatingMotionEffect *yTilt = + [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"layer.transform" + type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis]; + + // CATransform3D value for minimumRelativeValue + CATransform3D transMinimumTiltAboutX = CATransform3DIdentity; + transMinimumTiltAboutX.m34 = 1.0 / 500; + transMinimumTiltAboutX = CATransform3DRotate(transMinimumTiltAboutX, tiltAngle * -1.0, 1, 0, 0); + + // CATransform3D value for minimumRelativeValue + CATransform3D transMaximumTiltAboutX = CATransform3DIdentity; + transMaximumTiltAboutX.m34 = 1.0 / 500; + transMaximumTiltAboutX = CATransform3DRotate(transMaximumTiltAboutX, tiltAngle, 1, 0, 0); + + // Set the transform property boundaries for the interpolation + yTilt.minimumRelativeValue = [NSValue valueWithCATransform3D:transMinimumTiltAboutX]; + yTilt.maximumRelativeValue = [NSValue valueWithCATransform3D:transMaximumTiltAboutX]; + + // Add all of the motion effects to this group + self.motionEffects = @[ xShift, yShift, xTilt, yTilt ]; + + float magnification = _tvParallaxProperties.magnification; + + [UIView animateWithDuration:0.2 animations:^{ + self.transform = CGAffineTransformScale(self.transform, magnification, magnification); + }]; + + _motionEffectsAdded = YES; +} + +- (void)removeParallaxMotionEffects +{ + if(_motionEffectsAdded == NO) { + return; + } + + [UIView animateWithDuration:0.2 animations:^{ + float magnification = self->_tvParallaxProperties.magnification; + BOOL enabled = self->_tvParallaxProperties.enabled; + if (enabled && magnification) { + self.transform = CGAffineTransformScale(self.transform, 1.0/magnification, 1.0/magnification); + } + }]; + + for (UIMotionEffect *effect in [self.motionEffects copy]){ + [self removeMotionEffect:effect]; + } + + _motionEffectsAdded = NO; +} + + +- (BOOL)isUserInteractionEnabled +{ + return YES; +} + +- (BOOL)canBecomeFocused +{ + return _props->isTVSelectable; +} + +- (NSArray> *)preferredFocusEnvironments { + if (_nextFocusActiveTarget == nil) return [super preferredFocusEnvironments]; + UIView * nextFocusActiveTarget = _nextFocusActiveTarget; + _nextFocusActiveTarget = nil; + NSArray> * focusEnvironment = @[nextFocusActiveTarget]; + return focusEnvironment; +} + +- (BOOL) shouldUpdateFocusInContext:(UIFocusUpdateContext *)context { + if (self.isFocused) { + if (_nextFocusUp != nil && context.focusHeading == UIFocusHeadingUp) { + self->_nextFocusActiveTarget = _nextFocusUp; + [self setNeedsFocusUpdate]; + return false; + } + if (_nextFocusDown != nil && context.focusHeading == UIFocusHeadingDown) { + self->_nextFocusActiveTarget = _nextFocusDown; + [self setNeedsFocusUpdate]; + return false; + } + if (_nextFocusLeft != nil && context.focusHeading == UIFocusHeadingLeft) { + self->_nextFocusActiveTarget = _nextFocusLeft; + [self setNeedsFocusUpdate]; + return false; + } + if (_nextFocusRight != nil && context.focusHeading == UIFocusHeadingRight) { + self->_nextFocusActiveTarget = _nextFocusRight; + [self setNeedsFocusUpdate]; + return false; + } + self->_nextFocusActiveTarget = nil; + return true; + } + self->_nextFocusActiveTarget = nil; + return true; +} + +- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator +{ + if (context.previouslyFocusedView == context.nextFocusedView) { + return; + } + if (context.nextFocusedView == self && self.isUserInteractionEnabled ) { + [self becomeFirstResponder]; + [coordinator addCoordinatedAnimations:^(void){ + [self addParallaxMotionEffects]; + [self sendFocusNotification:context]; + } completion:^(void){}]; + } else { + [coordinator addCoordinatedAnimations:^(void){ + [self removeParallaxMotionEffects]; + [self sendBlurNotification:context]; + } completion:^(void){}]; + [self resignFirstResponder]; + } +} + +#endif + +#pragma mark - Native Commands + +- (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args +{ + NSLog(@"RCTViewComponentView handleCommand: %@", commandName); + if ([commandName isEqualToString:@"setDestinations"]) { +#if TARGET_OS_TV + if ([args count] != 1) { + RCTLogError( + @"%@ command %@ received %d arguments, expected %d.", @"View", commandName, (int)[args count], 1); + return; + } + if (!RCTValidateTypeOfViewCommandArgument(args[0], [NSArray class], @"number", @"View", commandName, @"1st")) { + return; + } + NSArray *destinationTags = (NSArray *)args[0]; + NSMutableArray *destinations = [NSMutableArray new]; + RCTRootComponentView *rootView = [self containingRootView]; + for (NSNumber *tag in destinationTags) { + UIView *view = [rootView viewWithTag:[tag intValue]]; + if (view != nil) { + [destinations addObject:view]; + } + } + [self addFocusDestinations:destinations]; + return; +#endif + } +#if RCT_DEBUG + RCTLogError(@"%@ received command %@, which is not a supported command.", @"View", commandName); +#endif +} + #pragma mark - RCTComponentViewProtocol + (ComponentDescriptorProvider)componentDescriptorProvider @@ -356,11 +677,87 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & self.accessibilityIdentifier = RCTNSStringFromString(newViewProps.testId); } +#if TARGET_OS_TV + // `isTVSelectable` + if (oldViewProps.isTVSelectable != newViewProps.isTVSelectable) { + if (newViewProps.isTVSelectable) { + UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self + action:@selector(handleSelect:)]; + recognizer.allowedPressTypes = @[ @(UIPressTypeSelect) ]; + _selectRecognizer = recognizer; + [self addGestureRecognizer:_selectRecognizer]; + } else { + if (_selectRecognizer) { + [self removeGestureRecognizer:_selectRecognizer]; + } + } + } + // `tvParallaxProperties + if (oldViewProps.tvParallaxProperties != newViewProps.tvParallaxProperties) { + _tvParallaxProperties.enabled = newViewProps.tvParallaxProperties.enabled.has_value() ? + newViewProps.tvParallaxProperties.enabled.value() : + newViewProps.isTVSelectable; + _tvParallaxProperties.shiftDistanceX = [self getFloat:newViewProps.tvParallaxProperties.shiftDistanceX orDefault:2.0]; + _tvParallaxProperties.shiftDistanceY = [self getFloat:newViewProps.tvParallaxProperties.shiftDistanceY orDefault:2.0]; + _tvParallaxProperties.tiltAngle = [self getFloat:newViewProps.tvParallaxProperties.tiltAngle orDefault:0.05]; + _tvParallaxProperties.magnification = [self getFloat:newViewProps.tvParallaxProperties.magnification orDefault:1.0]; + _tvParallaxProperties.pressMagnification = [self getFloat:newViewProps.tvParallaxProperties.pressMagnification orDefault:1.0]; + _tvParallaxProperties.pressDuration = [self getFloat:newViewProps.tvParallaxProperties.pressDuration orDefault:0.3]; + _tvParallaxProperties.pressDelay = [self getFloat:newViewProps.tvParallaxProperties.pressDelay orDefault:0.0]; + } + // `hasTVPreferredFocus + if (oldViewProps.hasTVPreferredFocus != newViewProps.hasTVPreferredFocus) { + _hasTVPreferredFocus = newViewProps.hasTVPreferredFocus; + } + // `nextFocusUp` + if (oldViewProps.nextFocusUp != newViewProps.nextFocusUp) { + if (newViewProps.nextFocusUp.has_value()) { + UIView *rootView = [self containingRootView]; + _nextFocusUp = [rootView viewWithTag:newViewProps.nextFocusUp.value()]; + } else { + _nextFocusUp = nil; + } + } + // `nextFocusDown` + if (oldViewProps.nextFocusDown != newViewProps.nextFocusDown) { + if (newViewProps.nextFocusDown.has_value()) { + UIView *rootView = [self containingRootView]; + _nextFocusDown = [rootView viewWithTag:newViewProps.nextFocusDown.value()]; + } else { + _nextFocusDown = nil; + } + } + // `nextFocusLeft` + if (oldViewProps.nextFocusLeft != newViewProps.nextFocusLeft) { + if (newViewProps.nextFocusLeft.has_value()) { + UIView *rootView = [self containingRootView]; + _nextFocusLeft = [rootView viewWithTag:newViewProps.nextFocusLeft.value()]; + } else { + _nextFocusLeft = nil; + } + } + // `nextFocusRight` + if (oldViewProps.nextFocusRight != newViewProps.nextFocusRight) { + if (newViewProps.nextFocusRight.has_value()) { + UIView *rootView = [self containingRootView]; + _nextFocusRight = [rootView viewWithTag:newViewProps.nextFocusRight.value()]; + } else { + _nextFocusRight = nil; + } + } +#endif + + _needsInvalidateLayer = _needsInvalidateLayer || needsInvalidateLayer; _props = std::static_pointer_cast(props); } +- (float)getFloat:(better::optional)property orDefault:(float)defaultValue +{ + return property.has_value() ? (float)property.value() : defaultValue; +} + - (void)updateEventEmitter:(EventEmitter::Shared const &)eventEmitter { assert(std::dynamic_pointer_cast(eventEmitter)); @@ -384,6 +781,25 @@ - (void)updateLayoutMetrics:(LayoutMetrics const &)layoutMetrics if (_contentView) { _contentView.frame = RCTCGRectFromRect(_layoutMetrics.getContentFrame()); } + +#if TARGET_OS_TV + [self.focusGuide.topAnchor constraintEqualToAnchor:self.topAnchor].active = YES; + [self.focusGuide.bottomAnchor constraintEqualToAnchor:self.bottomAnchor].active = YES; + [self.focusGuide.widthAnchor constraintEqualToAnchor:self.widthAnchor].active = YES; + [self.focusGuide.heightAnchor constraintEqualToAnchor:self.heightAnchor].active = YES; + + if (_hasTVPreferredFocus) { + RCTRootComponentView *rootview = [self containingRootView]; + if (rootview != nil && rootview.reactPreferredFocusedView != self) { + rootview.reactPreferredFocusedView = self; + [rootview setNeedsFocusUpdate]; + [rootview updateFocusIfNeeded]; + } + } + +#endif + + } - (BOOL)isJSResponder diff --git a/React/Fabric/Mounting/RCTComponentViewFactory.mm b/React/Fabric/Mounting/RCTComponentViewFactory.mm index cf5a918124..52e13aa630 100644 --- a/React/Fabric/Mounting/RCTComponentViewFactory.mm +++ b/React/Fabric/Mounting/RCTComponentViewFactory.mm @@ -30,6 +30,7 @@ #import "RCTFabricComponentsPlugins.h" #import "RCTImageComponentView.h" #import "RCTLegacyViewManagerInteropComponentView.h" +#import "RCTModalHostViewComponentView.h" #import "RCTMountingTransactionObserving.h" #import "RCTParagraphComponentView.h" #import "RCTRootComponentView.h" @@ -74,6 +75,7 @@ + (RCTComponentViewFactory *)currentComponentViewFactory [componentViewFactory registerComponentViewClass:[RCTViewComponentView class]]; [componentViewFactory registerComponentViewClass:[RCTParagraphComponentView class]]; [componentViewFactory registerComponentViewClass:[RCTTextInputComponentView class]]; + [componentViewFactory registerComponentViewClass:[RCTModalHostViewComponentView class]]; Class imageClass = RCTComponentViewClassWithName("Image"); [componentViewFactory registerComponentViewClass:imageClass]; diff --git a/React/Modules/RCTLayoutAnimation.m b/React/Modules/RCTLayoutAnimation.m index d88fa0fba6..b0c827d05c 100644 --- a/React/Modules/RCTLayoutAnimation.m +++ b/React/Modules/RCTLayoutAnimation.m @@ -39,6 +39,7 @@ static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnim // `UIKeyboardWillChangeFrameNotification`s. + (void)initializeStatics { +#if !TARGET_OS_TV static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [[NSNotificationCenter defaultCenter] addObserver:self @@ -46,12 +47,15 @@ + (void)initializeStatics name:UIKeyboardWillChangeFrameNotification object:nil]; }); +#endif } + (void)keyboardWillChangeFrame:(NSNotification *)notification { +#if !TARGET_OS_TV NSDictionary *userInfo = notification.userInfo; _currentKeyboardAnimationCurve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]; +#endif } - (instancetype)initWithDuration:(NSTimeInterval)duration diff --git a/React/Modules/RCTRedBoxExtraDataViewController.m b/React/Modules/RCTRedBoxExtraDataViewController.m index fb2b2e3384..fab6c4f379 100644 --- a/React/Modules/RCTRedBoxExtraDataViewController.m +++ b/React/Modules/RCTRedBoxExtraDataViewController.m @@ -33,8 +33,10 @@ - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSStr self.keyLabel.textColor = [UIColor whiteColor]; self.keyLabel.numberOfLines = 0; +#if !TARGET_OS_TV self.keyLabel.lineBreakMode = NSLineBreakByWordWrapping; self.keyLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:12.0f]; +#endif self.valueLabel = [UILabel new]; [self.contentView addSubview:self.valueLabel]; @@ -46,8 +48,10 @@ - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSStr self.valueLabel.textColor = [UIColor whiteColor]; self.valueLabel.numberOfLines = 0; +#if !TARGET_OS_TV self.valueLabel.lineBreakMode = NSLineBreakByWordWrapping; self.valueLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:12.0f]; +#endif } return self; } @@ -78,7 +82,9 @@ - (instancetype)init _tableView.dataSource = self; _tableView.backgroundColor = [UIColor clearColor]; _tableView.estimatedRowHeight = 200; +#if !TARGET_OS_TV _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; +#endif _tableView.rowHeight = UITableViewAutomaticDimension; _tableView.allowsSelection = NO; diff --git a/React/Modules/RCTTVMenuBridge.h b/React/Modules/RCTTVMenuBridge.h new file mode 100644 index 0000000000..0f4c1d0315 --- /dev/null +++ b/React/Modules/RCTTVMenuBridge.h @@ -0,0 +1,16 @@ +// +// RCTTVMenuBridge.h +// +// Created by Douglas Lowder on 6/4/19. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface RCTTVMenuBridge : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/React/Modules/RCTTVMenuBridge.m b/React/Modules/RCTTVMenuBridge.m new file mode 100644 index 0000000000..8468bf0872 --- /dev/null +++ b/React/Modules/RCTTVMenuBridge.m @@ -0,0 +1,27 @@ +// +// RCTTVMenuBridge.m +// +// Created by Douglas Lowder on 6/4/19. +// + +#import "RCTTVMenuBridge.h" +#import +#import + +@implementation RCTTVMenuBridge + +RCT_EXPORT_MODULE(); + +RCT_EXPORT_METHOD(enableTVMenuKey) +{ + [RCTTVRemoteHandler setUseMenuKey:YES]; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTTVEnableMenuKeyNotification object:nil]; +} + +RCT_EXPORT_METHOD(disableTVMenuKey) +{ + [RCTTVRemoteHandler setUseMenuKey:NO]; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTTVDisableMenuKeyNotification object:nil]; +} + +@end diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 995068e584..55d5de311a 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -184,10 +184,12 @@ - (void)setBridge:(RCTBridge *)bridge object:[self->_bridge moduleForName:@"AccessibilityManager" lazilyLoadIfNecessary:YES]]; }); +#if !TARGET_OS_TV [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(namedOrientationDidChange) name:UIDeviceOrientationDidChangeNotification object:nil]; +#endif [RCTLayoutAnimation initializeStatics]; } @@ -214,6 +216,7 @@ - (void)didReceiveNewContentSizeMultiplier }); } +#if !TARGET_OS_TV // Names and coordinate system from html5 spec: // https://developer.mozilla.org/en-US/docs/Web/API/Screen.orientation // https://developer.mozilla.org/en-US/docs/Web/API/Screen.lockOrientation @@ -266,6 +269,7 @@ - (void)namedOrientationDidChange body:orientationEvent]; #pragma clang diagnostic pop } +#endif - (dispatch_queue_t)methodQueue { diff --git a/React/Profiler/RCTProfile.m b/React/Profiler/RCTProfile.m index 75200bed74..041f19b260 100644 --- a/React/Profiler/RCTProfile.m +++ b/React/Profiler/RCTProfile.m @@ -393,6 +393,7 @@ + (void)toggle:(UIButton *)target RCTProfileEnd(RCTProfilingBridge(), ^(NSString *result) { NSString *outFile = [NSTemporaryDirectory() stringByAppendingString:@"tmp_trace.json"]; [result writeToFile:outFile atomically:YES encoding:NSUTF8StringEncoding error:nil]; +#if !TARGET_OS_TV UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[ [NSURL fileURLWithPath:outFile] ] applicationActivities:nil]; @@ -409,6 +410,7 @@ + (void)toggle:(UIButton *)target animated:YES completion:nil]; }); +#endif }); } else { RCTProfileInit(RCTProfilingBridge()); @@ -746,6 +748,7 @@ void RCTProfileSendResult(RCTBridge *bridge, NSString *route, NSData *data) NSString *message = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]; if (message.length) { +#if !TARGET_OS_TV dispatch_async(dispatch_get_main_queue(), ^{ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Profile" @@ -756,6 +759,7 @@ void RCTProfileSendResult(RCTBridge *bridge, NSString *route, NSData *data) handler:nil]]; [RCTPresentedViewController() presentViewController:alertController animated:YES completion:nil]; }); +#endif } } }]; diff --git a/React/React-RCTFabric.podspec b/React/React-RCTFabric.podspec index fb7c850c49..d60921a711 100644 --- a/React/React-RCTFabric.podspec +++ b/React/React-RCTFabric.podspec @@ -28,11 +28,14 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.source = source s.source_files = "Fabric/**/*.{c,h,m,mm,S,cpp}" s.exclude_files = "**/tests/*", "**/android/*", + s.tvos.exclude_files = "Fabric/**/RCTPullToRefreshViewComponentView*", + "Fabric/**/RCTSwitchComponentView*", + "Fabric/**/RCTSliderComponentView*" s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags s.header_dir = "React" s.framework = "JavaScriptCore" diff --git a/React/Views/RCTComponentData.m b/React/Views/RCTComponentData.m index 99807d4deb..2430997073 100644 --- a/React/Views/RCTComponentData.m +++ b/React/Views/RCTComponentData.m @@ -81,7 +81,9 @@ - (UIView *)createViewWithTag:(nullable NSNumber *)tag rootTag:(nullable NSNumbe UIView *view = [self.manager view]; view.reactTag = tag; view.rootTag = rootTag; +#if !TARGET_OS_TV view.multipleTouchEnabled = YES; +#endif view.userInteractionEnabled = YES; // required for touch handling view.layer.allowsGroupOpacity = YES; // required for touch handling return view; diff --git a/React/Views/RCTFont.mm b/React/Views/RCTFont.mm index 679f2cea43..61db3c8031 100644 --- a/React/Views/RCTFont.mm +++ b/React/Views/RCTFont.mm @@ -344,7 +344,11 @@ + (UIFont *)updateFont:(UIFont *)font isCondensed = isCondensedFont(font); } else { // Not a valid font or family +#if TARGET_OS_TV + RCTLogInfo(@"Unrecognized font family '%@'", familyName); +#else RCTLogError(@"Unrecognized font family '%@'", familyName); +#endif if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) { font = [UIFont systemFontOfSize:fontSize weight:fontWeight]; } else if (fontWeight > UIFontWeightRegular) { diff --git a/React/Views/RCTModalHostView.h b/React/Views/RCTModalHostView.h index 0246328f99..6798eeb8e8 100644 --- a/React/Views/RCTModalHostView.h +++ b/React/Views/RCTModalHostView.h @@ -13,6 +13,7 @@ @class RCTBridge; @class RCTModalHostViewController; +@class RCTTVRemoteHandler; @protocol RCTModalHostViewInteractor; @@ -39,6 +40,10 @@ // Fabric only @property (nonatomic, copy) RCTBubblingEventBlock onDismiss; +#if TARGET_OS_TV +@property (nonatomic, copy) RCTDirectEventBlock onRequestClose; +@property (nonatomic, strong) RCTTVRemoteHandler *tvRemoteHandler; +#endif - (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; diff --git a/React/Views/RCTModalHostView.m b/React/Views/RCTModalHostView.m index 65428a037d..42b576b456 100644 --- a/React/Views/RCTModalHostView.m +++ b/React/Views/RCTModalHostView.m @@ -16,6 +16,9 @@ #import "RCTUIManager.h" #import "RCTUtils.h" #import "UIView+React.h" +#if TARGET_OS_TV +#import "RCTTVRemoteHandler.h" +#endif @implementation RCTModalHostView { __weak RCTBridge *_bridge; @@ -23,8 +26,12 @@ @implementation RCTModalHostView { RCTModalHostViewController *_modalViewController; RCTTouchHandler *_touchHandler; UIView *_reactSubview; - UIInterfaceOrientation _lastKnownOrientation; RCTDirectEventBlock _onRequestClose; +#if TARGET_OS_TV + UITapGestureRecognizer *_menuButtonGestureRecognizer; +#else + UIInterfaceOrientation _lastKnownOrientation; +#endif } RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame) @@ -39,6 +46,12 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; _modalViewController.view = containerView; _touchHandler = [[RCTTouchHandler alloc] initWithBridge:bridge]; +#if TARGET_OS_TV + _menuButtonGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self + action:@selector(menuButtonPressed:)]; + _menuButtonGestureRecognizer.allowedPressTypes = @[ @(UIPressTypeMenu) ]; + self.tvRemoteHandler = [[RCTTVRemoteHandler alloc] initWithView:self]; +#endif _isPresented = NO; __weak typeof(self) weakSelf = self; @@ -50,17 +63,40 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge return self; } -- (void)notifyForBoundsChange:(CGRect)newBounds +#if TARGET_OS_TV +- (void)dealloc { - if (_reactSubview && _isPresented) { - [_bridge.uiManager setSize:newBounds.size forView:_reactSubview]; - [self notifyForOrientationChange]; + self.tvRemoteHandler = nil; +} + +- (void)menuButtonPressed:(__unused UIGestureRecognizer *)gestureRecognizer +{ + if (_onRequestClose) { + _onRequestClose(nil); } } +#endif - (void)setOnRequestClose:(RCTDirectEventBlock)onRequestClose { _onRequestClose = onRequestClose; +#if TARGET_OS_TV + if (_reactSubview) { + if (_onRequestClose && _menuButtonGestureRecognizer) { + [_reactSubview addGestureRecognizer:_menuButtonGestureRecognizer]; + } else { + [_reactSubview removeGestureRecognizer:_menuButtonGestureRecognizer]; + } + } +#endif +} + +- (void)notifyForBoundsChange:(CGRect)newBounds +{ + if (_reactSubview && _isPresented) { + [_bridge.uiManager setSize:newBounds.size forView:_reactSubview]; + [self notifyForOrientationChange]; + } } - (void)presentationControllerDidAttemptToDismiss:(UIPresentationController *)controller @@ -72,6 +108,7 @@ - (void)presentationControllerDidAttemptToDismiss:(UIPresentationController *)co - (void)notifyForOrientationChange { +#if !TARGET_OS_TV if (!_onOrientationChange) { return; } @@ -88,6 +125,7 @@ - (void)notifyForOrientationChange @"orientation" : isPortrait ? @"portrait" : @"landscape", }; _onOrientationChange(eventPayload); +#endif } - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex @@ -95,6 +133,11 @@ - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex RCTAssert(_reactSubview == nil, @"Modal view can only have one subview"); [super insertReactSubview:subview atIndex:atIndex]; [_touchHandler attachToView:subview]; +#if TARGET_OS_TV + if (_onRequestClose) { + [subview addGestureRecognizer:_menuButtonGestureRecognizer]; + } +#endif [_modalViewController.view insertSubview:subview atIndex:0]; _reactSubview = subview; @@ -106,6 +149,11 @@ - (void)removeReactSubview:(UIView *)subview // Superclass (category) removes the `subview` from actual `superview`. [super removeReactSubview:subview]; [_touchHandler detachFromView:subview]; +#if TARGET_OS_TV + if (_menuButtonGestureRecognizer) { + [subview removeGestureRecognizer:_menuButtonGestureRecognizer]; + } +#endif _reactSubview = nil; } @@ -172,7 +220,9 @@ - (void)ensurePresentedOnlyIfNeeded if (shouldBePresented) { RCTAssert(self.reactViewController, @"Can't present modal view controller without a presenting view controller"); +#if !TARGET_OS_TV _modalViewController.supportedInterfaceOrientations = [self supportedOrientationsMask]; +#endif if ([self.animationType isEqualToString:@"fade"]) { _modalViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; @@ -205,6 +255,7 @@ - (void)setTransparent:(BOOL)transparent transparent ? UIModalPresentationOverFullScreen : UIModalPresentationFullScreen; } +#if !TARGET_OS_TV - (UIInterfaceOrientationMask)supportedOrientationsMask { if (_supportedOrientations.count == 0) { @@ -231,5 +282,6 @@ - (UIInterfaceOrientationMask)supportedOrientationsMask } return supportedOrientations; } +#endif @end diff --git a/React/Views/RCTModalHostViewController.h b/React/Views/RCTModalHostViewController.h index b12b0f7fc1..a01e511098 100644 --- a/React/Views/RCTModalHostViewController.h +++ b/React/Views/RCTModalHostViewController.h @@ -11,6 +11,8 @@ @property (nonatomic, copy) void (^boundsDidChangeBlock)(CGRect newBounds); +#if !TARGET_OS_TV @property (nonatomic, assign) UIInterfaceOrientationMask supportedInterfaceOrientations; +#endif @end diff --git a/React/Views/RCTModalHostViewController.m b/React/Views/RCTModalHostViewController.m index c9f717e094..4e6aa569bc 100644 --- a/React/Views/RCTModalHostViewController.m +++ b/React/Views/RCTModalHostViewController.m @@ -12,8 +12,10 @@ @implementation RCTModalHostViewController { CGRect _lastViewFrame; +#if !TARGET_OS_TV UIStatusBarStyle _preferredStatusBarStyle; BOOL _preferredStatusBarHidden; +#endif } - (instancetype)init @@ -24,13 +26,15 @@ - (instancetype)init #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \ __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 - if (@available(iOS 13.0, *)) { + if (@available(iOS 13.0, tvOS 13.0, *)) { self.modalInPresentation = YES; } #endif +#if !TARGET_OS_TV _preferredStatusBarStyle = [RCTSharedApplication() statusBarStyle]; _preferredStatusBarHidden = [RCTSharedApplication() isStatusBarHidden]; +#endif return self; } @@ -45,6 +49,7 @@ - (void)viewDidLayoutSubviews } } +#if !TARGET_OS_TV - (UIStatusBarStyle)preferredStatusBarStyle { return _preferredStatusBarStyle; @@ -73,5 +78,6 @@ - (UIInterfaceOrientationMask)supportedInterfaceOrientations return _supportedInterfaceOrientations; } #endif // RCT_DEV +#endif // !TARGET_OS_TV @end diff --git a/React/Views/RCTModalHostViewManager.m b/React/Views/RCTModalHostViewManager.m index 7fab70ef63..ec4465b5d5 100644 --- a/React/Views/RCTModalHostViewManager.m +++ b/React/Views/RCTModalHostViewManager.m @@ -20,8 +20,10 @@ @implementation RCTConvert (RCTModalHostView) UIModalPresentationStyle, (@{ @"fullScreen" : @(UIModalPresentationFullScreen), +#if !TARGET_OS_TV @"pageSheet" : @(UIModalPresentationPageSheet), @"formSheet" : @(UIModalPresentationFormSheet), +#endif @"overFullScreen" : @(UIModalPresentationOverFullScreen), }), UIModalPresentationFullScreen, diff --git a/React/Views/RCTProgressViewManager.m b/React/Views/RCTProgressViewManager.m index 12299b5c53..90099f416e 100644 --- a/React/Views/RCTProgressViewManager.m +++ b/React/Views/RCTProgressViewManager.m @@ -15,7 +15,9 @@ @implementation RCTConvert (RCTProgressViewManager) UIProgressViewStyle, (@{ @"default" : @(UIProgressViewStyleDefault), +#if !TARGET_OS_TV @"bar" : @(UIProgressViewStyleBar), +#endif }), UIProgressViewStyleDefault, integerValue) diff --git a/React/Views/RCTSegmentedControl.m b/React/Views/RCTSegmentedControl.m index 254b8ce6d5..b9b071e185 100644 --- a/React/Views/RCTSegmentedControl.m +++ b/React/Views/RCTSegmentedControl.m @@ -41,7 +41,7 @@ - (void)setBackgroundColor:(UIColor *)backgroundColor { #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \ __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 - if (@available(iOS 13.0, *)) { + if (@available(iOS 13.0, tvOS 13.0, *)) { [super setBackgroundColor:backgroundColor]; } #endif @@ -51,7 +51,7 @@ - (void)setTextColor:(UIColor *)textColor { #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \ __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 - if (@available(iOS 13.0, *)) { + if (@available(iOS 13.0, tvOS 13.0, *)) { [self setTitleTextAttributes:@{NSForegroundColorAttributeName : textColor} forState:UIControlStateNormal]; } #endif @@ -62,7 +62,7 @@ - (void)setTintColor:(UIColor *)tintColor [super setTintColor:tintColor]; #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \ __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 - if (@available(iOS 13.0, *)) { + if (@available(iOS 13.0, tvOS 13.0, *)) { [self setSelectedSegmentTintColor:tintColor]; [self setTitleTextAttributes:@{NSForegroundColorAttributeName : [UIColor whiteColor]} forState:UIControlStateSelected]; diff --git a/React/Views/RCTTVView.h b/React/Views/RCTTVView.h new file mode 100644 index 0000000000..3e805564ea --- /dev/null +++ b/React/Views/RCTTVView.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import +#import + +#import +#import + +// A RCTView with additional properties and methods for user interaction using the Apple TV focus engine. +@interface RCTTVView : RCTView + +/** + * TV event handlers + */ +@property (nonatomic, assign) BOOL isTVSelectable; // True if this view is TV-focusable + +/** + * Properties for Apple TV focus parallax effects + */ +@property (nonatomic, copy) NSDictionary *tvParallaxProperties; + +/** + * TV preferred focus + */ +@property (nonatomic, assign) BOOL hasTVPreferredFocus; + +/** + * Focus direction tags + */ +@property (nonatomic, strong) RCTTVView * nextFocusUp; +@property (nonatomic, strong) RCTTVView * nextFocusDown; +@property (nonatomic, strong) RCTTVView * nextFocusLeft; +@property (nonatomic, strong) RCTTVView * nextFocusRight; +@property (nonatomic, strong) RCTTVView * nextFocusActiveTarget; + +- (instancetype)initWithBridge:(RCTBridge *)bridge; + +/** + * Send Focus Notifications to listeners + */ +- (void)sendFocusNotification:(UIFocusUpdateContext *)context; + +/** + * Send Blur Notifications to listeners + */ +- (void)sendBlurNotification:(UIFocusUpdateContext *)context; + +/** + * Send Select Notification to listeners + */ +- (void)sendSelectNotification:(UIGestureRecognizer *)recognizer; + +/** + * Adds Parallax Motion Effects if tvParallaxProperty is enabled + */ +- (void)addParallaxMotionEffects; + +/** + * Removes Parallax Motion Effects if tvParallaxProperty is enabled + */ +- (void)removeParallaxMotionEffects; + + +- (void)addFocusGuide:(NSArray*)destinations; + +@property (nonatomic, strong) UIFocusGuide * focusGuide; + +@end diff --git a/React/Views/RCTTVView.m b/React/Views/RCTTVView.m new file mode 100644 index 0000000000..6a72a748ae --- /dev/null +++ b/React/Views/RCTTVView.m @@ -0,0 +1,394 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTTVView.h" + +#import "RCTAutoInsetsProtocol.h" +#import "RCTBorderDrawing.h" +#import "RCTBridge.h" +#import "RCTConvert.h" +#import "RCTEventDispatcher.h" +#import "RCTLog.h" +#import "RCTRootViewInternal.h" +#import "RCTUtils.h" +#import "RCTView.h" +#import "UIView+React.h" +#import + +@implementation RCTTVView { + __weak RCTBridge *_bridge; + UITapGestureRecognizer *_selectRecognizer; + BOOL motionEffectsAdded; +} + +- (instancetype)initWithBridge:(RCTBridge *)bridge { + if (self = [super init]) { + _bridge = bridge; + dispatch_once(&onceToken, ^{ + defaultTVParallaxProperties = @{ + @"enabled" : @YES, + @"shiftDistanceX" : @2.0f, + @"shiftDistanceY" : @2.0f, + @"tiltAngle" : @0.05f, + @"magnification" : @1.0f, + @"pressMagnification" : @1.0f, + @"pressDuration" : @0.3f, + @"pressDelay" : @0.0f + }; + }); + self.tvParallaxProperties = defaultTVParallaxProperties; + motionEffectsAdded = NO; + } + + return self; +} + +static NSDictionary *defaultTVParallaxProperties = nil; +static dispatch_once_t onceToken; + +- (void)setTvParallaxProperties:(NSDictionary *)tvParallaxProperties +{ + if (_tvParallaxProperties == nil) { + _tvParallaxProperties = [defaultTVParallaxProperties copy]; + return; + } + + NSMutableDictionary *newParallaxProperties = [NSMutableDictionary dictionaryWithDictionary:_tvParallaxProperties]; + for (NSString *k in [defaultTVParallaxProperties allKeys]) { + if (tvParallaxProperties[k]) { + newParallaxProperties[k] = tvParallaxProperties[k]; + } + } + _tvParallaxProperties = [newParallaxProperties copy]; +} + +RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused) + +- (void)setIsTVSelectable:(BOOL)isTVSelectable +{ + self->_isTVSelectable = isTVSelectable; + if (isTVSelectable) { + UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self + action:@selector(handleSelect:)]; + recognizer.allowedPressTypes = @[ @(UIPressTypeSelect) ]; + _selectRecognizer = recognizer; + [self addGestureRecognizer:_selectRecognizer]; + } else { + if (_selectRecognizer) { + [self removeGestureRecognizer:_selectRecognizer]; + } + } +} + +- (void)handleSelect:(__unused UIGestureRecognizer *)r +{ + if ([self.tvParallaxProperties[@"enabled"] boolValue] == YES) { + float magnification = [self.tvParallaxProperties[@"magnification"] floatValue]; + float pressMagnification = [self.tvParallaxProperties[@"pressMagnification"] floatValue]; + + // Duration of press animation + float pressDuration = [self.tvParallaxProperties[@"pressDuration"] floatValue]; + + // Delay of press animation + float pressDelay = [self.tvParallaxProperties[@"pressDelay"] floatValue]; + + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:pressDelay]]; + + [UIView animateWithDuration:(pressDuration/2) + animations:^{ + self.transform = CGAffineTransformMakeScale(pressMagnification, pressMagnification); + } + completion:^(__unused BOOL finished1){ + [UIView animateWithDuration:(pressDuration/2) + animations:^{ + self.transform = CGAffineTransformMakeScale(magnification, magnification); + } + completion:^(__unused BOOL finished2) { + [self sendSelectNotification:r]; + }]; + }]; + + } else { + [self sendSelectNotification:r]; + } +} + +- (BOOL)isUserInteractionEnabled +{ + return YES; +} + +- (BOOL)canBecomeFocused +{ + return (self.isTVSelectable); +} + +- (void)addParallaxMotionEffects +{ + if(![self.tvParallaxProperties[@"enabled"] boolValue]) { + return; + } + + if(motionEffectsAdded == YES) { + return; + } + + // Size of shift movements + CGFloat const shiftDistanceX = [self.tvParallaxProperties[@"shiftDistanceX"] floatValue]; + CGFloat const shiftDistanceY = [self.tvParallaxProperties[@"shiftDistanceY"] floatValue]; + + // Make horizontal movements shift the centre left and right + UIInterpolatingMotionEffect *xShift = + [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" + type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis]; + xShift.minimumRelativeValue = @(shiftDistanceX * -1.0f); + xShift.maximumRelativeValue = @(shiftDistanceX); + + // Make vertical movements shift the centre up and down + UIInterpolatingMotionEffect *yShift = + [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" + type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis]; + yShift.minimumRelativeValue = @(shiftDistanceY * -1.0f); + yShift.maximumRelativeValue = @(shiftDistanceY); + + // Size of tilt movements + CGFloat const tiltAngle = [self.tvParallaxProperties[@"tiltAngle"] floatValue]; + + // Now make horizontal movements effect a rotation about the Y axis for side-to-side rotation. + UIInterpolatingMotionEffect *xTilt = + [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"layer.transform" + type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis]; + + // CATransform3D value for minimumRelativeValue + CATransform3D transMinimumTiltAboutY = CATransform3DIdentity; + transMinimumTiltAboutY.m34 = 1.0 / 500; + transMinimumTiltAboutY = CATransform3DRotate(transMinimumTiltAboutY, tiltAngle * -1.0, 0, 1, 0); + + // CATransform3D value for minimumRelativeValue + CATransform3D transMaximumTiltAboutY = CATransform3DIdentity; + transMaximumTiltAboutY.m34 = 1.0 / 500; + transMaximumTiltAboutY = CATransform3DRotate(transMaximumTiltAboutY, tiltAngle, 0, 1, 0); + + // Set the transform property boundaries for the interpolation + xTilt.minimumRelativeValue = [NSValue valueWithCATransform3D:transMinimumTiltAboutY]; + xTilt.maximumRelativeValue = [NSValue valueWithCATransform3D:transMaximumTiltAboutY]; + + // Now make vertical movements effect a rotation about the X axis for up and down rotation. + UIInterpolatingMotionEffect *yTilt = + [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"layer.transform" + type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis]; + + // CATransform3D value for minimumRelativeValue + CATransform3D transMinimumTiltAboutX = CATransform3DIdentity; + transMinimumTiltAboutX.m34 = 1.0 / 500; + transMinimumTiltAboutX = CATransform3DRotate(transMinimumTiltAboutX, tiltAngle * -1.0, 1, 0, 0); + + // CATransform3D value for minimumRelativeValue + CATransform3D transMaximumTiltAboutX = CATransform3DIdentity; + transMaximumTiltAboutX.m34 = 1.0 / 500; + transMaximumTiltAboutX = CATransform3DRotate(transMaximumTiltAboutX, tiltAngle, 1, 0, 0); + + // Set the transform property boundaries for the interpolation + yTilt.minimumRelativeValue = [NSValue valueWithCATransform3D:transMinimumTiltAboutX]; + yTilt.maximumRelativeValue = [NSValue valueWithCATransform3D:transMaximumTiltAboutX]; + + // Add all of the motion effects to this group + self.motionEffects = @[ xShift, yShift, xTilt, yTilt ]; + + float magnification = [self.tvParallaxProperties[@"magnification"] floatValue]; + + [UIView animateWithDuration:0.2 animations:^{ + self.transform = CGAffineTransformScale(self.transform, magnification, magnification); + }]; + + motionEffectsAdded = YES; +} + +- (void)removeParallaxMotionEffects +{ + if(motionEffectsAdded == NO) { + return; + } + + [UIView animateWithDuration:0.2 animations:^{ + float magnification = [self.tvParallaxProperties[@"magnification"] floatValue]; + BOOL enabled = [self.tvParallaxProperties[@"enabled"] boolValue]; + if (enabled && magnification) { + self.transform = CGAffineTransformScale(self.transform, 1.0/magnification, 1.0/magnification); + } + }]; + + for (UIMotionEffect *effect in [self.motionEffects copy]){ + [self removeMotionEffect:effect]; + } + + motionEffectsAdded = NO; +} + +- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context + withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator +{ + if (context.previouslyFocusedView == context.nextFocusedView) { + return; + } + if (context.nextFocusedView == self && self.isTVSelectable ) { + [self becomeFirstResponder]; + [coordinator addCoordinatedAnimations:^(void){ + [self addParallaxMotionEffects]; + [self sendFocusNotification:context]; + } completion:^(void){}]; + } else { + [coordinator addCoordinatedAnimations:^(void){ + [self sendBlurNotification:context]; + [self removeParallaxMotionEffects]; + } completion:^(void){}]; + [self resignFirstResponder]; + } +} + +- (BOOL) shouldUpdateFocusInContext:(UIFocusUpdateContext *)context { + if (self.isFocused) { + if (_nextFocusUp != nil && context.focusHeading == UIFocusHeadingUp) { + self->_nextFocusActiveTarget = _nextFocusUp; + [self setNeedsFocusUpdate]; + return false; + } + if (_nextFocusDown != nil && context.focusHeading == UIFocusHeadingDown) { + self->_nextFocusActiveTarget = _nextFocusDown; + [self setNeedsFocusUpdate]; + return false; + } + if (_nextFocusLeft != nil && context.focusHeading == UIFocusHeadingLeft) { + self->_nextFocusActiveTarget = _nextFocusLeft; + [self setNeedsFocusUpdate]; + return false; + } + if (_nextFocusRight != nil && context.focusHeading == UIFocusHeadingRight) { + self->_nextFocusActiveTarget = _nextFocusRight; + [self setNeedsFocusUpdate]; + return false; + } + self->_nextFocusActiveTarget = nil; + return true; + } + self->_nextFocusActiveTarget = nil; + return true; +} + +- (NSArray> *)preferredFocusEnvironments { + if (_nextFocusActiveTarget == nil) return [super preferredFocusEnvironments]; + RCTTVView * nextFocusActiveTarget = _nextFocusActiveTarget; + _nextFocusActiveTarget = nil; + NSArray> * focusEnvironment = @[nextFocusActiveTarget]; + return focusEnvironment; +} + +- (void)sendFocusNotification:(__unused UIFocusUpdateContext *)context +{ + [self sendNotificationWithEventType:@"focus"]; +} + +- (void)sendBlurNotification:(__unused UIFocusUpdateContext *)context +{ + [self sendNotificationWithEventType:@"blur"]; +} + +- (void)sendSelectNotification:(UIGestureRecognizer *)recognizer +{ + [self sendNotificationWithEventType:@"select"]; +} + +- (void)sendNotificationWithEventType:(NSString * __nonnull)eventType +{ + [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTTVNavigationEventNotification" + object:@{ + @"eventType":eventType, + @"tag":self.reactTag, + @"target":self.reactTag + }]; +} + +- (RCTTVView *)getViewById:(NSNumber *)viewId { + if (viewId == nil) return nil; + NSDictionary *views = [_bridge.uiManager valueForKey:@"viewRegistry"]; + return (RCTTVView*)views[viewId]; +} + +- (void)setNextFocusUp:(NSNumber *)nextFocusUp { + self->_nextFocusUp = [self getViewById: nextFocusUp]; +} + +- (void)setNextFocusDown:(NSNumber *)nextFocusDown { + self->_nextFocusDown = [self getViewById: nextFocusDown]; +} + +- (void)setNextFocusLeft:(NSNumber *)nextFocusLeft { + self->_nextFocusLeft = [self getViewById: nextFocusLeft]; +} + +- (void)setNextFocusRight:(NSNumber *)nextFocusRight { + self->_nextFocusRight = [self getViewById: nextFocusRight]; +} + +- (void)setPreferredFocus:(BOOL)hasTVPreferredFocus +{ + _hasTVPreferredFocus = hasTVPreferredFocus; + if (hasTVPreferredFocus) { + dispatch_async(dispatch_get_main_queue(), ^{ + UIView *rootview = self; + while (![rootview isReactRootView] && rootview != nil) { + rootview = [rootview superview]; + } + if (rootview == nil) return; + + rootview = [rootview superview]; + + [(RCTRootView *)rootview setReactPreferredFocusedView:self]; + [rootview setNeedsFocusUpdate]; + [rootview updateFocusIfNeeded]; + }); + } +} + +- (void)setHasTVPreferredFocus:(BOOL)hasTVPreferredFocus +{ + _hasTVPreferredFocus = hasTVPreferredFocus; + if (hasTVPreferredFocus) { + dispatch_async(dispatch_get_main_queue(), ^{ + UIView *rootview = self; + while (![rootview isReactRootView] && rootview != nil) { + rootview = [rootview superview]; + } + if (rootview == nil) + return; + + rootview = [rootview superview]; + + [(RCTRootView *)rootview setReactPreferredFocusedView:self]; + [rootview setNeedsFocusUpdate]; + [rootview updateFocusIfNeeded]; + }); + } +} + +- (void)addFocusGuide:(NSArray*)destinations { + + UIView *origin = [self reactSuperview]; + if (self.focusGuide == nil && origin != nil) { + self.focusGuide = [UIFocusGuide new]; + [self addLayoutGuide:self.focusGuide]; + + [self.focusGuide.widthAnchor constraintEqualToAnchor:origin.widthAnchor].active = YES; + [self.focusGuide.heightAnchor constraintEqualToAnchor:origin.heightAnchor].active = YES; + [self.focusGuide.topAnchor constraintEqualToAnchor:origin.topAnchor].active = YES; + [self.focusGuide.leftAnchor constraintEqualToAnchor:origin.leftAnchor].active = YES; + } + + self.focusGuide.preferredFocusEnvironments = destinations; +} + +@end diff --git a/React/Views/RCTTabBar.h b/React/Views/RCTTabBar.h new file mode 100644 index 0000000000..a2a7e62eb8 --- /dev/null +++ b/React/Views/RCTTabBar.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import + +@interface RCTTabBar : UIView + +@property (nonatomic, strong) UIColor *unselectedTintColor; +@property (nonatomic, strong) UIColor *tintColor; +@property (nonatomic, strong) UIColor *barTintColor; +@property (nonatomic, assign) BOOL translucent; +#if !TARGET_OS_TV +@property (nonatomic, assign) UIBarStyle barStyle; +#endif + +- (void)uiManagerDidPerformMounting; + +@end diff --git a/React/Views/RCTTabBar.m b/React/Views/RCTTabBar.m new file mode 100644 index 0000000000..f1f8f4de00 --- /dev/null +++ b/React/Views/RCTTabBar.m @@ -0,0 +1,237 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTTabBar.h" + +#import "RCTEventDispatcher.h" +#import "RCTLog.h" +#import "RCTTabBarItem.h" +#import "RCTUtils.h" +#import "RCTView.h" +#import "RCTWrapperViewController.h" +#import "UIView+React.h" + +@interface RCTTabBar() + +@end + +@implementation RCTTabBar +{ + BOOL _tabsChanged; + UITabBarController *_tabController; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + if ((self = [super initWithFrame:frame])) { + _tabController = [UITabBarController new]; + _tabController.delegate = self; + [self addSubview:_tabController.view]; + } + return self; +} + +RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) + +- (UIViewController *)reactViewController +{ + return _tabController; +} + +- (void)dealloc +{ + _tabController.delegate = nil; + [_tabController removeFromParentViewController]; +} + +- (void)insertReactSubview:(RCTTabBarItem *)subview atIndex:(NSInteger)atIndex +{ + if (![subview isKindOfClass:[RCTTabBarItem class]]) { + RCTLogError(@"subview should be of type RCTTabBarItem"); + return; + } + [super insertReactSubview:subview atIndex:atIndex]; + _tabsChanged = YES; +} + +- (void)removeReactSubview:(RCTTabBarItem *)subview +{ + if (self.reactSubviews.count == 0) { + RCTLogError(@"should have at least one view to remove a subview"); + return; + } + [super removeReactSubview:subview]; + _tabsChanged = YES; +} + +- (void)didUpdateReactSubviews +{ + // Do nothing, as subviews are managed by `uiManagerDidPerformMounting` +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + [self reactAddControllerToClosestParent:_tabController]; + _tabController.view.frame = self.bounds; +} + +- (void)uiManagerDidPerformMounting +{ + // we can't hook up the VC hierarchy in 'init' because the subviews aren't + // hooked up yet, so we do it on demand here whenever a transaction has finished + [self reactAddControllerToClosestParent:_tabController]; + + if (_tabsChanged) { + + NSMutableArray *viewControllers = [NSMutableArray array]; + for (RCTTabBarItem *tab in [self reactSubviews]) { + UIViewController *controller = tab.reactViewController; + if (!controller) { + controller = [[RCTWrapperViewController alloc] initWithContentView:tab]; + } + [viewControllers addObject:controller]; + } + + _tabController.viewControllers = viewControllers; + _tabsChanged = NO; + } + + [self.reactSubviews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger index, __unused BOOL *stop) { + + RCTTabBarItem *tab = (RCTTabBarItem *)view; + UIViewController *controller = self->_tabController.viewControllers[index]; + if (self->_unselectedTintColor) { + [tab.barItem setTitleTextAttributes:@{NSForegroundColorAttributeName: self->_unselectedTintColor} forState:UIControlStateNormal]; + } + + [tab.barItem setTitleTextAttributes:@{NSForegroundColorAttributeName: self.tintColor} forState:UIControlStateSelected]; + + controller.tabBarItem = tab.barItem; +#if TARGET_OS_TV +// On Apple TV, disable JS control of selection after initial render + if (tab.selected && !tab.wasSelectedInJS) { + self->_tabController.selectedViewController = controller; + } + tab.wasSelectedInJS = YES; +#else + if (tab.selected) { + self->_tabController.selectedViewController = controller; + } +#endif + }]; +} + +- (UIColor *)barTintColor +{ + return _tabController.tabBar.barTintColor; +} + +- (void)setBarTintColor:(UIColor *)barTintColor +{ + _tabController.tabBar.barTintColor = barTintColor; +} + +- (UIColor *)tintColor +{ + return _tabController.tabBar.tintColor; +} + +- (void)setTintColor:(UIColor *)tintColor +{ + _tabController.tabBar.tintColor = tintColor; +} + +- (BOOL)translucent +{ + return _tabController.tabBar.isTranslucent; +} + +- (void)setTranslucent:(BOOL)translucent +{ + _tabController.tabBar.translucent = translucent; +} + +#if !TARGET_OS_TV +- (UIBarStyle)barStyle +{ + return _tabController.tabBar.barStyle; +} + +- (void)setBarStyle:(UIBarStyle)barStyle +{ + _tabController.tabBar.barStyle = barStyle; +} +#endif + +- (void)setUnselectedItemTintColor:(UIColor *)unselectedItemTintColor { +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + if ([_tabController.tabBar respondsToSelector:@selector(unselectedItemTintColor)]) { + _tabController.tabBar.unselectedItemTintColor = unselectedItemTintColor; + } +#endif +} + +- (UITabBarItemPositioning)itemPositioning +{ +#if TARGET_OS_TV + return 0; +#else + return _tabController.tabBar.itemPositioning; +#endif +} + +- (void)setItemPositioning:(UITabBarItemPositioning)itemPositioning +{ +#if !TARGET_OS_TV + _tabController.tabBar.itemPositioning = itemPositioning; +#endif +} + +#pragma mark - UITabBarControllerDelegate + +#if TARGET_OS_TV + +- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(nonnull UIViewController *)viewController +{ + NSUInteger index = [tabBarController.viewControllers indexOfObject:viewController]; + RCTTabBarItem *tab = (RCTTabBarItem *)self.reactSubviews[index]; + if (tab.onPress) tab.onPress(nil); + return; +} + +#else + +- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController +{ + NSUInteger index = [tabBarController.viewControllers indexOfObject:viewController]; + RCTTabBarItem *tab = (RCTTabBarItem *)self.reactSubviews[index]; + if (tab.onPress) tab.onPress(nil); + return NO; +} + +#endif + +#if TARGET_OS_TV + +- (BOOL)isUserInteractionEnabled +{ + return YES; +} + +- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator +{ + if (context.nextFocusedView == self) { + [self becomeFirstResponder]; + } else { + [self resignFirstResponder]; + } +} + +#endif + +@end diff --git a/React/Views/RCTTabBarItem.h b/React/Views/RCTTabBarItem.h new file mode 100644 index 0000000000..431108066a --- /dev/null +++ b/React/Views/RCTTabBarItem.h @@ -0,0 +1,35 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import + +#import +#import + +@interface RCTConvert (UITabBarSystemItem) + ++ (UITabBarSystemItem)UITabBarSystemItem:(id)json; + +@end + +@interface RCTTabBarItem : UIView + +@property (nonatomic, copy) id /* NSString or NSNumber */ badge; +@property (nonatomic, strong) UIImage *icon; +@property (nonatomic, strong) UIImage *selectedIcon; +@property (nonatomic, assign) UITabBarSystemItem systemIcon; +@property (nonatomic, assign) BOOL renderAsOriginal; +@property (nonatomic, assign, getter=isSelected) BOOL selected; +@property (nonatomic, readonly) UITabBarItem *barItem; +@property (nonatomic, copy) RCTBubblingEventBlock onPress; +@property (nonatomic, strong) NSString *testID; + +#if TARGET_OS_TV +@property (nonatomic, assign) BOOL wasSelectedInJS; +#endif + +@end diff --git a/React/Views/RCTTabBarItem.m b/React/Views/RCTTabBarItem.m new file mode 100644 index 0000000000..b7bdfc7f8f --- /dev/null +++ b/React/Views/RCTTabBarItem.m @@ -0,0 +1,139 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTTabBarItem.h" + +#import "RCTConvert.h" +#import "RCTLog.h" +#import "UIView+React.h" + +@implementation RCTConvert (UITabBarSystemItem) + +RCT_ENUM_CONVERTER(UITabBarSystemItem, (@{ + @"bookmarks": @(UITabBarSystemItemBookmarks), + @"contacts": @(UITabBarSystemItemContacts), + @"downloads": @(UITabBarSystemItemDownloads), + @"favorites": @(UITabBarSystemItemFavorites), + @"featured": @(UITabBarSystemItemFeatured), + @"history": @(UITabBarSystemItemHistory), + @"more": @(UITabBarSystemItemMore), + @"most-recent": @(UITabBarSystemItemMostRecent), + @"most-viewed": @(UITabBarSystemItemMostViewed), + @"recents": @(UITabBarSystemItemRecents), + @"search": @(UITabBarSystemItemSearch), + @"top-rated": @(UITabBarSystemItemTopRated), +}), NSNotFound, integerValue) + +@end + +@implementation RCTTabBarItem{ + UITapGestureRecognizer *_selectRecognizer; +} + +@synthesize barItem = _barItem; + +- (instancetype)initWithFrame:(CGRect)frame +{ + if ((self = [super initWithFrame:frame])) { + _systemIcon = NSNotFound; +#if TARGET_OS_TV + _wasSelectedInJS = NO; +#endif + } + return self; +} + +- (UITabBarItem *)barItem +{ + if (!_barItem) { + _barItem = [UITabBarItem new]; + _systemIcon = NSNotFound; + } + return _barItem; +} + +- (void)setTestID:(NSString *)testID +{ + self.barItem.accessibilityIdentifier = testID; +} + +- (void)setBadge:(id)badge +{ + _badge = [badge copy]; + self.barItem.badgeValue = [badge description]; +} + +- (void)setSystemIcon:(UITabBarSystemItem)systemIcon +{ + if (_systemIcon != systemIcon) { + _systemIcon = systemIcon; + UITabBarItem *oldItem = _barItem; + _barItem = [[UITabBarItem alloc] initWithTabBarSystemItem:_systemIcon + tag:oldItem.tag]; + _barItem.title = oldItem.title; + _barItem.imageInsets = oldItem.imageInsets; + _barItem.badgeValue = oldItem.badgeValue; + } +} + +- (void)setIcon:(UIImage *)icon +{ + _icon = icon; + if (_icon && _systemIcon != NSNotFound) { + _systemIcon = NSNotFound; + UITabBarItem *oldItem = _barItem; + _barItem = [UITabBarItem new]; + _barItem.title = oldItem.title; + _barItem.imageInsets = oldItem.imageInsets; + _barItem.selectedImage = oldItem.selectedImage; + _barItem.badgeValue = oldItem.badgeValue; + } + + if (_renderAsOriginal) { + self.barItem.image = [_icon imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; + } else { + self.barItem.image = _icon; + } +} + +- (void)setSelectedIcon:(UIImage *)selectedIcon +{ + _selectedIcon = selectedIcon; + + if (_renderAsOriginal) { + self.barItem.selectedImage = [_selectedIcon imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; + } else { + self.barItem.selectedImage = _selectedIcon; + } +} + +- (void)setBadgeColor:(UIColor *)badgeColor +{ + // badgeColor available since iOS 10 + if ([self.barItem respondsToSelector:@selector(badgeColor)]) { + self.barItem.badgeColor = badgeColor; + } +} + +- (UIViewController *)reactViewController +{ + return self.superview.reactViewController; +} + +#if TARGET_OS_TV + +// On Apple TV, we let native control the tab bar selection after initial render +- (void)setSelected:(BOOL)selected +{ + if (!_wasSelectedInJS) { + _selected = selected; + } +} + +#endif + +@end diff --git a/React/Views/RCTTabBarItemManager.h b/React/Views/RCTTabBarItemManager.h new file mode 100644 index 0000000000..d738ecb6ba --- /dev/null +++ b/React/Views/RCTTabBarItemManager.h @@ -0,0 +1,12 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import + +@interface RCTTabBarItemManager : RCTViewManager + +@end diff --git a/React/Views/RCTTabBarItemManager.m b/React/Views/RCTTabBarItemManager.m new file mode 100644 index 0000000000..130aef44cc --- /dev/null +++ b/React/Views/RCTTabBarItemManager.m @@ -0,0 +1,38 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTTabBarItemManager.h" + +#import "RCTConvert.h" +#import "RCTTabBarItem.h" + +@implementation RCTTabBarItemManager + +RCT_EXPORT_MODULE() + +- (UIView *)view +{ + return [RCTTabBarItem new]; +} + +RCT_EXPORT_VIEW_PROPERTY(badge, id /* NSString or NSNumber */) +RCT_EXPORT_VIEW_PROPERTY(renderAsOriginal, BOOL) +RCT_EXPORT_VIEW_PROPERTY(selected, BOOL) +RCT_EXPORT_VIEW_PROPERTY(icon, UIImage) +RCT_EXPORT_VIEW_PROPERTY(selectedIcon, UIImage) +RCT_EXPORT_VIEW_PROPERTY(systemIcon, UITabBarSystemItem) +RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock) +RCT_EXPORT_VIEW_PROPERTY(badgeColor, UIColor) +RCT_EXPORT_VIEW_PROPERTY(isTVSelectable, BOOL) +RCT_EXPORT_VIEW_PROPERTY(testID, NSString) +RCT_CUSTOM_VIEW_PROPERTY(title, NSString, RCTTabBarItem) +{ + view.barItem.title = json ? [RCTConvert NSString:json] : defaultView.barItem.title; + view.barItem.imageInsets = view.barItem.title.length ? UIEdgeInsetsZero : (UIEdgeInsets){6, 0, -6, 0}; +} + +@end diff --git a/React/Views/RCTTabBarManager.h b/React/Views/RCTTabBarManager.h new file mode 100644 index 0000000000..7e0b49d343 --- /dev/null +++ b/React/Views/RCTTabBarManager.h @@ -0,0 +1,12 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import + +@interface RCTTabBarManager : RCTViewManager + +@end diff --git a/React/Views/RCTTabBarManager.m b/React/Views/RCTTabBarManager.m new file mode 100644 index 0000000000..0f9d3b3439 --- /dev/null +++ b/React/Views/RCTTabBarManager.m @@ -0,0 +1,81 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTTabBarManager.h" + +#import "RCTBridge.h" +#import "RCTTabBar.h" +#import "RCTUIManager.h" +#import "RCTUIManagerObserverCoordinator.h" + +@implementation RCTConvert (UITabBar) + +RCT_ENUM_CONVERTER(UITabBarItemPositioning, (@{ + @"fill" : @(UITabBarItemPositioningFill), + @"auto" : @(UITabBarItemPositioningAutomatic), + @"center" : @(UITabBarItemPositioningCentered) +}), UITabBarItemPositioningAutomatic, integerValue) + +@end + +@interface RCTTabBarManager () + +@end + +@implementation RCTTabBarManager +{ + // The main thread only. + NSHashTable *_viewRegistry; +} + +- (void)setBridge:(RCTBridge *)bridge +{ + [super setBridge:bridge]; + + [self.bridge.uiManager.observerCoordinator addObserver:self]; +} + +- (void)invalidate +{ + [self.bridge.uiManager.observerCoordinator removeObserver:self]; +} + +RCT_EXPORT_MODULE() + +- (UIView *)view +{ + if (!_viewRegistry) { + _viewRegistry = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory]; + } + + RCTTabBar *view = [RCTTabBar new]; + [_viewRegistry addObject:view]; + return view; +} + +RCT_EXPORT_VIEW_PROPERTY(unselectedTintColor, UIColor) +RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor) +RCT_EXPORT_VIEW_PROPERTY(barTintColor, UIColor) +RCT_EXPORT_VIEW_PROPERTY(translucent, BOOL) +#if !TARGET_OS_TV +RCT_EXPORT_VIEW_PROPERTY(barStyle, UIBarStyle) +#endif +RCT_EXPORT_VIEW_PROPERTY(itemPositioning, UITabBarItemPositioning) +RCT_EXPORT_VIEW_PROPERTY(unselectedItemTintColor, UIColor) + +#pragma mark - RCTUIManagerObserver + +- (void)uiManagerDidPerformMounting:(__unused RCTUIManager *)manager +{ + RCTExecuteOnMainQueue(^{ + for (RCTTabBar *view in self->_viewRegistry) { + [view uiManagerDidPerformMounting]; + } + }); +} + +@end diff --git a/React/Views/RCTView.m b/React/Views/RCTView.m index 8ed93cdf51..ead0697866 100644 --- a/React/Views/RCTView.m +++ b/React/Views/RCTView.m @@ -588,7 +588,7 @@ - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { [super traitCollectionDidChange:previousTraitCollection]; #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 - if (@available(iOS 13.0, *)) { + if (@available(iOS 13.0, tvOS 13.0, *)) { if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) { [self.layer setNeedsDisplay]; } @@ -775,7 +775,7 @@ - (void)displayLayer:(CALayer *)layer CGColorRef backgroundColor; #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 - if (@available(iOS 13.0, *)) { + if (@available(iOS 13.0, tvOS 13.0, *)) { backgroundColor = [_backgroundColor resolvedColorWithTraitCollection:self.traitCollection].CGColor; } else { backgroundColor = _backgroundColor.CGColor; diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index a46158dcee..82b4e55aff 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -20,6 +20,10 @@ #import "RCTView.h" #import "UIView+React.h" +#if TARGET_OS_TV +#import "RCTTVView.h" +#endif + @implementation RCTConvert (UIAccessibilityTraits) RCT_MULTI_ENUM_CONVERTER( @@ -88,7 +92,11 @@ - (void)setBridge:(RCTBridge *)bridge - (UIView *)view { +#if TARGET_OS_TV + return [[RCTTVView alloc] initWithBridge:self.bridge]; +#else return [RCTView new]; +#endif } - (RCTShadowView *)shadowView @@ -119,6 +127,17 @@ - (RCTShadowView *)shadowView #pragma mark - View properties +#if TARGET_OS_TV +// TODO: Delete props for Apple TV. +RCT_EXPORT_VIEW_PROPERTY(isTVSelectable, BOOL) +RCT_EXPORT_VIEW_PROPERTY(hasTVPreferredFocus, BOOL) +RCT_EXPORT_VIEW_PROPERTY(tvParallaxProperties, NSDictionary) +RCT_EXPORT_VIEW_PROPERTY(nextFocusUp, NSNumber) +RCT_EXPORT_VIEW_PROPERTY(nextFocusDown, NSNumber) +RCT_EXPORT_VIEW_PROPERTY(nextFocusLeft, NSNumber) +RCT_EXPORT_VIEW_PROPERTY(nextFocusRight, NSNumber) +#endif + // Accessibility related properties RCT_REMAP_VIEW_PROPERTY(accessible, reactAccessibilityElement.isAccessibilityElement, BOOL) RCT_REMAP_VIEW_PROPERTY(accessibilityActions, reactAccessibilityElement.accessibilityActions, NSDictionaryArray) @@ -418,4 +437,21 @@ - (RCTShadowView *)shadowView RCT_EXPORT_SHADOW_PROPERTY(direction, YGDirection) +#if TARGET_OS_TV +RCT_EXPORT_METHOD(setDestinations : (nonnull NSNumber *)viewTag reactTags : (NSArray *)destinationTags) +{ + [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { + RCTTVView *view = (RCTTVView *)viewRegistry[viewTag]; + NSMutableArray* destinations = [NSMutableArray array]; + for (NSNumber * tag in destinationTags) { + RCTTVView *destination = (RCTTVView*)viewRegistry[tag]; + if (destination != nil) { + [destinations addObject:destination]; + } + } + [view addFocusGuide:destinations]; + }]; +} +#endif + @end diff --git a/React/Views/ScrollView/RCTScrollView.h b/React/Views/ScrollView/RCTScrollView.h index 14554f688a..6d19e52991 100644 --- a/React/Views/ScrollView/RCTScrollView.h +++ b/React/Views/ScrollView/RCTScrollView.h @@ -11,11 +11,19 @@ #import #import #import +#if TARGET_OS_TV +#import +#else #import +#endif @protocol UIScrollViewDelegate; +#if TARGET_OS_TV +@interface RCTScrollView : RCTTVView +#else @interface RCTScrollView : RCTView +#endif - (instancetype)initWithEventDispatcher:(id)eventDispatcher NS_DESIGNATED_INITIALIZER; diff --git a/React/Views/ScrollView/RCTScrollView.m b/React/Views/ScrollView/RCTScrollView.m index f0f64021ac..a5adf9c416 100644 --- a/React/Views/ScrollView/RCTScrollView.m +++ b/React/Views/ScrollView/RCTScrollView.m @@ -11,7 +11,6 @@ #import "RCTConvert.h" #import "RCTLog.h" -#import "RCTRefreshControl.h" #import "RCTScrollEvent.h" #import "RCTUIManager.h" #import "RCTUIManagerObserverCoordinator.h" @@ -21,6 +20,16 @@ #import "UIView+Private.h" #import "UIView+React.h" +#if TARGET_OS_TV +#import "RCTTVRemoteHandler.h" +#endif + +#if !TARGET_OS_TV +#import "RCTRefreshControl.h" +#endif + +#define TV_DEFAULT_SWIPE_DURATION 0.3 + /** * Include a custom scroll view subclass because we want to limit certain * default UIKit behaviors such as textFields automatically scrolling @@ -29,8 +38,10 @@ @interface RCTCustomScrollView : UIScrollView @property (nonatomic, assign) BOOL centerContent; +#if !TARGET_OS_TV @property (nonatomic, strong) UIView *customRefreshControl; @property (nonatomic, assign) BOOL pinchGestureEnabled; +#endif @end @@ -48,7 +59,9 @@ - (instancetype)initWithFrame:(CGRect)frame self.semanticContentAttribute = UISemanticContentAttributeForceLeftToRight; } +#if !TARGET_OS_TV _pinchGestureEnabled = YES; +#endif } return self; } @@ -215,6 +228,7 @@ - (void)setFrame:(CGRect)frame } } +#if !TARGET_OS_TV - (void)setCustomRefreshControl:(UIView *)refreshControl { if (_customRefreshControl) { @@ -247,6 +261,7 @@ - (void)didMoveToWindow // in the setter gets overridden when the view loads. self.pinchGestureRecognizer.enabled = _pinchGestureEnabled; } +#endif // TARGET_OS_TV - (BOOL)shouldGroupAccessibilityChildren { @@ -272,6 +287,7 @@ @implementation RCTScrollView { uint16_t _coalescingKey; NSString *_lastEmittedEventName; NSHashTable *_scrollListeners; + NSMutableDictionary *_tvRemoteGestureRecognizers; } - (void)_registerKeyboardListener @@ -358,7 +374,10 @@ - (instancetype)initWithEventDispatcher:(id)eventDis // We set the default behavior to "never" so that iOS // doesn't do weird things to UIScrollView insets automatically // and keeps it as an opt-in behavior. - _scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + if ([_scrollView respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) { + _scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + } +#endif _automaticallyAdjustContentInsets = YES; _contentInset = UIEdgeInsetsZero; @@ -370,6 +389,8 @@ - (instancetype)initWithEventDispatcher:(id)eventDis _scrollListeners = [NSHashTable weakObjectsHashTable]; + _tvRemoteGestureRecognizers = [NSMutableDictionary new]; + [self addSubview:_scrollView]; } return self; @@ -402,12 +423,15 @@ - (void)setRemoveClippedSubviews:(__unused BOOL)removeClippedSubviews - (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex { [super insertReactSubview:view atIndex:atIndex]; +#if !TARGET_OS_TV if ([view conformsToProtocol:@protocol(RCTCustomRefreshContolProtocol)]) { [_scrollView setCustomRefreshControl:(UIView *)view]; if (![view isKindOfClass:[UIRefreshControl class]] && [view conformsToProtocol:@protocol(UIScrollViewDelegate)]) { [self addScrollListener:(UIView *)view]; } - } else { + } else +#endif + { RCTAssert( _contentView == nil, @"RCTScrollView may only contain a single subview, the already set subview looks like: %@", @@ -421,13 +445,16 @@ - (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex - (void)removeReactSubview:(UIView *)subview { [super removeReactSubview:subview]; +#if !TARGET_OS_TV if ([subview conformsToProtocol:@protocol(RCTCustomRefreshContolProtocol)]) { [_scrollView setCustomRefreshControl:nil]; if (![subview isKindOfClass:[UIRefreshControl class]] && [subview conformsToProtocol:@protocol(UIScrollViewDelegate)]) { [self removeScrollListener:(UIView *)subview]; } - } else { + } else +#endif + { RCTAssert(_contentView == subview, @"Attempted to remove non-existent subview"); _contentView = nil; } @@ -897,6 +924,164 @@ - (void)setMaintainVisibleContentPosition:(NSDictionary *)maintainVisibleContent _maintainVisibleContentPosition = maintainVisibleContentPosition; } +#pragma mark - +#pragma mark Apple TV swipe and focus handling + +#if TARGET_OS_TV +- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context + withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator +{ + if (context.previouslyFocusedView == context.nextFocusedView || !self.isTVSelectable) { + return; + } + if (context.nextFocusedView == self) { + [self becomeFirstResponder]; + [self addSwipeGestureRecognizers]; + [self sendFocusNotification]; + } else if (context.previouslyFocusedView == self) { + [self sendBlurNotification]; + [self removeSwipeGestureRecognizers]; + [self resignFirstResponder]; + // if we leave the scroll view and go up, then scroll to top; if going down, + // scroll to bottom + // Similarly for left and right + if (context.focusHeading == UIFocusHeadingUp && self.snapToStart) { + [self swipeVerticalScrollToOffset:0.0]; + } else if(context.focusHeading == UIFocusHeadingDown && self.snapToEnd) { + [self swipeVerticalScrollToOffset:self.scrollView.contentSize.height]; + } else if(context.focusHeading == UIFocusHeadingLeft && self.snapToStart) { + [self swipeHorizontalScrollToOffset:0.0]; + } else if(context.focusHeading == UIFocusHeadingRight && self.snapToEnd) { + [self swipeHorizontalScrollToOffset:self.scrollView.contentSize.width]; + } + + } +} + +- (void)sendFocusNotification +{ + [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTTVNavigationEventNotification" + object:@{@"eventType":@"focus",@"tag":self.reactTag}]; +} + +- (void)sendBlurNotification +{ + [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTTVNavigationEventNotification" + object:@{@"eventType":@"blur",@"tag":self.reactTag}]; +} + +- (NSInteger)swipeVerticalInterval +{ + if (self.snapToInterval) { + return self.snapToInterval; + } + return self.scrollView.visibleSize.height / 2; +} + +- (NSInteger)swipeHorizontalInterval +{ + if (self.snapToInterval) { + return self.snapToInterval; + } + return self.scrollView.visibleSize.width / 2; +} + +- (NSTimeInterval)swipeDuration +{ + float duration = [self.tvParallaxProperties[@"pressDuration"] floatValue]; + if (duration == 0.0) { + duration = TV_DEFAULT_SWIPE_DURATION; + } + return duration; +} + +- (void)swipeVerticalScrollToOffset:(CGFloat)yOffset +{ + dispatch_async(dispatch_get_main_queue(), ^{ + CGFloat limitedOffset = yOffset; + limitedOffset = MAX(limitedOffset, 0.0); + limitedOffset = MIN(limitedOffset, self.scrollView.contentSize.height - self.scrollView.visibleSize.height); + [UIView animateWithDuration:[self swipeDuration] animations:^{ + self.scrollView.contentOffset = + CGPointMake(self.scrollView.contentOffset.x, limitedOffset); + }]; + }); +} + +- (void)swipeHorizontalScrollToOffset:(CGFloat)xOffset +{ + dispatch_async(dispatch_get_main_queue(), ^{ + CGFloat limitedOffset = xOffset; + limitedOffset = MAX(limitedOffset, 0.0); + limitedOffset = MIN(limitedOffset, self.scrollView.contentSize.width - self.scrollView.visibleSize.width); + [UIView animateWithDuration:[self swipeDuration] animations:^{ + self.scrollView.contentOffset = + CGPointMake(limitedOffset, self.scrollView.contentOffset.y); + }]; + }); +} + +- (void)swipedUp +{ + CGFloat newOffset = self.scrollView.contentOffset.y - [self swipeVerticalInterval]; + NSLog(@"Swiped up to %f", newOffset); + [self swipeVerticalScrollToOffset:newOffset]; +} + +- (void)swipedDown +{ + CGFloat newOffset = self.scrollView.contentOffset.y + [self swipeVerticalInterval]; + NSLog(@"Swiped down to %f", newOffset); + [self swipeVerticalScrollToOffset:newOffset]; +} + +- (void)swipedLeft +{ + CGFloat newOffset = self.scrollView.contentOffset.x - [self swipeHorizontalInterval]; + NSLog(@"Swiped left to %f", newOffset); + [self swipeHorizontalScrollToOffset:newOffset]; +} + +- (void)swipedRight +{ + CGFloat newOffset = self.scrollView.contentOffset.x + [self swipeHorizontalInterval]; + NSLog(@"Swiped right to %f", newOffset); + [self swipeHorizontalScrollToOffset:newOffset]; +} + +- (void)addSwipeGestureRecognizers +{ + [self addSwipeGestureRecognizerWithSelector:@selector(swipedUp) direction:UISwipeGestureRecognizerDirectionUp name:RCTTVRemoteEventSwipeUp]; + [self addSwipeGestureRecognizerWithSelector:@selector(swipedDown) direction:UISwipeGestureRecognizerDirectionDown name:RCTTVRemoteEventSwipeDown]; + [self addSwipeGestureRecognizerWithSelector:@selector(swipedLeft) direction:UISwipeGestureRecognizerDirectionLeft name:RCTTVRemoteEventSwipeLeft]; + [self addSwipeGestureRecognizerWithSelector:@selector(swipedRight) direction:UISwipeGestureRecognizerDirectionRight name:RCTTVRemoteEventSwipeRight]; +} + +- (void)addSwipeGestureRecognizerWithSelector:(nonnull SEL)selector + direction:(UISwipeGestureRecognizerDirection)direction + name:(NSString *)name +{ + UISwipeGestureRecognizer *recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:selector]; + recognizer.direction = direction; + + _tvRemoteGestureRecognizers[name] = recognizer; + [self.scrollView addGestureRecognizer:recognizer]; +} + +- (void)removeSwipeGestureRecognizers +{ + NSArray *names = [self->_tvRemoteGestureRecognizers allKeys]; + for (NSString *name in names) { + UIGestureRecognizer *r = self->_tvRemoteGestureRecognizers[name]; + [self.scrollView removeGestureRecognizer:r]; + [self->_tvRemoteGestureRecognizers removeObjectForKey:name]; + } +} + +#endif // TARGET_OS_TV + + + #pragma mark - RCTUIManagerObserver - (void)uiManagerWillPerformMounting:(RCTUIManager *)manager @@ -998,8 +1183,10 @@ -(type)getter \ RCT_SET_AND_PRESERVE_OFFSET(setMaximumZoomScale, maximumZoomScale, CGFloat) RCT_SET_AND_PRESERVE_OFFSET(setMinimumZoomScale, minimumZoomScale, CGFloat) RCT_SET_AND_PRESERVE_OFFSET(setScrollEnabled, isScrollEnabled, BOOL) +#if !TARGET_OS_TV RCT_SET_AND_PRESERVE_OFFSET(setPagingEnabled, isPagingEnabled, BOOL) RCT_SET_AND_PRESERVE_OFFSET(setScrollsToTop, scrollsToTop, BOOL) +#endif RCT_SET_AND_PRESERVE_OFFSET(setShowsHorizontalScrollIndicator, showsHorizontalScrollIndicator, BOOL) RCT_SET_AND_PRESERVE_OFFSET(setShowsVerticalScrollIndicator, showsVerticalScrollIndicator, BOOL) RCT_SET_AND_PRESERVE_OFFSET(setZoomScale, zoomScale, CGFloat); diff --git a/React/Views/ScrollView/RCTScrollViewManager.m b/React/Views/ScrollView/RCTScrollViewManager.m index 37f6416c7c..75efbf9a5c 100644 --- a/React/Views/ScrollView/RCTScrollViewManager.m +++ b/React/Views/ScrollView/RCTScrollViewManager.m @@ -74,9 +74,11 @@ - (UIView *)view RCT_EXPORT_VIEW_PROPERTY(maximumZoomScale, CGFloat) RCT_EXPORT_VIEW_PROPERTY(minimumZoomScale, CGFloat) RCT_EXPORT_VIEW_PROPERTY(scrollEnabled, BOOL) +#if !TARGET_OS_TV RCT_EXPORT_VIEW_PROPERTY(pagingEnabled, BOOL) RCT_REMAP_VIEW_PROPERTY(pinchGestureEnabled, scrollView.pinchGestureEnabled, BOOL) RCT_EXPORT_VIEW_PROPERTY(scrollsToTop, BOOL) +#endif RCT_EXPORT_VIEW_PROPERTY(showsHorizontalScrollIndicator, BOOL) RCT_EXPORT_VIEW_PROPERTY(showsVerticalScrollIndicator, BOOL) RCT_EXPORT_VIEW_PROPERTY(scrollEventThrottle, NSTimeInterval) diff --git a/React/Views/UIView+React.m b/React/Views/UIView+React.m index 16169c62f2..b6d6da78f5 100644 --- a/React/Views/UIView+React.m +++ b/React/Views/UIView+React.m @@ -47,12 +47,17 @@ - (void)setNativeID:(NSString *)nativeID - (BOOL)shouldAccessibilityIgnoresInvertColors { +#if !TARGET_OS_TV return self.accessibilityIgnoresInvertColors; +#endif + return NO; } - (void)setShouldAccessibilityIgnoresInvertColors:(BOOL)shouldAccessibilityIgnoresInvertColors { +#if !TARGET_OS_TV self.accessibilityIgnoresInvertColors = shouldAccessibilityIgnoresInvertColors; +#endif } - (BOOL)isReactRootView diff --git a/ReactAndroid/build.gradle b/ReactAndroid/build.gradle index 5a384f23c1..bcf8ee81e1 100644 --- a/ReactAndroid/build.gradle +++ b/ReactAndroid/build.gradle @@ -86,11 +86,22 @@ task downloadFolly(dependsOn: createNativeDepsDirectories, type: Download) { dest(new File(downloadsDir, "folly-${FOLLY_VERSION}.tar.gz")) } +def follyReplaceContent = ''' + ssize_t r; + do { + r = open(name, flags, mode); + } while (r == -1 && errno == EINTR); + return r; +''' + task prepareFolly(dependsOn: dependenciesPath ? [] : [downloadFolly], type: Copy) { from(dependenciesPath ?: tarTree(downloadFolly.dest)) from("src/main/jni/third-party/folly/Android.mk") include("folly-${FOLLY_VERSION}/folly/**/*", "Android.mk") eachFile { fname -> fname.path = (fname.path - "folly-${FOLLY_VERSION}/") } + // Fixes problem with Folly failing to build on certain systems. See + // https://github.com/facebook/react-native/issues/28298 + filter { line -> line.replaceAll('return int\\(wrapNoInt\\(open, name, flags, mode\\)\\);', follyReplaceContent) } includeEmptyDirs = false into("$thirdPartyNdkDir/folly") } diff --git a/ReactAndroid/gradle.properties b/ReactAndroid/gradle.properties index 9881949181..ec28346d7e 100644 --- a/ReactAndroid/gradle.properties +++ b/ReactAndroid/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=0.68.0-rc.2 +VERSION_NAME=0.68.0 GROUP=com.facebook.react POM_NAME=ReactNative diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactAndroidHWInputDeviceHelper.java b/ReactAndroid/src/main/java/com/facebook/react/ReactAndroidHWInputDeviceHelper.java deleted file mode 100644 index 44a9fe79df..0000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactAndroidHWInputDeviceHelper.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react; - -import android.view.KeyEvent; -import android.view.View; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.bridge.WritableNativeMap; -import com.facebook.react.common.MapBuilder; -import java.util.Map; - -/** Responsible for dispatching events specific for hardware inputs. */ -public class ReactAndroidHWInputDeviceHelper { - - /** - * Contains a mapping between handled KeyEvents and the corresponding navigation event that should - * be fired when the KeyEvent is received. - */ - private static final Map KEY_EVENTS_ACTIONS = - MapBuilder.builder() - .put(KeyEvent.KEYCODE_DPAD_CENTER, "select") - .put(KeyEvent.KEYCODE_ENTER, "select") - .put(KeyEvent.KEYCODE_SPACE, "select") - .put(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, "playPause") - .put(KeyEvent.KEYCODE_MEDIA_REWIND, "rewind") - .put(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, "fastForward") - .put(KeyEvent.KEYCODE_MEDIA_STOP, "stop") - .put(KeyEvent.KEYCODE_MEDIA_NEXT, "next") - .put(KeyEvent.KEYCODE_MEDIA_PREVIOUS, "previous") - .put(KeyEvent.KEYCODE_DPAD_UP, "up") - .put(KeyEvent.KEYCODE_DPAD_RIGHT, "right") - .put(KeyEvent.KEYCODE_DPAD_DOWN, "down") - .put(KeyEvent.KEYCODE_DPAD_LEFT, "left") - .put(KeyEvent.KEYCODE_INFO, "info") - .put(KeyEvent.KEYCODE_MENU, "menu") - .build(); - - /** - * We keep a reference to the last focused view id so that we can send it as a target for key - * events and be able to send a blur event when focus changes. - */ - private int mLastFocusedViewId = View.NO_ID; - - private final ReactRootView mReactRootView; - - ReactAndroidHWInputDeviceHelper(ReactRootView mReactRootView) { - this.mReactRootView = mReactRootView; - } - - /** Called from {@link ReactRootView}. This is the main place the key events are handled. */ - public void handleKeyEvent(KeyEvent ev) { - int eventKeyCode = ev.getKeyCode(); - int eventKeyAction = ev.getAction(); - if ((eventKeyAction == KeyEvent.ACTION_UP || eventKeyAction == KeyEvent.ACTION_DOWN) - && KEY_EVENTS_ACTIONS.containsKey(eventKeyCode)) { - dispatchEvent(KEY_EVENTS_ACTIONS.get(eventKeyCode), mLastFocusedViewId, eventKeyAction); - } - } - - /** Called from {@link ReactRootView} when focused view changes. */ - public void onFocusChanged(View newFocusedView) { - if (mLastFocusedViewId == newFocusedView.getId()) { - return; - } - if (mLastFocusedViewId != View.NO_ID) { - dispatchEvent("blur", mLastFocusedViewId); - } - mLastFocusedViewId = newFocusedView.getId(); - dispatchEvent("focus", newFocusedView.getId()); - } - - /** Called from {@link ReactRootView} when the whole view hierarchy looses focus. */ - public void clearFocus() { - if (mLastFocusedViewId != View.NO_ID) { - dispatchEvent("blur", mLastFocusedViewId); - } - mLastFocusedViewId = View.NO_ID; - } - - private void dispatchEvent(String eventType, int targetViewId) { - dispatchEvent(eventType, targetViewId, -1); - } - - private void dispatchEvent(String eventType, int targetViewId, int eventKeyAction) { - WritableMap event = new WritableNativeMap(); - event.putString("eventType", eventType); - event.putInt("eventKeyAction", eventKeyAction); - if (targetViewId != View.NO_ID) { - event.putInt("tag", targetViewId); - } - mReactRootView.sendEvent("onHWKeyEvent", event); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java b/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java index 3d5ee96820..2b15e3132d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java @@ -48,7 +48,6 @@ import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.modules.appregistry.AppRegistry; -import com.facebook.react.modules.core.DeviceEventManagerModule; import com.facebook.react.modules.deviceinfo.DeviceInfoModule; import com.facebook.react.surface.ReactStage; import com.facebook.react.uimanager.DisplayMetricsHolder; @@ -62,6 +61,7 @@ import com.facebook.react.uimanager.UIManagerHelper; import com.facebook.react.uimanager.common.UIManagerType; import com.facebook.react.uimanager.events.EventDispatcher; +import com.facebook.react.modules.core.ReactAndroidHWInputDeviceHelper; import com.facebook.systrace.Systrace; import java.util.concurrent.atomic.AtomicInteger; @@ -98,7 +98,7 @@ public interface ReactRootViewEventListener { private boolean mShouldLogContentAppeared; private @Nullable JSTouchDispatcher mJSTouchDispatcher; private final ReactAndroidHWInputDeviceHelper mAndroidHWInputDeviceHelper = - new ReactAndroidHWInputDeviceHelper(this); + new ReactAndroidHWInputDeviceHelper(); private boolean mWasMeasured = false; private int mWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); private int mHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); @@ -268,7 +268,7 @@ public boolean dispatchKeyEvent(KeyEvent ev) { FLog.w(TAG, "Unable to handle key event as the catalyst instance has not been attached"); return super.dispatchKeyEvent(ev); } - mAndroidHWInputDeviceHelper.handleKeyEvent(ev); + mAndroidHWInputDeviceHelper.handleKeyEvent(ev, mReactInstanceManager.getCurrentReactContext()); return super.dispatchKeyEvent(ev); } @@ -283,7 +283,7 @@ protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyF super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); return; } - mAndroidHWInputDeviceHelper.clearFocus(); + mAndroidHWInputDeviceHelper.clearFocus(mReactInstanceManager.getCurrentReactContext()); super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); } @@ -298,7 +298,7 @@ public void requestChildFocus(View child, View focused) { super.requestChildFocus(child, focused); return; } - mAndroidHWInputDeviceHelper.onFocusChanged(focused); + mAndroidHWInputDeviceHelper.onFocusChanged(focused, mReactInstanceManager.getCurrentReactContext()); super.requestChildFocus(child, focused); } @@ -791,10 +791,11 @@ public ReactInstanceManager getReactInstanceManager() { /* package */ void sendEvent(String eventName, @Nullable WritableMap params) { if (mReactInstanceManager != null) { - mReactInstanceManager - .getCurrentReactContext() - .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) - .emit(eventName, params); + mAndroidHWInputDeviceHelper.emitNamedEvent( + eventName, + params, + mReactInstanceManager.getCurrentReactContext() + ); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java b/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java index 1ccc2230fe..82ee4de4f6 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java +++ b/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java @@ -102,6 +102,11 @@ public static boolean doesUseOverflowInset() { public static boolean insertZReorderBarriersOnViewGroupChildren = true; + /** + * Send key down events as well as key up events + */ + public static boolean enableKeyDownEvents = false; + /** TODO: T103427072 Delete ReactFeatureFlags.enableNestedTextOnPressEventFix */ public static boolean enableNestedTextOnPressEventFix = true; diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/core/ReactAndroidHWInputDeviceHelper.java b/ReactAndroid/src/main/java/com/facebook/react/modules/core/ReactAndroidHWInputDeviceHelper.java new file mode 100644 index 0000000000..d92b918027 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/core/ReactAndroidHWInputDeviceHelper.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.modules.core; + +import android.view.KeyEvent; +import android.view.View; + +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.bridge.WritableNativeMap; +import com.facebook.react.common.MapBuilder; +import com.facebook.react.common.SystemClock; +import com.facebook.react.config.ReactFeatureFlags; + +import java.util.Map; + +/** Responsible for dispatching events specific for hardware inputs. */ +public class ReactAndroidHWInputDeviceHelper { + + /** + * Contains a mapping between handled KeyEvents and the corresponding navigation event that should + * be fired when the KeyEvent is received. + */ + private static final Map KEY_EVENTS_ACTIONS = + MapBuilder.builder() + .put(KeyEvent.KEYCODE_DPAD_CENTER, "select") + .put(KeyEvent.KEYCODE_ENTER, "select") + .put(KeyEvent.KEYCODE_NUMPAD_ENTER, "select") + .put(KeyEvent.KEYCODE_BUTTON_SELECT, "select") + .put(KeyEvent.KEYCODE_SPACE, "select") + .put(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, "playPause") + .put(KeyEvent.KEYCODE_MEDIA_PLAY, "play") + .put(KeyEvent.KEYCODE_MEDIA_PAUSE, "pause") + .put(KeyEvent.KEYCODE_MEDIA_NEXT, "next") + .put(KeyEvent.KEYCODE_MEDIA_PREVIOUS, "previous") + .put(KeyEvent.KEYCODE_MEDIA_REWIND, "rewind") + .put(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, "fastForward") + .put(KeyEvent.KEYCODE_MEDIA_STOP, "stop") + .put(KeyEvent.KEYCODE_MEDIA_NEXT, "next") + .put(KeyEvent.KEYCODE_MEDIA_PREVIOUS, "previous") + .put(KeyEvent.KEYCODE_DPAD_UP, "up") + .put(KeyEvent.KEYCODE_DPAD_RIGHT, "right") + .put(KeyEvent.KEYCODE_DPAD_DOWN, "down") + .put(KeyEvent.KEYCODE_DPAD_LEFT, "left") + .put(KeyEvent.KEYCODE_INFO, "info") + .put(KeyEvent.KEYCODE_MENU, "menu") + .build(); + + /** + * We keep a reference to the last focused view id so that we can send it as a target for key + * events and be able to send a blur event when focus changes. + */ + private int mLastFocusedViewId = View.NO_ID; + + private long mLastKeyDownTime = 0; + private long mPressedDelta = 1000; + + public ReactAndroidHWInputDeviceHelper() {} + + private boolean isSelectEvent(int eventKeyCode) { + return eventKeyCode == KeyEvent.KEYCODE_DPAD_CENTER || + eventKeyCode == KeyEvent.KEYCODE_BUTTON_SELECT || + eventKeyCode == KeyEvent.KEYCODE_NUMPAD_ENTER || + eventKeyCode == KeyEvent.KEYCODE_ENTER; + } + + /** Called from {@link com.facebook.react.ReactRootView}. This is the main place the key events are handled. */ + public void handleKeyEvent(KeyEvent ev, ReactContext context) { + int eventKeyCode = ev.getKeyCode(); + int eventKeyAction = ev.getAction(); + long time = SystemClock.uptimeMillis(); + + // Simple implementation of long press detection + if ((eventKeyAction == KeyEvent.ACTION_DOWN) + && isSelectEvent(eventKeyCode) && mLastKeyDownTime == 0) { + mLastKeyDownTime = time; + } + if (shouldDispatchEvent(eventKeyCode, eventKeyAction)) { + long delta = time - mLastKeyDownTime; + if (isSelectEvent(eventKeyCode) && (delta > mPressedDelta)) { + dispatchEvent("longSelect", mLastFocusedViewId, eventKeyAction, context); + } else { + dispatchEvent(KEY_EVENTS_ACTIONS.get(eventKeyCode), mLastFocusedViewId, eventKeyAction, context); + } + mLastKeyDownTime = 0; + } + } + + // Android TV: Only send key up actions, unless key down events are enabled + private boolean shouldDispatchEvent(int eventKeyCode, int eventKeyAction) { + return KEY_EVENTS_ACTIONS.containsKey(eventKeyCode) && ( + (eventKeyAction == KeyEvent.ACTION_UP) || + (eventKeyAction == KeyEvent.ACTION_DOWN && ReactFeatureFlags.enableKeyDownEvents) + ); + } + + /** Called from {@link com.facebook.react.ReactRootView} when focused view changes. */ + public void onFocusChanged(View newFocusedView, ReactContext context) { + if (mLastFocusedViewId == newFocusedView.getId()) { + return; + } + if (mLastFocusedViewId != View.NO_ID) { + dispatchEvent("blur", mLastFocusedViewId, context); + } + mLastFocusedViewId = newFocusedView.getId(); + dispatchEvent("focus", newFocusedView.getId(), context); + } + + /** Called from {@link com.facebook.react.ReactRootView} when the whole view hierarchy looses focus. */ + public void clearFocus(ReactContext context) { + if (mLastFocusedViewId != View.NO_ID) { + dispatchEvent("blur", mLastFocusedViewId, context); + } + mLastFocusedViewId = View.NO_ID; + } + + private void dispatchEvent(String eventType, int targetViewId, ReactContext context) { + dispatchEvent(eventType, targetViewId, -1, context); + } + + private void dispatchEvent(String eventType, int targetViewId, int eventKeyAction, ReactContext context) { + WritableMap event = new WritableNativeMap(); + event.putString("eventType", eventType); + event.putInt("eventKeyAction", eventKeyAction); + if (targetViewId != View.NO_ID) { + event.putInt("tag", targetViewId); + event.putInt("target", targetViewId); + } + emitNamedEvent("onHWKeyEvent", event, context); + } + + public void emitNamedEvent(String eventName, WritableMap event, ReactContext context) { + if (context != null) { + context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) + .emit(eventName, event); + } + } + +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java b/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java index 1475b5d868..cd55478df4 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java @@ -18,5 +18,5 @@ public class ReactNativeVersion { "major", 0, "minor", 68, "patch", 0, - "prerelease", "rc.2"); + "prerelease", null); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/ViewProps.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/ViewProps.java index 661c3466dd..94f16a57b4 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/ViewProps.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/ViewProps.java @@ -204,6 +204,7 @@ public class ViewProps { FLEX_SHRINK, FLEX_WRAP, JUSTIFY_CONTENT, + OVERFLOW, ALIGN_CONTENT, DISPLAY, diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modal/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/modal/BUCK index 3eb01b3a51..826e162fc8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modal/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modal/BUCK @@ -22,6 +22,7 @@ rn_android_library( react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/module/annotations:annotations"), + react_native_target("java/com/facebook/react/modules/core:core"), react_native_target("java/com/facebook/react/touch:touch"), react_native_target("java/com/facebook/react/uimanager:uimanager"), react_native_target("java/com/facebook/react/uimanager/annotations:annotations"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java index adbd81d7fd..e907805e7d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java @@ -12,6 +12,7 @@ import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; +import android.graphics.Rect; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; @@ -24,8 +25,10 @@ import androidx.annotation.Nullable; import androidx.annotation.UiThread; import com.facebook.common.logging.FLog; + import com.facebook.infer.annotation.Assertions; import com.facebook.react.R; +import com.facebook.react.modules.core.ReactAndroidHWInputDeviceHelper; import com.facebook.react.bridge.GuardedRunnable; import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.ReactContext; @@ -414,8 +417,14 @@ static class DialogRootViewGroup extends ReactViewGroup private final JSTouchDispatcher mJSTouchDispatcher = new JSTouchDispatcher(this); + private final ReactAndroidHWInputDeviceHelper mAndroidHWInputDeviceHelper; + + private final ReactContext mReactContext; + public DialogRootViewGroup(Context context) { super(context); + mReactContext = (ReactContext)context; + mAndroidHWInputDeviceHelper = new ReactAndroidHWInputDeviceHelper(); } private void setEventDispatcher(EventDispatcher eventDispatcher) { @@ -430,6 +439,18 @@ protected void onSizeChanged(final int w, final int h, int oldw, int oldh) { updateFirstChildView(); } + @Override + protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { + mAndroidHWInputDeviceHelper.clearFocus(mReactContext); + super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); + } + + @Override + public void requestChildFocus(View child, View focused) { + mAndroidHWInputDeviceHelper.onFocusChanged(focused, mReactContext); + super.requestChildFocus(child, focused); + } + private void updateFirstChildView() { if (getChildCount() > 0) { hasAdjustedSize = false; diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollContainerView.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollContainerView.java index 3ac3d42565..f613ea8239 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollContainerView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollContainerView.java @@ -8,6 +8,7 @@ package com.facebook.react.views.scroll; import android.content.Context; +import android.widget.HorizontalScrollView; import androidx.core.view.ViewCompat; import com.facebook.react.modules.i18nmanager.I18nUtil; import com.facebook.react.views.view.ReactViewGroup; @@ -16,7 +17,12 @@ public class ReactHorizontalScrollContainerView extends ReactViewGroup { private int mLayoutDirection; - private int mCurrentWidth; + private int mLastWidth = 0; + private Listener rtlListener = null; + + public interface Listener { + void onLayout(); + } public ReactHorizontalScrollContainerView(Context context) { super(context); @@ -24,7 +30,6 @@ public ReactHorizontalScrollContainerView(Context context) { I18nUtil.getInstance().isRTL(context) ? ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR; - mCurrentWidth = 0; } @Override @@ -44,6 +49,11 @@ public void setRemoveClippedSubviews(boolean removeClippedSubviews) { @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + // This is to fix the overflowing (scaled) item being cropped + final HorizontalScrollView parent = (HorizontalScrollView) getParent(); + parent.setClipChildren(false); + this.setClipChildren(false); + if (mLayoutDirection == LAYOUT_DIRECTION_RTL) { // When the layout direction is RTL, we expect Yoga to give us a layout // that extends off the screen to the left so we re-center it with left=0 @@ -53,21 +63,25 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto setLeft(newLeft); setRight(newRight); - /** - * Note: in RTL mode, *when layout width changes*, we adjust the scroll position. Practically, - * this means that on the first (meaningful) layout we will go from position 0 to position - * (right - screenWidth). In theory this means if the width of the view ever changes during - * layout again, scrolling could jump. Which shouldn't happen in theory, but... if you find a - * weird product bug that looks related, keep this in mind. - */ - if (mCurrentWidth != getWidth()) { - // Call with the present values in order to re-layout if necessary - ReactHorizontalScrollView parent = (ReactHorizontalScrollView) getParent(); - // Fix the ScrollX position when using RTL language - int offsetX = parent.getScrollX() + getWidth() - mCurrentWidth - parent.getWidth(); - parent.scrollTo(offsetX, parent.getScrollY()); + // Fix the ScrollX position when using RTL language accounting for the case when new + // data is appended to the "end" (left) of the view (e.g. after fetching additional items) + final int offsetX = this.getMeasuredWidth() - mLastWidth + parent.getScrollX(); + + // Call with the present values in order to re-layout if necessary + parent.scrollTo(offsetX, parent.getScrollY()); + mLastWidth = this.getMeasuredWidth(); + + // Use the listener to adjust the scrollposition if new data was appended + if (rtlListener != null) { + rtlListener.onLayout(); } } - mCurrentWidth = getWidth(); + } + public int getLastWidth() { + return mLastWidth; + } + + public void setListener(Listener rtlListener) { + this.rtlListener = rtlListener; } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java index ddd60f19ac..1bce574f97 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java @@ -11,6 +11,7 @@ import android.util.DisplayMetrics; import androidx.annotation.Nullable; import androidx.core.view.ViewCompat; +import com.facebook.react.modules.i18nmanager.I18nUtil; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.module.annotations.ReactModule; @@ -78,6 +79,10 @@ public Object updateState( @ReactProp(name = "scrollEnabled", defaultBoolean = true) public void setScrollEnabled(ReactHorizontalScrollView view, boolean value) { view.setScrollEnabled(value); + + /*Set focusable to match whether scroll is enabled. This improves keyboarding + experience by not making scrollview to scroll when scroll enabled is to false.*/ + view.setFocusable(value); } @ReactProp(name = "showsHorizontalScrollIndicator") @@ -200,9 +205,36 @@ public void flashScrollIndicators(ReactHorizontalScrollView scrollView) { @Override public void scrollTo( - ReactHorizontalScrollView scrollView, ReactScrollViewCommandHelper.ScrollToCommandData data) { + final ReactHorizontalScrollView scrollView, final ReactScrollViewCommandHelper.ScrollToCommandData data) { + boolean isRTL = I18nUtil.getInstance().isRTL(scrollView.getContext()); + + int destX = data.mDestX; + + if (isRTL && scrollView.getChildAt(0) instanceof ReactHorizontalScrollContainerView) { + final ReactHorizontalScrollContainerView child = (ReactHorizontalScrollContainerView) scrollView.getChildAt(0); + + // correct the x offset destination, as on android the scrollOffset is not adjusted to RTL + // i.e. the right-most part of the view will be a positive index and the left-most negative + destX = child.getWidth() - scrollView.getWidth() - destX; + + // If the scrollContainerView is in RTL mode, fix the current scroll position in + // reaction to layout changes - need to listen for layout change events (e.g. because of + // fetching new data) and update the scroll position accordingly as soon as it's available + child.setListener(new ReactHorizontalScrollContainerView.Listener() { + @Override + public void onLayout() { + int destX = child.getWidth() - child.getLastWidth() + scrollView.getScrollX(); + + if (data.mAnimated) { + scrollView.smoothScrollTo(destX, data.mDestY); + } else { + scrollView.scrollTo(destX, data.mDestY); + } + } + }); + } if (data.mAnimated) { - scrollView.reactSmoothScrollTo(data.mDestX, data.mDestY); + scrollView.reactSmoothScrollTo(destX, data.mDestY); } else { scrollView.scrollTo(data.mDestX, data.mDestY); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java index 17ef123d0d..b56c588b5f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java @@ -10,7 +10,9 @@ import static com.facebook.react.uimanager.UIManagerHelper.getReactContext; import static com.facebook.react.views.text.TextAttributeProps.UNSET; +import android.app.UiModeManager; import android.content.Context; +import android.content.res.Configuration; import android.graphics.Rect; import android.graphics.Typeface; import android.graphics.drawable.Drawable; @@ -123,6 +125,8 @@ public class ReactEditText extends AppCompatEditText private static final KeyListener sKeyListener = QwertyKeyListener.getInstanceForFullKeyboard(); private @Nullable EventDispatcher mEventDispatcher; + private boolean isKeyboardOpened; + public ReactEditText(Context context) { super(context); setFocusableInTouchMode(false); @@ -183,6 +187,23 @@ protected void finalize() { TextLayoutManager.deleteCachedSpannableForTag(getId()); } + // Not currently used, but leaving in as a comment in case it's needed later + + // private boolean isTVDevice() { + // UiModeManager uiModeManager = (UiModeManager) getContext().getSystemService(Context.UI_MODE_SERVICE); + // return uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION; + // } + + public void showKeyboard() { + isKeyboardOpened = true; + showSoftKeyboard(); + } + + public void hideKeyboard() { + isKeyboardOpened = false; + hideSoftKeyboard(); + } + // After the text changes inside an EditText, TextView checks if a layout() has been requested. // If it has, it will not scroll the text to the end of the new text inserted, but wait for the // next layout() to be called. However, we do not perform a layout() after a requestLayout(), so @@ -207,6 +228,7 @@ public boolean onTouchEvent(MotionEvent ev) { // Disallow parent views to intercept touch events, until we can detect if we should be // capturing these touches or not. this.getParent().requestDisallowInterceptTouchEvent(true); + isKeyboardOpened = !isKeyboardOpened; break; case MotionEvent.ACTION_MOVE: if (mDetectScrollMovement) { @@ -232,6 +254,9 @@ public boolean onKeyUp(int keyCode, KeyEvent event) { hideSoftKeyboard(); return true; } + if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_BACK) { + isKeyboardOpened = !isKeyboardOpened; + } return super.onKeyUp(keyCode, event); } @@ -265,6 +290,7 @@ public InputConnection onCreateInputConnection(EditorInfo outAttrs) { public void clearFocus() { setFocusableInTouchMode(false); super.clearFocus(); + isKeyboardOpened = false; hideSoftKeyboard(); } @@ -274,16 +300,22 @@ public boolean requestFocus(int direction, Rect previouslyFocusedRect) { // is a controlled component, which means its focus is controlled by JS, with two exceptions: // autofocus when it's attached to the window, and responding to accessibility events. In both // of these cases, we call requestFocusInternal() directly. + // Always return true if we are already focused. This is used by android in certain places, + // such as text selection. return isFocused(); } private boolean requestFocusInternal() { setFocusableInTouchMode(true); - // We must explicitly call this method on the super class; if we call requestFocus() without - // any arguments, it will call into the overridden requestFocus(int, Rect) above, which no-ops. boolean focused = super.requestFocus(View.FOCUS_DOWN, null); if (getShowSoftInputOnFocus()) { showSoftKeyboard(); + } else { + if (isKeyboardOpened) { + showSoftKeyboard(); + } else { + hideSoftKeyboard(); + } } return focused; } @@ -372,6 +404,10 @@ protected void onFocusChanged(boolean focused, int direction, Rect previouslyFoc if (focused && mSelectionWatcher != null) { mSelectionWatcher.onSelectionChanged(getSelectionStart(), getSelectionEnd()); } + + if (!focused) { + isKeyboardOpened = false; + } } public void setSelectionWatcher(SelectionWatcher selectionWatcher) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java index df6cc09407..427bd30b3c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java @@ -13,6 +13,7 @@ import android.content.res.ColorStateList; import android.graphics.BlendMode; import android.graphics.BlendModeColorFilter; +import android.annotation.SuppressLint; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.os.Build; @@ -1089,8 +1090,37 @@ public boolean onEditorAction(TextView v, int actionId, KeyEvent keyEvent) { editText.getId(), editText.getText().toString())); - if (blurOnSubmit) { - editText.clearFocus(); + switch (actionId) { + case EditorInfo.IME_ACTION_PREVIOUS: { + @SuppressLint("WrongConstant") View view = editText.focusSearch(View.FOCUS_BACKWARD); + editText.hideKeyboard(); + } + break; + case EditorInfo.IME_ACTION_NEXT: + case EditorInfo.IME_ACTION_DONE: { + @SuppressLint("WrongConstant") View view = editText.focusSearch(View.FOCUS_FORWARD); + if (view instanceof ReactEditText && editText.getId() != view.getId()) { + view.requestFocus(); + ((ReactEditText) view).showKeyboard(); + } else { + if (view != null) { + view.requestFocus(); + } + + // manually triggering onFocusChanged when having only one input field on + // the screen and no other focusable elements + if (editText.getId() == view.getId()) { + editText.onFocusChanged(false, View.FOCUSABLES_ALL, null); + } + editText.hideKeyboard(); + } + } + break; + default: + if (blurOnSubmit) { + editText.clearFocus(); + } + break; } // Prevent default behavior except when we want it to insert a newline. diff --git a/ReactCommon/React-Fabric.podspec b/ReactCommon/React-Fabric.podspec index 096254395c..df0092454f 100644 --- a/ReactCommon/React-Fabric.podspec +++ b/ReactCommon/React-Fabric.podspec @@ -29,7 +29,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.source = source s.source_files = "dummyFile.cpp" s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES", diff --git a/ReactCommon/ReactCommon.podspec b/ReactCommon/ReactCommon.podspec index 7df9578b7f..53e4625095 100644 --- a/ReactCommon/ReactCommon.podspec +++ b/ReactCommon/ReactCommon.podspec @@ -28,7 +28,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.source = source s.header_dir = "ReactCommon" # Use global header_dir for all subspecs for use_frameworks! compatibility s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags diff --git a/ReactCommon/callinvoker/React-callinvoker.podspec b/ReactCommon/callinvoker/React-callinvoker.podspec index f6076050d9..785bae1888 100644 --- a/ReactCommon/callinvoker/React-callinvoker.podspec +++ b/ReactCommon/callinvoker/React-callinvoker.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.source = source s.source_files = "**/*.{cpp,h}" s.header_dir = "ReactCommon" diff --git a/ReactCommon/cxxreact/ReactNativeVersion.h b/ReactCommon/cxxreact/ReactNativeVersion.h index 7d24664a73..0dbc53d2c0 100644 --- a/ReactCommon/cxxreact/ReactNativeVersion.h +++ b/ReactCommon/cxxreact/ReactNativeVersion.h @@ -18,7 +18,7 @@ constexpr struct { int32_t Major = 0; int32_t Minor = 68; int32_t Patch = 0; - std::string_view Prerelease = "rc.2"; + std::string_view Prerelease = ""; } ReactNativeVersion; } // namespace facebook::react diff --git a/ReactCommon/hermes/inspector/tools/msggen/yarn.lock b/ReactCommon/hermes/inspector/tools/msggen/yarn.lock index b6b1be6b96..350bc9b5aa 100644 --- a/ReactCommon/hermes/inspector/tools/msggen/yarn.lock +++ b/ReactCommon/hermes/inspector/tools/msggen/yarn.lock @@ -1870,25 +1870,16 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^4.14.5: - version "4.16.5" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.5.tgz#952825440bca8913c62d0021334cbe928ef062ae" - integrity sha512-C2HAjrM1AI/djrpAUU/tr4pml1DqLIzJKSLDBXBrNErl9ZCCTXdhwxdJjYc16953+mBWf7Lw+uUJgpgb8cN71A== - dependencies: - caniuse-lite "^1.0.30001214" - colorette "^1.2.2" - electron-to-chromium "^1.3.719" - escalade "^3.1.1" - node-releases "^1.1.71" - -browserslist@^4.6.0, browserslist@^4.8.2: - version "4.8.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.2.tgz#b45720ad5fbc8713b7253c20766f701c9a694289" - integrity sha512-+M4oeaTplPm/f1pXDw84YohEv7B1i/2Aisei8s4s6k3QsoSHa7i5sz8u/cGQkkatCPxMASKxPualR4wwYgVboA== +browserslist@^4.14.5, browserslist@^4.6.0, browserslist@^4.8.2: + version "4.19.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3" + integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A== dependencies: - caniuse-lite "^1.0.30001015" - electron-to-chromium "^1.3.322" - node-releases "^1.1.42" + caniuse-lite "^1.0.30001286" + electron-to-chromium "^1.4.17" + escalade "^3.1.1" + node-releases "^2.0.1" + picocolors "^1.0.0" bser@2.1.1: version "2.1.1" @@ -1972,15 +1963,10 @@ camelcase@^6.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== -caniuse-lite@^1.0.30001015: - version "1.0.30001016" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001016.tgz#16ea48d7d6e8caf3cad3295c2d746fe38c4e7f66" - integrity sha512-yYQ2QfotceRiH4U+h1Us86WJXtVHDmy3nEKIdYPsZCYnOV5/tMgGbmoIlrMzmh2VXlproqYtVaKeGDBkMZifFA== - -caniuse-lite@^1.0.30001214: - version "1.0.30001216" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001216.tgz#47418a082a4f952d14d8964ae739e25efb2060a9" - integrity sha512-1uU+ww/n5WCJRwUcc9UH/W6925Se5aNnem/G5QaSDga2HzvjYMs8vRbekGUN/PnTZ7ezTHcxxTEb9fgiMYwH6Q== +caniuse-lite@^1.0.30001286: + version "1.0.30001295" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001295.tgz#68a60f8f0664f342b2835c5d8898b4faea7b3d51" + integrity sha512-lSP16vcyC0FEy0R4ECc9duSPoKoZy+YkpGkue9G4D81OfPnliopaZrU10+qtPdT8PbGXad/PNx43TIQrOmJZSQ== capture-exit@^2.0.0: version "2.0.0" @@ -2148,11 +2134,6 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colorette@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" - integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== - combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -2488,15 +2469,10 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" -electron-to-chromium@^1.3.322: - version "1.3.322" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.322.tgz#a6f7e1c79025c2b05838e8e344f6e89eb83213a8" - integrity sha512-Tc8JQEfGQ1MzfSzI/bTlSr7btJv/FFO7Yh6tanqVmIWOuNCu6/D1MilIEgLtmWqIrsv+o4IjpLAhgMBr/ncNAA== - -electron-to-chromium@^1.3.719: - version "1.3.720" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.720.tgz#f5d66df8754d993006b7b2ded15ff7738c58bd94" - integrity sha512-B6zLTxxaOFP4WZm6DrvgRk8kLFYWNhQ5TrHMC0l5WtkMXhU5UbnvWoTfeEwqOruUSlNMhVLfYak7REX6oC5Yfw== +electron-to-chromium@^1.4.17: + version "1.4.31" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.31.tgz#8d5ccc3f8253cd142b07afaa84f200fd33a7f2a6" + integrity sha512-t3XVQtk+Frkv6aTD4RRk0OqosU+VLe1dQFW83MDer78ZD6a52frgXuYOIsLYTQiH2Lm+JB2OKYcn7zrX+YGAiQ== elliptic@^6.5.3: version "6.5.4" @@ -3072,9 +3048,9 @@ hmac-drbg@^1.0.1: minimalistic-crypto-utils "^1.0.1" hosted-git-info@^2.1.4: - version "2.8.5" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" - integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== html-encoding-sniffer@^2.0.1: version "2.0.1" @@ -4011,12 +3987,7 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash@^4.17.13: - version "4.17.15" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" - integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== - -lodash@^4.17.19, lodash@^4.7.0: +lodash@^4.17.13, lodash@^4.17.19, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -4180,12 +4151,7 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimist@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= - -minimist@^1.2.0, minimist@^1.2.5: +minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== @@ -4331,17 +4297,10 @@ node-notifier@^8.0.0: uuid "^8.3.0" which "^2.0.2" -node-releases@^1.1.42: - version "1.1.43" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.43.tgz#2c6ca237f88ce11d49631f11190bb01f8d0549f2" - integrity sha512-Rmfnj52WNhvr83MvuAWHEqXVoZXCcDQssSOffU4n4XOL9sPrP61mSZ88g25NqmABDvH7PiAlFCzoSCSdzA293w== - dependencies: - semver "^6.3.0" - -node-releases@^1.1.71: - version "1.1.71" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" - integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== +node-releases@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" + integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== normalize-package-data@^2.5.0: version "2.5.0" @@ -4585,9 +4544,9 @@ path-key@^3.0.0, path-key@^3.1.0: integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== pbkdf2@^3.0.3: version "3.1.2" @@ -4605,6 +4564,11 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.3.tgz#465547f359ccc206d3c48e46a1bcb89bf7ee619d" @@ -6003,9 +5967,9 @@ write-file-atomic@^3.0.0: typedarray-to-buffer "^3.1.5" ws@^7.4.4: - version "7.4.5" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.5.tgz#a484dd851e9beb6fdb420027e3885e8ce48986c1" - integrity sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g== + version "7.5.7" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" + integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== xml-name-validator@^3.0.0: version "3.0.0" diff --git a/ReactCommon/jsi/React-jsi.podspec b/ReactCommon/jsi/React-jsi.podspec index 10c82d199e..edece62093 100644 --- a/ReactCommon/jsi/React-jsi.podspec +++ b/ReactCommon/jsi/React-jsi.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.source = source s.source_files = "**/*.{cpp,h}" s.exclude_files = "**/test/*" diff --git a/ReactCommon/jsiexecutor/React-jsiexecutor.podspec b/ReactCommon/jsiexecutor/React-jsiexecutor.podspec index 637ac644a0..fab2606f88 100644 --- a/ReactCommon/jsiexecutor/React-jsiexecutor.podspec +++ b/ReactCommon/jsiexecutor/React-jsiexecutor.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.source = source s.source_files = "jsireact/*.{cpp,h}" s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags diff --git a/ReactCommon/jsinspector/React-jsinspector.podspec b/ReactCommon/jsinspector/React-jsinspector.podspec index a518b68800..9cb94d3202 100644 --- a/ReactCommon/jsinspector/React-jsinspector.podspec +++ b/ReactCommon/jsinspector/React-jsinspector.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.source = source s.source_files = "*.{cpp,h}" s.header_dir = 'jsinspector' diff --git a/ReactCommon/react/renderer/components/view/ViewProps.cpp b/ReactCommon/react/renderer/components/view/ViewProps.cpp index bcc5390770..10a19229b5 100644 --- a/ReactCommon/react/renderer/components/view/ViewProps.cpp +++ b/ReactCommon/react/renderer/components/view/ViewProps.cpp @@ -24,6 +24,34 @@ ViewProps::ViewProps( RawProps const &rawProps) : YogaStylableProps(context, sourceProps, rawProps), AccessibilityProps(context, sourceProps, rawProps), +#if TARGET_OS_TV + isTVSelectable(convertRawProp( + context, + rawProps, + "isTVSelectable", + sourceProps.isTVSelectable, + (Boolean)false)), + hasTVPreferredFocus(convertRawProp( + context, + rawProps, + "hasTVPreferredFocus", + sourceProps.hasTVPreferredFocus, + (Boolean)false)), + tvParallaxProperties(convertRawProp( + context, + rawProps, + "tvParallaxProperties", + sourceProps.tvParallaxProperties, + {})), + nextFocusUp( + convertRawProp(context, rawProps, "nextFocusUp", sourceProps.nextFocusUp, {})), + nextFocusDown( + convertRawProp(context, rawProps, "nextFocusDown", sourceProps.nextFocusDown, {})), + nextFocusLeft( + convertRawProp(context, rawProps, "nextFocusLeft", sourceProps.nextFocusLeft, {})), + nextFocusRight( + convertRawProp(context, rawProps, "nextFocusRight", sourceProps.nextFocusRight, {})), +#endif opacity(convertRawProp( context, rawProps, diff --git a/ReactCommon/react/renderer/components/view/ViewProps.h b/ReactCommon/react/renderer/components/view/ViewProps.h index 1f43dfa60d..fa2bea4a5e 100644 --- a/ReactCommon/react/renderer/components/view/ViewProps.h +++ b/ReactCommon/react/renderer/components/view/ViewProps.h @@ -71,6 +71,16 @@ class ViewProps : public YogaStylableProps, public AccessibilityProps { bool removeClippedSubviews{false}; +#if TARGET_OS_TV + bool isTVSelectable{false}; + bool hasTVPreferredFocus{false}; + TVParallaxProperties tvParallaxProperties; + better::optional nextFocusUp; + better::optional nextFocusDown; + better::optional nextFocusLeft; + better::optional nextFocusRight; +#endif + Float elevation{}; /* Android-only */ #pragma mark - Convenience Methods diff --git a/ReactCommon/react/renderer/components/view/primitives.h b/ReactCommon/react/renderer/components/view/primitives.h index 5f86de9a49..c4edc05f60 100644 --- a/ReactCommon/react/renderer/components/view/primitives.h +++ b/ReactCommon/react/renderer/components/view/primitives.h @@ -178,5 +178,39 @@ struct BorderMetrics { } }; +#if TARGET_OS_TV + +struct TVParallaxProperties { + better::optional enabled{}; + better::optional shiftDistanceX{}; + better::optional shiftDistanceY{}; + better::optional tiltAngle{}; + better::optional magnification{}; + better::optional pressMagnification{}; + better::optional pressDuration{}; + better::optional pressDelay{}; +}; + +constexpr bool operator==( + TVParallaxProperties const &lhs, + TVParallaxProperties const &rhs) { + return lhs.enabled == rhs.enabled && + lhs.shiftDistanceX == rhs.shiftDistanceX && + lhs.shiftDistanceY == rhs.shiftDistanceY && + lhs.tiltAngle == rhs.tiltAngle && + lhs.magnification == rhs.magnification && + lhs.pressMagnification == rhs.pressMagnification && + lhs.pressDuration == rhs.pressDuration && + lhs.pressDelay == rhs.pressDelay; +} + +constexpr bool operator!=( + TVParallaxProperties const &lhs, + TVParallaxProperties const &rhs) { + return !(rhs == lhs); +} + +#endif + } // namespace react } // namespace facebook diff --git a/ReactCommon/react/renderer/components/view/propsConversions.h b/ReactCommon/react/renderer/components/view/propsConversions.h index 6d7233f50c..af964b2d5b 100644 --- a/ReactCommon/react/renderer/components/view/propsConversions.h +++ b/ReactCommon/react/renderer/components/view/propsConversions.h @@ -463,5 +463,71 @@ static inline CascadedRectangleEdges convertRawProp( return result; } +#if TARGET_OS_TV +inline void fromRawValue( + const PropsParserContext &, + const RawValue &value, + TVParallaxProperties &result) { + auto map = (better::map)value; + + auto enabled = map.find("enabled"); + if (enabled != map.end()) { + if (enabled->second.hasType()) { + result.enabled = (bool)enabled->second; + } + } + + auto shiftDistanceX = map.find("shiftDistanceX"); + if (shiftDistanceX != map.end()) { + if (shiftDistanceX->second.hasType()) { + result.shiftDistanceX = (float)shiftDistanceX->second; + } + } + + auto shiftDistanceY = map.find("shiftDistanceY"); + if (shiftDistanceY != map.end()) { + if (shiftDistanceY->second.hasType()) { + result.shiftDistanceY = (float)shiftDistanceY->second; + } + } + + auto tiltAngle = map.find("tiltAngle"); + if (tiltAngle != map.end()) { + if (tiltAngle->second.hasType()) { + result.tiltAngle = (float)tiltAngle->second; + } + } + + auto magnification = map.find("magnification"); + if (magnification != map.end()) { + if (magnification->second.hasType()) { + result.magnification = (float)magnification->second; + } + } + + auto pressMagnification = map.find("pressMagnification"); + if (pressMagnification != map.end()) { + if (pressMagnification->second.hasType()) { + result.pressMagnification = (float)pressMagnification->second; + } + } + + auto pressDuration = map.find("pressDuration"); + if (pressDuration != map.end()) { + if (pressDuration->second.hasType()) { + result.pressDuration = (float)pressDuration->second; + } + } + + auto pressDelay = map.find("pressDelay"); + if (pressDelay != map.end()) { + if (pressDelay->second.hasType()) { + result.pressDelay = (float)pressDelay->second; + } + } + +} +#endif + } // namespace react } // namespace facebook diff --git a/ReactCommon/reactperflogger/React-perflogger.podspec b/ReactCommon/reactperflogger/React-perflogger.podspec index f1f1650256..e38d06be4d 100644 --- a/ReactCommon/reactperflogger/React-perflogger.podspec +++ b/ReactCommon/reactperflogger/React-perflogger.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.source = source s.source_files = "**/*.{cpp,h}" s.header_dir = "reactperflogger" diff --git a/ReactCommon/runtimeexecutor/React-runtimeexecutor.podspec b/ReactCommon/runtimeexecutor/React-runtimeexecutor.podspec index b5a5c126f8..0954fbca1b 100644 --- a/ReactCommon/runtimeexecutor/React-runtimeexecutor.podspec +++ b/ReactCommon/runtimeexecutor/React-runtimeexecutor.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.source = source s.source_files = "**/*.{cpp,h}" s.header_dir = "ReactCommon" diff --git a/ReactCommon/yoga/Yoga.podspec b/ReactCommon/yoga/Yoga.podspec index bdaefc340e..ea9bc6e86d 100644 --- a/ReactCommon/yoga/Yoga.podspec +++ b/ReactCommon/yoga/Yoga.podspec @@ -43,7 +43,7 @@ Pod::Spec.new do |spec| ] # Pinning to the same version as React.podspec. - spec.platforms = { :ios => "11.0" } + spec.platforms = { :ios => "11.0", :tvos => "11.0" } # Set this environment variable when *not* using the `:path` option to install the pod. # E.g. when publishing this spec to a spec repo. diff --git a/bots/yarn.lock b/bots/yarn.lock index cfde2dcbc8..043244d652 100644 --- a/bots/yarn.lock +++ b/bots/yarn.lock @@ -760,6 +760,11 @@ chalk@^2.3.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chownr@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz" @@ -1005,9 +1010,9 @@ end-of-stream@^1.1.0: once "^1.4.0" es6-promise@^4.0.3: - version "4.2.5" - resolved "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz" - integrity sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg== + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== es6-promisify@^5.0.0: version "5.0.0" @@ -1174,6 +1179,13 @@ fs-exists-sync@^0.1.0: resolved "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz" integrity sha1-mC1ok6+RjnLQjeyehnP/K1qNat0= +fs-minipass@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" @@ -1298,7 +1310,7 @@ https-proxy-agent@^2.2.1: resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz" integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ== dependencies: - agent-base "^4.1.0" + agent-base "^4.3.0" debug "^3.1.0" hyperlinker@^1.0.0: @@ -1737,19 +1749,42 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +minipass@^2.6.0, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" mixin-deep@^1.2.0: - version "1.3.1" - resolved "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz" - integrity sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ== + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: for-in "^1.0.2" is-extendable "^1.0.1" +mkdirp@^0.5.1, mkdirp@^0.5.5: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + ms@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" @@ -2095,12 +2130,7 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -safe-buffer@>=5.1.0: - version "5.2.0" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz" - integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== - -safe-buffer@^5.0.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== @@ -2416,11 +2446,9 @@ websocket-driver@>=0.5.1: websocket-extensions ">=0.1.1" websocket-extensions@>=0.1.1: - version "0.1.3" - resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz" - integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== - -whatwg-fetch@2.0.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== version "2.0.4" resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz" integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== diff --git a/index.js b/index.js index e594bbeb96..cd3b2a1588 100644 --- a/index.js +++ b/index.js @@ -80,9 +80,15 @@ import typeof StyleSheet from './Libraries/StyleSheet/StyleSheet'; import typeof Systrace from './Libraries/Performance/Systrace'; import typeof ToastAndroid from './Libraries/Components/ToastAndroid/ToastAndroid'; import typeof * as TurboModuleRegistry from './Libraries/TurboModule/TurboModuleRegistry'; +import typeof TabBarIOS from './Libraries/Components/TabBarIOS/TabBarIOS'; +import typeof TVEventHandler from './Libraries/Components/AppleTV/TVEventHandler'; +import typeof TVFocusGuideView from './Libraries/Components/AppleTV/TVFocusGuideView'; +import typeof TVMenuControl from './Libraries/Components/AppleTV/TVMenuControl'; +import typeof TVTextScrollView from './Libraries/Components/AppleTV/TVTextScrollView'; import typeof UIManager from './Libraries/ReactNative/UIManager'; import typeof useColorScheme from './Libraries/Utilities/useColorScheme'; import typeof useWindowDimensions from './Libraries/Utilities/useWindowDimensions'; +import typeof useTVEventHandler from './Libraries/Components/AppleTV/useTVEventHandler'; import typeof UTFSequence from './Libraries/UTFSequence'; import typeof Vibration from './Libraries/Vibration/Vibration'; import typeof YellowBox from './Libraries/YellowBox/YellowBoxDeprecated'; @@ -97,6 +103,14 @@ import typeof RCTDeviceEventEmitter from './Libraries/EventEmitter/RCTDeviceEven import typeof RCTNativeAppEventEmitter from './Libraries/EventEmitter/RCTNativeAppEventEmitter'; import typeof {RootTagContext} from './Libraries/ReactNative/RootTag'; +// Prop Types +import typeof DeprecatedColorPropType from './Libraries/DeprecatedPropTypes/DeprecatedColorPropType'; +import typeof DeprecatedEdgeInsetsPropType from './Libraries/DeprecatedPropTypes/DeprecatedEdgeInsetsPropType'; +import typeof DeprecatedPointPropType from './Libraries/DeprecatedPropTypes/DeprecatedPointPropType'; +import typeof DeprecatedViewPropTypes from './Libraries/DeprecatedPropTypes/DeprecatedViewPropTypes'; + +import CheckPlatform from './Libraries/Utilities/Platform'; + import type {HostComponent as _HostComponentInternal} from './Libraries/Renderer/shims/ReactNativeTypes'; export type HostComponent = _HostComponentInternal; @@ -218,6 +232,9 @@ module.exports = { get Switch(): Switch { return require('./Libraries/Components/Switch/Switch').default; }, + get TabBarIOS(): TabBarIOS { + return require('./Libraries/Components/TabBarIOS/TabBarIOS'); + }, get Text(): Text { return require('./Libraries/Text/Text'); }, @@ -276,6 +293,12 @@ module.exports = { "It can now be installed and imported from '@react-native-async-storage/async-storage' instead of 'react-native'. " + 'See https://github.com/react-native-async-storage/async-storage', ); + if (CheckPlatform.isTVOS) { + warnOnce( + 'async-storage-tvos', + 'Persistent storage is not supported on tvOS, your data may be removed at any point.', + ); + } return require('./Libraries/Storage/AsyncStorage'); }, get BackHandler(): BackHandler { @@ -380,6 +403,18 @@ module.exports = { get TurboModuleRegistry(): TurboModuleRegistry { return require('./Libraries/TurboModule/TurboModuleRegistry'); }, + get TVEventHandler(): TVEventHandler { + return require('./Libraries/Components/AppleTV/TVEventHandler'); + }, + get TVFocusGuideView(): TVFocusGuideView { + return require('./Libraries/Components/AppleTV/TVFocusGuideView'); + }, + get TVMenuControl(): TVMenuControl { + return require('./Libraries/Components/AppleTV/TVMenuControl'); + }, + get TVTextScrollView(): TVTextScrollView { + return require('./Libraries/Components/AppleTV/TVTextScrollView'); + }, get UIManager(): UIManager { return require('./Libraries/ReactNative/UIManager'); }, @@ -396,6 +431,9 @@ module.exports = { get useWindowDimensions(): useWindowDimensions { return require('./Libraries/Utilities/useWindowDimensions').default; }, + get useTVEventHandler(): useTVEventHandler { + return require('./Libraries/Components/AppleTV/useTVEventHandler'); + }, get UTFSequence(): UTFSequence { return require('./Libraries/UTFSequence'); }, diff --git a/package.json b/package.json index 58b5756b95..5d049d9e20 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,13 @@ { - "name": "react-native", - "version": "0.68.0-rc.2", + "name": "react-native-tvos", + "version": "0.68.0", "bin": "./cli.js", "description": "A framework for building native apps using React", "license": "MIT", - "repository": "github:facebook/react-native", + "repository": { + "type": "git", + "url": "git@github.com:react-native-tvos/react-native-tvos.git" + }, "engines": { "node": ">=14" }, @@ -53,7 +56,8 @@ "scripts/react-native-xcode.sh", "template.config.js", "template", - "third-party-podspecs" + "third-party-podspecs", + "tvos-types.d.ts" ], "scripts": { "start": "react-native start", @@ -72,6 +76,10 @@ "format-check": "prettier --list-different \"./**/*.{js,md,yml}\"", "update-lock": "npx yarn-deduplicate", "docker-setup-android": "docker pull reactnativecommunity/react-native-android:5.2", + "bundle_ios_dev": "react-native bundle --entry-file packages/rn-tester/js/RNTesterApp.ios.js --platform ios --dev true --bundle-output RNTester.ios.jsbundle", + "bundle_ios": "react-native bundle --entry-file packages/rn-tester/js/RNTesterApp.ios.js --platform ios --dev false --bundle-output RNTester.ios.jsbundle", + "bundle_android_dev": "react-native bundle --entry-file packages/rn-tester/js/RNTesterApp.android.js --platform android --dev true --bundle-output RNTester.android.jsbundle", + "bundle_android": "react-native bundle --entry-file packages/rn-tester/js/RNTesterApp.android.js --platform android --dev false --bundle-output RNTester.android.jsbundle", "docker-build-android": "docker build -t reactnativeci/android -f .circleci/Dockerfiles/Dockerfile.android .", "test-android-run-instrumentation": "docker run --cap-add=SYS_ADMIN -it reactnativeci/android bash .circleci/Dockerfiles/scripts/run-android-docker-instrumentation-tests.sh", "test-android-run-unit": "docker run --cap-add=SYS_ADMIN -it reactnativeci/android bash .circleci/Dockerfiles/scripts/run-android-docker-unit-tests.sh", @@ -83,6 +91,7 @@ "test-ios": "./scripts/objc-test.sh test" }, "peerDependencies": { + "@types/react-native": "^0.63.17", "react": "17.0.2" }, "dependencies": { @@ -107,6 +116,7 @@ "nullthrows": "^1.1.1", "pretty-format": "^26.5.2", "promise": "^8.0.3", + "prop-types": "^15.7.2", "react-devtools-core": "^4.23.0", "react-native-gradle-plugin": "^0.0.5", "react-refresh": "^0.4.0", @@ -177,4 +187,4 @@ } ] } -} \ No newline at end of file +} diff --git a/packages/rn-tester/Podfile b/packages/rn-tester/Podfile index 041804c4a8..0440e39bab 100644 --- a/packages/rn-tester/Podfile +++ b/packages/rn-tester/Podfile @@ -1,7 +1,8 @@ require_relative '../../scripts/react_native_pods' +source 'https://github.com/react-native-tvos/react-native-tvos-podspecs.git' source 'https://cdn.cocoapods.org/' -platform :ios, '11.0' +platform :tvos, '11.0' # Temporary solution to suppress duplicated GUID error. # Can be removed once we move to generate files outside pod install. @@ -51,9 +52,7 @@ end target 'RNTester' do pods() - if !USE_FRAMEWORKS - use_flipper! - end + # use_flipper_tvos! end target 'RNTesterUnitTests' do diff --git a/packages/rn-tester/RNTester/Info.plist b/packages/rn-tester/RNTester/Info.plist index 788e4d6193..2d451925f6 100644 --- a/packages/rn-tester/RNTester/Info.plist +++ b/packages/rn-tester/RNTester/Info.plist @@ -10,8 +10,6 @@ fb geo - UIStatusBarStyle - UIStatusBarStyleBlackTranslucent CFBundleDevelopmentRegion en CFBundleExecutable @@ -52,12 +50,16 @@ NSLocationWhenInUseUsageDescription You need to add NSLocationWhenInUseUsageDescription key in Info.plist to enable geolocation, otherwise it is going to *fail silently*! - UILaunchStoryboardName - LaunchScreen + NSPhotoLibraryUsageDescription + You need to add NSPhotoLibraryUsageDescription key in Info.plist to enable photo library usage, otherwise it is going to *fail silently*! + RN_BUNDLE_PREFIX + $(RN_BUNDLE_PREFIX) UIRequiredDeviceCapabilities armv7 + UIStatusBarStyle + UIStatusBarStyleBlackTranslucent UISupportedInterfaceOrientations UIInterfaceOrientationPortrait diff --git a/packages/rn-tester/RNTesterIntegrationTests/RNTesterSnapshotTests.m b/packages/rn-tester/RNTesterIntegrationTests/RNTesterSnapshotTests.m index 177eb15341..5c9891bfac 100644 --- a/packages/rn-tester/RNTesterIntegrationTests/RNTesterSnapshotTests.m +++ b/packages/rn-tester/RNTesterIntegrationTests/RNTesterSnapshotTests.m @@ -37,8 +37,13 @@ -(void)test##name \ RCT_TEST(LayoutExample) RCT_TEST(ScrollViewExample) RCT_TEST(TextExample) + +#if TARGET_OS_TV +RCT_TEST(TVFocusGuideExample) +#endif + #if !TARGET_OS_TV -// No switch available on tvOS +// No switch or slider available on tvOS RCT_TEST(SwitchExample) #endif diff --git a/packages/rn-tester/RNTesterIntegrationTests/ReferenceImages/packages-rn-tester-js-RNTesterApp.ios/testLayoutExample_1-iOS15_tvOS.png b/packages/rn-tester/RNTesterIntegrationTests/ReferenceImages/packages-rn-tester-js-RNTesterApp.ios/testLayoutExample_1-iOS15_tvOS.png new file mode 100644 index 0000000000..c69fe7cf04 Binary files /dev/null and b/packages/rn-tester/RNTesterIntegrationTests/ReferenceImages/packages-rn-tester-js-RNTesterApp.ios/testLayoutExample_1-iOS15_tvOS.png differ diff --git a/packages/rn-tester/RNTesterIntegrationTests/ReferenceImages/packages-rn-tester-js-RNTesterApp.ios/testScrollViewExample_1-iOS15_tvOS.png b/packages/rn-tester/RNTesterIntegrationTests/ReferenceImages/packages-rn-tester-js-RNTesterApp.ios/testScrollViewExample_1-iOS15_tvOS.png new file mode 100644 index 0000000000..9d0be0607f Binary files /dev/null and b/packages/rn-tester/RNTesterIntegrationTests/ReferenceImages/packages-rn-tester-js-RNTesterApp.ios/testScrollViewExample_1-iOS15_tvOS.png differ diff --git a/packages/rn-tester/RNTesterIntegrationTests/ReferenceImages/packages-rn-tester-js-RNTesterApp.ios/testTVFocusGuideExample_1-iOS14_tvOS.png b/packages/rn-tester/RNTesterIntegrationTests/ReferenceImages/packages-rn-tester-js-RNTesterApp.ios/testTVFocusGuideExample_1-iOS14_tvOS.png new file mode 100644 index 0000000000..47c2516a77 Binary files /dev/null and b/packages/rn-tester/RNTesterIntegrationTests/ReferenceImages/packages-rn-tester-js-RNTesterApp.ios/testTVFocusGuideExample_1-iOS14_tvOS.png differ diff --git a/packages/rn-tester/RNTesterIntegrationTests/ReferenceImages/packages-rn-tester-js-RNTesterApp.ios/testTVFocusGuideExample_1-iOS15_tvOS.png b/packages/rn-tester/RNTesterIntegrationTests/ReferenceImages/packages-rn-tester-js-RNTesterApp.ios/testTVFocusGuideExample_1-iOS15_tvOS.png new file mode 100644 index 0000000000..f315d10b62 Binary files /dev/null and b/packages/rn-tester/RNTesterIntegrationTests/ReferenceImages/packages-rn-tester-js-RNTesterApp.ios/testTVFocusGuideExample_1-iOS15_tvOS.png differ diff --git a/packages/rn-tester/RNTesterIntegrationTests/ReferenceImages/packages-rn-tester-js-RNTesterApp.ios/testTextExample_1-iOS15_tvOS.png b/packages/rn-tester/RNTesterIntegrationTests/ReferenceImages/packages-rn-tester-js-RNTesterApp.ios/testTextExample_1-iOS15_tvOS.png new file mode 100644 index 0000000000..297b48193e Binary files /dev/null and b/packages/rn-tester/RNTesterIntegrationTests/ReferenceImages/packages-rn-tester-js-RNTesterApp.ios/testTextExample_1-iOS15_tvOS.png differ diff --git a/packages/rn-tester/RNTesterIntegrationTests/ReferenceImages/packages-rn-tester-js-RNTesterApp.ios/testViewExample_1-iOS15_tvOS.png b/packages/rn-tester/RNTesterIntegrationTests/ReferenceImages/packages-rn-tester-js-RNTesterApp.ios/testViewExample_1-iOS15_tvOS.png new file mode 100644 index 0000000000..f8b911374e Binary files /dev/null and b/packages/rn-tester/RNTesterIntegrationTests/ReferenceImages/packages-rn-tester-js-RNTesterApp.ios/testViewExample_1-iOS15_tvOS.png differ diff --git a/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj b/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj index 668d70803b..50df123d57 100644 --- a/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj +++ b/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj @@ -17,7 +17,6 @@ 5C60EB1C226440DB0018C04F /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C60EB1B226440DB0018C04F /* AppDelegate.mm */; }; 5CB07C9B226467E60039471C /* RNTesterTurboModuleProvider.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CB07C99226467E60039471C /* RNTesterTurboModuleProvider.mm */; }; 606C582DFD50595CEE33C07F /* libPods-RNTesterUnitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B3B45782C648174786D3883A /* libPods-RNTesterUnitTests.a */; }; - 8145AE06241172D900A3F8DA /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8145AE05241172D900A3F8DA /* LaunchScreen.storyboard */; }; D7017CD9E6ECFF49B1DE42F1 /* libPods-RNTester.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AA78234A2C4A4512CE754F9A /* libPods-RNTester.a */; }; E7C1241A22BEC44B00DA25C0 /* RNTesterIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E7C1241922BEC44B00DA25C0 /* RNTesterIntegrationTests.m */; }; E7DB20D122B2BAA6005AC45F /* RCTBundleURLProviderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E7DB20A922B2BAA3005AC45F /* RCTBundleURLProviderTests.m */; }; @@ -95,7 +94,6 @@ 5CB07C99226467E60039471C /* RNTesterTurboModuleProvider.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = RNTesterTurboModuleProvider.mm; path = RNTester/RNTesterTurboModuleProvider.mm; sourceTree = ""; }; 5CB07C9A226467E60039471C /* RNTesterTurboModuleProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNTesterTurboModuleProvider.h; path = RNTester/RNTesterTurboModuleProvider.h; sourceTree = ""; }; 6D3E7ECE8F9BEC26E5786555 /* Pods-RNTester.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNTester.debug.xcconfig"; path = "Target Support Files/Pods-RNTester/Pods-RNTester.debug.xcconfig"; sourceTree = ""; }; - 8145AE05241172D900A3F8DA /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = RNTester/LaunchScreen.storyboard; sourceTree = ""; }; 8735BC063632C9712E25C7D9 /* Pods-RNTesterUnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNTesterUnitTests.release.xcconfig"; path = "Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests.release.xcconfig"; sourceTree = ""; }; AA78234A2C4A4512CE754F9A /* libPods-RNTester.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RNTester.a"; sourceTree = BUILT_PRODUCTS_DIR; }; B3B45782C648174786D3883A /* libPods-RNTesterUnitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RNTesterUnitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -228,7 +226,6 @@ 5CB07C99226467E60039471C /* RNTesterTurboModuleProvider.mm */, 13B07FB71A68108700A75B9A /* main.m */, 2DDEF00F1F84BF7B00DBDF73 /* Images.xcassets */, - 8145AE05241172D900A3F8DA /* LaunchScreen.storyboard */, 680759612239798500290469 /* Fabric */, 272E6B3A1BEA846C001FCF37 /* NativeExampleViews */, 1323F18D1C04ABAC0091BED0 /* Supporting Files */, @@ -407,7 +404,6 @@ 13B07F8E1A680F5B00A75B9A /* Resources */, 68CD48B71D2BCB2C007E06A9 /* Build JS Bundle */, 5CF0FD27207FC6EC00C13D65 /* Start Metro */, - 4C8C07C0E5F2E32A7AAB5757 /* [CP] Embed Pods Frameworks */, FA8D4E92C71D381C484B8772 /* [CP] Copy Pods Resources */, ); buildRules = ( @@ -503,7 +499,6 @@ buildActionMask = 2147483647; files = ( 2DDEF0101F84BF7B00DBDF73 /* Images.xcassets in Resources */, - 8145AE06241172D900A3F8DA /* LaunchScreen.storyboard in Resources */, 3D2AFAF51D646CF80089D1A3 /* legacy_image@2x.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -565,23 +560,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 4C8C07C0E5F2E32A7AAB5757 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RNTester/Pods-RNTester-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RNTester/Pods-RNTester-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNTester/Pods-RNTester-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; 5CF0FD27207FC6EC00C13D65 /* Start Metro */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -773,10 +751,15 @@ baseConfigurationReference = 6D3E7ECE8F9BEC26E5786555 /* Pods-RNTester.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_ENTITLEMENTS = RNTester/RNTester.entitlements; DEVELOPMENT_TEAM = ""; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "FB_SONARKIT_NOT_ENABLED=1", + ); + HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = "$(SRCROOT)/RNTester/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( /usr/lib/swift, "$(inherited)", @@ -796,7 +779,9 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.facebook.react.uiapp; PRODUCT_NAME = RNTester; - TARGETED_DEVICE_FAMILY = "1,2"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 11.0; }; name = Debug; }; @@ -805,11 +790,11 @@ baseConfigurationReference = 3DC317D3EE16C63CD9243667 /* Pods-RNTester.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_ENTITLEMENTS = RNTester/RNTester.entitlements; DEVELOPMENT_TEAM = ""; EXCLUDED_ARCHS = ""; INFOPLIST_FILE = "$(SRCROOT)/RNTester/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( /usr/lib/swift, "$(inherited)", @@ -828,7 +813,9 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.facebook.react.uiapp; PRODUCT_NAME = RNTester; - TARGETED_DEVICE_FAMILY = "1,2"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 11.0; }; name = Release; }; @@ -894,7 +881,6 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; OTHER_CPLUSPLUSFLAGS = ( @@ -908,6 +894,7 @@ "-lc++", ); SDKROOT = iphoneos; + TVOS_DEPLOYMENT_TARGET = 11.0; WARNING_CFLAGS = ( "-Wextra", "-Wall", @@ -971,7 +958,6 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; OTHER_CPLUSPLUSFLAGS = ( "$(OTHER_CFLAGS)", @@ -984,6 +970,7 @@ "-lc++", ); SDKROOT = iphoneos; + TVOS_DEPLOYMENT_TARGET = 11.0; VALIDATE_PRODUCT = YES; WARNING_CFLAGS = ( "-Wextra", @@ -1006,7 +993,6 @@ DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = RNTesterUnitTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.4; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1020,12 +1006,14 @@ MTL_FAST_MATH = YES; OTHER_CFLAGS = ( "$(inherited)", - "-DFB_SONARKIT_ENABLED=1", + "-DFB_SONARKIT_NOT_ENABLED=1", ); - OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -DFB_SONARKIT_ENABLED"; + OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -DFB_SONARKIT_NOT_ENABLED"; PRODUCT_BUNDLE_IDENTIFIER = com.facebook.RNTesterUnitTests; PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 12.4; }; name = Debug; }; @@ -1043,7 +1031,6 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = RNTesterUnitTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.4; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1056,12 +1043,14 @@ MTL_FAST_MATH = YES; OTHER_CFLAGS = ( "$(inherited)", - "-DFB_SONARKIT_ENABLED=1", + "-DFB_SONARKIT_NOT_ENABLED=1", ); - OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -DFB_SONARKIT_ENABLED"; + OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -DFB_SONARKIT_NOT_ENABLED"; PRODUCT_BUNDLE_IDENTIFIER = com.facebook.RNTesterUnitTests; PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 12.4; }; name = Release; }; @@ -1083,7 +1072,6 @@ "$(inherited)", ); INFOPLIST_FILE = RNTesterIntegrationTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.4; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1093,13 +1081,15 @@ MTL_FAST_MATH = YES; OTHER_CFLAGS = ( "$(inherited)", - "-DFB_SONARKIT_ENABLED=1", + "-DFB_SONARKIT_NOT_ENABLED=1", ); - OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -DFB_SONARKIT_ENABLED"; + OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -DFB_SONARKIT_NOT_ENABLED"; PRODUCT_BUNDLE_IDENTIFIER = com.facebook.RNTesterIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RNTester.app/RNTester"; + TVOS_DEPLOYMENT_TARGET = 12.4; }; name = Debug; }; @@ -1118,7 +1108,6 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = RNTesterIntegrationTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.4; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1127,13 +1116,15 @@ MTL_FAST_MATH = YES; OTHER_CFLAGS = ( "$(inherited)", - "-DFB_SONARKIT_ENABLED=1", + "-DFB_SONARKIT_NOT_ENABLED=1", ); - OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -DFB_SONARKIT_ENABLED"; + OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -DFB_SONARKIT_NOT_ENABLED"; PRODUCT_BUNDLE_IDENTIFIER = com.facebook.RNTesterIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RNTester.app/RNTester"; + TVOS_DEPLOYMENT_TARGET = 12.4; }; name = Release; }; diff --git a/packages/rn-tester/RNTesterUnitTests/RCTConvert_UIColorTests.m b/packages/rn-tester/RNTesterUnitTests/RCTConvert_UIColorTests.m index 1d942d3caf..53f8605b4d 100644 --- a/packages/rn-tester/RNTesterUnitTests/RCTConvert_UIColorTests.m +++ b/packages/rn-tester/RNTesterUnitTests/RCTConvert_UIColorTests.m @@ -29,12 +29,14 @@ static BOOL CGColorsAreEqual(CGColorRef color1, CGColorRef color2) @implementation RCTConvert_NSColorTests +#if !TARGET_OS_TV - (void)testColor { id json = RCTJSONParse(@"{ \"semantic\": \"lightTextColor\" }", nil); UIColor *value = [RCTConvert UIColor:json]; XCTAssertEqualObjects(value, [UIColor lightTextColor]); } +#endif - (void)testColorFailure { @@ -76,7 +78,7 @@ - (void)testDynamicColor XCTAssertNotNil(value); #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 - if (@available(iOS 13.0, *)) { + if (@available(iOS 13.0, tvOS 13.0, *)) { id savedTraitCollection = [UITraitCollection currentTraitCollection]; [UITraitCollection @@ -110,7 +112,7 @@ - (void)testCompositeDynamicColor XCTAssertNotNil(value); #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 - if (@available(iOS 13.0, *)) { + if (@available(iOS 13.0, tvOS 13.0, *)) { id savedTraitCollection = [UITraitCollection currentTraitCollection]; [UITraitCollection @@ -134,6 +136,11 @@ - (void)testGenerateFallbacks // https://developer.apple.com/documentation/uikit/uicolor/ui_element_colors // Label Colors @"labelColor" : @(0xFF000000), + @"secondaryLabelColor" : @(0x993c3c43), + @"tertiaryLabelColor" : @(0x4c3c3c43), + @"quaternaryLabelColor" : @(0x2d3c3c43), + @"labelColor": @(0xFF000000), +#if !TARGET_OS_TV @"secondaryLabelColor" : @(0x993c3c43), @"tertiaryLabelColor" : @(0x4c3c3c43), @"quaternaryLabelColor" : @(0x2d3c3c43), @@ -144,6 +151,7 @@ - (void)testGenerateFallbacks @"quaternarySystemFillColor" : @(0x14747480), // Text Colors @"placeholderTextColor" : @(0x4c3c3c43), +#endif // Standard Content Background Colors @"systemBackgroundColor" : @(0xFFffffff), @"secondarySystemBackgroundColor" : @(0xFFf2f2f7), @@ -173,7 +181,7 @@ - (void)testGenerateFallbacks #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 id savedTraitCollection = nil; - if (@available(iOS 13.0, *)) { + if (@available(iOS 13.0, tvOS 13.0, *)) { savedTraitCollection = [UITraitCollection currentTraitCollection]; [UITraitCollection @@ -207,7 +215,7 @@ - (void)testGenerateFallbacks } #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 - if (@available(iOS 13.0, *)) { + if (@available(iOS 13.0, tvOS 13.0, *)) { [UITraitCollection setCurrentTraitCollection:savedTraitCollection]; } #endif diff --git a/packages/rn-tester/RNTesterUnitTests/RCTJSONTests.m b/packages/rn-tester/RNTesterUnitTests/RCTJSONTests.m index 32fcbd360c..942bfe363f 100644 --- a/packages/rn-tester/RNTesterUnitTests/RCTJSONTests.m +++ b/packages/rn-tester/RNTesterUnitTests/RCTJSONTests.m @@ -104,6 +104,8 @@ - (void)testNaN XCTAssertEqualObjects(json, RCTJSONStringify(obj, NULL)); } +// This crashes with EXC_BAD_ACCESS in Xcode 11 +/* - (void)testNotUTF8Convertible { // see https://gist.github.com/0xced/56035d2f57254cf518b5 @@ -112,6 +114,7 @@ - (void)testNotUTF8Convertible NSString *json = @"{\"foo\":null}"; XCTAssertEqualObjects(json, RCTJSONStringify(obj, NULL)); } + */ - (void)testErrorPointer { diff --git a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java index 1a393ce051..a22f4e225a 100644 --- a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java +++ b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java @@ -187,6 +187,9 @@ public JSIModuleProvider getJSIModuleProvider() { @Override public void onCreate() { ReactFeatureFlags.useTurboModules = BuildConfig.ENABLE_TURBOMODULE; + // Normally we only send key up events in ReactAndroidHWInputDeviceHelper + // Change enableKeyDownEvents to true to send both key down and key up events + ReactFeatureFlags.enableKeyDownEvents = false; ReactFontManager.getInstance().addCustomFont(this, "Rubik", R.font.rubik); super.onCreate(); SoLoader.init(this, /* native exopackage */ false); diff --git a/packages/rn-tester/js/RNTesterAppShared.js b/packages/rn-tester/js/RNTesterAppShared.js index fc35f5c89f..671bf974d1 100644 --- a/packages/rn-tester/js/RNTesterAppShared.js +++ b/packages/rn-tester/js/RNTesterAppShared.js @@ -12,6 +12,8 @@ import { BackHandler, StyleSheet, useColorScheme, + Platform, + TVMenuControl, View, LogBox, } from 'react-native'; @@ -76,6 +78,7 @@ const RNTesterApp = (): React.Node => { const handleBackPress = React.useCallback(() => { if (activeModuleKey != null) { + TVMenuControl.enableTVMenuKey(); dispatch({type: RNTesterActionsType.BACK_BUTTON_PRESS}); } }, [dispatch, activeModuleKey]); @@ -85,6 +88,7 @@ const RNTesterApp = (): React.Node => { const handleHardwareBackPress = () => { if (activeModuleKey) { handleBackPress(); + TVMenuControl.disableTVMenuKey(); return true; } return false; @@ -197,7 +201,7 @@ const RNTesterApp = (): React.Node => { /> )}
- + [ + style={({pressed, focused}) => [ styles.row, - typeof style === 'function' ? style(pressed) : style, - pressed + typeof style === 'function' ? style(pressed || focused) : style, + pressed || focused ? {backgroundColor: theme.SecondarySystemFillColor} : {backgroundColor: theme.SecondaryGroupedBackgroundColor}, ]} diff --git a/packages/rn-tester/js/components/RNTesterExampleFilter.js b/packages/rn-tester/js/components/RNTesterExampleFilter.js index 03daeb35e1..b4049f23a1 100644 --- a/packages/rn-tester/js/components/RNTesterExampleFilter.js +++ b/packages/rn-tester/js/components/RNTesterExampleFilter.js @@ -11,6 +11,7 @@ const React = require('react'); const RNTesterListFilters = require('./RNTesterListFilters'); const { + Platform, StyleSheet, TextInput, View, @@ -106,6 +107,9 @@ class RNTesterExampleFilter extends React.Component, State> { if (this.props.disableSearch) { return null; } + if (Platform.isTV) { + return null; + } return ( {theme => { diff --git a/packages/rn-tester/js/components/RNTesterListFilters.js b/packages/rn-tester/js/components/RNTesterListFilters.js index ddb14c3bd4..4111902899 100644 --- a/packages/rn-tester/js/components/RNTesterListFilters.js +++ b/packages/rn-tester/js/components/RNTesterListFilters.js @@ -14,7 +14,7 @@ const React = require('react'); const {StyleSheet, Text, TouchableOpacity, View} = require('react-native'); -const filters = ['Basic', 'UI', 'ListView', 'iOS', 'Android']; +const filters = ['Basic', 'UI', 'ListView', 'iOS', 'Android', 'TV']; type Props = { onFilterButtonPress: (filter: string) => mixed, //optional only for testing diff --git a/packages/rn-tester/js/components/RNTesterModuleList.js b/packages/rn-tester/js/components/RNTesterModuleList.js index e7e6b2fc0f..32f3ed1e1c 100644 --- a/packages/rn-tester/js/components/RNTesterModuleList.js +++ b/packages/rn-tester/js/components/RNTesterModuleList.js @@ -121,7 +121,7 @@ const RNTesterModuleList: React$AbstractComponent = React.memo( const filter = ({example, filterRegex, category}: any) => filterRegex.test(example.module.title) && (!category || example.category === category) && - (!Platform.isTV || example.supportsTVOS); + (!Platform.isTVOS || example.supportsTVOS); const renderListItem = ({item, section, separators}) => { return ( diff --git a/packages/rn-tester/js/components/RNTesterNavbar.js b/packages/rn-tester/js/components/RNTesterNavbar.js index 97ebb99f55..bb1d364995 100644 --- a/packages/rn-tester/js/components/RNTesterNavbar.js +++ b/packages/rn-tester/js/components/RNTesterNavbar.js @@ -11,7 +11,7 @@ import type {RNTesterTheme} from './RNTesterTheme'; import * as React from 'react'; -import {Text, View, StyleSheet, Image, Pressable} from 'react-native'; +import {Text, View, StyleSheet, Image, Platform, Pressable} from 'react-native'; import {RNTesterThemeContext} from './RNTesterTheme'; @@ -60,24 +60,32 @@ const NavbarButton = ({ label, handlePress, iconStyle, -}) => ( - - - - - {label} - - - -); +}) => { + const [isFocused, setIsFocused] = React.useState(false); + return ( + setIsFocused(true)} + onBlur={() => setIsFocused(false)} + style={[styles.navButton, {backgroundColor: theme.BackgroundColor}]}> + + + + {label} + + + + ); +}; const ComponentTab = ({ isComponentActive, @@ -146,11 +154,13 @@ const RNTesterNavbar = ({ handleNavBarPress={handleNavBarPress} theme={theme} /> - + {Platform.isTV ? null : ( + + )} ; + }, + }, +]; + +const padding = 100; +const width = 200; +const height = 120; + +class Button extends React.Component<$FlowFixMeProps> { + render() { + return ( + + {theme => { + return ( + + + {this.props.label} + + + ); + }} + + ); + } +} + +class ThemedView extends React.Component<$FlowFixMeProps> { + render() { + return ( + + {theme => { + return ( + + + {this.props.label} + + + ); + }} + + ); + } +} + +class DirectionalNextFocusExample extends React.Component< + $FlowFixMeProps, + { + destinations: { + up: ?Object, + down: ?Object, + left: ?Object, + right: ?Object, + }, + }, +> { + constructor(props: Object) { + super(props); + this.state = { + destinations: { + up: undefined, + down: undefined, + left: undefined, + right: undefined, + }, + }; + } + + render() { + const {destinations} = this.state; + return ( + + +