@applicaster/zapp-react-native-ui-components 15.0.0-alpha.5485312433 → 15.0.0-alpha.5531846549
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/GeneralContentScreen/GeneralContentScreen.tsx +39 -28
- package/Components/GeneralContentScreen/__tests__/GeneralContentScreen.test.tsx +104 -0
- package/Components/GeneralContentScreen/utils/__tests__/getScreenDataSource.test.ts +19 -0
- package/Components/GeneralContentScreen/utils/getScreenDataSource.ts +9 -0
- package/Components/HandlePlayable/HandlePlayable.tsx +16 -29
- package/Components/HandlePlayable/utils.ts +31 -0
- package/Components/HookRenderer/HookRenderer.tsx +40 -10
- package/Components/HookRenderer/__tests__/HookRenderer.test.tsx +60 -0
- package/Components/Layout/TV/NavBarContainer.tsx +1 -10
- package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +7 -12
- package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +7 -12
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/model.test.ts +80 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +187 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/selectors.test.ts +45 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/style.test.ts +49 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/model.ts +47 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +170 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/selectors.ts +26 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/style.ts +29 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/types.ts +37 -0
- package/Components/MasterCell/DefaultComponents/Button.tsx +0 -15
- package/Components/MasterCell/DefaultComponents/LiveImage/__tests__/prepareEntry.test.ts +352 -0
- package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +136 -0
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +33 -16
- package/Components/MasterCell/DefaultComponents/PressableView.tsx +261 -0
- package/Components/MasterCell/DefaultComponents/Text/index.tsx +2 -6
- package/Components/MasterCell/DefaultComponents/index.ts +2 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Asset.ts +42 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +127 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ButtonContainerView.ts +23 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +16 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabel.ts +67 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +32 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +195 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/builders.test.ts +140 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +222 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/helpers.ts +105 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +104 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +118 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +73 -0
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/index.test.ts +86 -0
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +35 -52
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +35 -171
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +36 -145
- package/Components/MasterCell/elementMapper.tsx +1 -0
- package/Components/PlayerContainer/PlayerContainer.tsx +1 -18
- package/Components/PreloaderWrapper/__tests__/index.test.tsx +26 -0
- package/Components/PreloaderWrapper/index.tsx +15 -0
- package/Components/River/ComponentsMap/ComponentsMap.tsx +18 -16
- package/Components/River/RefreshControl.tsx +19 -83
- package/Components/River/River.tsx +9 -82
- package/Components/River/RiverItem.tsx +26 -20
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -0
- package/Components/River/__tests__/componentsMap.test.js +38 -0
- package/Components/River/hooks/__tests__/usePullToRefresh.test.ts +132 -0
- package/Components/River/hooks/index.ts +1 -0
- package/Components/River/hooks/usePullToRefresh.ts +51 -0
- package/Components/Screen/__tests__/Screen.test.tsx +1 -0
- package/Components/Screen/hooks.ts +73 -3
- package/Components/Screen/index.tsx +7 -1
- package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +46 -0
- package/Components/ScreenFeedLoader/__tests__/ScreenFeedLoader.test.tsx +94 -0
- package/Components/ScreenFeedLoader/index.ts +1 -0
- package/Components/ScreenResolver/__tests__/screenResolver.test.js +24 -0
- package/Components/ScreenResolver/hooks/index.ts +3 -0
- package/Components/ScreenResolver/hooks/useGetComponent.ts +15 -0
- package/Components/ScreenResolver/hooks/useScreenComponentResolver.tsx +90 -0
- package/Components/ScreenResolver/index.tsx +15 -117
- package/Components/ScreenResolver/utils/__tests__/getScreenTypeProps.test.ts +45 -0
- package/Components/ScreenResolver/utils/getScreenTypeProps.ts +43 -0
- package/Components/ScreenResolver/utils/index.ts +1 -0
- package/Components/ScreenResolver/withDefaultScreenContext.tsx +16 -0
- package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +25 -0
- package/Components/ScreenResolverFeedProvider/__tests__/ScreenResolverFeedProvider.test.tsx +44 -0
- package/Components/ScreenResolverFeedProvider/index.ts +1 -0
- package/Components/Tabs/TabContent.tsx +7 -4
- package/Components/TopCutoffOverlay/hooks/__tests__/useMarginTop.test.ts +130 -0
- package/Components/TopCutoffOverlay/hooks/index.ts +1 -0
- package/Components/TopCutoffOverlay/hooks/useMarginTop.ts +59 -0
- package/Components/TopCutoffOverlay/index.tsx +55 -0
- package/Components/Transitioner/Scene.tsx +9 -15
- package/Components/VideoLive/LiveImageManager.ts +199 -54
- package/Components/VideoLive/PlayerLiveImageComponent.tsx +31 -33
- package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +2 -17
- package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +0 -2
- package/Components/Viewport/ViewportAware/index.tsx +16 -7
- package/Components/ZappUIComponent/index.tsx +12 -6
- package/Components/default-cell-renderer/viewTrees/mobile/index.ts +0 -3
- package/Components/index.js +1 -1
- package/Contexts/ScreenContext/__tests__/index.test.tsx +57 -0
- package/Contexts/ScreenContext/index.tsx +71 -19
- package/Contexts/ZappPipesContext/ZappPipesContextFactory.tsx +18 -7
- package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -7
- package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +212 -5
- package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +39 -21
- package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +18 -7
- package/events/index.ts +2 -0
- package/events/scrollEndReached.ts +15 -0
- package/package.json +5 -5
- package/Components/MasterCell/DefaultComponents/Text/utils/__tests__/withAdjustedLineHeight.test.ts +0 -46
- package/Components/MasterCell/DefaultComponents/Text/utils/index.ts +0 -21
- package/Components/PlayerContainer/ErrorDisplay/ErrorDisplay.tsx +0 -57
- package/Components/PlayerContainer/ErrorDisplay/index.ts +0 -9
- package/Components/PlayerContainer/useRestrictMobilePlayback.tsx +0 -101
- /package/Components/HookRenderer/{index.tsx → index.ts} +0 -0
|
@@ -12,12 +12,26 @@ import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
|
|
|
12
12
|
import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
13
13
|
import { ScreenTrackedViewPositionsContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenTrackedViewPositionsContext";
|
|
14
14
|
import { useEventAlerts } from "./utils/useEventAlerts";
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
import {
|
|
16
|
+
selectRiverById,
|
|
17
|
+
useAppSelector,
|
|
18
|
+
} from "@applicaster/zapp-react-native-redux";
|
|
19
|
+
import { getScreenDataSource } from "./utils/getScreenDataSource";
|
|
20
|
+
import { ScreenResolverFeedProvider } from "../ScreenResolverFeedProvider/ScreenResolverFeedProvider";
|
|
21
|
+
|
|
22
|
+
const { log_debug } = createLogger({
|
|
17
23
|
category: "ScreenContainer",
|
|
18
24
|
subsystem: "General",
|
|
19
25
|
});
|
|
20
26
|
|
|
27
|
+
/** Provides screen-feed from general-screen configuration (if defined) */
|
|
28
|
+
const useFeedData = (id) => {
|
|
29
|
+
const river = useAppSelector((state) => selectRiverById(state, id));
|
|
30
|
+
const feedData = getScreenDataSource(river);
|
|
31
|
+
|
|
32
|
+
return feedData;
|
|
33
|
+
};
|
|
34
|
+
|
|
21
35
|
export const GeneralContentScreen = ({
|
|
22
36
|
feed,
|
|
23
37
|
screenId,
|
|
@@ -54,20 +68,15 @@ export const GeneralContentScreen = ({
|
|
|
54
68
|
useEffect(() => {
|
|
55
69
|
if (!riverActionProvidersReady) {
|
|
56
70
|
if (actionsInitialStateSetters.length > 0) {
|
|
57
|
-
log_info(
|
|
58
|
-
"ScreenContainer: starting to check river action providers to initialize",
|
|
59
|
-
{ actionsInitialStateSetters }
|
|
60
|
-
);
|
|
61
|
-
|
|
62
71
|
allSettled(actionsInitialStateSetters).finally(() => {
|
|
63
|
-
|
|
72
|
+
log_debug(
|
|
64
73
|
"ScreenContainer: action provider ready, completed. Starting to present screen"
|
|
65
74
|
);
|
|
66
75
|
|
|
67
76
|
setRiverActionProvidersReady(true);
|
|
68
77
|
});
|
|
69
78
|
} else {
|
|
70
|
-
|
|
79
|
+
log_debug(
|
|
71
80
|
"ScreenContainer: no action provider to check, completed. Starting to present screen"
|
|
72
81
|
);
|
|
73
82
|
|
|
@@ -108,24 +117,26 @@ export const GeneralContentScreen = ({
|
|
|
108
117
|
if (!isReady || isNilOrEmpty(components || uiComponents)) return null;
|
|
109
118
|
|
|
110
119
|
return (
|
|
111
|
-
<
|
|
112
|
-
<
|
|
113
|
-
<
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
120
|
+
<ScreenResolverFeedProvider id={screenId} useFeedData={useFeedData}>
|
|
121
|
+
<ScreenTrackedViewPositionsContext.Provider>
|
|
122
|
+
<CellTapContext.Provider value={contextValue}>
|
|
123
|
+
<ComponentsMap
|
|
124
|
+
feed={feed}
|
|
125
|
+
riverId={screenId}
|
|
126
|
+
groupId={groupId || `general-content-screen-${screenId}`}
|
|
127
|
+
riverComponents={components || uiComponents}
|
|
128
|
+
scrollViewExtraProps={scrollViewExtraProps}
|
|
129
|
+
getStaticComponentFeed={getStaticComponentFeed}
|
|
130
|
+
extraAnchorPointYOffset={extraAnchorPointYOffset}
|
|
131
|
+
isScreenWrappedInContainer={isScreenWrappedInContainer}
|
|
132
|
+
parentFocus={parentFocus}
|
|
133
|
+
focused={focused}
|
|
134
|
+
containerHeight={containerHeight}
|
|
135
|
+
preferredFocus={preferredFocus}
|
|
136
|
+
{...componentsMapExtraProps}
|
|
137
|
+
/>
|
|
138
|
+
</CellTapContext.Provider>
|
|
139
|
+
</ScreenTrackedViewPositionsContext.Provider>
|
|
140
|
+
</ScreenResolverFeedProvider>
|
|
130
141
|
);
|
|
131
142
|
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@testing-library/react-native";
|
|
3
|
+
import { GeneralContentScreen } from "../GeneralContentScreen";
|
|
4
|
+
|
|
5
|
+
const mockUseAppSelector = jest.fn();
|
|
6
|
+
const mockSelectRiverById = jest.fn();
|
|
7
|
+
const mockProviderSpy = jest.fn();
|
|
8
|
+
|
|
9
|
+
jest.mock("../../River/ComponentsMap", () => ({
|
|
10
|
+
ComponentsMap: () => {
|
|
11
|
+
const React = require("react");
|
|
12
|
+
const { View } = require("react-native");
|
|
13
|
+
|
|
14
|
+
return <View testID="components-map" />;
|
|
15
|
+
},
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/actions", () => ({
|
|
19
|
+
useActions: jest.fn(() => jest.fn()),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
jest.mock("../utils", () => ({
|
|
23
|
+
logger: { warn: jest.fn() },
|
|
24
|
+
whenMatchingType: jest.fn((_type, value) => value),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/layout", () => ({
|
|
28
|
+
useLayoutVersion: jest.fn(() => false),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
jest.mock(
|
|
32
|
+
"@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenData",
|
|
33
|
+
() => ({
|
|
34
|
+
useScreenData: jest.fn(() => ({
|
|
35
|
+
ui_components: [{ id: "ui-component" }],
|
|
36
|
+
general: {},
|
|
37
|
+
})),
|
|
38
|
+
})
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
jest.mock("../utils/useCurationAPI", () => ({
|
|
42
|
+
useCurationAPI: jest.fn(() => [{ id: "curation-component" }]),
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
jest.mock("@applicaster/quick-brick-core/App/ActionSetters", () => ({
|
|
46
|
+
useRiverInitialState: jest.fn(() => []),
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
jest.mock("../utils/useEventAlerts", () => ({
|
|
50
|
+
useEventAlerts: jest.fn(),
|
|
51
|
+
}));
|
|
52
|
+
|
|
53
|
+
jest.mock("@applicaster/zapp-react-native-redux", () => ({
|
|
54
|
+
useAppSelector: (...args) => mockUseAppSelector(...args),
|
|
55
|
+
selectRiverById: (...args) => mockSelectRiverById(...args),
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
jest.mock("../utils/getScreenDataSource", () => ({
|
|
59
|
+
getScreenDataSource: jest.fn(() => ({
|
|
60
|
+
source: "https://feed",
|
|
61
|
+
mapping: {},
|
|
62
|
+
})),
|
|
63
|
+
}));
|
|
64
|
+
|
|
65
|
+
jest.mock(
|
|
66
|
+
"../../ScreenResolverFeedProvider/ScreenResolverFeedProvider",
|
|
67
|
+
() => ({
|
|
68
|
+
ScreenResolverFeedProvider: ({ id, useFeedData, children }) => {
|
|
69
|
+
const React = require("react");
|
|
70
|
+
const { View } = require("react-native");
|
|
71
|
+
|
|
72
|
+
mockProviderSpy(id, useFeedData);
|
|
73
|
+
useFeedData(id);
|
|
74
|
+
|
|
75
|
+
return <View testID="screen-resolver-feed-provider">{children}</View>;
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
describe("GeneralContentScreen", () => {
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
jest.clearAllMocks();
|
|
83
|
+
mockUseAppSelector.mockImplementation((selector) => selector({}));
|
|
84
|
+
mockSelectRiverById.mockReturnValue({ id: "screen-1" });
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("wraps content with ScreenResolverFeedProvider and renders ComponentsMap", () => {
|
|
88
|
+
const { getByTestId } = render(
|
|
89
|
+
<GeneralContentScreen
|
|
90
|
+
screenId="screen-1"
|
|
91
|
+
feed={null}
|
|
92
|
+
components={[{ id: "component-1" }]}
|
|
93
|
+
/>
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
expect(getByTestId("screen-resolver-feed-provider")).toBeDefined();
|
|
97
|
+
expect(getByTestId("components-map")).toBeDefined();
|
|
98
|
+
|
|
99
|
+
expect(mockProviderSpy).toHaveBeenCalledWith(
|
|
100
|
+
"screen-1",
|
|
101
|
+
expect.any(Function)
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getScreenDataSource } from "../getScreenDataSource";
|
|
2
|
+
|
|
3
|
+
describe("getScreenDataSource", () => {
|
|
4
|
+
it("returns screen_feed data when present", () => {
|
|
5
|
+
const result = getScreenDataSource({
|
|
6
|
+
data: {
|
|
7
|
+
screen_feed: {
|
|
8
|
+
source: "https://feed",
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
expect(result).toEqual({ source: "https://feed" });
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("returns undefined when screen_feed is missing", () => {
|
|
17
|
+
expect(getScreenDataSource({ data: {} })).toBeUndefined();
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { get } from "@applicaster/zapp-react-native-utils/utils";
|
|
2
|
+
|
|
3
|
+
const lookupPath = ["data", "screen_feed"];
|
|
4
|
+
|
|
5
|
+
export const getScreenDataSource = (
|
|
6
|
+
screenData: any
|
|
7
|
+
): Option<ZappDataSource> => {
|
|
8
|
+
return get(screenData, lookupPath) as ZappDataSource | undefined;
|
|
9
|
+
};
|
|
@@ -5,8 +5,6 @@ import {
|
|
|
5
5
|
usePlugins,
|
|
6
6
|
} from "@applicaster/zapp-react-native-redux/hooks";
|
|
7
7
|
import {
|
|
8
|
-
useDimensions,
|
|
9
|
-
useIsTablet as isTablet,
|
|
10
8
|
useNavigation,
|
|
11
9
|
useRivers,
|
|
12
10
|
} from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
@@ -15,8 +13,8 @@ import { BufferAnimation } from "../PlayerContainer/BufferAnimation";
|
|
|
15
13
|
import { PlayerContainer } from "../PlayerContainer";
|
|
16
14
|
import { useModalSize } from "../VideoModal/hooks";
|
|
17
15
|
import { ViewStyle } from "react-native";
|
|
18
|
-
import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
19
16
|
import { findCastPlugin, getPlayer } from "./utils";
|
|
17
|
+
import { useWaitForValidOrientation } from "../Screen/hooks";
|
|
20
18
|
|
|
21
19
|
type Props = {
|
|
22
20
|
item: ZappEntry;
|
|
@@ -31,13 +29,6 @@ type PlayableComponent = {
|
|
|
31
29
|
Component: React.ComponentType<any>;
|
|
32
30
|
};
|
|
33
31
|
|
|
34
|
-
const dimensionsContext: "window" | "screen" = platformSelect({
|
|
35
|
-
android_tv: "window",
|
|
36
|
-
amazon: "window",
|
|
37
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
38
|
-
default: isTablet() ? "window" : "screen", // on tablet, window represents correct values, on phone it's not as the screen could be rotated
|
|
39
|
-
});
|
|
40
|
-
|
|
41
32
|
export function HandlePlayable({
|
|
42
33
|
item,
|
|
43
34
|
isModal,
|
|
@@ -97,27 +88,23 @@ export function HandlePlayable({
|
|
|
97
88
|
});
|
|
98
89
|
}, [casting]);
|
|
99
90
|
|
|
100
|
-
const { width: screenWidth, height: screenHeight } =
|
|
101
|
-
useDimensions(dimensionsContext);
|
|
102
|
-
|
|
103
91
|
const modalSize = useModalSize();
|
|
104
92
|
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
);
|
|
93
|
+
const isOrientationReady = useWaitForValidOrientation();
|
|
94
|
+
|
|
95
|
+
const style = React.useMemo(() => {
|
|
96
|
+
const isFullScreenReady =
|
|
97
|
+
mode === "PIP" || (mode === "FULLSCREEN" && isOrientationReady);
|
|
98
|
+
|
|
99
|
+
const getDimensionValue = (value: string | number) => {
|
|
100
|
+
return isModal ? value : isFullScreenReady ? "100%" : 0; // do not show player, until full screen mode is ready
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
width: getDimensionValue(modalSize.width),
|
|
105
|
+
height: getDimensionValue(modalSize.height),
|
|
106
|
+
} as ViewStyle;
|
|
107
|
+
}, [modalSize, isModal, mode, isOrientationReady]);
|
|
121
108
|
|
|
122
109
|
const Component = playable?.Component;
|
|
123
110
|
|
|
@@ -5,6 +5,14 @@ import {
|
|
|
5
5
|
|
|
6
6
|
import { CHROMECAST_PLUGIN_ID, YOUTUBE_PLUGIN_ID } from "./const";
|
|
7
7
|
import { omit } from "@applicaster/zapp-react-native-utils/utils";
|
|
8
|
+
import { getXray } from "@applicaster/zapp-react-native-utils/logger";
|
|
9
|
+
|
|
10
|
+
const { Logger } = getXray();
|
|
11
|
+
|
|
12
|
+
const logger = new Logger(
|
|
13
|
+
"QuickBrick",
|
|
14
|
+
"packages/zapp-react-native-ui-components/Components/HandlePlayable"
|
|
15
|
+
);
|
|
8
16
|
|
|
9
17
|
const getPlayerModuleProperties = (PlayerModule: ZappPlugin) => {
|
|
10
18
|
if (PlayerModule?.Component && typeof PlayerModule.Component === "object") {
|
|
@@ -52,10 +60,25 @@ export const getPlayer = (
|
|
|
52
60
|
if (type) {
|
|
53
61
|
PlayerModule = findPluginByIdentifier(type, plugins)?.module;
|
|
54
62
|
|
|
63
|
+
if (!PlayerModule) {
|
|
64
|
+
logger.error({
|
|
65
|
+
message:
|
|
66
|
+
"PlayerModule is undefined – type mapping may be wrong or type not set for player",
|
|
67
|
+
data: {
|
|
68
|
+
type,
|
|
69
|
+
screen_id,
|
|
70
|
+
item_type_value: item?.type?.value,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return [null, {}];
|
|
75
|
+
}
|
|
76
|
+
|
|
55
77
|
return getPlayerWithModuleProperties(PlayerModule);
|
|
56
78
|
}
|
|
57
79
|
}
|
|
58
80
|
|
|
81
|
+
// TODO: Probably should be removed, Youtube plugin is deprecated
|
|
59
82
|
if (item?.content?.type === "youtube-id") {
|
|
60
83
|
PlayerModule = findYoutubePlugin(plugins)?.module;
|
|
61
84
|
|
|
@@ -70,5 +93,13 @@ export const getPlayer = (
|
|
|
70
93
|
)
|
|
71
94
|
);
|
|
72
95
|
|
|
96
|
+
if (!PlayerModule) {
|
|
97
|
+
logger.error({
|
|
98
|
+
message: "PlayerModule is undefined – playable plugin not found",
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return [null, {}];
|
|
102
|
+
}
|
|
103
|
+
|
|
73
104
|
return getPlayerWithModuleProperties(PlayerModule);
|
|
74
105
|
};
|
|
@@ -6,7 +6,12 @@ import {
|
|
|
6
6
|
} from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
|
|
7
7
|
import { useHookAnalytics } from "@applicaster/zapp-react-native-utils/analyticsUtils/helpers/hooks";
|
|
8
8
|
import { useSetNavbarState } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
import { componentsLogger } from "../../Helpers/logger";
|
|
11
|
+
|
|
12
|
+
const logger = componentsLogger.addSubsystem("HookRenderer");
|
|
13
|
+
|
|
14
|
+
const HOOK_PRESENTATION_TYPE = "Hook";
|
|
10
15
|
|
|
11
16
|
type Props = {
|
|
12
17
|
focused?: boolean;
|
|
@@ -15,20 +20,17 @@ type Props = {
|
|
|
15
20
|
callback: hookCallback;
|
|
16
21
|
};
|
|
17
22
|
|
|
18
|
-
|
|
19
|
-
const {
|
|
20
|
-
|
|
21
|
-
screenData: { payload, hookPlugin },
|
|
22
|
-
callback,
|
|
23
|
-
} = props;
|
|
24
|
-
|
|
25
|
-
const { setVisible: showNavBar } = useSetNavbarState();
|
|
23
|
+
const HookRenderer = (props: Props) => {
|
|
24
|
+
const { focused, screenData, callback } = props;
|
|
25
|
+
const { payload, hookPlugin } = screenData;
|
|
26
26
|
|
|
27
27
|
const {
|
|
28
28
|
module: { Component: HookComponent, presentFullScreen },
|
|
29
29
|
configuration,
|
|
30
30
|
} = hookPlugin;
|
|
31
31
|
|
|
32
|
+
const { setVisible: showNavBar } = useSetNavbarState();
|
|
33
|
+
|
|
32
34
|
useHookAnalytics(props);
|
|
33
35
|
|
|
34
36
|
const isNavBarVisible = useIsNavBarVisible();
|
|
@@ -63,8 +65,36 @@ export const HookRenderer = (props: Props) => {
|
|
|
63
65
|
hookPlugin,
|
|
64
66
|
focused,
|
|
65
67
|
parentFocus,
|
|
66
|
-
presentationType:
|
|
68
|
+
presentationType: HOOK_PRESENTATION_TYPE,
|
|
67
69
|
}}
|
|
68
70
|
/>
|
|
69
71
|
);
|
|
70
72
|
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Guard component to prevent rendering HookRenderer when screenData or hookPlugin is missing. This is to avoid potential crashes due to missing data.
|
|
76
|
+
*/
|
|
77
|
+
const HookRendererGuard = (props: Props) => {
|
|
78
|
+
React.useEffect(() => {
|
|
79
|
+
if (!props.screenData) {
|
|
80
|
+
logger.error(
|
|
81
|
+
"HookRenderer received no screenData, screen cannot be rendered"
|
|
82
|
+
);
|
|
83
|
+
} else if (!props.screenData.hookPlugin) {
|
|
84
|
+
logger.error(
|
|
85
|
+
"HookRenderer received screenData with no hookPlugin, screen cannot be rendered",
|
|
86
|
+
{
|
|
87
|
+
screenData: props.screenData,
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}, [props.screenData]);
|
|
92
|
+
|
|
93
|
+
if (!props.screenData || !props.screenData.hookPlugin) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return <HookRenderer {...props} />;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export { HookRendererGuard as HookRenderer };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { render } from "@testing-library/react-native";
|
|
4
|
+
import { HookRenderer } from "..";
|
|
5
|
+
|
|
6
|
+
jest.mock("@applicaster/zapp-react-native-utils/reactUtils", () => ({
|
|
7
|
+
isTV: jest.fn(() => false),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
|
|
11
|
+
useBackHandler: jest.fn(),
|
|
12
|
+
useIsNavBarVisible: jest.fn(() => true),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
jest.mock(
|
|
16
|
+
"@applicaster/zapp-react-native-utils/analyticsUtils/helpers/hooks",
|
|
17
|
+
() => ({
|
|
18
|
+
useHookAnalytics: jest.fn(),
|
|
19
|
+
})
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
|
|
23
|
+
useSetNavbarState: jest.fn(() => ({
|
|
24
|
+
setVisible: jest.fn(),
|
|
25
|
+
})),
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
describe("HookRenderer", () => {
|
|
29
|
+
it("returns null when hookPlugin is missing", () => {
|
|
30
|
+
const { toJSON } = render(
|
|
31
|
+
<HookRenderer callback={jest.fn()} screenData={{ payload: {} } as any} />
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
expect(toJSON()).toBeNull();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("passes Hook presentationType to rendered hook component", () => {
|
|
38
|
+
const HookComponent = (props) => (
|
|
39
|
+
<View testID="hook-component" {...props} />
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const { getByTestId } = render(
|
|
43
|
+
<HookRenderer
|
|
44
|
+
callback={jest.fn()}
|
|
45
|
+
screenData={{
|
|
46
|
+
payload: { foo: "bar" },
|
|
47
|
+
hookPlugin: {
|
|
48
|
+
module: {
|
|
49
|
+
Component: HookComponent,
|
|
50
|
+
presentFullScreen: false,
|
|
51
|
+
},
|
|
52
|
+
configuration: {},
|
|
53
|
+
},
|
|
54
|
+
}}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
expect(getByTestId("hook-component").props.presentationType).toBe("Hook");
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -15,12 +15,6 @@ const styles = StyleSheet.create({
|
|
|
15
15
|
flex: 1,
|
|
16
16
|
width: "100%",
|
|
17
17
|
},
|
|
18
|
-
themeStyles: {
|
|
19
|
-
// limits the height of the focusable container of the TopMenuBarTV component
|
|
20
|
-
// to prevent it from being overlapped by the screen content,
|
|
21
|
-
// as it makes TopMenuBarTV unfocusable on tvOS
|
|
22
|
-
maxHeight: 1,
|
|
23
|
-
},
|
|
24
18
|
});
|
|
25
19
|
|
|
26
20
|
export const NavBarContainer = ({ children, isVisible, onReady }: Props) => {
|
|
@@ -31,10 +25,7 @@ export const NavBarContainer = ({ children, isVisible, onReady }: Props) => {
|
|
|
31
25
|
}, [onReady]);
|
|
32
26
|
|
|
33
27
|
return isVisible ? (
|
|
34
|
-
<View
|
|
35
|
-
testID="nav-bar-container"
|
|
36
|
-
style={[styles.container, styles.themeStyles]}
|
|
37
|
-
>
|
|
28
|
+
<View testID="nav-bar-container" style={styles.container}>
|
|
38
29
|
{children}
|
|
39
30
|
</View>
|
|
40
31
|
) : null;
|
|
@@ -3,18 +3,13 @@
|
|
|
3
3
|
exports[`NavBarContainer renders 1`] = `
|
|
4
4
|
<View
|
|
5
5
|
style={
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
"maxHeight": 1,
|
|
16
|
-
},
|
|
17
|
-
]
|
|
6
|
+
{
|
|
7
|
+
"flex": 1,
|
|
8
|
+
"position": "absolute",
|
|
9
|
+
"top": 0,
|
|
10
|
+
"width": "100%",
|
|
11
|
+
"zIndex": 10,
|
|
12
|
+
}
|
|
18
13
|
}
|
|
19
14
|
testID="nav-bar-container"
|
|
20
15
|
>
|
|
@@ -14,18 +14,13 @@ exports[`ScreenContainer renders 1`] = `
|
|
|
14
14
|
>
|
|
15
15
|
<View
|
|
16
16
|
style={
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
"maxHeight": 1,
|
|
27
|
-
},
|
|
28
|
-
]
|
|
17
|
+
{
|
|
18
|
+
"flex": 1,
|
|
19
|
+
"position": "absolute",
|
|
20
|
+
"top": 0,
|
|
21
|
+
"width": "100%",
|
|
22
|
+
"zIndex": 10,
|
|
23
|
+
}
|
|
29
24
|
}
|
|
30
25
|
testID="nav-bar-container"
|
|
31
26
|
>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { buildActionButtonsModel } from "../model";
|
|
2
|
+
|
|
3
|
+
describe("buildActionButtonsModel", () => {
|
|
4
|
+
it("returns null when the container is disabled", () => {
|
|
5
|
+
const configuration = {
|
|
6
|
+
mobile_buttons_container_buttons_enabled: false,
|
|
7
|
+
mobile_button_1_button_enabled: true,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const value = (key) => configuration[key];
|
|
11
|
+
|
|
12
|
+
expect(
|
|
13
|
+
buildActionButtonsModel({
|
|
14
|
+
configuration,
|
|
15
|
+
value,
|
|
16
|
+
containerPrefix: "mobile_buttons_container",
|
|
17
|
+
buttonPrefix: "mobile_button",
|
|
18
|
+
})
|
|
19
|
+
).toBeNull();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("returns explicit enabled slots and semantic container data", () => {
|
|
23
|
+
const configuration = {
|
|
24
|
+
mobile_buttons_container_buttons_enabled: true,
|
|
25
|
+
mobile_buttons_container_align: "right",
|
|
26
|
+
mobile_buttons_container_margin_top: 1,
|
|
27
|
+
mobile_buttons_container_margin_right: 2,
|
|
28
|
+
mobile_buttons_container_margin_bottom: 3,
|
|
29
|
+
mobile_buttons_container_margin_left: 4,
|
|
30
|
+
mobile_buttons_container_stacking: "vertical",
|
|
31
|
+
mobile_buttons_container_horizontal_gutter: 8,
|
|
32
|
+
mobile_buttons_container_vertical_gutter: 12,
|
|
33
|
+
mobile_buttons_container_independent_styles: false,
|
|
34
|
+
mobile_button_1_button_enabled: true,
|
|
35
|
+
mobile_button_2_button_enabled: false,
|
|
36
|
+
mobile_button_3_button_enabled: true,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const value = (key) => configuration[key];
|
|
40
|
+
|
|
41
|
+
expect(
|
|
42
|
+
buildActionButtonsModel({
|
|
43
|
+
configuration,
|
|
44
|
+
value,
|
|
45
|
+
containerPrefix: "mobile_buttons_container",
|
|
46
|
+
buttonPrefix: "mobile_button",
|
|
47
|
+
})
|
|
48
|
+
).toEqual({
|
|
49
|
+
enabledSlots: [1, 3],
|
|
50
|
+
buttonsCount: 2,
|
|
51
|
+
container: {
|
|
52
|
+
horizontalAlign: "flex-end",
|
|
53
|
+
margins: {
|
|
54
|
+
top: 1,
|
|
55
|
+
right: 2,
|
|
56
|
+
bottom: 3,
|
|
57
|
+
left: 4,
|
|
58
|
+
},
|
|
59
|
+
stacking: "vertical",
|
|
60
|
+
horizontalGutter: 8,
|
|
61
|
+
verticalGutter: 12,
|
|
62
|
+
independentStyles: false,
|
|
63
|
+
},
|
|
64
|
+
buttons: [
|
|
65
|
+
{
|
|
66
|
+
slot: 1,
|
|
67
|
+
renderIndex: 0,
|
|
68
|
+
specificPrefix: "mobile_button_1",
|
|
69
|
+
stylePrefix: "mobile_button_1",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
slot: 3,
|
|
73
|
+
renderIndex: 1,
|
|
74
|
+
specificPrefix: "mobile_button_3",
|
|
75
|
+
stylePrefix: "mobile_button_1",
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
});
|