@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.
Files changed (138) hide show
  1. package/Components/Cell/TvOSCellComponent.tsx +1 -3
  2. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +39 -28
  3. package/Components/GeneralContentScreen/__tests__/GeneralContentScreen.test.tsx +104 -0
  4. package/Components/GeneralContentScreen/utils/__tests__/getScreenDataSource.test.ts +19 -0
  5. package/Components/GeneralContentScreen/utils/getScreenDataSource.ts +9 -0
  6. package/Components/HandlePlayable/HandlePlayable.tsx +16 -29
  7. package/Components/HandlePlayable/utils.ts +31 -0
  8. package/Components/HookRenderer/HookRenderer.tsx +40 -10
  9. package/Components/HookRenderer/__tests__/HookRenderer.test.tsx +60 -0
  10. package/Components/Layout/TV/NavBarContainer.tsx +1 -10
  11. package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +7 -12
  12. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +7 -12
  13. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +5 -0
  14. package/Components/MasterCell/CONFIG_BUILDER_TO_REACT_COMPONENT.md +144 -0
  15. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/model.test.ts +80 -0
  16. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +187 -0
  17. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/selectors.test.ts +45 -0
  18. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/style.test.ts +49 -0
  19. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +165 -0
  20. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +405 -0
  21. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/index.ts +1 -0
  22. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/model.ts +47 -0
  23. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +170 -0
  24. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/selectors.ts +26 -0
  25. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/style.ts +29 -0
  26. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/types.ts +37 -0
  27. package/Components/MasterCell/DefaultComponents/Button.tsx +0 -15
  28. package/Components/MasterCell/DefaultComponents/ButtonContainerView/components/HorizontalSeparator.tsx +8 -0
  29. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tsx +15 -0
  30. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tv.android.tsx +58 -0
  31. package/Components/MasterCell/DefaultComponents/{tv/ButtonContainerView/index.tsx → ButtonContainerView/index.tv.tsx} +3 -11
  32. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.web.ts +1 -0
  33. package/Components/MasterCell/DefaultComponents/ButtonContainerView/types.ts +40 -0
  34. package/Components/MasterCell/DefaultComponents/DataProvider/index.tsx +163 -0
  35. package/Components/MasterCell/DefaultComponents/FocusableView/index.android.tsx +2 -23
  36. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -22
  37. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +3 -1
  38. package/Components/MasterCell/DefaultComponents/LiveImage/__tests__/prepareEntry.test.ts +352 -0
  39. package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +136 -0
  40. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +33 -16
  41. package/Components/MasterCell/DefaultComponents/PressableView.tsx +34 -0
  42. package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +11 -0
  43. package/Components/MasterCell/DefaultComponents/Text/index.tsx +2 -6
  44. package/Components/MasterCell/DefaultComponents/__tests__/DataProvider.test.tsx +141 -0
  45. package/Components/MasterCell/DefaultComponents/index.ts +9 -3
  46. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +135 -0
  47. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Asset.ts +33 -0
  48. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +22 -0
  49. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +125 -0
  50. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +16 -0
  51. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabel.ts +67 -0
  52. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +37 -0
  53. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +393 -0
  54. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/builders.test.ts +141 -0
  55. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +343 -0
  56. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/helpers.ts +105 -0
  57. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +122 -0
  58. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +118 -0
  59. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +238 -0
  60. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Asset.ts +4 -18
  61. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Button.ts +24 -73
  62. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TextLabelsContainer.ts +37 -18
  63. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TvActionButton.tsx +27 -0
  64. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/index.test.ts +89 -0
  65. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/renderedTree.test.tsx +231 -0
  66. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +47 -52
  67. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +35 -171
  68. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +98 -145
  69. package/Components/MasterCell/MappingFunctions/index.js +3 -2
  70. package/Components/MasterCell/README.md +4 -0
  71. package/Components/MasterCell/__tests__/__snapshots__/dataAdapter.test.js.snap +24 -0
  72. package/Components/MasterCell/__tests__/configInflater.test.js +1 -0
  73. package/Components/MasterCell/__tests__/elementMapper.test.js +46 -0
  74. package/Components/MasterCell/dataAdapter.ts +4 -1
  75. package/Components/MasterCell/elementMapper.tsx +52 -7
  76. package/Components/MasterCell/utils/__tests__/cloneChildrenWithIds.test.tsx +43 -0
  77. package/Components/MasterCell/utils/__tests__/useFilterChildren.test.tsx +80 -0
  78. package/Components/MasterCell/utils/index.ts +85 -15
  79. package/Components/Navigator/StackNavigator.tsx +6 -0
  80. package/Components/PlayerContainer/PlayerContainer.tsx +2 -19
  81. package/Components/PreloaderWrapper/__tests__/index.test.tsx +26 -0
  82. package/Components/PreloaderWrapper/index.tsx +15 -0
  83. package/Components/River/ComponentsMap/ComponentsMap.tsx +2 -16
  84. package/Components/River/RefreshControl.tsx +19 -82
  85. package/Components/River/River.tsx +9 -82
  86. package/Components/River/RiverItem.tsx +26 -20
  87. package/Components/River/hooks/__tests__/usePullToRefresh.test.ts +132 -0
  88. package/Components/River/hooks/index.ts +1 -0
  89. package/Components/River/hooks/usePullToRefresh.ts +51 -0
  90. package/Components/Screen/__tests__/Screen.test.tsx +1 -0
  91. package/Components/Screen/hooks.ts +73 -3
  92. package/Components/Screen/index.tsx +7 -1
  93. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +46 -0
  94. package/Components/ScreenFeedLoader/__tests__/ScreenFeedLoader.test.tsx +94 -0
  95. package/Components/ScreenFeedLoader/index.ts +1 -0
  96. package/Components/ScreenResolver/__tests__/screenResolver.test.js +24 -0
  97. package/Components/ScreenResolver/hooks/index.ts +3 -0
  98. package/Components/ScreenResolver/hooks/useGetComponent.ts +15 -0
  99. package/Components/ScreenResolver/hooks/useScreenComponentResolver.tsx +90 -0
  100. package/Components/ScreenResolver/index.tsx +15 -117
  101. package/Components/ScreenResolver/utils/__tests__/getScreenTypeProps.test.ts +45 -0
  102. package/Components/ScreenResolver/utils/getScreenTypeProps.ts +43 -0
  103. package/Components/ScreenResolver/utils/index.ts +1 -0
  104. package/Components/ScreenResolver/withDefaultScreenContext.tsx +16 -0
  105. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +25 -0
  106. package/Components/ScreenResolverFeedProvider/__tests__/ScreenResolverFeedProvider.test.tsx +44 -0
  107. package/Components/ScreenResolverFeedProvider/index.ts +1 -0
  108. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +4 -1
  109. package/Components/TopCutoffOverlay/__tests__/TopCutoffOverlay.test.tsx +201 -0
  110. package/Components/TopCutoffOverlay/hooks/__tests__/useMarginTop.test.ts +130 -0
  111. package/Components/TopCutoffOverlay/hooks/index.ts +1 -0
  112. package/Components/TopCutoffOverlay/hooks/useMarginTop.ts +59 -0
  113. package/Components/TopCutoffOverlay/index.tsx +55 -0
  114. package/Components/Transitioner/Scene.tsx +9 -15
  115. package/Components/VideoLive/LiveImageManager.ts +199 -54
  116. package/Components/VideoLive/PlayerLiveImageComponent.tsx +31 -33
  117. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +2 -17
  118. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +0 -2
  119. package/Components/Viewport/ViewportAware/index.tsx +16 -7
  120. package/Components/ZappUIComponent/index.tsx +12 -6
  121. package/Components/default-cell-renderer/viewTrees/mobile/index.ts +0 -3
  122. package/Components/index.js +1 -1
  123. package/Contexts/ScreenContext/__tests__/index.test.tsx +57 -0
  124. package/Contexts/ScreenContext/index.tsx +46 -1
  125. package/Contexts/ZappPipesContext/ZappPipesContextFactory.tsx +18 -7
  126. package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -7
  127. package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +212 -5
  128. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +39 -21
  129. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +18 -7
  130. package/package.json +5 -5
  131. package/Components/MasterCell/DefaultComponents/Text/utils/__tests__/withAdjustedLineHeight.test.ts +0 -46
  132. package/Components/MasterCell/DefaultComponents/Text/utils/index.ts +0 -21
  133. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +0 -135
  134. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/types.ts +0 -25
  135. package/Components/PlayerContainer/ErrorDisplay/ErrorDisplay.tsx +0 -57
  136. package/Components/PlayerContainer/ErrorDisplay/index.ts +0 -9
  137. package/Components/PlayerContainer/useRestrictMobilePlayback.tsx +0 -101
  138. /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
- item: any;
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 item = child.props.item;
143
- const actionIdentifier = child.props.pluginIdentifier;
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: child.props.pluginIdentifier },
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 cloneElementsWithIds = (ids, children) => {
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
- React.cloneElement(element, {
212
- nextFocusLeft: previous(index, ids),
213
- nextFocusRight: next(index, ids),
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
- hasElementsSpecificViewType("ButtonContainerView"),
257
- hasElementsSpecificViewType("FocusableView"),
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: "transparent",
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
- {isRestricted ? null : (
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
- pullToRefreshEnabled ? (
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 * as R from "ramda";
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 { useLoadPipesDataDispatch } from "@applicaster/zapp-react-native-utils/reactHooks";
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.ui_components,
148
- props.pullToRefreshPipesV1RefreshingStateUpdater,
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: R.prop(
168
- "pull_to_refresh_indicator_color",
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: R.prop(
176
- "pull_to_refresh_indicator_bg_color",
177
- screenData.styles
112
+ indicatorBackgroundColor: get(
113
+ screenData.styles,
114
+ "pull_to_refresh_indicator_bg_color"
178
115
  ),
179
116
  indicatorSize:
180
- R.prop("pull_to_refresh_indicator_size", screenData.styles) === "large"
117
+ get(screenData.styles, "pull_to_refresh_indicator_size") === "large"
181
118
  ? "large"
182
119
  : "default",
183
- generalContentBackgroungColor: R.prop(
184
- "screen_background_color",
185
- screenData.styles
120
+ generalContentBackgroungColor: get(
121
+ screenData.styles,
122
+ "screen_background_color"
186
123
  ),
187
- displayTitleIOS: R.prop(
188
- "pull_to_refresh_display_title_ios",
189
- screenData.styles
124
+ displayTitleIOS: get(
125
+ screenData.styles,
126
+ "pull_to_refresh_display_title_ios"
190
127
  ),
191
128
  }),
192
129
  [screenData]