@applicaster/zapp-react-native-ui-components 15.1.0-rc.8 → 16.0.0-alpha.1011304583
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/BackgroundImage/BackgroundImage.android.tv.tsx +28 -0
- package/Components/BackgroundImage/BackgroundImage.ios.tv.tsx +24 -0
- package/Components/BackgroundImage/BackgroundImage.tsx +42 -0
- package/Components/BackgroundImage/index.ts +1 -0
- package/Components/BaseFocusable/index.ios.ts +12 -2
- package/Components/Cell/FocusableWrapper.tsx +3 -0
- package/Components/Cell/TvOSCellComponent.tsx +6 -3
- package/Components/CellRendererResolver/index.ts +2 -2
- package/Components/ComponentResolver/__tests__/componentResolver.test.js +1 -1
- package/Components/Focusable/Focusable.tsx +4 -2
- package/Components/Focusable/FocusableTvOS.tsx +18 -1
- package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +1 -0
- package/Components/FocusableGroup/FocusableTvOS.tsx +41 -8
- package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +39 -0
- package/Components/GeneralContentScreen/__tests__/GeneralContentScreenHookAdapter.test.tsx +64 -0
- package/Components/GeneralContentScreen/__tests__/HookContentFocusGroup.web.test.tsx +91 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/networkService.test.ts +74 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/runInBackground.test.ts +139 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +124 -0
- package/Components/GeneralContentScreen/hookAdapter/logger.ts +6 -0
- package/Components/GeneralContentScreen/hookAdapter/networkService.ts +53 -0
- package/Components/GeneralContentScreen/hookAdapter/runInBackground.ts +48 -0
- package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +72 -0
- package/Components/GeneralContentScreen/hookFocus/index.tsx +13 -0
- package/Components/GeneralContentScreen/hookFocus/index.web.tsx +69 -0
- package/Components/GeneralContentScreen/index.ts +2 -0
- package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
- package/Components/HandlePlayable/HandlePlayable.tsx +13 -8
- package/Components/Layout/FullWidthRow.tsx +38 -0
- package/Components/Layout/TV/NavBarContainer.tsx +1 -10
- package/Components/Layout/TV/ScreenContainer.tsx +2 -6
- 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/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +0 -1
- package/Components/Layout/TV/index.tsx +5 -7
- package/Components/Layout/TV/index.web.tsx +5 -7
- package/Components/LinkHandler/LinkHandler.tsx +2 -2
- package/Components/MasterCell/CONFIG_BUILDER_TO_REACT_COMPONENT.md +144 -0
- package/Components/MasterCell/DefaultComponents/ActionButton.tsx +16 -5
- 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/components/ActionButtonController.tsx +165 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +405 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/index.ts +1 -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/BorderContainerView/index.tsx +4 -10
- package/Components/MasterCell/DefaultComponents/Button.tsx +0 -15
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/components/HorizontalSeparator.tsx +8 -0
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.android.tv.tsx +58 -0
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tsx +15 -0
- package/Components/MasterCell/DefaultComponents/{tv/ButtonContainerView/index.tsx → ButtonContainerView/index.tv.tsx} +3 -11
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.web.ts +1 -0
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/types.ts +40 -0
- package/Components/MasterCell/DefaultComponents/DataProvider/index.tsx +163 -0
- package/Components/MasterCell/DefaultComponents/FocusableView/index.android.tsx +2 -23
- package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -22
- package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +8 -2
- 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 +1 -2
- package/Components/MasterCell/DefaultComponents/PressableView.tsx +34 -0
- package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +7 -6
- package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +11 -0
- package/Components/MasterCell/DefaultComponents/__tests__/DataProvider.test.tsx +141 -0
- package/Components/MasterCell/DefaultComponents/index.ts +9 -3
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +135 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Asset.ts +33 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +22 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +125 -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 +37 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +393 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/builders.test.ts +141 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +343 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/helpers.ts +105 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +122 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +118 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +238 -0
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Asset.ts +4 -18
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Button.ts +24 -73
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TextLabelsContainer.ts +37 -18
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TvActionButton.tsx +27 -0
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/index.test.ts +89 -0
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/renderedTree.test.tsx +231 -0
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +47 -48
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +115 -29
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +101 -144
- package/Components/MasterCell/MappingFunctions/index.js +3 -2
- package/Components/MasterCell/README.md +4 -0
- package/Components/MasterCell/__tests__/__snapshots__/dataAdapter.test.js.snap +24 -0
- package/Components/MasterCell/__tests__/configInflater.test.js +1 -0
- package/Components/MasterCell/__tests__/elementMapper.test.js +46 -0
- package/Components/MasterCell/dataAdapter.ts +19 -6
- package/Components/MasterCell/elementMapper.tsx +52 -7
- package/Components/MasterCell/index.tsx +30 -5
- package/Components/MasterCell/utils/__tests__/cloneChildrenWithIds.test.tsx +43 -0
- package/Components/MasterCell/utils/__tests__/resolveColor.test.js +169 -132
- package/Components/MasterCell/utils/__tests__/resolveColorForProp.test.js +92 -0
- package/Components/MasterCell/utils/__tests__/useFilterChildren.test.tsx +80 -0
- package/Components/MasterCell/utils/index.ts +170 -61
- 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 +22 -17
- package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +284 -0
- package/Components/River/ComponentsMap/ComponentsMap.tsx +8 -21
- package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
- package/Components/River/RefreshControl.tsx +19 -88
- package/Components/River/River.tsx +9 -82
- package/Components/River/TV/River.tsx +31 -14
- package/Components/River/TV/index.tsx +8 -4
- package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +30 -0
- package/Components/River/TV/utils/index.ts +4 -0
- package/Components/River/TV/withFocusableGroupForContent.tsx +71 -0
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -15
- 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/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 +10 -7
- package/Components/Screen/orientationHandler.ts +3 -3
- package/Components/ScreenResolver/index.tsx +9 -5
- package/Components/ScreenRevealManager/Overlay.tsx +34 -0
- package/Components/ScreenRevealManager/ScreenRevealManager.ts +40 -8
- package/Components/ScreenRevealManager/__tests__/Overlay.test.tsx +88 -0
- package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +86 -69
- package/Components/ScreenRevealManager/withScreenRevealManager.tsx +9 -20
- package/Components/Tabs/TabContent.tsx +7 -4
- package/Components/TopCutoffOverlay/__tests__/TopCutoffOverlay.test.tsx +201 -0
- 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/index.js +3 -3
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +5 -5
- package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +15 -7
- package/Components/VideoModal/utils.ts +18 -10
- package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +0 -2
- package/Components/Viewport/ViewportAware/index.tsx +16 -7
- package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
- package/Components/ZappFrameworkComponents/BarView/BarView.tsx +9 -4
- package/Components/default-cell-renderer/viewTrees/mobile/index.ts +0 -3
- package/Contexts/CachedDimensionsContext/__tests__/index.test.ts +154 -0
- package/Contexts/CachedDimensionsContext/index.ts +17 -2
- package/Contexts/ScreenContext/index.tsx +25 -18
- package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
- package/Decorators/Analytics/index.tsx +6 -5
- package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +1 -0
- package/Decorators/ConfigurationWrapper/const.ts +1 -0
- package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +39 -21
- package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
- package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
- package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
- package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +18 -7
- package/Helpers/ComponentCellSelectionHelper/index.js +0 -6
- package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +80 -0
- package/Helpers/DataSourceHelper/index.ts +19 -0
- package/Helpers/index.js +7 -40
- package/package.json +5 -5
- package/Components/Layout/TV/LayoutBackground.tsx +0 -28
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +0 -135
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/types.ts +0 -25
- package/Components/River/TV/withTVEventHandler.tsx +0 -36
- package/Components/Screen/utils.ts +0 -16
- package/Helpers/Analytics/index.js +0 -95
- package/Helpers/DataSourceHelper/index.js +0 -19
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { renderHook } from "@testing-library/react-native";
|
|
3
|
+
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
4
|
+
|
|
5
|
+
import { DataProvider } from "../../DefaultComponents/DataProvider";
|
|
6
|
+
import { useFilterChildren } from "..";
|
|
7
|
+
|
|
8
|
+
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/actions", () => ({
|
|
9
|
+
useActions: jest.fn(),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
const mockUseActions = useActions as jest.Mock;
|
|
13
|
+
const Probe = () => null;
|
|
14
|
+
|
|
15
|
+
describe("useFilterChildren", () => {
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
jest.clearAllMocks();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("filters DataProvider-wrapped button children using the wrapped button props", () => {
|
|
21
|
+
const available = jest.fn(() => true);
|
|
22
|
+
const unavailable = jest.fn(() => false);
|
|
23
|
+
|
|
24
|
+
mockUseActions.mockReturnValue({
|
|
25
|
+
actions: {
|
|
26
|
+
available_action: {
|
|
27
|
+
module: {
|
|
28
|
+
context: {
|
|
29
|
+
_currentValue: {
|
|
30
|
+
isActionAvailable: available,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
unavailable_action: {
|
|
36
|
+
module: {
|
|
37
|
+
context: {
|
|
38
|
+
_currentValue: {
|
|
39
|
+
isActionAvailable: unavailable,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const availableItem = { id: "entry-1" };
|
|
48
|
+
const unavailableItem = { id: "entry-2" };
|
|
49
|
+
|
|
50
|
+
const children = [
|
|
51
|
+
<DataProvider key="available" entry={availableItem}>
|
|
52
|
+
<Probe
|
|
53
|
+
entry={availableItem}
|
|
54
|
+
pluginIdentifier="available_action"
|
|
55
|
+
suffixId="button_1"
|
|
56
|
+
cellUUID="cell-1"
|
|
57
|
+
/>
|
|
58
|
+
</DataProvider>,
|
|
59
|
+
<DataProvider key="unavailable" entry={unavailableItem}>
|
|
60
|
+
<Probe
|
|
61
|
+
entry={unavailableItem}
|
|
62
|
+
pluginIdentifier="unavailable_action"
|
|
63
|
+
suffixId="button_2"
|
|
64
|
+
cellUUID="cell-1"
|
|
65
|
+
/>
|
|
66
|
+
</DataProvider>,
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const { result } = renderHook(() => useFilterChildren(children));
|
|
70
|
+
|
|
71
|
+
expect(result.current).toHaveLength(1);
|
|
72
|
+
|
|
73
|
+
expect(result.current[0].props.children.props.pluginIdentifier).toBe(
|
|
74
|
+
"available_action"
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
expect(available).toHaveBeenCalledWith(availableItem);
|
|
78
|
+
expect(unavailable).toHaveBeenCalledWith(unavailableItem);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import React, { useMemo } from "react";
|
|
2
2
|
import * as R from "ramda";
|
|
3
|
-
import
|
|
3
|
+
import memoizee from "memoizee";
|
|
4
|
+
|
|
4
5
|
import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
5
6
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
6
|
-
|
|
7
|
-
import { masterCellLogger } from "../logger";
|
|
8
|
-
import { getCellState } from "../../Cell/utils";
|
|
7
|
+
import { isValidColor } from "@applicaster/zapp-react-native-utils/colorUtils";
|
|
9
8
|
import { getColorFromData } from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
10
|
-
import { get } from "@applicaster/zapp-react-native-utils/utils";
|
|
11
|
-
import {
|
|
9
|
+
import { pathOr, get, isNil } from "@applicaster/zapp-react-native-utils/utils";
|
|
10
|
+
import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
12
11
|
import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
13
12
|
import { useScreenStateStore } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useScreenStateStore";
|
|
14
|
-
import
|
|
15
|
-
import
|
|
13
|
+
import { isNotEmptyString } from "@applicaster/zapp-react-native-utils/stringUtils";
|
|
14
|
+
import { masterCellLogger } from "../logger";
|
|
15
|
+
import { getCellState } from "../../Cell/utils";
|
|
16
|
+
import { isCellSelected, useBehaviorUpdate } from "./behaviorProvider";
|
|
17
|
+
import { DataProvider } from "../DefaultComponents/DataProvider";
|
|
16
18
|
|
|
17
19
|
const hasElementSpecificViewType = (viewType) => (element) => {
|
|
18
|
-
if (
|
|
20
|
+
if (isNil(element)) {
|
|
19
21
|
return false;
|
|
20
22
|
}
|
|
21
23
|
|
|
@@ -27,12 +29,20 @@ const hasElementSpecificViewType = (viewType) => (element) => {
|
|
|
27
29
|
return hasElementsSpecificViewType(viewType)(element.elements);
|
|
28
30
|
};
|
|
29
31
|
|
|
32
|
+
export const hasElementsSpecificViewType = (viewType) => (elements) => {
|
|
33
|
+
if (isNilOrEmpty(elements)) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return R.any(hasElementSpecificViewType(viewType))(elements);
|
|
38
|
+
};
|
|
39
|
+
|
|
30
40
|
const logWarning = (
|
|
31
41
|
colorValueFromCellStyle,
|
|
32
42
|
colorFromProp,
|
|
33
43
|
colorValueFromEntry
|
|
34
44
|
) => {
|
|
35
|
-
if (
|
|
45
|
+
if (isNil(colorValueFromEntry)) {
|
|
36
46
|
masterCellLogger.warn({
|
|
37
47
|
message: `Cannot resolve property ${colorValueFromCellStyle} from the entry.`,
|
|
38
48
|
data: {
|
|
@@ -43,15 +53,10 @@ const logWarning = (
|
|
|
43
53
|
}
|
|
44
54
|
};
|
|
45
55
|
|
|
46
|
-
export
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return R.any(hasElementSpecificViewType(viewType))(elements);
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
function resolveColorForProp(entry: any, colorFromProp: string | undefined) {
|
|
56
|
+
export function resolveColorForProp(
|
|
57
|
+
entry: any,
|
|
58
|
+
colorFromProp: string | undefined
|
|
59
|
+
) {
|
|
55
60
|
if (!colorFromProp) {
|
|
56
61
|
return undefined;
|
|
57
62
|
}
|
|
@@ -61,9 +66,7 @@ function resolveColorForProp(entry: any, colorFromProp: string | undefined) {
|
|
|
61
66
|
colorFromProp.split(".")
|
|
62
67
|
);
|
|
63
68
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (nestedEntryValue === undefined && !validateColor(color)) {
|
|
69
|
+
if (nestedEntryValue === undefined && !isValidColor(colorFromProp)) {
|
|
67
70
|
logWarning(colorFromProp, colorFromProp, nestedEntryValue);
|
|
68
71
|
|
|
69
72
|
return undefined;
|
|
@@ -85,36 +88,73 @@ function resolveColorForProp(entry: any, colorFromProp: string | undefined) {
|
|
|
85
88
|
|
|
86
89
|
const getColorKeys = memoizee((style) => {
|
|
87
90
|
const styleKeys = Object.keys(style);
|
|
88
|
-
const colorKeys = styleKeys.filter((key) => /color/i.test(key));
|
|
89
91
|
|
|
90
|
-
return
|
|
92
|
+
return styleKeys.filter((key) => /color/i.test(key));
|
|
91
93
|
});
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
/**
|
|
96
|
+
* A color style value is either a literal color (e.g. "#FFFFFF") or a
|
|
97
|
+
* data-mapping path into the entry. Data mappings always point at the entry
|
|
98
|
+
* `extensions` object (e.g. "extensions.brandColor"), so we only need to do a
|
|
99
|
+
* path lookup when the value starts with "extensions" — everything else is a
|
|
100
|
+
* literal color and can be returned as-is.
|
|
101
|
+
*
|
|
102
|
+
* This avoids the previous, very expensive approach that ran `validateColor`
|
|
103
|
+
* on every value and memoized on a full `fast-json-stable-stringify` of the
|
|
104
|
+
* entire entry.
|
|
105
|
+
*/
|
|
106
|
+
const EXTENSIONS_PREFIX = "extensions";
|
|
107
|
+
|
|
108
|
+
const isDataMappingPath = (value: string): boolean =>
|
|
109
|
+
typeof value === "string" && value.startsWith(EXTENSIONS_PREFIX);
|
|
110
|
+
|
|
111
|
+
export const resolveColor = (
|
|
112
|
+
entry,
|
|
113
|
+
style,
|
|
114
|
+
allowDynamicColorsOutsideExtensions
|
|
115
|
+
) => {
|
|
116
|
+
if (style === null || style === undefined) {
|
|
117
|
+
return style;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const colorKeys = getColorKeys(style);
|
|
121
|
+
|
|
122
|
+
if (colorKeys.length === 0) {
|
|
123
|
+
return style;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return colorKeys.reduce(
|
|
127
|
+
(acc, key) => {
|
|
128
|
+
const value = acc[key];
|
|
98
129
|
|
|
99
|
-
|
|
100
|
-
(
|
|
101
|
-
|
|
102
|
-
const colorStyle = resolveColorForProp(entry, acc[value]);
|
|
130
|
+
// 1. The Expensive Edge-Case (Only for the app with root mappings)
|
|
131
|
+
if (allowDynamicColorsOutsideExtensions && isNotEmptyString(value)) {
|
|
132
|
+
const possibleColor = resolveColorForProp(entry, value);
|
|
103
133
|
|
|
104
|
-
|
|
105
|
-
}
|
|
134
|
+
acc[key] = isValidColor(possibleColor) ? possibleColor : null;
|
|
106
135
|
|
|
107
136
|
return acc;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 2. The Fast Path (For 99% of apps)
|
|
140
|
+
if (isDataMappingPath(value)) {
|
|
141
|
+
// resolve the mapped color from the entry; fall back to null
|
|
142
|
+
// (RN ignores null style values) when the path is missing
|
|
143
|
+
const possibleColor = pathOr(null, value.split("."), entry);
|
|
144
|
+
|
|
145
|
+
acc[key] = possibleColor;
|
|
146
|
+
|
|
147
|
+
return acc;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 3. Default Case: Treat as a raw color string
|
|
151
|
+
acc[key] = value;
|
|
152
|
+
|
|
153
|
+
return acc;
|
|
115
154
|
},
|
|
116
|
-
|
|
117
|
-
);
|
|
155
|
+
{ ...style }
|
|
156
|
+
);
|
|
157
|
+
};
|
|
118
158
|
|
|
119
159
|
export function isVideoPreviewEnabled({
|
|
120
160
|
enable_video_preview = false,
|
|
@@ -126,10 +166,50 @@ export function isVideoPreviewEnabled({
|
|
|
126
166
|
return enable_video_preview && !R.isEmpty(player_screen_id);
|
|
127
167
|
}
|
|
128
168
|
|
|
169
|
+
export const unwrapDataProviderChild = (
|
|
170
|
+
child: React.ReactElement
|
|
171
|
+
): React.ReactElement => {
|
|
172
|
+
if ((child as any)?.type !== DataProvider) {
|
|
173
|
+
return child;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const [wrappedChild] = React.Children.toArray((child as any).props.children);
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
React.isValidElement(wrappedChild) ? wrappedChild : child
|
|
180
|
+
) as React.ReactElement;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Resolves the entry value passed by DataProvider.
|
|
185
|
+
* Prefer the direct `entry` prop and fall back to `dataProviderProps[_dataKey]`
|
|
186
|
+
* for older wrappers that still read from namespaced provider props.
|
|
187
|
+
*/
|
|
188
|
+
const getEntryFromProps = (props: {
|
|
189
|
+
dataProviderProps?: {
|
|
190
|
+
_dataKey: string;
|
|
191
|
+
[key: string]: unknown;
|
|
192
|
+
};
|
|
193
|
+
entry?: any;
|
|
194
|
+
}) => {
|
|
195
|
+
const dataProviderProps = props.dataProviderProps;
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
props.entry ??
|
|
199
|
+
(dataProviderProps?._dataKey
|
|
200
|
+
? dataProviderProps[dataProviderProps._dataKey]
|
|
201
|
+
: undefined)
|
|
202
|
+
);
|
|
203
|
+
};
|
|
204
|
+
|
|
129
205
|
export const useFilterChildren = <
|
|
130
206
|
T extends {
|
|
131
207
|
props: {
|
|
132
|
-
|
|
208
|
+
dataProviderProps?: {
|
|
209
|
+
_dataKey: string;
|
|
210
|
+
[key: string]: unknown;
|
|
211
|
+
};
|
|
212
|
+
entry?: any;
|
|
133
213
|
pluginIdentifier: string;
|
|
134
214
|
};
|
|
135
215
|
},
|
|
@@ -139,8 +219,13 @@ export const useFilterChildren = <
|
|
|
139
219
|
const actions = useActions("");
|
|
140
220
|
|
|
141
221
|
const filteredChildren = children.filter((child) => {
|
|
142
|
-
const
|
|
143
|
-
|
|
222
|
+
const wrappedChild = unwrapDataProviderChild(
|
|
223
|
+
child as unknown as React.ReactElement
|
|
224
|
+
) as unknown as T;
|
|
225
|
+
|
|
226
|
+
const item = getEntryFromProps(wrappedChild.props);
|
|
227
|
+
|
|
228
|
+
const actionIdentifier = wrappedChild.props.pluginIdentifier;
|
|
144
229
|
const action = actions.actions[actionIdentifier];
|
|
145
230
|
|
|
146
231
|
// context value of specific plugin
|
|
@@ -154,7 +239,7 @@ export const useFilterChildren = <
|
|
|
154
239
|
|
|
155
240
|
masterCellLogger.error({
|
|
156
241
|
message: `Action plugin for ${actionIdentifier} could not be found, check the configuration of your action button`,
|
|
157
|
-
data: { item, action:
|
|
242
|
+
data: { item, action: wrappedChild.props.pluginIdentifier },
|
|
158
243
|
});
|
|
159
244
|
|
|
160
245
|
return false;
|
|
@@ -192,7 +277,7 @@ const recursiveCloneElement = (focused: boolean) => (element) => {
|
|
|
192
277
|
};
|
|
193
278
|
|
|
194
279
|
export const recursiveCloneElementsWithState = (focused: boolean, children) => {
|
|
195
|
-
if (
|
|
280
|
+
if (isNilOrEmpty(children)) {
|
|
196
281
|
return undefined;
|
|
197
282
|
}
|
|
198
283
|
|
|
@@ -202,17 +287,42 @@ export const recursiveCloneElementsWithState = (focused: boolean, children) => {
|
|
|
202
287
|
const next = (currentIndex, items) => items[currentIndex + 1];
|
|
203
288
|
const previous = (currentIndex, items) => items[currentIndex - 1];
|
|
204
289
|
|
|
205
|
-
export const
|
|
206
|
-
|
|
290
|
+
export const cloneChildrenWithIds = (
|
|
291
|
+
ids: string[],
|
|
292
|
+
children: React.ReactElement[]
|
|
293
|
+
) => {
|
|
294
|
+
if (isNilOrEmpty(children)) {
|
|
207
295
|
return undefined;
|
|
208
296
|
}
|
|
209
297
|
|
|
210
|
-
return React.Children.map(children, (element, index) =>
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
298
|
+
return React.Children.map(children, (element, index) => {
|
|
299
|
+
const nextFocusLeft = previous(index, ids);
|
|
300
|
+
const nextFocusRight = next(index, ids);
|
|
301
|
+
|
|
302
|
+
if (element?.type !== DataProvider) {
|
|
303
|
+
return React.cloneElement(element, {
|
|
304
|
+
nextFocusLeft,
|
|
305
|
+
nextFocusRight,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const wrappedChild = unwrapDataProviderChild(element) as React.ReactElement<
|
|
310
|
+
Record<string, unknown>
|
|
311
|
+
>;
|
|
312
|
+
|
|
313
|
+
if (!React.isValidElement(wrappedChild)) {
|
|
314
|
+
return element;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const injectedWrappedChild = React.cloneElement(wrappedChild, {
|
|
318
|
+
nextFocusLeft,
|
|
319
|
+
nextFocusRight,
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
return React.cloneElement(element, {
|
|
323
|
+
children: injectedWrappedChild,
|
|
324
|
+
});
|
|
325
|
+
});
|
|
216
326
|
};
|
|
217
327
|
|
|
218
328
|
export const getFocusedButtonId = (focusable) => {
|
|
@@ -252,10 +362,9 @@ export const useCellState = ({
|
|
|
252
362
|
export const hasFocusableInsideBuilder = (elementsBuilder) => (item) => {
|
|
253
363
|
const elements = elementsBuilder({ entry: item });
|
|
254
364
|
|
|
255
|
-
return R.anyPass([
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
])(elements);
|
|
365
|
+
return R.anyPass([hasElementsSpecificViewType("ButtonContainerView")])(
|
|
366
|
+
elements
|
|
367
|
+
);
|
|
259
368
|
};
|
|
260
369
|
|
|
261
370
|
export function getEntryState(state, selected) {
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
TouchableWithoutFeedback,
|
|
9
9
|
} from "react-native";
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { usePlugins } from "@applicaster/zapp-react-native-redux";
|
|
12
12
|
import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
13
13
|
import { textTransform } from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
14
14
|
import { useNotificationHeight } from "../utils";
|
|
@@ -46,7 +46,7 @@ export const NotificationView = ({
|
|
|
46
46
|
online = true,
|
|
47
47
|
dismiss,
|
|
48
48
|
}: Props) => {
|
|
49
|
-
const
|
|
49
|
+
const plugins = usePlugins();
|
|
50
50
|
const { statusHeight, notificationHeight } = useNotificationHeight();
|
|
51
51
|
|
|
52
52
|
const offlinePlugin = R.find(
|
|
@@ -8,25 +8,24 @@ import {
|
|
|
8
8
|
offlinePhrase,
|
|
9
9
|
} from "../NotificationView";
|
|
10
10
|
|
|
11
|
-
jest.mock("@applicaster/zapp-react-native-redux
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
},
|
|
11
|
+
jest.mock("@applicaster/zapp-react-native-redux", () => ({
|
|
12
|
+
...jest.requireActual("@applicaster/zapp-react-native-redux"),
|
|
13
|
+
usePlugins: () => [
|
|
14
|
+
{
|
|
15
|
+
name: "offline experience",
|
|
16
|
+
identifier: "offline-experience",
|
|
17
|
+
type: "general",
|
|
18
|
+
module: {
|
|
19
|
+
useOfflineExperienceConfiguration: () => ({
|
|
20
|
+
configurationFields: {},
|
|
21
|
+
localizations: {
|
|
22
|
+
offline_toast_message: "No internet connection",
|
|
23
|
+
online_toast_message: "You are back online",
|
|
24
|
+
},
|
|
25
|
+
}),
|
|
27
26
|
},
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
},
|
|
28
|
+
],
|
|
30
29
|
}));
|
|
31
30
|
|
|
32
31
|
jest.mock("react-native-safe-area-context", () => ({
|
|
@@ -8,36 +8,45 @@ const mockPreviousValue = null;
|
|
|
8
8
|
|
|
9
9
|
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/connection", () => {
|
|
10
10
|
return {
|
|
11
|
+
...jest.requireActual(
|
|
12
|
+
"@applicaster/zapp-react-native-utils/reactHooks/connection"
|
|
13
|
+
),
|
|
11
14
|
useConnectionInfo: jest.fn(() => mockConnectionStatus),
|
|
12
15
|
};
|
|
13
16
|
});
|
|
14
17
|
|
|
15
18
|
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/utils", () => {
|
|
16
19
|
return {
|
|
20
|
+
...jest.requireActual(
|
|
21
|
+
"@applicaster/zapp-react-native-utils/reactHooks/utils"
|
|
22
|
+
),
|
|
17
23
|
usePrevious: jest.fn(() => mockPreviousValue),
|
|
18
24
|
};
|
|
19
25
|
});
|
|
20
26
|
|
|
27
|
+
const mock_storeObj = {
|
|
28
|
+
plugins: [
|
|
29
|
+
{
|
|
30
|
+
name: "offline experience",
|
|
31
|
+
identifier: "offline-experience",
|
|
32
|
+
type: "general",
|
|
33
|
+
module: {
|
|
34
|
+
useOfflineExperienceConfiguration: () => ({
|
|
35
|
+
configurationFields: {},
|
|
36
|
+
localizations: {
|
|
37
|
+
offline_toast_message: "No internet connection",
|
|
38
|
+
online_toast_message: "You are back online",
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
|
|
21
46
|
jest.mock("@applicaster/zapp-react-native-redux/hooks", () => ({
|
|
22
47
|
...jest.requireActual("@applicaster/zapp-react-native-redux/hooks"),
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
{
|
|
26
|
-
name: "offline experience",
|
|
27
|
-
identifier: "offline-experience",
|
|
28
|
-
type: "general",
|
|
29
|
-
module: {
|
|
30
|
-
useOfflineExperienceConfiguration: () => ({
|
|
31
|
-
configurationFields: {},
|
|
32
|
-
localizations: {
|
|
33
|
-
offline_toast_message: "No internet connection",
|
|
34
|
-
online_toast_message: "You are back online",
|
|
35
|
-
},
|
|
36
|
-
}),
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
],
|
|
40
|
-
}),
|
|
48
|
+
usePlugins: () => mock_storeObj.plugins,
|
|
49
|
+
usePickFromState: () => ({}),
|
|
41
50
|
}));
|
|
42
51
|
|
|
43
52
|
jest.mock("react-native-safe-area-context", () => ({
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
|
|
18
18
|
import { TVEventHandlerComponent } from "@applicaster/zapp-react-native-tvos-ui-components/Components/TVEventHandlerComponent";
|
|
19
19
|
import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
import {
|
|
22
22
|
useBackHandler,
|
|
23
23
|
useNavigation,
|
|
@@ -59,6 +59,7 @@ import {
|
|
|
59
59
|
PlayerNativeCommandTypes,
|
|
60
60
|
PlayerNativeSendCommand,
|
|
61
61
|
} from "@applicaster/zapp-react-native-utils/appUtils/playerManager/playerNativeCommand";
|
|
62
|
+
import { useAppData } from "@applicaster/zapp-react-native-redux";
|
|
62
63
|
|
|
63
64
|
type Props = {
|
|
64
65
|
Player: React.ComponentType<any>;
|
|
@@ -237,7 +238,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
237
238
|
const [isLoadingNextVideo, setIsLoadingNextVideo] = React.useState(false);
|
|
238
239
|
|
|
239
240
|
const navigator = useNavigation();
|
|
240
|
-
const {
|
|
241
|
+
const { isTabletPortrait } = useAppData();
|
|
241
242
|
const prevItemId = usePrevious(item?.id);
|
|
242
243
|
const screenData = useTargetScreenData(item);
|
|
243
244
|
const { setVisible: showNavBar } = useSetNavbarState();
|
|
@@ -262,9 +263,14 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
262
263
|
|
|
263
264
|
showNavBar(true);
|
|
264
265
|
navigator.goBack();
|
|
265
|
-
}, [isModal,
|
|
266
|
+
}, [isModal, state.playerId, showNavBar, navigator]);
|
|
267
|
+
|
|
268
|
+
const pluginConfiguration = React.useMemo(
|
|
269
|
+
() => player?.getPluginConfiguration(),
|
|
270
|
+
[player]
|
|
271
|
+
);
|
|
266
272
|
|
|
267
|
-
const playEntry = (entry) => navigator.replaceTop(entry, { mode });
|
|
273
|
+
const playEntry = (entry: ZappEntry) => navigator.replaceTop(entry, { mode });
|
|
268
274
|
|
|
269
275
|
const onPlayNextPerformNextVideoPlay = React.useCallback(() => {
|
|
270
276
|
if (!playNextOverlayState.entry) {
|
|
@@ -338,11 +344,15 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
338
344
|
setIsLoadingNextVideo(false);
|
|
339
345
|
|
|
340
346
|
const resumeTime = Number(item?.extensions?.resumeTime);
|
|
347
|
+
const activePlayer = playerManager.getActivePlayer();
|
|
341
348
|
|
|
342
|
-
// Сhecking that the player itself knows where to start playing, and there is no need to call seekTo after returning from the Сhromecast
|
|
349
|
+
// Сhecking that the player itself knows where to start playing, and there is no need to call seekTo after returning from the Сhromecast.
|
|
350
|
+
// Live streams and a missing/zero resume position are skipped: seeking to 0 on
|
|
351
|
+
// a live stream pulls playback off the live edge into a buffer-starvation seek loop.
|
|
343
352
|
if (
|
|
344
|
-
!
|
|
345
|
-
|
|
353
|
+
!activePlayer?.hasResumePosition() &&
|
|
354
|
+
resumeTime > 0 &&
|
|
355
|
+
!activePlayer?.isLive()
|
|
346
356
|
) {
|
|
347
357
|
player?.seekTo(resumeTime);
|
|
348
358
|
}
|
|
@@ -448,13 +458,6 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
448
458
|
}
|
|
449
459
|
}, []);
|
|
450
460
|
|
|
451
|
-
const pluginConfiguration = React.useMemo(() => {
|
|
452
|
-
return (
|
|
453
|
-
playerManager.getPluginConfiguration() ||
|
|
454
|
-
R.prop("__plugin_configuration", Player)
|
|
455
|
-
);
|
|
456
|
-
}, [playerManager.isRegistered()]);
|
|
457
|
-
|
|
458
461
|
const disableMiniPlayer = React.useMemo(() => {
|
|
459
462
|
return pluginConfiguration?.disable_mini_player_when_inline;
|
|
460
463
|
}, [pluginConfiguration]);
|
|
@@ -467,6 +470,8 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
467
470
|
if (isModal && mode === VideoModalMode.MAXIMIZED) {
|
|
468
471
|
if (disableMiniPlayer) {
|
|
469
472
|
navigator.closeVideoModal();
|
|
473
|
+
} else {
|
|
474
|
+
navigator.minimiseVideoModal();
|
|
470
475
|
}
|
|
471
476
|
}
|
|
472
477
|
|
|
@@ -654,7 +659,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
654
659
|
<PlayerFocusableWrapperView
|
|
655
660
|
nextFocusDown={context.bottomFocusableId}
|
|
656
661
|
>
|
|
657
|
-
{Player ? (
|
|
662
|
+
{!Player ? null : (
|
|
658
663
|
<Player
|
|
659
664
|
source={{
|
|
660
665
|
uri,
|
|
@@ -669,7 +674,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
669
674
|
fullscreen={mode === VideoModalMode.FULLSCREEN}
|
|
670
675
|
inline={inline}
|
|
671
676
|
isModal={isModal}
|
|
672
|
-
isTabletPortrait={
|
|
677
|
+
isTabletPortrait={isTabletPortrait}
|
|
673
678
|
muted={false}
|
|
674
679
|
playableItem={item}
|
|
675
680
|
playerEvent={playerEvent}
|
|
@@ -685,7 +690,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
685
690
|
>
|
|
686
691
|
{renderApplePlayer(applePlayerProps)}
|
|
687
692
|
</Player>
|
|
688
|
-
)
|
|
693
|
+
)}
|
|
689
694
|
</PlayerFocusableWrapperView>
|
|
690
695
|
</View>
|
|
691
696
|
{/* Components container */}
|