@applicaster/zapp-react-native-ui-components 15.0.0-rc.99 → 16.0.0-rc.1
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/Cell/TvOSCellComponent.tsx +1 -3
- 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/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +5 -0
- package/Components/MasterCell/CONFIG_BUILDER_TO_REACT_COMPONENT.md +144 -0
- 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/Button.tsx +0 -15
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/components/HorizontalSeparator.tsx +8 -0
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tsx +15 -0
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tv.android.tsx +58 -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 +3 -1
- 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 +34 -0
- package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +11 -0
- package/Components/MasterCell/DefaultComponents/Text/index.tsx +2 -6
- 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 -52
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +35 -171
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +98 -145
- 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 +4 -1
- package/Components/MasterCell/elementMapper.tsx +52 -7
- package/Components/MasterCell/utils/__tests__/cloneChildrenWithIds.test.tsx +43 -0
- package/Components/MasterCell/utils/__tests__/useFilterChildren.test.tsx +80 -0
- package/Components/MasterCell/utils/index.ts +85 -15
- package/Components/Navigator/StackNavigator.tsx +6 -0
- package/Components/PlayerContainer/PlayerContainer.tsx +2 -19
- package/Components/PreloaderWrapper/__tests__/index.test.tsx +26 -0
- package/Components/PreloaderWrapper/index.tsx +15 -0
- package/Components/River/ComponentsMap/ComponentsMap.tsx +2 -16
- package/Components/River/RefreshControl.tsx +19 -82
- package/Components/River/River.tsx +9 -82
- package/Components/River/RiverItem.tsx +26 -20
- 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/ScreenRevealManager/withScreenRevealManager.tsx +4 -1
- 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/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 +46 -1
- 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/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/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +0 -135
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/types.ts +0 -25
- 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
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { DataProvider } from "../../DefaultComponents/DataProvider";
|
|
4
|
+
import { cloneChildrenWithIds } from "..";
|
|
5
|
+
|
|
6
|
+
const Probe = () => null;
|
|
7
|
+
|
|
8
|
+
describe("cloneChildrenWithIds", () => {
|
|
9
|
+
it("injects next focus props into plain children", () => {
|
|
10
|
+
const children = [
|
|
11
|
+
<Probe key="one" />,
|
|
12
|
+
<Probe key="two" />,
|
|
13
|
+
<Probe key="three" />,
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
const result = cloneChildrenWithIds(["id-1", "id-2", "id-3"], children);
|
|
17
|
+
|
|
18
|
+
expect(result[0].props.nextFocusLeft).toBeUndefined();
|
|
19
|
+
expect(result[0].props.nextFocusRight).toBe("id-2");
|
|
20
|
+
expect(result[1].props.nextFocusLeft).toBe("id-1");
|
|
21
|
+
expect(result[1].props.nextFocusRight).toBe("id-3");
|
|
22
|
+
expect(result[2].props.nextFocusLeft).toBe("id-2");
|
|
23
|
+
expect(result[2].props.nextFocusRight).toBeUndefined();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("injects next focus props into DataProvider-wrapped children", () => {
|
|
27
|
+
const children = [
|
|
28
|
+
<DataProvider key="one" entry={{ id: "entry-1" }}>
|
|
29
|
+
<Probe suffixId="button_1" />
|
|
30
|
+
</DataProvider>,
|
|
31
|
+
<DataProvider key="two" entry={{ id: "entry-2" }}>
|
|
32
|
+
<Probe suffixId="button_2" />
|
|
33
|
+
</DataProvider>,
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const result = cloneChildrenWithIds(["id-1", "id-2"], children);
|
|
37
|
+
|
|
38
|
+
expect(result[0].props.children.props.nextFocusLeft).toBeUndefined();
|
|
39
|
+
expect(result[0].props.children.props.nextFocusRight).toBe("id-2");
|
|
40
|
+
expect(result[1].props.children.props.nextFocusLeft).toBe("id-1");
|
|
41
|
+
expect(result[1].props.children.props.nextFocusRight).toBeUndefined();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -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
|
+
});
|
|
@@ -13,6 +13,7 @@ import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
|
13
13
|
import { useScreenStateStore } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useScreenStateStore";
|
|
14
14
|
import memoizee from "memoizee";
|
|
15
15
|
import stringify from "fast-json-stable-stringify";
|
|
16
|
+
import { DataProvider } from "../DefaultComponents/DataProvider";
|
|
16
17
|
|
|
17
18
|
const hasElementSpecificViewType = (viewType) => (element) => {
|
|
18
19
|
if (R.isNil(element)) {
|
|
@@ -126,10 +127,50 @@ export function isVideoPreviewEnabled({
|
|
|
126
127
|
return enable_video_preview && !R.isEmpty(player_screen_id);
|
|
127
128
|
}
|
|
128
129
|
|
|
130
|
+
export const unwrapDataProviderChild = (
|
|
131
|
+
child: React.ReactElement
|
|
132
|
+
): React.ReactElement => {
|
|
133
|
+
if ((child as any)?.type !== DataProvider) {
|
|
134
|
+
return child;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const [wrappedChild] = React.Children.toArray((child as any).props.children);
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
React.isValidElement(wrappedChild) ? wrappedChild : child
|
|
141
|
+
) as React.ReactElement;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Resolves the entry value passed by DataProvider.
|
|
146
|
+
* Prefer the direct `entry` prop and fall back to `dataProviderProps[_dataKey]`
|
|
147
|
+
* for older wrappers that still read from namespaced provider props.
|
|
148
|
+
*/
|
|
149
|
+
const getEntryFromProps = (props: {
|
|
150
|
+
dataProviderProps?: {
|
|
151
|
+
_dataKey: string;
|
|
152
|
+
[key: string]: unknown;
|
|
153
|
+
};
|
|
154
|
+
entry?: any;
|
|
155
|
+
}) => {
|
|
156
|
+
const dataProviderProps = props.dataProviderProps;
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
props.entry ??
|
|
160
|
+
(dataProviderProps?._dataKey
|
|
161
|
+
? dataProviderProps[dataProviderProps._dataKey]
|
|
162
|
+
: undefined)
|
|
163
|
+
);
|
|
164
|
+
};
|
|
165
|
+
|
|
129
166
|
export const useFilterChildren = <
|
|
130
167
|
T extends {
|
|
131
168
|
props: {
|
|
132
|
-
|
|
169
|
+
dataProviderProps?: {
|
|
170
|
+
_dataKey: string;
|
|
171
|
+
[key: string]: unknown;
|
|
172
|
+
};
|
|
173
|
+
entry?: any;
|
|
133
174
|
pluginIdentifier: string;
|
|
134
175
|
};
|
|
135
176
|
},
|
|
@@ -139,8 +180,13 @@ export const useFilterChildren = <
|
|
|
139
180
|
const actions = useActions("");
|
|
140
181
|
|
|
141
182
|
const filteredChildren = children.filter((child) => {
|
|
142
|
-
const
|
|
143
|
-
|
|
183
|
+
const wrappedChild = unwrapDataProviderChild(
|
|
184
|
+
child as unknown as React.ReactElement
|
|
185
|
+
) as unknown as T;
|
|
186
|
+
|
|
187
|
+
const item = getEntryFromProps(wrappedChild.props);
|
|
188
|
+
|
|
189
|
+
const actionIdentifier = wrappedChild.props.pluginIdentifier;
|
|
144
190
|
const action = actions.actions[actionIdentifier];
|
|
145
191
|
|
|
146
192
|
// context value of specific plugin
|
|
@@ -154,7 +200,7 @@ export const useFilterChildren = <
|
|
|
154
200
|
|
|
155
201
|
masterCellLogger.error({
|
|
156
202
|
message: `Action plugin for ${actionIdentifier} could not be found, check the configuration of your action button`,
|
|
157
|
-
data: { item, action:
|
|
203
|
+
data: { item, action: wrappedChild.props.pluginIdentifier },
|
|
158
204
|
});
|
|
159
205
|
|
|
160
206
|
return false;
|
|
@@ -202,17 +248,42 @@ export const recursiveCloneElementsWithState = (focused: boolean, children) => {
|
|
|
202
248
|
const next = (currentIndex, items) => items[currentIndex + 1];
|
|
203
249
|
const previous = (currentIndex, items) => items[currentIndex - 1];
|
|
204
250
|
|
|
205
|
-
export const
|
|
251
|
+
export const cloneChildrenWithIds = (
|
|
252
|
+
ids: string[],
|
|
253
|
+
children: React.ReactElement[]
|
|
254
|
+
) => {
|
|
206
255
|
if (R.isNil(children) || R.isEmpty(children)) {
|
|
207
256
|
return undefined;
|
|
208
257
|
}
|
|
209
258
|
|
|
210
|
-
return React.Children.map(children, (element, index) =>
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
259
|
+
return React.Children.map(children, (element, index) => {
|
|
260
|
+
const nextFocusLeft = previous(index, ids);
|
|
261
|
+
const nextFocusRight = next(index, ids);
|
|
262
|
+
|
|
263
|
+
if (element?.type !== DataProvider) {
|
|
264
|
+
return React.cloneElement(element, {
|
|
265
|
+
nextFocusLeft,
|
|
266
|
+
nextFocusRight,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const wrappedChild = unwrapDataProviderChild(element) as React.ReactElement<
|
|
271
|
+
Record<string, unknown>
|
|
272
|
+
>;
|
|
273
|
+
|
|
274
|
+
if (!React.isValidElement(wrappedChild)) {
|
|
275
|
+
return element;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const injectedWrappedChild = React.cloneElement(wrappedChild, {
|
|
279
|
+
nextFocusLeft,
|
|
280
|
+
nextFocusRight,
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
return React.cloneElement(element, {
|
|
284
|
+
children: injectedWrappedChild,
|
|
285
|
+
});
|
|
286
|
+
});
|
|
216
287
|
};
|
|
217
288
|
|
|
218
289
|
export const getFocusedButtonId = (focusable) => {
|
|
@@ -252,10 +323,9 @@ export const useCellState = ({
|
|
|
252
323
|
export const hasFocusableInsideBuilder = (elementsBuilder) => (item) => {
|
|
253
324
|
const elements = elementsBuilder({ entry: item });
|
|
254
325
|
|
|
255
|
-
return R.anyPass([
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
])(elements);
|
|
326
|
+
return R.anyPass([hasElementsSpecificViewType("ButtonContainerView")])(
|
|
327
|
+
elements
|
|
328
|
+
);
|
|
259
329
|
};
|
|
260
330
|
|
|
261
331
|
export function getEntryState(state, selected) {
|
|
@@ -6,11 +6,16 @@ import { getPathAttributes } from "@applicaster/zapp-react-native-utils/navigati
|
|
|
6
6
|
import { FocusableGroup } from "@applicaster/zapp-react-native-ui-components/Components/FocusableGroup";
|
|
7
7
|
import { ScreenDataContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenDataContext";
|
|
8
8
|
import { PathnameContext } from "@applicaster/zapp-react-native-ui-components/Contexts/PathnameContext";
|
|
9
|
+
import { StyleSheet } from "react-native";
|
|
9
10
|
|
|
10
11
|
import { Screen } from "../Screen/TV/index.web";
|
|
11
12
|
import { isLast } from "@applicaster/zapp-react-native-utils/arrayUtils";
|
|
12
13
|
import { ScreenContextProvider } from "../../Contexts/ScreenContext";
|
|
13
14
|
|
|
15
|
+
const styles = StyleSheet.create({
|
|
16
|
+
container: { flex: 1 },
|
|
17
|
+
});
|
|
18
|
+
|
|
14
19
|
type Components = {
|
|
15
20
|
NavBar: React.ComponentType<any>;
|
|
16
21
|
Background: React.ComponentType<any>;
|
|
@@ -41,6 +46,7 @@ export const StackNavigator = ({ Components }: Props) => {
|
|
|
41
46
|
preferredFocus={isLast(index, mainStack.length)}
|
|
42
47
|
excludeFromFocusSearching
|
|
43
48
|
key={routeId}
|
|
49
|
+
style={styles.container}
|
|
44
50
|
>
|
|
45
51
|
<ScreenDataContext.Provider value={state}>
|
|
46
52
|
<PathnameContext.Provider value={route}>
|
|
@@ -46,7 +46,6 @@ import {
|
|
|
46
46
|
PlayerContainerContextProvider,
|
|
47
47
|
} from "./PlayerContainerContext";
|
|
48
48
|
import { FocusableGroup } from "@applicaster/zapp-react-native-ui-components/Components/FocusableGroup";
|
|
49
|
-
import { ErrorDisplay } from "./ErrorDisplay";
|
|
50
49
|
import { PlayerFocusableWrapperView } from "./WappersView/PlayerFocusableWrapperView";
|
|
51
50
|
import { FocusableGroupMainContainerId } from "./index";
|
|
52
51
|
import { isPlayable } from "@applicaster/zapp-react-native-utils/navigationUtils/itemTypeMatchers";
|
|
@@ -61,7 +60,6 @@ import {
|
|
|
61
60
|
PlayerNativeSendCommand,
|
|
62
61
|
} from "@applicaster/zapp-react-native-utils/appUtils/playerManager/playerNativeCommand";
|
|
63
62
|
import { useAppData } from "@applicaster/zapp-react-native-redux";
|
|
64
|
-
import { useRestrictMobilePlayback } from "./useRestrictMobilePlayback";
|
|
65
63
|
|
|
66
64
|
type Props = {
|
|
67
65
|
Player: React.ComponentType<any>;
|
|
@@ -111,7 +109,7 @@ const withBorderHack = () => {
|
|
|
111
109
|
/* @HACK: see GH#7269 */
|
|
112
110
|
return {
|
|
113
111
|
borderWidth: 1,
|
|
114
|
-
borderColor: "
|
|
112
|
+
borderColor: "black",
|
|
115
113
|
};
|
|
116
114
|
}
|
|
117
115
|
|
|
@@ -274,13 +272,6 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
274
272
|
);
|
|
275
273
|
}, [playerManager.isRegistered()]);
|
|
276
274
|
|
|
277
|
-
const { isRestricted } = useRestrictMobilePlayback({
|
|
278
|
-
player,
|
|
279
|
-
entry: item,
|
|
280
|
-
pluginConfiguration,
|
|
281
|
-
close,
|
|
282
|
-
});
|
|
283
|
-
|
|
284
275
|
const playEntry = (entry) => navigator.replaceTop(entry, { mode });
|
|
285
276
|
|
|
286
277
|
const onPlayNextPerformNextVideoPlay = React.useCallback(() => {
|
|
@@ -347,12 +338,6 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
347
338
|
playerContainerLogger.error(errorObj);
|
|
348
339
|
|
|
349
340
|
setState({ error: errorObj });
|
|
350
|
-
|
|
351
|
-
if (!isTvOS) {
|
|
352
|
-
setTimeout(() => {
|
|
353
|
-
close();
|
|
354
|
-
}, 800);
|
|
355
|
-
}
|
|
356
341
|
},
|
|
357
342
|
[close]
|
|
358
343
|
);
|
|
@@ -670,7 +655,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
670
655
|
<PlayerFocusableWrapperView
|
|
671
656
|
nextFocusDown={context.bottomFocusableId}
|
|
672
657
|
>
|
|
673
|
-
{
|
|
658
|
+
{!Player ? null : (
|
|
674
659
|
<Player
|
|
675
660
|
source={{
|
|
676
661
|
uri,
|
|
@@ -703,8 +688,6 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
703
688
|
</Player>
|
|
704
689
|
)}
|
|
705
690
|
</PlayerFocusableWrapperView>
|
|
706
|
-
|
|
707
|
-
{state.error ? <ErrorDisplay error={state.error} /> : null}
|
|
708
691
|
</View>
|
|
709
692
|
{/* Components container */}
|
|
710
693
|
{isInlineTV && context.showComponentsContainer ? (
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text } from "react-native";
|
|
3
|
+
import { render } from "@testing-library/react-native";
|
|
4
|
+
import { PreloaderWrapper } from "..";
|
|
5
|
+
|
|
6
|
+
describe("PreloaderWrapper", () => {
|
|
7
|
+
it("renders children when preloader is hidden", () => {
|
|
8
|
+
const { getByText } = render(
|
|
9
|
+
<PreloaderWrapper showPreloader={false}>
|
|
10
|
+
<Text>content</Text>
|
|
11
|
+
</PreloaderWrapper>
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
expect(getByText("content")).toBeDefined();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("renders null when preloader is shown", () => {
|
|
18
|
+
const { queryByText } = render(
|
|
19
|
+
<PreloaderWrapper showPreloader>
|
|
20
|
+
<Text>content</Text>
|
|
21
|
+
</PreloaderWrapper>
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
expect(queryByText("content")).toBeNull();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
type PreloaderWrapperProps = {
|
|
4
|
+
showPreloader?: boolean;
|
|
5
|
+
children?: React.ReactNode;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const PreloaderWrapper: React.FC<PreloaderWrapperProps> = ({
|
|
9
|
+
showPreloader = false,
|
|
10
|
+
children,
|
|
11
|
+
}) => {
|
|
12
|
+
return !showPreloader ? children : null;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default PreloaderWrapper;
|
|
@@ -39,8 +39,6 @@ type Props = {
|
|
|
39
39
|
scrollViewExtraProps?: {};
|
|
40
40
|
riverId?: string;
|
|
41
41
|
getStaticComponentFeed: any;
|
|
42
|
-
pullToRefreshPipesV1RefreshingStateUpdater: () => boolean;
|
|
43
|
-
refreshingPipesV1?: boolean;
|
|
44
42
|
stickyHeaderIndices?: number[];
|
|
45
43
|
};
|
|
46
44
|
|
|
@@ -65,10 +63,6 @@ function ComponentsMapComponent(props: Props) {
|
|
|
65
63
|
groupId,
|
|
66
64
|
riverId,
|
|
67
65
|
getStaticComponentFeed,
|
|
68
|
-
// Method added to keep pipes v1 logic up to date with the pullToRefresh state.
|
|
69
|
-
// TODO: Remove when pipes v1 is deprecated.
|
|
70
|
-
pullToRefreshPipesV1RefreshingStateUpdater,
|
|
71
|
-
refreshingPipesV1,
|
|
72
66
|
stickyHeaderIndices,
|
|
73
67
|
} = props;
|
|
74
68
|
|
|
@@ -163,16 +157,8 @@ function ComponentsMapComponent(props: Props) {
|
|
|
163
157
|
usePipesCacheReset(riverId, riverComponents);
|
|
164
158
|
|
|
165
159
|
const refreshControl = React.useMemo(
|
|
166
|
-
() =>
|
|
167
|
-
|
|
168
|
-
<RefreshControl
|
|
169
|
-
pullToRefreshPipesV1RefreshingStateUpdater={
|
|
170
|
-
pullToRefreshPipesV1RefreshingStateUpdater
|
|
171
|
-
}
|
|
172
|
-
refreshingPipesV1={refreshingPipesV1}
|
|
173
|
-
/>
|
|
174
|
-
) : null,
|
|
175
|
-
[]
|
|
160
|
+
() => (pullToRefreshEnabled ? <RefreshControl /> : null),
|
|
161
|
+
[pullToRefreshEnabled]
|
|
176
162
|
);
|
|
177
163
|
|
|
178
164
|
const navBarStore = useScreenContextV2()._navBarStore;
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
RefreshControl as RNRefreshControl,
|
|
5
5
|
StyleSheet,
|
|
6
6
|
} from "react-native";
|
|
7
|
-
import
|
|
7
|
+
import { get } from "@applicaster/zapp-react-native-utils/utils";
|
|
8
8
|
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
9
9
|
import { useLocalizedStrings } from "@applicaster/zapp-react-native-utils/localizationUtils";
|
|
10
10
|
import { useAnalytics } from "@applicaster/zapp-react-native-utils/analyticsUtils";
|
|
@@ -13,7 +13,7 @@ import { useCurrentScreenData } from "@applicaster/zapp-react-native-utils/react
|
|
|
13
13
|
import { useShallow } from "zustand/react/shallow";
|
|
14
14
|
import { useScreenContextV2 } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
|
|
15
15
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
16
|
-
import {
|
|
16
|
+
import { usePullToRefresh } from "./hooks";
|
|
17
17
|
|
|
18
18
|
const BRIGHTNESS_THRESHOLD = 160;
|
|
19
19
|
const ABOVE_DEFAULT_COLOR = "gray";
|
|
@@ -53,62 +53,6 @@ const getBrightness = (RGBcolor) => {
|
|
|
53
53
|
);
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
-
export const usePullToRefresh = (
|
|
57
|
-
riverComponents,
|
|
58
|
-
pullToRefreshPipesV1RefreshingStateUpdater,
|
|
59
|
-
refreshingPipesV1
|
|
60
|
-
) => {
|
|
61
|
-
const isPipesV1 = !!pullToRefreshPipesV1RefreshingStateUpdater;
|
|
62
|
-
|
|
63
|
-
const [refreshing, setRefreshing] = React.useState(false);
|
|
64
|
-
|
|
65
|
-
const feeds: string[] =
|
|
66
|
-
riverComponents?.map(R.path(["data", "source"])).filter((feed) => !!feed) ??
|
|
67
|
-
[];
|
|
68
|
-
|
|
69
|
-
const feedsLength = feeds.length;
|
|
70
|
-
|
|
71
|
-
const [requestsCompletedCounter, setRequestsCompletedCounter] =
|
|
72
|
-
React.useState(0);
|
|
73
|
-
|
|
74
|
-
const loadPipesDataDispatcher = useLoadPipesDataDispatch();
|
|
75
|
-
|
|
76
|
-
React.useEffect(() => {
|
|
77
|
-
// will not work for pipes v1 on 1st level screens
|
|
78
|
-
if (refreshing && !isPipesV1) {
|
|
79
|
-
feeds.forEach((feed) => {
|
|
80
|
-
loadPipesDataDispatcher(feed, {
|
|
81
|
-
silentRefresh: true,
|
|
82
|
-
clearCache: true,
|
|
83
|
-
callback: () => {
|
|
84
|
-
setRequestsCompletedCounter(R.inc);
|
|
85
|
-
},
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
}, [refreshing, isPipesV1, feeds, loadPipesDataDispatcher]);
|
|
90
|
-
|
|
91
|
-
React.useEffect(() => {
|
|
92
|
-
if (requestsCompletedCounter === feedsLength) {
|
|
93
|
-
setRefreshing(false);
|
|
94
|
-
}
|
|
95
|
-
}, [requestsCompletedCounter, feedsLength]);
|
|
96
|
-
|
|
97
|
-
const onRefresh = React.useCallback(() => {
|
|
98
|
-
if (isPipesV1) {
|
|
99
|
-
pullToRefreshPipesV1RefreshingStateUpdater(true);
|
|
100
|
-
} else {
|
|
101
|
-
setRefreshing(true);
|
|
102
|
-
setRequestsCompletedCounter(0);
|
|
103
|
-
}
|
|
104
|
-
}, [isPipesV1]);
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
refreshing: isPipesV1 ? refreshingPipesV1 : refreshing,
|
|
108
|
-
onRefresh,
|
|
109
|
-
};
|
|
110
|
-
};
|
|
111
|
-
|
|
112
56
|
/** Returns the offset for the progress view of the RefreshControl component
|
|
113
57
|
* based on navbar content position */
|
|
114
58
|
export const useGetProgressViewOffset = () => {
|
|
@@ -137,16 +81,12 @@ export const useGetProgressViewOffset = () => {
|
|
|
137
81
|
}
|
|
138
82
|
};
|
|
139
83
|
|
|
140
|
-
export function RefreshControl(props
|
|
141
|
-
pullToRefreshPipesV1RefreshingStateUpdater?: (refreshing: boolean) => void;
|
|
142
|
-
refreshingPipesV1?: boolean;
|
|
143
|
-
}) {
|
|
84
|
+
export function RefreshControl(props) {
|
|
144
85
|
const screenData = useCurrentScreenData();
|
|
145
86
|
|
|
146
87
|
const { refreshing, onRefresh } = usePullToRefresh(
|
|
147
|
-
screenData.
|
|
148
|
-
|
|
149
|
-
props.refreshingPipesV1
|
|
88
|
+
screenData.id,
|
|
89
|
+
screenData.ui_components
|
|
150
90
|
);
|
|
151
91
|
|
|
152
92
|
const { app_background_color: themeBackgroundColor } = useTheme();
|
|
@@ -164,29 +104,26 @@ export function RefreshControl(props: {
|
|
|
164
104
|
displayTitleIOS,
|
|
165
105
|
} = React.useMemo(
|
|
166
106
|
() => ({
|
|
167
|
-
indicatorColor:
|
|
168
|
-
|
|
169
|
-
screenData.styles
|
|
170
|
-
|
|
171
|
-
titleUnderIndicatorColor: R.prop(
|
|
172
|
-
"pull_to_refresh_title_color_under_indicator",
|
|
173
|
-
screenData.styles
|
|
107
|
+
indicatorColor: get(screenData.styles, "pull_to_refresh_indicator_color"),
|
|
108
|
+
titleUnderIndicatorColor: get(
|
|
109
|
+
screenData.styles,
|
|
110
|
+
"pull_to_refresh_title_color_under_indicator"
|
|
174
111
|
),
|
|
175
|
-
indicatorBackgroundColor:
|
|
176
|
-
|
|
177
|
-
|
|
112
|
+
indicatorBackgroundColor: get(
|
|
113
|
+
screenData.styles,
|
|
114
|
+
"pull_to_refresh_indicator_bg_color"
|
|
178
115
|
),
|
|
179
116
|
indicatorSize:
|
|
180
|
-
|
|
117
|
+
get(screenData.styles, "pull_to_refresh_indicator_size") === "large"
|
|
181
118
|
? "large"
|
|
182
119
|
: "default",
|
|
183
|
-
generalContentBackgroungColor:
|
|
184
|
-
|
|
185
|
-
|
|
120
|
+
generalContentBackgroungColor: get(
|
|
121
|
+
screenData.styles,
|
|
122
|
+
"screen_background_color"
|
|
186
123
|
),
|
|
187
|
-
displayTitleIOS:
|
|
188
|
-
|
|
189
|
-
|
|
124
|
+
displayTitleIOS: get(
|
|
125
|
+
screenData.styles,
|
|
126
|
+
"pull_to_refresh_display_title_ios"
|
|
190
127
|
),
|
|
191
128
|
}),
|
|
192
129
|
[screenData]
|