Skip to content

Commit

Permalink
Merge branch 'tvos-v0.66.3' into tvos-v0.68.0
Browse files Browse the repository at this point in the history
  • Loading branch information
douglowder committed Mar 14, 2022
2 parents 65e4d98 + 3213dbd commit be2a5fb
Show file tree
Hide file tree
Showing 234 changed files with 12,597 additions and 655 deletions.
5 changes: 5 additions & 0 deletions .circleci/Dockerfiles/scripts/run-ci-e2e-tests.sh
Expand Up @@ -67,6 +67,11 @@ while :; do
shift
;;

--tvos)
RUN_IOS=1
shift
;;

*)
break
esac
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Expand Up @@ -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*
Expand Down
4 changes: 2 additions & 2 deletions Libraries/ActionSheetIOS/React-RCTActionSheet.podspec
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Libraries/Blob/React-RCTBlob.podspec
Expand Up @@ -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}"
Expand Down
24 changes: 24 additions & 0 deletions 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<Spec>(
'TVNavigationEventEmitter',
): ?Spec);

51 changes: 51 additions & 0 deletions 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;
51 changes: 51 additions & 0 deletions 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<any, Function> = 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;
79 changes: 79 additions & 0 deletions 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
<ReactNative.View style={[{minHeight: 1, minWidth: 1}, props.style]}>
{
/**
* 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 ? (
<ReactNative.View
style={[props.style, styles.focusGuideLayout]}
ref={focusGuideRef}
>
{props.children}
</ReactNative.View>
) : (
props.children
)}
</ReactNative.View>
);

};

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;
17 changes: 17 additions & 0 deletions 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();
},
};
104 changes: 104 additions & 0 deletions 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<string> {
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 <ScrollView {...props}>{this.props.children}</ScrollView>;
}
}

module.exports = TVTextScrollView;

0 comments on commit be2a5fb

Please sign in to comment.