@applicaster/zapp-react-native-ui-components 12.0.0-alpha.4536343143 → 12.0.0-alpha.4576271826
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/BaseFocusable/index.tsx +2 -2
- package/Components/Cell/Cell.tsx +26 -5
- package/Components/Cell/CellWithFocusable.tsx +6 -4
- package/Components/Cell/TvOSCellComponent.tsx +3 -7
- package/Components/Focusable/Focusable.tsx +6 -1
- package/Components/FocusableGroup/index.tsx +10 -2
- package/Components/FreezeWithCallback/Freeze.tsx +31 -0
- package/Components/FreezeWithCallback/LICENSE +21 -0
- package/Components/FreezeWithCallback/Suspender.tsx +17 -0
- package/Components/FreezeWithCallback/__tests__/index.test.tsx +170 -0
- package/Components/FreezeWithCallback/index.tsx +3 -0
- package/Components/Layout/TV/ScreenContainer.tsx +79 -5
- package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +38 -28
- package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +25 -13
- package/Components/Layout/TV/__tests__/index.test.tsx +2 -1
- package/Components/Layout/TV/index.tsx +11 -65
- package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -1
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +8 -9
- package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -1
- package/Components/MasterCell/DefaultComponents/ImageBorderContainer/index.ts +71 -0
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +20 -0
- package/Components/MasterCell/DefaultComponents/index.ts +2 -0
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +6 -1
- package/Components/MasterCell/SharedUI/ProgressBar/hooks/useFillInPercent.ts +7 -1
- package/Components/MasterCell/dataAdapter.ts +21 -7
- package/Components/MasterCell/index.tsx +5 -3
- package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
- package/Components/MasterCell/utils/index.ts +17 -30
- package/Components/Navigator/StackNavigator.tsx +14 -5
- package/Components/PlayerContainer/PlayerContainer.tsx +2 -0
- package/Components/Screen/TV/hooks/index.ts +0 -2
- package/Components/Screen/TV/index.web.tsx +40 -41
- package/Components/Screen/__tests__/Screen.test.tsx +3 -49
- package/Components/Screen/index.tsx +55 -76
- package/Components/Screen/navigationHandler.ts +6 -3
- package/Components/Screen/orientationHandler.ts +18 -7
- package/Components/Screen/utils.ts +16 -0
- package/Components/ScreenResolver/index.tsx +6 -11
- package/Components/Transitioner/Scene.tsx +20 -12
- package/Components/Transitioner/Transitioner.tsx +40 -49
- package/Components/Transitioner/__tests__/Scene.test.js +7 -0
- package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +8 -4
- package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +1 -0
- package/Components/VideoLive/LiveImageManager.ts +10 -0
- package/Components/VideoLive/PlayerLiveImageComponent.tsx +52 -24
- package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +26 -24
- package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +2 -14
- package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +79 -45
- package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +47 -33
- package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +121 -86
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +32 -4
- package/Components/VideoModal/ModalAnimation/utils.ts +7 -4
- package/Components/VideoModal/PlayerWrapper.tsx +2 -2
- package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +80 -14
- package/Components/VideoModal/hooks/useModalSize.ts +10 -12
- package/Components/VideoModal/utils.ts +5 -2
- package/Components/ZappFrameworkComponents/BarView/BarView.tsx +4 -3
- package/Components/default-cell-renderer/__tests__/__snapshots__/defaultCellRenderer.test.tsx.snap +4 -4
- package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
- package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
- package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
- package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
- package/Contexts/FocusableGroupContext/index.ts +7 -0
- package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
- package/Contexts/ScreenContext/index.tsx +0 -6
- package/package.json +5 -5
- package/Components/Screen/TV/hooks/useRestoreFocus.ts +0 -22
- /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
- /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { View } from "react-native";
|
|
3
|
-
import { render } from "@testing-library/react-native";
|
|
3
|
+
import { render, waitFor } from "@testing-library/react-native";
|
|
4
|
+
import { WrappedWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
|
|
4
5
|
|
|
5
6
|
import { ScreenLayoutContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenLayoutContext";
|
|
6
7
|
|
|
7
8
|
const navigator_mock = {
|
|
8
9
|
currentRoute: "/river/A1234",
|
|
9
10
|
screenData: {},
|
|
11
|
+
activeRiver: {
|
|
12
|
+
navigations: {},
|
|
13
|
+
id: "A1234",
|
|
14
|
+
home: false,
|
|
15
|
+
},
|
|
10
16
|
};
|
|
11
17
|
|
|
18
|
+
jest.mock("@applicaster/zapp-react-native-utils/navigationUtils", () => ({
|
|
19
|
+
...jest.requireActual<any>(
|
|
20
|
+
"@applicaster/zapp-react-native-utils/navigationUtils"
|
|
21
|
+
),
|
|
22
|
+
findMenuPlugin: () => ({}),
|
|
23
|
+
}));
|
|
24
|
+
|
|
12
25
|
jest.mock("@applicaster/quick-brick-core/App/ActionSetters", () => ({
|
|
13
26
|
useRiverInitialState: () => ({}),
|
|
14
27
|
}));
|
|
@@ -17,13 +30,13 @@ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
|
|
|
17
30
|
useNavigation: () => navigator_mock,
|
|
18
31
|
}));
|
|
19
32
|
|
|
20
|
-
jest.mock("../../../River/useScreenConfiguration
|
|
33
|
+
jest.mock("../../../River/useScreenConfiguration", () => ({
|
|
21
34
|
useScreenConfiguration: () => ({}),
|
|
22
35
|
}));
|
|
23
36
|
|
|
24
|
-
jest.mock(
|
|
25
|
-
useCurrentScreenData
|
|
26
|
-
|
|
37
|
+
jest.mock(
|
|
38
|
+
"@applicaster/zapp-react-native-utils/reactHooks/screen/useCurrentScreenData"
|
|
39
|
+
);
|
|
27
40
|
|
|
28
41
|
jest.mock("@applicaster/zapp-react-native-utils/theme", () => ({
|
|
29
42
|
useTheme: () => ({}),
|
|
@@ -44,42 +57,39 @@ describe("ScreenContainer", () => {
|
|
|
44
57
|
const setScreenLayout = () => {};
|
|
45
58
|
|
|
46
59
|
const wrapper = ({ children }) => (
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
60
|
+
<WrappedWithProviders>
|
|
61
|
+
<ScreenLayoutContext.Provider
|
|
62
|
+
value={{ ...screenLayout, setScreenLayout }}
|
|
63
|
+
>
|
|
64
|
+
<ScreenContainer NavBar={View}>{children}</ScreenContainer>
|
|
65
|
+
</ScreenLayoutContext.Provider>
|
|
66
|
+
</WrappedWithProviders>
|
|
50
67
|
);
|
|
51
68
|
|
|
52
69
|
const TestComp = () => <View testID="test-component" />;
|
|
53
70
|
|
|
54
71
|
it("renders", () => {
|
|
55
|
-
const { toJSON } = render(
|
|
56
|
-
<ScreenContainer>
|
|
57
|
-
<TestComp />
|
|
58
|
-
</ScreenContainer>,
|
|
59
|
-
{ wrapper }
|
|
60
|
-
);
|
|
72
|
+
const { toJSON } = render(<TestComp />, { wrapper });
|
|
61
73
|
|
|
62
74
|
expect(toJSON()).toMatchSnapshot();
|
|
63
75
|
});
|
|
64
76
|
|
|
65
|
-
it("renders passed component", () => {
|
|
66
|
-
const { getByTestId } = render(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
);
|
|
77
|
+
it("renders passed component", async () => {
|
|
78
|
+
const { getByTestId } = render(<TestComp />, { wrapper });
|
|
79
|
+
|
|
80
|
+
await waitFor(() => {
|
|
81
|
+
return expect(getByTestId("screen-container")).toBeDefined();
|
|
82
|
+
});
|
|
72
83
|
|
|
73
84
|
expect(getByTestId("test-component")).toBeDefined();
|
|
74
85
|
});
|
|
75
86
|
|
|
76
|
-
it("sets margins provided by the theme", () => {
|
|
77
|
-
const { getByTestId } = render(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
);
|
|
87
|
+
it("sets margins provided by the theme", async () => {
|
|
88
|
+
const { getByTestId } = render(<TestComp />, { wrapper });
|
|
89
|
+
|
|
90
|
+
await waitFor(() => {
|
|
91
|
+
return expect(getByTestId("screen-container")).toBeDefined();
|
|
92
|
+
});
|
|
83
93
|
|
|
84
94
|
const screen = getByTestId("screen-container");
|
|
85
95
|
expect(screen.props.style).toMatchSnapshot();
|
|
@@ -1,28 +1,40 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
3
|
exports[`ScreenContainer renders 1`] = `
|
|
4
|
-
<
|
|
4
|
+
<RNCSafeAreaProvider
|
|
5
|
+
onInsetsChange={[Function]}
|
|
5
6
|
style={
|
|
6
7
|
Array [
|
|
7
8
|
Object {
|
|
8
9
|
"flex": 1,
|
|
9
|
-
"height": "100%",
|
|
10
|
-
"width": "100%",
|
|
11
|
-
},
|
|
12
|
-
Object {
|
|
13
|
-
"backgroundColor": "transparent",
|
|
14
|
-
"marginBottom": 10,
|
|
15
|
-
"paddingBottom": 0,
|
|
16
|
-
"paddingTop": 0,
|
|
17
10
|
},
|
|
11
|
+
undefined,
|
|
18
12
|
]
|
|
19
13
|
}
|
|
20
|
-
testID="screen-container"
|
|
21
14
|
>
|
|
22
15
|
<View
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
style={
|
|
17
|
+
Array [
|
|
18
|
+
Object {
|
|
19
|
+
"position": "absolute",
|
|
20
|
+
"top": 0,
|
|
21
|
+
"width": 1920,
|
|
22
|
+
"zIndex": 10,
|
|
23
|
+
},
|
|
24
|
+
Object {
|
|
25
|
+
"maxHeight": 10,
|
|
26
|
+
},
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
testID="nav-bar-container"
|
|
30
|
+
>
|
|
31
|
+
<View
|
|
32
|
+
isVisible={true}
|
|
33
|
+
menuPlugin={Object {}}
|
|
34
|
+
navigationProps={null}
|
|
35
|
+
/>
|
|
36
|
+
</View>
|
|
37
|
+
</RNCSafeAreaProvider>
|
|
26
38
|
`;
|
|
27
39
|
|
|
28
40
|
exports[`ScreenContainer sets margins provided by the theme 1`] = `
|
|
@@ -21,10 +21,11 @@ const mockStore = configureStore()({
|
|
|
21
21
|
describe("Layout TV", () => {
|
|
22
22
|
const navigator = {
|
|
23
23
|
currentRoute: "/river/A1234",
|
|
24
|
+
canGoBack: jest.fn(),
|
|
24
25
|
mainStack: [
|
|
25
26
|
{
|
|
26
27
|
route: "/river/A1234",
|
|
27
|
-
state:
|
|
28
|
+
state: { screen: {} },
|
|
28
29
|
},
|
|
29
30
|
],
|
|
30
31
|
};
|
|
@@ -4,22 +4,14 @@ import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
|
4
4
|
import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
|
|
5
5
|
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
getNavigationProps,
|
|
9
|
-
resolveNavigationPlugin,
|
|
10
|
-
routeIsPlayerScreen,
|
|
11
|
-
} from "@applicaster/zapp-react-native-utils/navigationUtils";
|
|
12
|
-
|
|
13
7
|
import { getBackgroundImageUrl } from "../utils";
|
|
14
8
|
import { LayoutContainer } from "./LayoutContainer";
|
|
15
|
-
import { NavBarContainer } from "./NavBarContainer";
|
|
16
9
|
import { ScreenContainer } from "./ScreenContainer";
|
|
17
10
|
|
|
18
11
|
import { ScreenLayoutContextProvider } from "./ScreenLayoutContextProvider";
|
|
19
|
-
import { isApplePlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
20
12
|
import { PathnameContext } from "../../../Contexts/PathnameContext";
|
|
21
|
-
import { useNavbarState } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
|
|
22
13
|
import { ScreenDataContext } from "../../../Contexts/ScreenDataContext";
|
|
14
|
+
import { ScreenContextProvider } from "../../../Contexts/ScreenContext";
|
|
23
15
|
|
|
24
16
|
type Components = {
|
|
25
17
|
NavBar: React.ComponentType<any>;
|
|
@@ -40,20 +32,9 @@ type Props = {
|
|
|
40
32
|
const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
|
|
41
33
|
const navigator = useNavigation();
|
|
42
34
|
const theme = useTheme();
|
|
43
|
-
const { title, visible } = useNavbarState();
|
|
44
|
-
const { activeRiver, currentRoute } = navigator;
|
|
45
|
-
|
|
46
|
-
const {
|
|
47
|
-
appState: { appReady = false } = {},
|
|
48
|
-
remoteConfigurations,
|
|
49
|
-
plugins = [],
|
|
50
|
-
} = usePickFromState(["appState", "remoteConfigurations", "plugins"]);
|
|
51
35
|
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
title,
|
|
55
|
-
category: "menu",
|
|
56
|
-
});
|
|
36
|
+
const { appState: { appReady = false } = {}, remoteConfigurations } =
|
|
37
|
+
usePickFromState(["appState", "remoteConfigurations", "plugins"]);
|
|
57
38
|
|
|
58
39
|
const backgroundColor = React.useMemo(() => theme.app_background_color, []);
|
|
59
40
|
|
|
@@ -62,37 +43,6 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
|
|
|
62
43
|
[remoteConfigurations]
|
|
63
44
|
);
|
|
64
45
|
|
|
65
|
-
const menuPlugin = React.useMemo(
|
|
66
|
-
() =>
|
|
67
|
-
resolveNavigationPlugin({
|
|
68
|
-
category: "menu",
|
|
69
|
-
navigations: activeRiver?.navigations,
|
|
70
|
-
plugins,
|
|
71
|
-
}) as QuickBrickPlugin,
|
|
72
|
-
[]
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
const NavBar = React.useMemo(
|
|
76
|
-
() => Components.NavBar || (menuPlugin.module as React.ComponentType<any>),
|
|
77
|
-
[]
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
const [navBarReady, setNavBarReady] = React.useState(false);
|
|
81
|
-
|
|
82
|
-
const navBarContainer = (
|
|
83
|
-
<NavBarContainer isVisible={visible} onReady={setNavBarReady}>
|
|
84
|
-
<NavBar
|
|
85
|
-
ref={ComponentsExtraProps?.NavBar?.ref}
|
|
86
|
-
isVisible={visible && !routeIsPlayerScreen(currentRoute)}
|
|
87
|
-
menuPlugin={menuPlugin}
|
|
88
|
-
navigationProps={navigationProps}
|
|
89
|
-
{...R.omit(["ref"])(ComponentsExtraProps?.NavBar)}
|
|
90
|
-
/>
|
|
91
|
-
</NavBarContainer>
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
const isTVOS = isApplePlatform();
|
|
95
|
-
|
|
96
46
|
if (!appReady) {
|
|
97
47
|
return null;
|
|
98
48
|
}
|
|
@@ -107,18 +57,14 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
|
|
|
107
57
|
>
|
|
108
58
|
<ScreenDataContext.Provider value={navigator.data}>
|
|
109
59
|
<PathnameContext.Provider value={navigator.currentRoute}>
|
|
110
|
-
{
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
) : null}
|
|
119
|
-
{/** Render NavBarContainer over ScreenContainer to let tvOS native
|
|
120
|
-
focus manager works correctly to focus nav bar items first **/}
|
|
121
|
-
{isTVOS ? navBarContainer : null}
|
|
60
|
+
<ScreenContextProvider pathname={navigator.currentRoute}>
|
|
61
|
+
<ScreenContainer
|
|
62
|
+
ComponentsExtraProps={ComponentsExtraProps}
|
|
63
|
+
NavBar={Components.NavBar}
|
|
64
|
+
>
|
|
65
|
+
{children}
|
|
66
|
+
</ScreenContainer>
|
|
67
|
+
</ScreenContextProvider>
|
|
122
68
|
</PathnameContext.Provider>
|
|
123
69
|
</ScreenDataContext.Provider>
|
|
124
70
|
</Components.Background>
|
|
@@ -53,6 +53,7 @@ export function ActionButton(props: Props) {
|
|
|
53
53
|
const { item, action, asset, flavour = "flavour_1", cellUUID } = props;
|
|
54
54
|
const actionContext = useActions(action?.identifier);
|
|
55
55
|
|
|
56
|
+
// TODO: add subscription API for action availability
|
|
56
57
|
const actionDisabled =
|
|
57
58
|
typeof actionContext?.isActionAvailable === "function" &&
|
|
58
59
|
!actionContext.isActionAvailable(item);
|
|
@@ -68,7 +69,7 @@ export function ActionButton(props: Props) {
|
|
|
68
69
|
if (actionDisabled || !actionContext) return;
|
|
69
70
|
|
|
70
71
|
setActionState(actionContext.initialEntryState(item));
|
|
71
|
-
}, [actionDisabled, item?.id]);
|
|
72
|
+
}, [actionDisabled, item?.id, actionContext]);
|
|
72
73
|
|
|
73
74
|
const onPress = useCallback(() => {
|
|
74
75
|
actionContext.invokeAction(item, {
|
|
@@ -22,6 +22,10 @@ const styles = StyleSheet.create({
|
|
|
22
22
|
borderWidth: 0,
|
|
23
23
|
borderColor: COLOR_TRANSPARENT,
|
|
24
24
|
},
|
|
25
|
+
border: {
|
|
26
|
+
...StyleSheet.absoluteFillObject,
|
|
27
|
+
zIndex: 1,
|
|
28
|
+
},
|
|
25
29
|
});
|
|
26
30
|
|
|
27
31
|
const getBorderPadding = (borderPosition: BorderPosition, borderWidth) => {
|
|
@@ -87,8 +91,9 @@ const Border = ({
|
|
|
87
91
|
|
|
88
92
|
return (
|
|
89
93
|
<View
|
|
94
|
+
pointerEvents="none"
|
|
90
95
|
style={{
|
|
91
|
-
...
|
|
96
|
+
...styles.border,
|
|
92
97
|
right: -containerPadding - paddingRight,
|
|
93
98
|
bottom: -containerPadding - paddingBottom,
|
|
94
99
|
left: -containerPadding - paddingLeft,
|
|
@@ -112,9 +117,6 @@ export const BorderContainerView = (props: Props) => {
|
|
|
112
117
|
children,
|
|
113
118
|
} = props;
|
|
114
119
|
|
|
115
|
-
const borderStyleForMissingBorder =
|
|
116
|
-
style?.borderWidth === 0 || !style?.borderColor ? { borderRadius: 0 } : {};
|
|
117
|
-
|
|
118
120
|
const padding =
|
|
119
121
|
borderPosition === "outside"
|
|
120
122
|
? {
|
|
@@ -126,12 +128,9 @@ export const BorderContainerView = (props: Props) => {
|
|
|
126
128
|
: {};
|
|
127
129
|
|
|
128
130
|
return (
|
|
129
|
-
<View
|
|
130
|
-
{...props}
|
|
131
|
-
style={[style, styles.container, borderStyleForMissingBorder]}
|
|
132
|
-
>
|
|
133
|
-
{children}
|
|
131
|
+
<View pointerEvents="box-none" {...props} style={[style, styles.container]}>
|
|
134
132
|
<Border style={style} padding={padding} borderPosition={borderPosition} />
|
|
133
|
+
{children}
|
|
135
134
|
</View>
|
|
136
135
|
);
|
|
137
136
|
};
|
|
@@ -4,6 +4,7 @@ import { Focusable } from "@applicaster/zapp-react-native-ui-components/Componen
|
|
|
4
4
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
5
5
|
import * as R from "ramda";
|
|
6
6
|
import { getXray } from "@applicaster/zapp-react-native-utils/logger";
|
|
7
|
+
import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
|
|
7
8
|
|
|
8
9
|
const { Logger } = getXray();
|
|
9
10
|
|
|
@@ -35,7 +36,9 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
|
|
|
35
36
|
|
|
36
37
|
const focusableId = getFocusableId(prefixId, suffixId);
|
|
37
38
|
|
|
38
|
-
const focused =
|
|
39
|
+
const focused =
|
|
40
|
+
focusableId === focusedButtonId ||
|
|
41
|
+
toBooleanWithDefaultFalse(otherProps.focused);
|
|
39
42
|
|
|
40
43
|
const additionalStyles = focused ? focusedStyles : normalStyles;
|
|
41
44
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ImageStyle } from "react-native";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getImageContainerMarginStyles,
|
|
5
|
+
ifElseFocusedSelected,
|
|
6
|
+
} from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
7
|
+
import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
8
|
+
|
|
9
|
+
export const ImageBorderContainer = ({
|
|
10
|
+
value,
|
|
11
|
+
state,
|
|
12
|
+
imageStyles,
|
|
13
|
+
}: {
|
|
14
|
+
value: (key: string) => string;
|
|
15
|
+
state: CellState;
|
|
16
|
+
imageStyles: ImageStyle;
|
|
17
|
+
}) => {
|
|
18
|
+
const imageMargins = getImageContainerMarginStyles({ value });
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
type: "BorderContainerView",
|
|
22
|
+
style: {
|
|
23
|
+
position: "absolute",
|
|
24
|
+
// Image border now separated from the image, so cell padding affects the border position
|
|
25
|
+
marginTop:
|
|
26
|
+
toNumberWithDefaultZero(imageMargins.marginTop) +
|
|
27
|
+
toNumberWithDefaultZero(value("cell_padding_top")),
|
|
28
|
+
marginLeft:
|
|
29
|
+
toNumberWithDefaultZero(imageMargins.marginLeft) +
|
|
30
|
+
toNumberWithDefaultZero(value("cell_padding_left")),
|
|
31
|
+
marginRight:
|
|
32
|
+
toNumberWithDefaultZero(imageMargins.marginRight) +
|
|
33
|
+
toNumberWithDefaultZero(value("cell_padding_right")),
|
|
34
|
+
marginBottom:
|
|
35
|
+
toNumberWithDefaultZero(imageMargins.marginBottom) +
|
|
36
|
+
toNumberWithDefaultZero(value("cell_padding_bottom")),
|
|
37
|
+
zIndex: 1,
|
|
38
|
+
borderWidth: value("image_border_size"),
|
|
39
|
+
borderColor: ifElseFocusedSelected(
|
|
40
|
+
state,
|
|
41
|
+
value("image_focused_border_color"),
|
|
42
|
+
value("image_selected_border_color"),
|
|
43
|
+
value("image_focused_selected_border_color"),
|
|
44
|
+
value("image_border_color")
|
|
45
|
+
),
|
|
46
|
+
borderRadius: toNumberWithDefaultZero(value("image_corner_radius")),
|
|
47
|
+
},
|
|
48
|
+
additionalProps: {
|
|
49
|
+
pointerEvents: "none",
|
|
50
|
+
borderPosition: value("image_border_position"),
|
|
51
|
+
borderPaddingTop: toNumberWithDefaultZero(
|
|
52
|
+
value("image_border_padding_top")
|
|
53
|
+
),
|
|
54
|
+
borderPaddingRight: toNumberWithDefaultZero(
|
|
55
|
+
value("image_border_padding_right")
|
|
56
|
+
),
|
|
57
|
+
borderPaddingBottom: toNumberWithDefaultZero(
|
|
58
|
+
value("image_border_padding_bottom")
|
|
59
|
+
),
|
|
60
|
+
borderPaddingLeft: toNumberWithDefaultZero(
|
|
61
|
+
value("image_border_padding_left")
|
|
62
|
+
),
|
|
63
|
+
},
|
|
64
|
+
elements: [
|
|
65
|
+
{
|
|
66
|
+
type: "View",
|
|
67
|
+
style: imageStyles,
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
};
|
|
@@ -108,6 +108,26 @@ const prepareEntry = (entry) => {
|
|
|
108
108
|
};
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
const previewPlayback =
|
|
112
|
+
entry.extensions?.["brightcove"]?.["preview_playback"];
|
|
113
|
+
|
|
114
|
+
if (previewPlayback) {
|
|
115
|
+
return {
|
|
116
|
+
...entry,
|
|
117
|
+
extensions: {
|
|
118
|
+
...entry.extensions,
|
|
119
|
+
brightcove: {
|
|
120
|
+
...entry?.extensions?.["brightcove"],
|
|
121
|
+
video_id: previewPlayback,
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (entry.extensions?.["brightcove"]?.["video_id"]) {
|
|
128
|
+
return entry;
|
|
129
|
+
}
|
|
130
|
+
|
|
111
131
|
const src = entry.content?.src;
|
|
112
132
|
|
|
113
133
|
if (!src) {
|
|
@@ -15,6 +15,7 @@ import { BorderContainerView } from "./BorderContainerView";
|
|
|
15
15
|
import { CellBadge } from "./CellBadge";
|
|
16
16
|
import { TvActionButtons } from "./tv/TvActionButtons";
|
|
17
17
|
import { ButtonContainerView } from "./tv/ButtonContainerView";
|
|
18
|
+
import { ImageBorderContainer } from "./ImageBorderContainer";
|
|
18
19
|
|
|
19
20
|
export const defaultComponents = {
|
|
20
21
|
View,
|
|
@@ -28,6 +29,7 @@ export const defaultComponents = {
|
|
|
28
29
|
ProgressBar,
|
|
29
30
|
Toggle,
|
|
30
31
|
LiveImage,
|
|
32
|
+
ImageBorderContainer,
|
|
31
33
|
BorderContainerView,
|
|
32
34
|
DynamicBadge,
|
|
33
35
|
SecondaryImage,
|
|
@@ -7,6 +7,10 @@ import type {
|
|
|
7
7
|
ContainerProps,
|
|
8
8
|
ContainerChildren,
|
|
9
9
|
} from "./types";
|
|
10
|
+
import {
|
|
11
|
+
useIsRTL,
|
|
12
|
+
applyRTLStylesIfNeeded,
|
|
13
|
+
} from "@applicaster/zapp-react-native-utils/localizationUtils";
|
|
10
14
|
|
|
11
15
|
const HorizontalSeparator = ({ width }: HorizontalSeparatorProps) => (
|
|
12
16
|
<View style={{ width }} />
|
|
@@ -17,6 +21,7 @@ export function ButtonContainerView({
|
|
|
17
21
|
children,
|
|
18
22
|
...otherProps
|
|
19
23
|
}: ContainerProps) {
|
|
24
|
+
const isRTL = useIsRTL();
|
|
20
25
|
const horizontalGutter = R.pathOr(0, ["horizontalGutter"], otherProps);
|
|
21
26
|
|
|
22
27
|
const filteredChildren = useFilterChildren<ContainerChildren>(children);
|
|
@@ -26,7 +31,7 @@ export function ButtonContainerView({
|
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
return (
|
|
29
|
-
<View style={style}>
|
|
34
|
+
<View style={applyRTLStylesIfNeeded(style, isRTL)}>
|
|
30
35
|
{insertBetween(
|
|
31
36
|
(index) => (
|
|
32
37
|
<HorizontalSeparator
|
|
@@ -5,6 +5,7 @@ import * as R from "ramda";
|
|
|
5
5
|
|
|
6
6
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
7
7
|
import { normalizeProgressValue } from "./utils";
|
|
8
|
+
import { useIsScreenActive } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
8
9
|
|
|
9
10
|
const ACTION_NAME = "continue-watching";
|
|
10
11
|
const DEFAULT_FILL = 0;
|
|
@@ -27,6 +28,7 @@ const getProgressValue = (
|
|
|
27
28
|
|
|
28
29
|
export const useFillInPercent = (entry: ZappEntry): number => {
|
|
29
30
|
const action = useActions(ACTION_NAME);
|
|
31
|
+
const isActive = useIsScreenActive();
|
|
30
32
|
|
|
31
33
|
const [fillInPercent, setFillInPercent] =
|
|
32
34
|
React.useState<number>(DEFAULT_FILL);
|
|
@@ -39,6 +41,10 @@ export const useFillInPercent = (entry: ZappEntry): number => {
|
|
|
39
41
|
}, []);
|
|
40
42
|
|
|
41
43
|
React.useEffect(() => {
|
|
44
|
+
if (!isActive) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
42
48
|
// refresh progress in UI when it was changed in CW
|
|
43
49
|
const unsubscribe = action?.addDataSourceListener?.(() => {
|
|
44
50
|
getProgressValue(action, entry, (progress) => {
|
|
@@ -49,7 +55,7 @@ export const useFillInPercent = (entry: ZappEntry): number => {
|
|
|
49
55
|
return () => {
|
|
50
56
|
unsubscribe?.();
|
|
51
57
|
};
|
|
52
|
-
}, []);
|
|
58
|
+
}, [isActive]);
|
|
53
59
|
|
|
54
60
|
return fillInPercent;
|
|
55
61
|
};
|
|
@@ -37,14 +37,28 @@ function retrieveData(entry, func, args) {
|
|
|
37
37
|
*/
|
|
38
38
|
|
|
39
39
|
export function configInflater(
|
|
40
|
-
entry,
|
|
41
|
-
{
|
|
40
|
+
entry: any,
|
|
41
|
+
{
|
|
42
|
+
type,
|
|
43
|
+
style,
|
|
44
|
+
additionalProps = {},
|
|
45
|
+
data = [],
|
|
46
|
+
elements,
|
|
47
|
+
}: {
|
|
48
|
+
type: any;
|
|
49
|
+
style: any;
|
|
50
|
+
additionalProps?: Record<string, any>;
|
|
51
|
+
data?: Array<{ propName: string; func: Function; args: any[] }>;
|
|
52
|
+
elements?: any[];
|
|
53
|
+
}
|
|
42
54
|
) {
|
|
43
|
-
const props =
|
|
44
|
-
(acc, curr) =>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
55
|
+
const props = data.reduce(
|
|
56
|
+
(acc, curr) => {
|
|
57
|
+
acc[curr.propName] = retrieveData(entry, curr.func, curr.args);
|
|
58
|
+
|
|
59
|
+
return acc;
|
|
60
|
+
},
|
|
61
|
+
{ ...additionalProps }
|
|
48
62
|
);
|
|
49
63
|
|
|
50
64
|
let adjustedElements;
|
|
@@ -52,6 +52,8 @@ export function masterCellBuilder({
|
|
|
52
52
|
const { View } = componentsMap;
|
|
53
53
|
const elementsBuilder = dataAdapter(cellConfiguration);
|
|
54
54
|
|
|
55
|
+
// This is very expensive operation
|
|
56
|
+
// Keep calls to minimum
|
|
55
57
|
const hasFocusableInside = hasFocusableInsideBuilder(elementsBuilder);
|
|
56
58
|
|
|
57
59
|
/**
|
|
@@ -74,8 +76,8 @@ export function masterCellBuilder({
|
|
|
74
76
|
const selectedState = screen?.selectedEntry;
|
|
75
77
|
|
|
76
78
|
const entryIsSelected = React.useMemo(
|
|
77
|
-
() => selectedState?.id === item?.id,
|
|
78
|
-
[selectedState?.id]
|
|
79
|
+
() => !!(selectedState?.id && selectedState?.id === item?.id),
|
|
80
|
+
[selectedState?.id, item?.id]
|
|
79
81
|
);
|
|
80
82
|
|
|
81
83
|
const elements = React.useMemo(
|
|
@@ -84,7 +86,7 @@ export function masterCellBuilder({
|
|
|
84
86
|
entry: item,
|
|
85
87
|
state: getEntryState(state, entryIsSelected),
|
|
86
88
|
}),
|
|
87
|
-
[state, item, entryIsSelected]
|
|
89
|
+
[state, item?.id, entryIsSelected] // Assuming that item won't mutate
|
|
88
90
|
);
|
|
89
91
|
|
|
90
92
|
const wrapperRef = React.useRef(null);
|
|
@@ -9,4 +9,13 @@ describe("insertBetween", () => {
|
|
|
9
9
|
|
|
10
10
|
expect(result).toEqual([1, "|", 2, "|", 3]);
|
|
11
11
|
});
|
|
12
|
+
|
|
13
|
+
it("insertBetween", () => {
|
|
14
|
+
const separator = (index) => `separator:${index}`;
|
|
15
|
+
const items = [1, 2, 3];
|
|
16
|
+
|
|
17
|
+
const result = insertBetween(separator, items);
|
|
18
|
+
|
|
19
|
+
expect(result).toEqual([1, "separator:0", 2, "separator:1", 3]);
|
|
20
|
+
});
|
|
12
21
|
});
|