@applicaster/zapp-react-native-ui-components 15.0.0-alpha.1692821627 → 15.0.0-alpha.1966607451
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Components/AnimatedInOut/index.tsx +69 -26
- package/Components/BaseFocusable/index.ios.ts +12 -2
- package/Components/Cell/Cell.tsx +8 -3
- package/Components/Cell/FocusableWrapper.tsx +47 -0
- package/Components/Cell/TvOSCellComponent.tsx +106 -19
- package/Components/Focusable/FocusableTvOS.tsx +12 -2
- package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +1 -0
- package/Components/FocusableGroup/FocusableTvOS.tsx +11 -0
- package/Components/HandlePlayable/HandlePlayable.tsx +17 -65
- package/Components/HandlePlayable/const.ts +3 -0
- package/Components/HandlePlayable/utils.ts +74 -0
- package/Components/Layout/TV/LayoutBackground.tsx +5 -2
- package/Components/Layout/TV/ScreenContainer.tsx +2 -6
- package/Components/Layout/TV/index.tsx +3 -4
- package/Components/Layout/TV/index.web.tsx +3 -4
- package/Components/LinkHandler/LinkHandler.tsx +2 -2
- package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +16 -1
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +30 -2
- package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +5 -1
- package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +11 -3
- package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +9 -1
- package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +15 -14
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +10 -6
- package/Components/MasterCell/DefaultComponents/Text/index.tsx +8 -8
- package/Components/MasterCell/index.tsx +2 -0
- package/Components/MasterCell/utils/__tests__/resolveColor.test.js +82 -3
- package/Components/MasterCell/utils/index.ts +61 -31
- package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +102 -87
- package/Components/MeasurmentsPortal/__tests__/MeasurementsPortal.test.tsx +355 -0
- package/Components/OfflineHandler/NotificationView/NotificationView.tsx +2 -2
- package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +17 -18
- package/Components/OfflineHandler/__tests__/index.test.tsx +27 -18
- package/Components/PlayerContainer/PlayerContainer.tsx +5 -19
- package/Components/PlayerImageBackground/index.tsx +3 -22
- package/Components/Screen/TV/index.web.tsx +4 -2
- package/Components/Screen/__tests__/Screen.test.tsx +65 -42
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +68 -44
- package/Components/Screen/hooks.ts +2 -3
- package/Components/Screen/index.tsx +2 -3
- package/Components/Screen/navigationHandler.ts +49 -24
- package/Components/Screen/orientationHandler.ts +3 -3
- package/Components/ScreenResolver/index.tsx +13 -7
- package/Components/ScreenRevealManager/ScreenRevealManager.ts +40 -8
- package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +86 -69
- package/Components/ScreenRevealManager/withScreenRevealManager.tsx +44 -26
- package/Components/Tabs/TV/Tabs.tsx +20 -3
- package/Components/Transitioner/Scene.tsx +15 -2
- package/Components/Transitioner/index.js +3 -3
- package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +1 -0
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +118 -171
- package/Components/VideoModal/ModalAnimation/index.ts +2 -13
- package/Components/VideoModal/ModalAnimation/utils.ts +1 -327
- package/Components/VideoModal/PlayerWrapper.tsx +14 -88
- package/Components/VideoModal/VideoModal.tsx +1 -5
- package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -0
- package/Components/VideoModal/hooks/useModalSize.ts +10 -5
- package/Components/VideoModal/playerWrapperStyle.ts +70 -0
- package/Components/VideoModal/playerWrapperUtils.ts +91 -0
- package/Components/VideoModal/utils.ts +19 -9
- package/Decorators/Analytics/index.tsx +6 -5
- package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
- package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
- package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +80 -0
- package/Helpers/DataSourceHelper/index.ts +19 -0
- package/index.d.ts +7 -0
- package/package.json +6 -5
- package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +0 -60
- package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +0 -417
- package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.web.tsx +0 -294
- package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +0 -176
- package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.web.tsx +0 -93
- package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +0 -500
- package/Components/VideoModal/ModalAnimation/__tests__/getMoveUpValue.test.ts +0 -108
- package/Helpers/DataSourceHelper/index.js +0 -19
|
@@ -6,10 +6,6 @@ import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
|
6
6
|
|
|
7
7
|
type AnimatedInterpolatedStyle = any;
|
|
8
8
|
|
|
9
|
-
// type AnimatedInterpolatedStyle =
|
|
10
|
-
// | Animated.AnimatedInterpolation
|
|
11
|
-
// | [{ [Key: string]: Animated.AnimatedInterpolation }];
|
|
12
|
-
|
|
13
9
|
type AnimationConfig = {
|
|
14
10
|
duration: number;
|
|
15
11
|
easing: EasingFunction;
|
|
@@ -45,32 +41,57 @@ export function AnimatedInOut({
|
|
|
45
41
|
children,
|
|
46
42
|
}: Props) {
|
|
47
43
|
const [animatedValue] = React.useState(new Animated.Value(visible ? 1 : 0));
|
|
48
|
-
const
|
|
44
|
+
const animationRef = React.useRef<Animated.CompositeAnimation | null>(null);
|
|
45
|
+
const delayTimerRef = React.useRef<NodeJS.Timeout | null>(null);
|
|
49
46
|
|
|
50
47
|
const previousVisible = usePrevious(toBooleanWithDefaultFalse(visible));
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
49
|
+
const startAnimation = React.useCallback(
|
|
50
|
+
(toValue: number, config: AnimationConfig) => {
|
|
51
|
+
if (delayTimerRef.current) {
|
|
52
|
+
clearTimeout(delayTimerRef.current);
|
|
53
|
+
delayTimerRef.current = null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (animationRef.current) {
|
|
57
|
+
animationRef.current.stop();
|
|
58
|
+
animationRef.current = null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const { duration, easing, delay = 0, onAnimationEnd = noop } = config;
|
|
62
|
+
|
|
63
|
+
const runAnimation = () => {
|
|
64
|
+
animationRef.current = Animated.timing(animatedValue, {
|
|
65
|
+
duration,
|
|
66
|
+
toValue,
|
|
67
|
+
easing,
|
|
68
|
+
useNativeDriver: true,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
animationRef.current.start(({ finished }) => {
|
|
72
|
+
if (finished) {
|
|
73
|
+
animationRef.current = null;
|
|
74
|
+
onAnimationEnd();
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
if (delay > 0) {
|
|
80
|
+
delayTimerRef.current = setTimeout(runAnimation, delay);
|
|
81
|
+
} else {
|
|
82
|
+
runAnimation();
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
[animatedValue]
|
|
86
|
+
);
|
|
58
87
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
easing,
|
|
63
|
-
delay,
|
|
64
|
-
useNativeDriver: true,
|
|
65
|
-
}).start(() => {
|
|
66
|
-
setAnimating(undefined);
|
|
67
|
-
onAnimationEnd();
|
|
68
|
-
});
|
|
88
|
+
React.useEffect(() => {
|
|
89
|
+
if (previousVisible === undefined) {
|
|
90
|
+
animatedValue.setValue(visible ? 1 : 0);
|
|
69
91
|
|
|
70
|
-
|
|
71
|
-
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
72
94
|
|
|
73
|
-
React.useEffect(() => {
|
|
74
95
|
if (!previousVisible && visible) {
|
|
75
96
|
startAnimation(1.0, getAnimation(animationConfig, "in"));
|
|
76
97
|
}
|
|
@@ -78,7 +99,29 @@ export function AnimatedInOut({
|
|
|
78
99
|
if (previousVisible && !visible) {
|
|
79
100
|
startAnimation(0.0, getAnimation(animationConfig, "out"));
|
|
80
101
|
}
|
|
81
|
-
}, [
|
|
102
|
+
}, [
|
|
103
|
+
visible,
|
|
104
|
+
previousVisible,
|
|
105
|
+
animatedValue,
|
|
106
|
+
startAnimation,
|
|
107
|
+
animationConfig,
|
|
108
|
+
]);
|
|
109
|
+
|
|
110
|
+
React.useEffect(() => {
|
|
111
|
+
return () => {
|
|
112
|
+
if (delayTimerRef.current) {
|
|
113
|
+
clearTimeout(delayTimerRef.current);
|
|
114
|
+
delayTimerRef.current = null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (animationRef.current) {
|
|
118
|
+
animationRef.current.stop();
|
|
119
|
+
animationRef.current = null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
animatedValue.stopAnimation();
|
|
123
|
+
};
|
|
124
|
+
}, [animatedValue]);
|
|
82
125
|
|
|
83
126
|
const styles = visible
|
|
84
127
|
? getAnimation(animationConfig, "in").styles
|
|
@@ -86,7 +129,7 @@ export function AnimatedInOut({
|
|
|
86
129
|
|
|
87
130
|
return (
|
|
88
131
|
<Animated.View
|
|
89
|
-
renderToHardwareTextureAndroid={
|
|
132
|
+
renderToHardwareTextureAndroid={!!animationRef.current}
|
|
90
133
|
style={[styles(animatedValue), staticStyles]}
|
|
91
134
|
>
|
|
92
135
|
{children}
|
|
@@ -22,6 +22,7 @@ type Props = {
|
|
|
22
22
|
onFocus?: FocusManager.FocusEventCB;
|
|
23
23
|
onBlur?: FocusManager.FocusEventCB;
|
|
24
24
|
selected?: boolean;
|
|
25
|
+
skipFocusManagerRegistration?: boolean;
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
export class BaseFocusable<
|
|
@@ -61,10 +62,14 @@ export class BaseFocusable<
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
componentDidMount() {
|
|
64
|
-
const { id } = this.props;
|
|
65
|
+
const { id, skipFocusManagerRegistration } = this.props;
|
|
65
66
|
const component = this;
|
|
66
67
|
this.node = this.ref.current;
|
|
67
68
|
|
|
69
|
+
if (skipFocusManagerRegistration) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
68
73
|
focusManager.register({
|
|
69
74
|
id,
|
|
70
75
|
component: component,
|
|
@@ -118,7 +123,12 @@ export class BaseFocusable<
|
|
|
118
123
|
|
|
119
124
|
componentWillUnmount() {
|
|
120
125
|
this._isMounted = false;
|
|
121
|
-
const { id } = this.props;
|
|
126
|
+
const { id, skipFocusManagerRegistration } = this.props;
|
|
127
|
+
|
|
128
|
+
if (skipFocusManagerRegistration) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
122
132
|
focusManager.unregister(id, { group: this.isGroup || false });
|
|
123
133
|
}
|
|
124
134
|
|
package/Components/Cell/Cell.tsx
CHANGED
|
@@ -208,14 +208,14 @@ export class CellComponent extends React.Component<Props, State> {
|
|
|
208
208
|
this.accessibilityManager.readText({
|
|
209
209
|
text: " ",
|
|
210
210
|
});
|
|
211
|
-
} else {
|
|
211
|
+
} else if (this.state.cellFocused) {
|
|
212
212
|
this.accessibilityManager.readText({
|
|
213
213
|
text: `${positionLabel}`,
|
|
214
214
|
});
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
componentDidUpdate(prevProps: Readonly<Props>) {
|
|
218
|
+
componentDidUpdate(prevProps: Readonly<Props>, prevState: Readonly<State>) {
|
|
219
219
|
if (prevProps.item !== this.props.item) {
|
|
220
220
|
this.setState({
|
|
221
221
|
hasFocusableInside: this.props.CellRenderer.hasFocusableInside?.(
|
|
@@ -224,7 +224,12 @@ export class CellComponent extends React.Component<Props, State> {
|
|
|
224
224
|
});
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
|
|
227
|
+
if (
|
|
228
|
+
prevState.cellFocused !== this.state.cellFocused ||
|
|
229
|
+
this.state.hasFocusableInside
|
|
230
|
+
) {
|
|
231
|
+
this.handleAccessibilityFocus(this.props.index, this.props.dataLength);
|
|
232
|
+
}
|
|
228
233
|
}
|
|
229
234
|
|
|
230
235
|
render() {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable/FocusableTvOS";
|
|
3
|
+
import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
id: string;
|
|
7
|
+
groupId: string;
|
|
8
|
+
isParallaxDisabled: boolean;
|
|
9
|
+
applyWrapper: boolean;
|
|
10
|
+
children: (focused: boolean) => React.ReactNode;
|
|
11
|
+
onFocus: (arg1: any, index?: number) => void;
|
|
12
|
+
onBlur: Callback;
|
|
13
|
+
skipFocusManagerRegistration?: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const FocusableWrapper = ({
|
|
17
|
+
id,
|
|
18
|
+
groupId,
|
|
19
|
+
isParallaxDisabled,
|
|
20
|
+
children,
|
|
21
|
+
applyWrapper,
|
|
22
|
+
onFocus,
|
|
23
|
+
onBlur,
|
|
24
|
+
skipFocusManagerRegistration,
|
|
25
|
+
}: Props) => {
|
|
26
|
+
if (applyWrapper) {
|
|
27
|
+
return (
|
|
28
|
+
<Focusable
|
|
29
|
+
id={id}
|
|
30
|
+
groupId={groupId}
|
|
31
|
+
isParallaxDisabled={isParallaxDisabled}
|
|
32
|
+
onFocus={onFocus}
|
|
33
|
+
onBlur={onBlur}
|
|
34
|
+
willReceiveFocus={noop}
|
|
35
|
+
hasReceivedFocus={noop}
|
|
36
|
+
// @ts-ignore
|
|
37
|
+
offsetUpdater={noop}
|
|
38
|
+
isFocusable
|
|
39
|
+
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
40
|
+
>
|
|
41
|
+
{(focused) => children(focused)}
|
|
42
|
+
</Focusable>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return <>{children(false)}</>;
|
|
47
|
+
};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import * as R from "ramda";
|
|
3
3
|
import { View, StyleSheet } from "react-native";
|
|
4
|
+
import { first, filter } from "rxjs/operators";
|
|
5
|
+
|
|
6
|
+
import { compose } from "@applicaster/zapp-react-native-utils/utils";
|
|
4
7
|
|
|
5
8
|
import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable/FocusableTvOS";
|
|
6
9
|
import { FocusableCell } from "@applicaster/zapp-react-native-ui-components/Components/FocusableCell";
|
|
@@ -9,7 +12,12 @@ import { SCREEN_TYPES } from "@applicaster/zapp-react-native-utils/navigationUti
|
|
|
9
12
|
import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
|
|
10
13
|
import { sendSelectCellEvent } from "@applicaster/zapp-react-native-utils/analyticsUtils";
|
|
11
14
|
import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
15
|
+
import { toBooleanWithDefaultTrue } from "@applicaster/zapp-react-native-utils/booleanUtils";
|
|
12
16
|
import { CellWithFocusable } from "./CellWithFocusable";
|
|
17
|
+
import { FocusableWrapper } from "./FocusableWrapper";
|
|
18
|
+
|
|
19
|
+
import { focusableButtonsRegistration$ } from "@applicaster/zapp-react-native-utils/appUtils/focusManagerAux/utils/utils.ios";
|
|
20
|
+
import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
13
21
|
|
|
14
22
|
type Props = {
|
|
15
23
|
item: ZappEntry;
|
|
@@ -30,6 +38,10 @@ type Props = {
|
|
|
30
38
|
component: {
|
|
31
39
|
id: number | string;
|
|
32
40
|
component_type: string;
|
|
41
|
+
styles?: {
|
|
42
|
+
component_margin_top?: number;
|
|
43
|
+
component_padding_top?: number;
|
|
44
|
+
};
|
|
33
45
|
};
|
|
34
46
|
selected: boolean;
|
|
35
47
|
CellRenderer: React.FunctionComponent<any> & {
|
|
@@ -66,6 +78,9 @@ type Props = {
|
|
|
66
78
|
shouldUpdate: boolean;
|
|
67
79
|
behavior: Behavior;
|
|
68
80
|
componentsMapOffset: number;
|
|
81
|
+
applyFocusableWrapper: boolean;
|
|
82
|
+
hasFocusableInside: boolean;
|
|
83
|
+
skipFocusManagerRegistration?: boolean;
|
|
69
84
|
};
|
|
70
85
|
|
|
71
86
|
type State = {
|
|
@@ -82,7 +97,7 @@ const baseCellStyles = {
|
|
|
82
97
|
flex: 1,
|
|
83
98
|
} as const;
|
|
84
99
|
|
|
85
|
-
|
|
100
|
+
class TvOSCell extends React.Component<Props, State> {
|
|
86
101
|
cell: any;
|
|
87
102
|
target: any;
|
|
88
103
|
layout: any;
|
|
@@ -192,14 +207,25 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
192
207
|
) {
|
|
193
208
|
const { headerOffset } = getHeaderOffset();
|
|
194
209
|
|
|
195
|
-
const extraAnchorPointYOffset =
|
|
196
|
-
screenLayout?.extraAnchorPointYOffset
|
|
210
|
+
const extraAnchorPointYOffset = toNumberWithDefaultZero(
|
|
211
|
+
screenLayout?.extraAnchorPointYOffset
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
const componentMarginTop = toNumberWithDefaultZero(
|
|
215
|
+
component?.styles?.component_margin_top
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
const componentPaddingTop = toNumberWithDefaultZero(
|
|
219
|
+
component?.styles?.component_padding_top
|
|
220
|
+
);
|
|
197
221
|
|
|
198
222
|
const totalOffset =
|
|
199
223
|
headerOffset +
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
224
|
+
toNumberWithDefaultZero(componentAnchorPointY) +
|
|
225
|
+
extraAnchorPointYOffset -
|
|
226
|
+
toNumberWithDefaultZero(componentsMapOffset) +
|
|
227
|
+
componentMarginTop +
|
|
228
|
+
componentPaddingTop;
|
|
203
229
|
|
|
204
230
|
mainOffsetUpdater?.(
|
|
205
231
|
{ tag: this.target },
|
|
@@ -239,6 +265,9 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
239
265
|
groupId,
|
|
240
266
|
isFocusable,
|
|
241
267
|
behavior,
|
|
268
|
+
applyFocusableWrapper,
|
|
269
|
+
hasFocusableInside,
|
|
270
|
+
skipFocusManagerRegistration,
|
|
242
271
|
} = this.props;
|
|
243
272
|
|
|
244
273
|
const { id } = item;
|
|
@@ -254,24 +283,35 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
254
283
|
this.onFocus(arg1, index);
|
|
255
284
|
};
|
|
256
285
|
|
|
257
|
-
const hasFocusableInside = CellRenderer.hasFocusableInside?.(item);
|
|
258
|
-
|
|
259
286
|
if (hasFocusableInside) {
|
|
260
287
|
return (
|
|
261
288
|
<View onLayout={this.onLayout}>
|
|
262
|
-
<
|
|
263
|
-
CellRenderer={CellRenderer}
|
|
264
|
-
item={item}
|
|
289
|
+
<FocusableWrapper
|
|
265
290
|
id={focusableId}
|
|
266
|
-
groupId={(groupId || component?.id)
|
|
291
|
+
groupId={String(groupId || component?.id)}
|
|
292
|
+
isParallaxDisabled={this.layout?.width > 1740}
|
|
267
293
|
onFocus={handleFocus}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
294
|
+
onBlur={onBlur || this.onBlur}
|
|
295
|
+
applyWrapper={applyFocusableWrapper}
|
|
296
|
+
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
297
|
+
>
|
|
298
|
+
{(focused) => (
|
|
299
|
+
<CellWithFocusable
|
|
300
|
+
CellRenderer={CellRenderer}
|
|
301
|
+
item={item}
|
|
302
|
+
id={focusableId}
|
|
303
|
+
groupId={(groupId || component?.id).toString()}
|
|
304
|
+
onFocus={handleFocus}
|
|
305
|
+
index={index}
|
|
306
|
+
scrollTo={this.scrollTo}
|
|
307
|
+
preferredFocus={preferredFocus}
|
|
308
|
+
focused={focused || this.props.focused}
|
|
309
|
+
behavior={behavior}
|
|
310
|
+
isFocusable={isFocusable}
|
|
311
|
+
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
312
|
+
/>
|
|
313
|
+
)}
|
|
314
|
+
</FocusableWrapper>
|
|
275
315
|
</View>
|
|
276
316
|
);
|
|
277
317
|
}
|
|
@@ -291,6 +331,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
291
331
|
offsetUpdater={offsetUpdater}
|
|
292
332
|
style={baseCellStyles}
|
|
293
333
|
isFocusable={isFocusable}
|
|
334
|
+
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
294
335
|
>
|
|
295
336
|
{(focused) => (
|
|
296
337
|
<FocusableCell
|
|
@@ -309,3 +350,49 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
309
350
|
);
|
|
310
351
|
}
|
|
311
352
|
}
|
|
353
|
+
|
|
354
|
+
export function withFocusableWrapperHOC(Component) {
|
|
355
|
+
return function WrappedComponent(props) {
|
|
356
|
+
const [focusableViewIsRendered, setFocusableViewIsRendered] =
|
|
357
|
+
React.useState(false);
|
|
358
|
+
|
|
359
|
+
const { CellRenderer, item, groupId, component } = props;
|
|
360
|
+
|
|
361
|
+
const isFocusable = toBooleanWithDefaultTrue(props?.isFocusable);
|
|
362
|
+
|
|
363
|
+
const focusableGroupId = String(groupId || component?.id);
|
|
364
|
+
|
|
365
|
+
const hasFocusableInside = CellRenderer.hasFocusableInside?.(item);
|
|
366
|
+
|
|
367
|
+
React.useEffect(() => {
|
|
368
|
+
// start waiting any first registration of FocusableButton inside this focusableGroup
|
|
369
|
+
// after it we could get rid of applying focusable-wrapper
|
|
370
|
+
const subscription = focusableButtonsRegistration$(focusableGroupId)
|
|
371
|
+
.pipe(
|
|
372
|
+
filter(() => isFocusable),
|
|
373
|
+
first()
|
|
374
|
+
)
|
|
375
|
+
.subscribe(() => {
|
|
376
|
+
setFocusableViewIsRendered(true);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
return () => {
|
|
380
|
+
subscription.unsubscribe();
|
|
381
|
+
};
|
|
382
|
+
}, [isFocusable, focusableGroupId]);
|
|
383
|
+
|
|
384
|
+
const applyFocusableWrapper = React.useMemo(() => {
|
|
385
|
+
return isFocusable && hasFocusableInside && !focusableViewIsRendered;
|
|
386
|
+
}, [isFocusable, hasFocusableInside, focusableViewIsRendered]);
|
|
387
|
+
|
|
388
|
+
return (
|
|
389
|
+
<Component
|
|
390
|
+
{...props}
|
|
391
|
+
applyFocusableWrapper={applyFocusableWrapper}
|
|
392
|
+
hasFocusableInside={hasFocusableInside}
|
|
393
|
+
/>
|
|
394
|
+
);
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export const TvOSCellComponent = compose(withFocusableWrapperHOC)(TvOSCell);
|
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
forceFocusableFocus,
|
|
11
11
|
} from "@applicaster/zapp-react-native-utils/appUtils/focusManager/index.ios";
|
|
12
12
|
import { findNodeHandle, ViewStyle } from "react-native";
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
import { emitNativeRegistered } from "@applicaster/zapp-react-native-utils/appUtils/focusManagerAux/utils/utils.ios";
|
|
14
|
+
import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
15
15
|
|
|
16
16
|
type Props = {
|
|
17
17
|
id: string;
|
|
@@ -39,6 +39,7 @@ type Props = {
|
|
|
39
39
|
hasReceivedFocus: () => void;
|
|
40
40
|
offsetUpdater: (arg1: string, arg2: number) => number;
|
|
41
41
|
style: ViewStyle;
|
|
42
|
+
skipFocusManagerRegistration?: boolean;
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
export class Focusable extends BaseFocusable<Props> {
|
|
@@ -53,6 +54,7 @@ export class Focusable extends BaseFocusable<Props> {
|
|
|
53
54
|
this.nextFocusableReactTags = {};
|
|
54
55
|
this.preferredFocus = this.preferredFocus.bind(this);
|
|
55
56
|
this.measureView = this.measureView.bind(this);
|
|
57
|
+
this.onRegistered = this.onRegistered.bind(this);
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
/**
|
|
@@ -169,6 +171,13 @@ export class Focusable extends BaseFocusable<Props> {
|
|
|
169
171
|
});
|
|
170
172
|
}
|
|
171
173
|
|
|
174
|
+
onRegistered({ nativeEvent }) {
|
|
175
|
+
const groupId = nativeEvent?.groupId;
|
|
176
|
+
const id = nativeEvent?.itemId;
|
|
177
|
+
|
|
178
|
+
emitNativeRegistered({ id, groupId, isGroup: false });
|
|
179
|
+
}
|
|
180
|
+
|
|
172
181
|
render() {
|
|
173
182
|
const {
|
|
174
183
|
children,
|
|
@@ -203,6 +212,7 @@ export class Focusable extends BaseFocusable<Props> {
|
|
|
203
212
|
focusable={isFocusable}
|
|
204
213
|
{...this.nextFocusableReactTags}
|
|
205
214
|
{...otherProps}
|
|
215
|
+
onRegistered={this.onRegistered}
|
|
206
216
|
>
|
|
207
217
|
{typeof children === "function" ? children(focused) : children}
|
|
208
218
|
</FocusableItemNative>
|
|
@@ -2,6 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import { FocusableGroupNative } from "@applicaster/zapp-react-native-ui-components/Components/NativeFocusables";
|
|
3
3
|
import { BaseFocusable } from "@applicaster/zapp-react-native-ui-components/Components/BaseFocusable";
|
|
4
4
|
import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
|
|
5
|
+
import { emitNativeRegistered } from "@applicaster/zapp-react-native-utils/appUtils/focusManagerAux/utils/utils.ios";
|
|
5
6
|
|
|
6
7
|
const { log_verbose } = createLogger({
|
|
7
8
|
subsystem: "General",
|
|
@@ -34,6 +35,15 @@ type Props = {
|
|
|
34
35
|
};
|
|
35
36
|
|
|
36
37
|
export class FocusableGroup extends BaseFocusable<Props> {
|
|
38
|
+
public readonly isGroup: boolean = true;
|
|
39
|
+
|
|
40
|
+
onRegistered = ({ nativeEvent }) => {
|
|
41
|
+
const groupId = nativeEvent?.groupId;
|
|
42
|
+
const id = nativeEvent?.itemId;
|
|
43
|
+
|
|
44
|
+
emitNativeRegistered({ id, groupId, isGroup: true });
|
|
45
|
+
};
|
|
46
|
+
|
|
37
47
|
render() {
|
|
38
48
|
const {
|
|
39
49
|
children,
|
|
@@ -66,6 +76,7 @@ export class FocusableGroup extends BaseFocusable<Props> {
|
|
|
66
76
|
onGroupBlur={onGroupBlur}
|
|
67
77
|
style={style}
|
|
68
78
|
{...otherProps}
|
|
79
|
+
onRegistered={this.onRegistered}
|
|
69
80
|
>
|
|
70
81
|
{children}
|
|
71
82
|
</FocusableGroupNative>
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import * as R from "ramda";
|
|
3
2
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
useAppData,
|
|
4
|
+
useContentTypes,
|
|
5
|
+
usePlugins,
|
|
6
|
+
} from "@applicaster/zapp-react-native-redux/hooks";
|
|
8
7
|
import {
|
|
9
8
|
useDimensions,
|
|
10
9
|
useIsTablet as isTablet,
|
|
11
10
|
useNavigation,
|
|
11
|
+
useRivers,
|
|
12
12
|
} from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
13
13
|
|
|
14
14
|
import { BufferAnimation } from "../PlayerContainer/BufferAnimation";
|
|
@@ -16,6 +16,7 @@ import { PlayerContainer } from "../PlayerContainer";
|
|
|
16
16
|
import { useModalSize } from "../VideoModal/hooks";
|
|
17
17
|
import { ViewStyle } from "react-native";
|
|
18
18
|
import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
19
|
+
import { findCastPlugin, getPlayer } from "./utils";
|
|
19
20
|
|
|
20
21
|
type Props = {
|
|
21
22
|
item: ZappEntry;
|
|
@@ -26,62 +27,6 @@ type Props = {
|
|
|
26
27
|
groupId?: string;
|
|
27
28
|
};
|
|
28
29
|
|
|
29
|
-
const YOUTUBE_PLUGIN_ID = "youtube-player-qb";
|
|
30
|
-
const CHROMECAST_PLUGIN_ID = "chromecast_qb";
|
|
31
|
-
|
|
32
|
-
const getPlayerWithModuleProperties = (
|
|
33
|
-
PlayerModule: ZappPlugin
|
|
34
|
-
): [ZappPlugin, PlayerModuleProperties] => {
|
|
35
|
-
const getPlayerModuleProperties = R.ifElse(
|
|
36
|
-
R.is(Object) && R.has("Component"),
|
|
37
|
-
R.omit(["Component"]),
|
|
38
|
-
() => ({})
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
return [
|
|
42
|
-
PlayerModule?.Component || PlayerModule,
|
|
43
|
-
getPlayerModuleProperties(PlayerModule),
|
|
44
|
-
];
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const getPlayer = (
|
|
48
|
-
item: ZappEntry,
|
|
49
|
-
state
|
|
50
|
-
): [ZappPlugin, PlayerModuleProperties] => {
|
|
51
|
-
const {
|
|
52
|
-
plugins,
|
|
53
|
-
contentTypes,
|
|
54
|
-
rivers,
|
|
55
|
-
appData: { layoutVersion },
|
|
56
|
-
} = state;
|
|
57
|
-
|
|
58
|
-
let PlayerModule;
|
|
59
|
-
|
|
60
|
-
if (layoutVersion === "v2") {
|
|
61
|
-
const { screen_id } = contentTypes?.[item?.type?.value] || {};
|
|
62
|
-
const { type } = rivers?.[screen_id] || {};
|
|
63
|
-
|
|
64
|
-
if (type) {
|
|
65
|
-
PlayerModule = findPluginByIdentifier(type, plugins)?.module;
|
|
66
|
-
|
|
67
|
-
return getPlayerWithModuleProperties(PlayerModule);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (item?.content?.type === "youtube-id") {
|
|
72
|
-
PlayerModule = findPluginByIdentifier(YOUTUBE_PLUGIN_ID, plugins)?.module;
|
|
73
|
-
|
|
74
|
-
return getPlayerWithModuleProperties(PlayerModule);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
PlayerModule = findPluginByType(
|
|
78
|
-
"playable",
|
|
79
|
-
plugins.filter(({ identifier }) => identifier !== YOUTUBE_PLUGIN_ID)
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
return getPlayerWithModuleProperties(PlayerModule);
|
|
83
|
-
};
|
|
84
|
-
|
|
85
30
|
type PlayableComponent = {
|
|
86
31
|
Component: React.ComponentType<any>;
|
|
87
32
|
};
|
|
@@ -99,14 +44,21 @@ export function HandlePlayable({
|
|
|
99
44
|
mode,
|
|
100
45
|
groupId,
|
|
101
46
|
}: Props): React.ReactElement | null {
|
|
102
|
-
const
|
|
47
|
+
const plugins = usePlugins();
|
|
48
|
+
const contentTypes = useContentTypes();
|
|
49
|
+
const rivers = useRivers();
|
|
50
|
+
const appData = useAppData();
|
|
103
51
|
|
|
104
52
|
const { closeVideoModal } = useNavigation();
|
|
105
53
|
|
|
106
|
-
const [Player, playerModuleProperties] = getPlayer(item,
|
|
54
|
+
const [Player, playerModuleProperties] = getPlayer(item, {
|
|
55
|
+
plugins,
|
|
56
|
+
contentTypes,
|
|
57
|
+
rivers,
|
|
58
|
+
appData,
|
|
59
|
+
});
|
|
107
60
|
|
|
108
|
-
const { module: CastPlugin } =
|
|
109
|
-
findPluginByIdentifier(CHROMECAST_PLUGIN_ID, state.plugins, true) || {};
|
|
61
|
+
const { module: CastPlugin } = findCastPlugin(plugins);
|
|
110
62
|
|
|
111
63
|
const [playable, setPlayable] =
|
|
112
64
|
React.useState<Nullable<PlayableComponent>>(null);
|