@applicaster/zapp-react-native-ui-components 15.0.0-rc.4 → 15.0.0-rc.41
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/Cell/Cell.tsx +8 -3
- package/Components/Cell/FocusableWrapper.tsx +44 -0
- package/Components/Cell/TvOSCellComponent.tsx +92 -17
- package/Components/HandlePlayable/HandlePlayable.tsx +14 -65
- package/Components/HandlePlayable/const.ts +3 -0
- package/Components/HandlePlayable/utils.ts +74 -0
- package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +16 -1
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +36 -2
- 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/PlayerContainer/PlayerContainer.tsx +1 -16
- package/Components/PlayerImageBackground/index.tsx +3 -22
- package/Components/Screen/TV/hooks/useInitialFocus.ts +14 -4
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
- package/Components/Screen/index.tsx +22 -5
- package/Components/ScreenRevealManager/ScreenRevealManager.ts +40 -8
- package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +86 -69
- package/Components/ScreenRevealManager/utils/index.ts +23 -0
- package/Components/ScreenRevealManager/withScreenRevealManager.tsx +54 -24
- package/Components/Tabs/TV/Tabs.tsx +20 -3
- package/Components/Transitioner/Scene.tsx +15 -2
- package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +1 -0
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +114 -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 +7 -0
- package/index.d.ts +7 -0
- package/package.json +5 -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
|
@@ -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}
|
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,44 @@
|
|
|
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
|
+
};
|
|
14
|
+
|
|
15
|
+
export const FocusableWrapper = ({
|
|
16
|
+
id,
|
|
17
|
+
groupId,
|
|
18
|
+
isParallaxDisabled,
|
|
19
|
+
children,
|
|
20
|
+
applyWrapper,
|
|
21
|
+
onFocus,
|
|
22
|
+
onBlur,
|
|
23
|
+
}: Props) => {
|
|
24
|
+
if (applyWrapper) {
|
|
25
|
+
return (
|
|
26
|
+
<Focusable
|
|
27
|
+
id={id}
|
|
28
|
+
groupId={groupId}
|
|
29
|
+
isParallaxDisabled={isParallaxDisabled}
|
|
30
|
+
onFocus={onFocus}
|
|
31
|
+
onBlur={onBlur}
|
|
32
|
+
willReceiveFocus={noop}
|
|
33
|
+
hasReceivedFocus={noop}
|
|
34
|
+
// @ts-ignore
|
|
35
|
+
offsetUpdater={noop}
|
|
36
|
+
isFocusable
|
|
37
|
+
>
|
|
38
|
+
{(focused) => children(focused)}
|
|
39
|
+
</Focusable>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return <>{children(false)}</>;
|
|
44
|
+
};
|
|
@@ -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,9 @@ type Props = {
|
|
|
30
38
|
component: {
|
|
31
39
|
id: number | string;
|
|
32
40
|
component_type: string;
|
|
41
|
+
styles?: {
|
|
42
|
+
component_margin_top?: number;
|
|
43
|
+
};
|
|
33
44
|
};
|
|
34
45
|
selected: boolean;
|
|
35
46
|
CellRenderer: React.FunctionComponent<any> & {
|
|
@@ -66,6 +77,8 @@ type Props = {
|
|
|
66
77
|
shouldUpdate: boolean;
|
|
67
78
|
behavior: Behavior;
|
|
68
79
|
componentsMapOffset: number;
|
|
80
|
+
applyFocusableWrapper: boolean;
|
|
81
|
+
hasFocusableInside: boolean;
|
|
69
82
|
};
|
|
70
83
|
|
|
71
84
|
type State = {
|
|
@@ -82,7 +95,7 @@ const baseCellStyles = {
|
|
|
82
95
|
flex: 1,
|
|
83
96
|
} as const;
|
|
84
97
|
|
|
85
|
-
|
|
98
|
+
class TvOSCell extends React.Component<Props, State> {
|
|
86
99
|
cell: any;
|
|
87
100
|
target: any;
|
|
88
101
|
layout: any;
|
|
@@ -195,11 +208,16 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
195
208
|
const extraAnchorPointYOffset =
|
|
196
209
|
screenLayout?.extraAnchorPointYOffset || 0;
|
|
197
210
|
|
|
211
|
+
const componentMarginTop = toNumberWithDefaultZero(
|
|
212
|
+
component?.styles?.component_margin_top
|
|
213
|
+
);
|
|
214
|
+
|
|
198
215
|
const totalOffset =
|
|
199
216
|
headerOffset +
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
217
|
+
(componentAnchorPointY || 0) +
|
|
218
|
+
extraAnchorPointYOffset -
|
|
219
|
+
(componentsMapOffset || 0) +
|
|
220
|
+
componentMarginTop;
|
|
203
221
|
|
|
204
222
|
mainOffsetUpdater?.(
|
|
205
223
|
{ tag: this.target },
|
|
@@ -239,6 +257,8 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
239
257
|
groupId,
|
|
240
258
|
isFocusable,
|
|
241
259
|
behavior,
|
|
260
|
+
applyFocusableWrapper,
|
|
261
|
+
hasFocusableInside,
|
|
242
262
|
} = this.props;
|
|
243
263
|
|
|
244
264
|
const { id } = item;
|
|
@@ -254,24 +274,33 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
254
274
|
this.onFocus(arg1, index);
|
|
255
275
|
};
|
|
256
276
|
|
|
257
|
-
const hasFocusableInside = CellRenderer.hasFocusableInside?.(item);
|
|
258
|
-
|
|
259
277
|
if (hasFocusableInside) {
|
|
260
278
|
return (
|
|
261
279
|
<View onLayout={this.onLayout}>
|
|
262
|
-
<
|
|
263
|
-
CellRenderer={CellRenderer}
|
|
264
|
-
item={item}
|
|
280
|
+
<FocusableWrapper
|
|
265
281
|
id={focusableId}
|
|
266
|
-
groupId={(groupId || component?.id)
|
|
282
|
+
groupId={String(groupId || component?.id)}
|
|
283
|
+
isParallaxDisabled={this.layout?.width > 1740}
|
|
267
284
|
onFocus={handleFocus}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
285
|
+
onBlur={onBlur || this.onBlur}
|
|
286
|
+
applyWrapper={applyFocusableWrapper}
|
|
287
|
+
>
|
|
288
|
+
{(focused) => (
|
|
289
|
+
<CellWithFocusable
|
|
290
|
+
CellRenderer={CellRenderer}
|
|
291
|
+
item={item}
|
|
292
|
+
id={focusableId}
|
|
293
|
+
groupId={(groupId || component?.id).toString()}
|
|
294
|
+
onFocus={handleFocus}
|
|
295
|
+
index={index}
|
|
296
|
+
scrollTo={this.scrollTo}
|
|
297
|
+
preferredFocus={preferredFocus}
|
|
298
|
+
focused={focused || this.props.focused}
|
|
299
|
+
behavior={behavior}
|
|
300
|
+
isFocusable={isFocusable}
|
|
301
|
+
/>
|
|
302
|
+
)}
|
|
303
|
+
</FocusableWrapper>
|
|
275
304
|
</View>
|
|
276
305
|
);
|
|
277
306
|
}
|
|
@@ -309,3 +338,49 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
309
338
|
);
|
|
310
339
|
}
|
|
311
340
|
}
|
|
341
|
+
|
|
342
|
+
export function withFocusableWrapperHOC(Component) {
|
|
343
|
+
return function WrappedComponent(props) {
|
|
344
|
+
const [focusableViewIsRendered, setFocusableViewIsRendered] =
|
|
345
|
+
React.useState(false);
|
|
346
|
+
|
|
347
|
+
const { CellRenderer, item, groupId, component } = props;
|
|
348
|
+
|
|
349
|
+
const isFocusable = toBooleanWithDefaultTrue(props?.isFocusable);
|
|
350
|
+
|
|
351
|
+
const focusableGroupId = String(groupId || component?.id);
|
|
352
|
+
|
|
353
|
+
const hasFocusableInside = CellRenderer.hasFocusableInside?.(item);
|
|
354
|
+
|
|
355
|
+
React.useEffect(() => {
|
|
356
|
+
// start waiting any first registration of FocusableButton inside this focusableGroup
|
|
357
|
+
// after it we could get rid of applying focusable-wrapper
|
|
358
|
+
const subscription = focusableButtonsRegistration$(focusableGroupId)
|
|
359
|
+
.pipe(
|
|
360
|
+
filter(() => isFocusable),
|
|
361
|
+
first()
|
|
362
|
+
)
|
|
363
|
+
.subscribe(() => {
|
|
364
|
+
setFocusableViewIsRendered(true);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
return () => {
|
|
368
|
+
subscription.unsubscribe();
|
|
369
|
+
};
|
|
370
|
+
}, [isFocusable, focusableGroupId]);
|
|
371
|
+
|
|
372
|
+
const applyFocusableWrapper = React.useMemo(() => {
|
|
373
|
+
return isFocusable && hasFocusableInside && !focusableViewIsRendered;
|
|
374
|
+
}, [isFocusable, hasFocusableInside, focusableViewIsRendered]);
|
|
375
|
+
|
|
376
|
+
return (
|
|
377
|
+
<Component
|
|
378
|
+
{...props}
|
|
379
|
+
applyFocusableWrapper={applyFocusableWrapper}
|
|
380
|
+
hasFocusableInside={hasFocusableInside}
|
|
381
|
+
/>
|
|
382
|
+
);
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export const TvOSCellComponent = compose(withFocusableWrapperHOC)(TvOSCell);
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import * as R from "ramda";
|
|
3
|
-
import {
|
|
4
|
-
findPluginByType,
|
|
5
|
-
findPluginByIdentifier,
|
|
6
|
-
} from "@applicaster/zapp-react-native-utils/pluginUtils";
|
|
7
2
|
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
8
3
|
import {
|
|
9
4
|
useDimensions,
|
|
@@ -16,6 +11,7 @@ import { PlayerContainer } from "../PlayerContainer";
|
|
|
16
11
|
import { useModalSize } from "../VideoModal/hooks";
|
|
17
12
|
import { ViewStyle } from "react-native";
|
|
18
13
|
import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
14
|
+
import { findCastPlugin, getPlayer } from "./utils";
|
|
19
15
|
|
|
20
16
|
type Props = {
|
|
21
17
|
item: ZappEntry;
|
|
@@ -26,62 +22,6 @@ type Props = {
|
|
|
26
22
|
groupId?: string;
|
|
27
23
|
};
|
|
28
24
|
|
|
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
25
|
type PlayableComponent = {
|
|
86
26
|
Component: React.ComponentType<any>;
|
|
87
27
|
};
|
|
@@ -99,14 +39,23 @@ export function HandlePlayable({
|
|
|
99
39
|
mode,
|
|
100
40
|
groupId,
|
|
101
41
|
}: Props): React.ReactElement | null {
|
|
102
|
-
const
|
|
42
|
+
const { plugins, contentTypes, rivers, appData } = usePickFromState([
|
|
43
|
+
"plugins",
|
|
44
|
+
"contentTypes",
|
|
45
|
+
"rivers",
|
|
46
|
+
"appData",
|
|
47
|
+
]);
|
|
103
48
|
|
|
104
49
|
const { closeVideoModal } = useNavigation();
|
|
105
50
|
|
|
106
|
-
const [Player, playerModuleProperties] = getPlayer(item,
|
|
51
|
+
const [Player, playerModuleProperties] = getPlayer(item, {
|
|
52
|
+
plugins,
|
|
53
|
+
contentTypes,
|
|
54
|
+
rivers,
|
|
55
|
+
appData,
|
|
56
|
+
});
|
|
107
57
|
|
|
108
|
-
const { module: CastPlugin } =
|
|
109
|
-
findPluginByIdentifier(CHROMECAST_PLUGIN_ID, state.plugins, true) || {};
|
|
58
|
+
const { module: CastPlugin } = findCastPlugin(plugins);
|
|
110
59
|
|
|
111
60
|
const [playable, setPlayable] =
|
|
112
61
|
React.useState<Nullable<PlayableComponent>>(null);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import {
|
|
2
|
+
findPluginByIdentifier,
|
|
3
|
+
findPluginByType,
|
|
4
|
+
} from "@applicaster/zapp-react-native-utils/pluginUtils";
|
|
5
|
+
|
|
6
|
+
import { CHROMECAST_PLUGIN_ID, YOUTUBE_PLUGIN_ID } from "./const";
|
|
7
|
+
import { omit } from "@applicaster/zapp-react-native-utils/utils";
|
|
8
|
+
|
|
9
|
+
const getPlayerModuleProperties = (PlayerModule: ZappPlugin) => {
|
|
10
|
+
if (PlayerModule?.Component && typeof PlayerModule.Component === "object") {
|
|
11
|
+
return omit(["Component"], PlayerModule);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return {};
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const getPlayerWithModuleProperties = (
|
|
18
|
+
PlayerModule: ZappPlugin
|
|
19
|
+
): [ZappPlugin, PlayerModuleProperties] => {
|
|
20
|
+
return [
|
|
21
|
+
PlayerModule?.Component || PlayerModule,
|
|
22
|
+
getPlayerModuleProperties(PlayerModule),
|
|
23
|
+
];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const findCastPlugin = (plugins: ZappPlugin[]) =>
|
|
27
|
+
findPluginByIdentifier(CHROMECAST_PLUGIN_ID, plugins, true) || {};
|
|
28
|
+
|
|
29
|
+
export const findYoutubePlugin = (plugins: ZappPlugin[]) =>
|
|
30
|
+
findPluginByIdentifier(YOUTUBE_PLUGIN_ID, plugins, true) || {};
|
|
31
|
+
|
|
32
|
+
export const getPlayer = (
|
|
33
|
+
item: ZappEntry,
|
|
34
|
+
{
|
|
35
|
+
plugins,
|
|
36
|
+
contentTypes,
|
|
37
|
+
rivers,
|
|
38
|
+
appData: { layoutVersion },
|
|
39
|
+
}: {
|
|
40
|
+
plugins: ZappPlugin[];
|
|
41
|
+
contentTypes: Record<string, any>;
|
|
42
|
+
rivers: Record<string, any>;
|
|
43
|
+
appData: { layoutVersion: string };
|
|
44
|
+
}
|
|
45
|
+
): [ZappPlugin, PlayerModuleProperties] => {
|
|
46
|
+
let PlayerModule;
|
|
47
|
+
|
|
48
|
+
if (layoutVersion === "v2") {
|
|
49
|
+
const screen_id = contentTypes?.[item?.type?.value]?.screen_id;
|
|
50
|
+
const type = rivers?.[screen_id]?.type;
|
|
51
|
+
|
|
52
|
+
if (type) {
|
|
53
|
+
PlayerModule = findPluginByIdentifier(type, plugins)?.module;
|
|
54
|
+
|
|
55
|
+
return getPlayerWithModuleProperties(PlayerModule);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (item?.content?.type === "youtube-id") {
|
|
60
|
+
PlayerModule = findYoutubePlugin(plugins)?.module;
|
|
61
|
+
|
|
62
|
+
return getPlayerWithModuleProperties(PlayerModule);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
PlayerModule = findPluginByType(
|
|
66
|
+
"playable",
|
|
67
|
+
(plugins as any[]).filter(
|
|
68
|
+
({ identifier }: { identifier: string }) =>
|
|
69
|
+
identifier !== YOUTUBE_PLUGIN_ID
|
|
70
|
+
)
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
return getPlayerWithModuleProperties(PlayerModule);
|
|
74
|
+
};
|
package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import * as React from "react";
|
|
1
2
|
import {
|
|
2
3
|
BorderContainerView,
|
|
3
4
|
getBorderPadding, // Export for testing (using a double underscore prefix is a common convention)
|
|
4
5
|
} from "../index";
|
|
5
|
-
import * as React from "react";
|
|
6
6
|
import { render } from "@testing-library/react-native";
|
|
7
7
|
import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
8
8
|
import { View } from "react-native";
|
|
@@ -11,6 +11,15 @@ jest.mock("@applicaster/zapp-react-native-utils/numberUtils", () => ({
|
|
|
11
11
|
toNumberWithDefaultZero: jest.fn((value) => Number(value) || 0),
|
|
12
12
|
}));
|
|
13
13
|
|
|
14
|
+
jest.mock(
|
|
15
|
+
"@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks",
|
|
16
|
+
() => ({
|
|
17
|
+
useAccessibilityManager: jest.fn(() => ({
|
|
18
|
+
addHeading: jest.fn(),
|
|
19
|
+
})),
|
|
20
|
+
})
|
|
21
|
+
);
|
|
22
|
+
|
|
14
23
|
describe("BorderContainerView", () => {
|
|
15
24
|
describe("getBorderPadding", () => {
|
|
16
25
|
it("returns 0 for inside", () => {
|
|
@@ -42,6 +51,8 @@ describe("BorderContainerView", () => {
|
|
|
42
51
|
};
|
|
43
52
|
|
|
44
53
|
const borderPosition = null;
|
|
54
|
+
const mockEntry = { id: "test-entry" } as ZappEntry;
|
|
55
|
+
const mockHasFocusableInside = jest.fn(() => false);
|
|
45
56
|
|
|
46
57
|
const { queryByTestId } = render(
|
|
47
58
|
<BorderContainerView
|
|
@@ -52,6 +63,10 @@ describe("BorderContainerView", () => {
|
|
|
52
63
|
borderPaddingRight={toNumberWithDefaultZero(padding.paddingRight)}
|
|
53
64
|
borderPaddingBottom={toNumberWithDefaultZero(padding.paddingBottom)}
|
|
54
65
|
borderPaddingLeft={toNumberWithDefaultZero(padding.paddingLeft)}
|
|
66
|
+
hasFocusableInside={mockHasFocusableInside}
|
|
67
|
+
entry={mockEntry}
|
|
68
|
+
state="focused"
|
|
69
|
+
hasTextLabels={false}
|
|
55
70
|
>
|
|
56
71
|
<View testID="child" />
|
|
57
72
|
</BorderContainerView>
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import * as React from "react";
|
|
1
|
+
import React, { useMemo, useContext, useEffect } from "react";
|
|
3
2
|
import { ImageStyle, StyleSheet, View, ViewStyle } from "react-native";
|
|
3
|
+
import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
|
|
4
|
+
import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
5
|
+
import { MeasurementPortalContext } from "../../../MeasurmentsPortal/MeasurementsPortal";
|
|
4
6
|
|
|
5
7
|
type BorderPosition = "inside" | "outside" | "center";
|
|
6
8
|
|
|
7
9
|
interface Props {
|
|
10
|
+
hasFocusableInside: (entry: ZappEntry) => boolean;
|
|
11
|
+
entry: ZappEntry;
|
|
12
|
+
state: CellState;
|
|
13
|
+
hasTextLabels: boolean;
|
|
8
14
|
style: ImageStyle | ViewStyle;
|
|
9
15
|
borderPosition: BorderPosition;
|
|
10
16
|
borderPaddingTop: number;
|
|
@@ -118,8 +124,36 @@ export const BorderContainerView = (props: Props) => {
|
|
|
118
124
|
borderPaddingLeft,
|
|
119
125
|
style,
|
|
120
126
|
children,
|
|
127
|
+
hasFocusableInside,
|
|
128
|
+
entry,
|
|
129
|
+
state,
|
|
130
|
+
hasTextLabels,
|
|
121
131
|
} = props;
|
|
122
132
|
|
|
133
|
+
const accessibilityManager = useAccessibilityManager();
|
|
134
|
+
const isMeasurement = useContext(MeasurementPortalContext);
|
|
135
|
+
|
|
136
|
+
const isImageOnlyCell = useMemo(
|
|
137
|
+
() =>
|
|
138
|
+
!hasFocusableInside(entry) &&
|
|
139
|
+
!hasTextLabels &&
|
|
140
|
+
state === "focused" &&
|
|
141
|
+
!isMeasurement?.measuringInProgress,
|
|
142
|
+
[
|
|
143
|
+
hasFocusableInside,
|
|
144
|
+
entry,
|
|
145
|
+
hasTextLabels,
|
|
146
|
+
state,
|
|
147
|
+
isMeasurement?.measuringInProgress,
|
|
148
|
+
]
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
if (isImageOnlyCell && entry?.title) {
|
|
153
|
+
accessibilityManager.addHeading(String(entry.title));
|
|
154
|
+
}
|
|
155
|
+
}, [isImageOnlyCell, entry?.title]);
|
|
156
|
+
|
|
123
157
|
const padding =
|
|
124
158
|
borderPosition === "outside"
|
|
125
159
|
? {
|