@applicaster/zapp-react-native-ui-components 15.0.0-rc.13 → 15.0.0-rc.132

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 (189) hide show
  1. package/Components/AnimatedInOut/index.tsx +69 -26
  2. package/Components/BaseFocusable/index.ios.ts +12 -2
  3. package/Components/Cell/Cell.tsx +14 -3
  4. package/Components/Cell/CellWithFocusable.tsx +9 -0
  5. package/Components/Cell/FocusableWrapper.tsx +3 -0
  6. package/Components/Cell/TvOSCellComponent.tsx +30 -6
  7. package/Components/Focusable/Focusable.tsx +4 -2
  8. package/Components/Focusable/FocusableTvOS.tsx +18 -1
  9. package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +1 -0
  10. package/Components/FocusableGroup/FocusableTvOS.tsx +32 -1
  11. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +39 -28
  12. package/Components/GeneralContentScreen/__tests__/GeneralContentScreen.test.tsx +104 -0
  13. package/Components/GeneralContentScreen/utils/__tests__/getScreenDataSource.test.ts +19 -0
  14. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
  15. package/Components/GeneralContentScreen/utils/getScreenDataSource.ts +9 -0
  16. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +22 -6
  17. package/Components/HandlePlayable/HandlePlayable.tsx +33 -94
  18. package/Components/HandlePlayable/const.ts +3 -0
  19. package/Components/HandlePlayable/utils.ts +105 -0
  20. package/Components/HookRenderer/HookRenderer.tsx +40 -10
  21. package/Components/HookRenderer/__tests__/HookRenderer.test.tsx +60 -0
  22. package/Components/Layout/TV/LayoutBackground.tsx +5 -2
  23. package/Components/Layout/TV/NavBarContainer.tsx +1 -10
  24. package/Components/Layout/TV/ScreenContainer.tsx +2 -6
  25. package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +7 -12
  26. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +7 -12
  27. package/Components/Layout/TV/index.tsx +3 -4
  28. package/Components/Layout/TV/index.web.tsx +3 -4
  29. package/Components/LinkHandler/LinkHandler.tsx +2 -2
  30. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/model.test.ts +80 -0
  31. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +187 -0
  32. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/selectors.test.ts +45 -0
  33. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/style.test.ts +49 -0
  34. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/model.ts +47 -0
  35. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +170 -0
  36. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/selectors.ts +26 -0
  37. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/style.ts +29 -0
  38. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/types.ts +37 -0
  39. package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +16 -1
  40. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +30 -2
  41. package/Components/MasterCell/DefaultComponents/Button.tsx +0 -15
  42. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +5 -1
  43. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +11 -3
  44. package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +9 -1
  45. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +15 -14
  46. package/Components/MasterCell/DefaultComponents/LiveImage/__tests__/prepareEntry.test.ts +352 -0
  47. package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +136 -0
  48. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +43 -22
  49. package/Components/MasterCell/DefaultComponents/PressableView.tsx +196 -0
  50. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +40 -39
  51. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/Image.test.tsx +95 -0
  52. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +86 -0
  53. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/index.test.ts +141 -0
  54. package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +7 -6
  55. package/Components/MasterCell/DefaultComponents/SecondaryImage/index.ts +1 -1
  56. package/Components/MasterCell/DefaultComponents/Text/index.tsx +10 -14
  57. package/Components/MasterCell/DefaultComponents/index.ts +2 -0
  58. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Asset.ts +46 -0
  59. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +126 -0
  60. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ButtonContainerView.ts +23 -0
  61. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +16 -0
  62. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabel.ts +67 -0
  63. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +32 -0
  64. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +191 -0
  65. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/builders.test.ts +140 -0
  66. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +222 -0
  67. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/helpers.ts +105 -0
  68. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +104 -0
  69. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +118 -0
  70. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +73 -0
  71. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/index.test.ts +86 -0
  72. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +35 -48
  73. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +115 -29
  74. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +39 -144
  75. package/Components/MasterCell/elementMapper.tsx +1 -0
  76. package/Components/MasterCell/hoc/__tests__/withAsyncRender.test.tsx +219 -0
  77. package/Components/MasterCell/hoc/withAsyncRender.tsx +9 -7
  78. package/Components/MasterCell/index.tsx +2 -0
  79. package/Components/MasterCell/utils/__tests__/resolveColor.test.js +82 -3
  80. package/Components/MasterCell/utils/index.ts +61 -31
  81. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +102 -87
  82. package/Components/MeasurmentsPortal/__tests__/MeasurementsPortal.test.tsx +355 -0
  83. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +17 -9
  84. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +16 -8
  85. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +2 -2
  86. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +17 -18
  87. package/Components/OfflineHandler/NotificationView/utils.ts +34 -0
  88. package/Components/OfflineHandler/__tests__/index.test.tsx +27 -18
  89. package/Components/PlayerContainer/PlayerContainer.tsx +43 -64
  90. package/Components/PlayerImageBackground/index.tsx +3 -22
  91. package/Components/PreloaderWrapper/__tests__/index.test.tsx +26 -0
  92. package/Components/PreloaderWrapper/index.tsx +15 -0
  93. package/Components/River/ComponentsMap/ComponentsMap.tsx +16 -0
  94. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
  95. package/Components/River/RefreshControl.tsx +36 -13
  96. package/Components/River/RiverItem.tsx +26 -20
  97. package/Components/River/TV/River.tsx +31 -14
  98. package/Components/River/TV/index.tsx +8 -4
  99. package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +30 -0
  100. package/Components/River/TV/utils/index.ts +4 -0
  101. package/Components/River/TV/withFocusableGroupForContent.tsx +71 -0
  102. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -0
  103. package/Components/River/__tests__/componentsMap.test.js +38 -0
  104. package/Components/Screen/TV/index.web.tsx +4 -2
  105. package/Components/Screen/__tests__/Screen.test.tsx +66 -42
  106. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +68 -44
  107. package/Components/Screen/hooks.ts +75 -6
  108. package/Components/Screen/index.tsx +9 -4
  109. package/Components/Screen/navigationHandler.ts +49 -24
  110. package/Components/Screen/orientationHandler.ts +10 -13
  111. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +46 -0
  112. package/Components/ScreenFeedLoader/__tests__/ScreenFeedLoader.test.tsx +94 -0
  113. package/Components/ScreenFeedLoader/index.ts +1 -0
  114. package/Components/ScreenResolver/__tests__/screenResolver.test.js +24 -0
  115. package/Components/ScreenResolver/hooks/index.ts +3 -0
  116. package/Components/ScreenResolver/hooks/useGetComponent.ts +15 -0
  117. package/Components/ScreenResolver/hooks/useScreenComponentResolver.tsx +90 -0
  118. package/Components/ScreenResolver/index.tsx +15 -111
  119. package/Components/ScreenResolver/utils/__tests__/getScreenTypeProps.test.ts +45 -0
  120. package/Components/ScreenResolver/utils/getScreenTypeProps.ts +43 -0
  121. package/Components/ScreenResolver/utils/index.ts +1 -0
  122. package/Components/ScreenResolver/withDefaultScreenContext.tsx +16 -0
  123. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +25 -0
  124. package/Components/ScreenResolverFeedProvider/__tests__/ScreenResolverFeedProvider.test.tsx +44 -0
  125. package/Components/ScreenResolverFeedProvider/index.ts +1 -0
  126. package/Components/ScreenRevealManager/ScreenRevealManager.ts +40 -8
  127. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +86 -69
  128. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +44 -26
  129. package/Components/Tabs/TV/Tabs.tsx +20 -3
  130. package/Components/Tabs/TabContent.tsx +7 -4
  131. package/Components/Transitioner/Scene.tsx +10 -3
  132. package/Components/Transitioner/index.js +3 -3
  133. package/Components/VideoLive/LiveImageManager.ts +199 -54
  134. package/Components/VideoLive/PlayerLiveImageComponent.tsx +31 -33
  135. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +2 -17
  136. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +1 -0
  137. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +118 -171
  138. package/Components/VideoModal/ModalAnimation/index.ts +2 -13
  139. package/Components/VideoModal/ModalAnimation/utils.ts +1 -327
  140. package/Components/VideoModal/PlayerWrapper.tsx +14 -88
  141. package/Components/VideoModal/VideoModal.tsx +1 -5
  142. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -0
  143. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +15 -7
  144. package/Components/VideoModal/hooks/useModalSize.ts +10 -5
  145. package/Components/VideoModal/playerWrapperStyle.ts +70 -0
  146. package/Components/VideoModal/playerWrapperUtils.ts +91 -0
  147. package/Components/VideoModal/utils.ts +19 -9
  148. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +0 -2
  149. package/Components/Viewport/ViewportAware/index.tsx +16 -7
  150. package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
  151. package/Components/ZappUIComponent/index.tsx +12 -6
  152. package/Components/default-cell-renderer/viewTrees/mobile/index.ts +0 -3
  153. package/Components/index.js +1 -1
  154. package/Contexts/ScreenContext/__tests__/index.test.tsx +57 -0
  155. package/Contexts/ScreenContext/index.tsx +71 -19
  156. package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
  157. package/Contexts/ZappHookModalContext/index.tsx +37 -61
  158. package/Contexts/ZappPipesContext/ZappPipesContextFactory.tsx +18 -7
  159. package/Contexts/index.ts +0 -2
  160. package/Decorators/Analytics/index.tsx +6 -5
  161. package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +1 -0
  162. package/Decorators/ConfigurationWrapper/const.ts +1 -0
  163. package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -7
  164. package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +212 -5
  165. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +39 -21
  166. package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
  167. package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
  168. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
  169. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +18 -7
  170. package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +80 -0
  171. package/Helpers/DataSourceHelper/index.ts +19 -0
  172. package/events/index.ts +3 -0
  173. package/events/scrollEndReached.ts +15 -0
  174. package/index.d.ts +7 -0
  175. package/package.json +6 -5
  176. package/Components/MasterCell/DefaultComponents/Text/utils/__tests__/withAdjustedLineHeight.test.ts +0 -46
  177. package/Components/MasterCell/DefaultComponents/Text/utils/index.ts +0 -21
  178. package/Components/PlayerContainer/ErrorDisplay/ErrorDisplay.tsx +0 -57
  179. package/Components/PlayerContainer/ErrorDisplay/index.ts +0 -9
  180. package/Components/River/TV/withTVEventHandler.tsx +0 -27
  181. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +0 -60
  182. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +0 -417
  183. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.web.tsx +0 -294
  184. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +0 -176
  185. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.web.tsx +0 -93
  186. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +0 -500
  187. package/Components/VideoModal/ModalAnimation/__tests__/getMoveUpValue.test.ts +0 -108
  188. package/Helpers/DataSourceHelper/index.js +0 -19
  189. /package/Components/HookRenderer/{index.tsx → index.ts} +0 -0
@@ -8,25 +8,24 @@ import {
8
8
  offlinePhrase,
9
9
  } from "../NotificationView";
10
10
 
11
- jest.mock("@applicaster/zapp-react-native-redux/hooks", () => ({
12
- usePickFromState: () => ({
13
- plugins: [
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
- }),
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", () => ({
@@ -0,0 +1,34 @@
1
+ import * as React from "react";
2
+ import { sessionStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/SessionStorage";
3
+
4
+ export const NETWORK_STATUS_LOCALIZATIONS_KEY = "network_status_localizations";
5
+
6
+ export const THEME_STORAGE_NAMESPACE = "quick-brick-theme";
7
+
8
+ export function useNetworkStatusLocalizations() {
9
+ const [storedLocalizations, setStoredLocalizations] = React.useState<Record<
10
+ string,
11
+ string
12
+ > | null>(null);
13
+
14
+ React.useEffect(() => {
15
+ async function loadStoredLocalizations() {
16
+ try {
17
+ const stored = await sessionStorage.getItem(
18
+ NETWORK_STATUS_LOCALIZATIONS_KEY,
19
+ THEME_STORAGE_NAMESPACE
20
+ );
21
+
22
+ if (stored) {
23
+ setStoredLocalizations(stored);
24
+ }
25
+ } catch (error) {
26
+ console.error("Error loading network status localizations", error);
27
+ }
28
+ }
29
+
30
+ loadStoredLocalizations();
31
+ }, []);
32
+
33
+ return storedLocalizations;
34
+ }
@@ -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
- usePickFromState: () => ({
24
- plugins: [
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
- import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
20
+
21
21
  import {
22
22
  useBackHandler,
23
23
  useNavigation,
@@ -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";
@@ -56,15 +55,11 @@ import { toNumber } from "@applicaster/zapp-react-native-utils/numberUtils";
56
55
  import { usePlayNextOverlay } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayNextOverlay";
57
56
  import { PlayNextState } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/OverlayObserver/OverlaysObserver";
58
57
 
59
- import {
60
- PlayerAnimationStateEnum,
61
- useModalAnimationContext,
62
- } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
63
-
64
58
  import {
65
59
  PlayerNativeCommandTypes,
66
60
  PlayerNativeSendCommand,
67
61
  } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/playerNativeCommand";
62
+ import { useAppData } from "@applicaster/zapp-react-native-redux";
68
63
 
69
64
  type Props = {
70
65
  Player: React.ComponentType<any>;
@@ -243,14 +238,11 @@ const PlayerContainerComponent = (props: Props) => {
243
238
  const [isLoadingNextVideo, setIsLoadingNextVideo] = React.useState(false);
244
239
 
245
240
  const navigator = useNavigation();
246
- const { appData } = usePickFromState(["appData"]);
241
+ const { isTabletPortrait } = useAppData();
247
242
  const prevItemId = usePrevious(item?.id);
248
243
  const screenData = useTargetScreenData(item);
249
244
  const { setVisible: showNavBar } = useSetNavbarState();
250
245
 
251
- const { isActiveGesture, startComponentsAnimation, setPlayerAnimationState } =
252
- useModalAnimationContext();
253
-
254
246
  const playerEvent = (event, ...args) => {
255
247
  playerManager.invokeHandler(event, ...args);
256
248
  };
@@ -271,7 +263,14 @@ const PlayerContainerComponent = (props: Props) => {
271
263
 
272
264
  showNavBar(true);
273
265
  navigator.goBack();
274
- }, [isModal, navigator.goBack, state.playerId, showNavBar]);
266
+ }, [isModal, state.playerId, showNavBar, navigator]);
267
+
268
+ const pluginConfiguration = React.useMemo(() => {
269
+ return (
270
+ playerManager.getPluginConfiguration() ||
271
+ R.prop("__plugin_configuration", Player)
272
+ );
273
+ }, [playerManager.isRegistered()]);
275
274
 
276
275
  const playEntry = (entry) => navigator.replaceTop(entry, { mode });
277
276
 
@@ -339,12 +338,6 @@ const PlayerContainerComponent = (props: Props) => {
339
338
  playerContainerLogger.error(errorObj);
340
339
 
341
340
  setState({ error: errorObj });
342
-
343
- if (!isTvOS) {
344
- setTimeout(() => {
345
- close();
346
- }, 800);
347
- }
348
341
  },
349
342
  [close]
350
343
  );
@@ -463,13 +456,6 @@ const PlayerContainerComponent = (props: Props) => {
463
456
  }
464
457
  }, []);
465
458
 
466
- const pluginConfiguration = React.useMemo(() => {
467
- return (
468
- playerManager.getPluginConfiguration() ||
469
- R.prop("__plugin_configuration", Player)
470
- );
471
- }, [playerManager.isRegistered()]);
472
-
473
459
  const disableMiniPlayer = React.useMemo(() => {
474
460
  return pluginConfiguration?.disable_mini_player_when_inline;
475
461
  }, [pluginConfiguration]);
@@ -482,8 +468,6 @@ const PlayerContainerComponent = (props: Props) => {
482
468
  if (isModal && mode === VideoModalMode.MAXIMIZED) {
483
469
  if (disableMiniPlayer) {
484
470
  navigator.closeVideoModal();
485
- } else {
486
- setPlayerAnimationState(PlayerAnimationStateEnum.minimize);
487
471
  }
488
472
  }
489
473
 
@@ -671,44 +655,39 @@ const PlayerContainerComponent = (props: Props) => {
671
655
  <PlayerFocusableWrapperView
672
656
  nextFocusDown={context.bottomFocusableId}
673
657
  >
674
- <Player
675
- source={{
676
- uri,
677
- entry: item,
678
- }}
679
- focused={isInlineTV ? true : undefined}
680
- autoplay={true}
681
- controls={false}
682
- disableCastAction={disableCastAction}
683
- docked={
684
- navigator.isVideoModalDocked() &&
685
- !startComponentsAnimation &&
686
- !isActiveGesture
687
- }
688
- entry={item}
689
- fullscreen={mode === VideoModalMode.FULLSCREEN}
690
- inline={inline}
691
- isModal={isModal}
692
- isTabletPortrait={appData.isTabletPortrait}
693
- muted={false}
694
- playableItem={item}
695
- playerEvent={playerEvent}
696
- playerId={state.playerId}
697
- pluginConfiguration={pluginConfiguration}
698
- ref={playerRef}
699
- toggleFullscreen={toggleFullscreen}
700
- style={videoStyle}
701
- playNextData={playNextData}
702
- setNextVideoPreloadThresholdPercentage={
703
- setNextVideoPreloadThresholdPercentage
704
- }
705
- startComponentsAnimation={startComponentsAnimation}
706
- >
707
- {renderApplePlayer(applePlayerProps)}
708
- </Player>
658
+ {!Player ? null : (
659
+ <Player
660
+ source={{
661
+ uri,
662
+ entry: item,
663
+ }}
664
+ focused={isInlineTV ? true : undefined}
665
+ autoplay={true}
666
+ controls={false}
667
+ disableCastAction={disableCastAction}
668
+ docked={navigator.isVideoModalDocked()}
669
+ entry={item}
670
+ fullscreen={mode === VideoModalMode.FULLSCREEN}
671
+ inline={inline}
672
+ isModal={isModal}
673
+ isTabletPortrait={isTabletPortrait}
674
+ muted={false}
675
+ playableItem={item}
676
+ playerEvent={playerEvent}
677
+ playerId={state.playerId}
678
+ pluginConfiguration={pluginConfiguration}
679
+ ref={playerRef}
680
+ toggleFullscreen={toggleFullscreen}
681
+ style={videoStyle}
682
+ playNextData={playNextData}
683
+ setNextVideoPreloadThresholdPercentage={
684
+ setNextVideoPreloadThresholdPercentage
685
+ }
686
+ >
687
+ {renderApplePlayer(applePlayerProps)}
688
+ </Player>
689
+ )}
709
690
  </PlayerFocusableWrapperView>
710
-
711
- {state.error ? <ErrorDisplay error={state.error} /> : null}
712
691
  </View>
713
692
  {/* Components container */}
714
693
  {isInlineTV && context.showComponentsContainer ? (
@@ -2,12 +2,6 @@ import React, { PropsWithChildren } from "react";
2
2
  import { ImageBackground, View } from "react-native";
3
3
 
4
4
  import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
5
- import {
6
- AnimationComponent,
7
- ComponentAnimationType,
8
- useModalAnimationContext,
9
- PlayerAnimationStateEnum,
10
- } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
11
5
 
12
6
  type Props = PropsWithChildren<{
13
7
  entry: ZappEntry;
@@ -25,30 +19,17 @@ const PlayerImageBackgroundComponent = ({
25
19
  style,
26
20
  imageStyle,
27
21
  imageKey,
28
- defaultImageDimensions,
29
22
  }: Props) => {
30
23
  const source = React.useMemo(
31
24
  () => ({ uri: imageSrcFromMediaItem(entry, [imageKey]) }),
32
25
  [imageKey, entry]
33
26
  );
34
27
 
35
- const { playerAnimationState } = useModalAnimationContext();
36
-
37
28
  if (!source) return <>{children}</>;
38
29
 
39
30
  return (
40
- <View
41
- style={
42
- playerAnimationState === PlayerAnimationStateEnum.maximize
43
- ? defaultImageDimensions
44
- : style
45
- }
46
- >
47
- <AnimationComponent
48
- style={style}
49
- animationType={ComponentAnimationType.player}
50
- additionalData={defaultImageDimensions}
51
- >
31
+ <View style={style}>
32
+ <View style={style}>
52
33
  <ImageBackground
53
34
  resizeMode="cover"
54
35
  style={imageSize}
@@ -57,7 +38,7 @@ const PlayerImageBackgroundComponent = ({
57
38
  >
58
39
  {children}
59
40
  </ImageBackground>
60
- </AnimationComponent>
41
+ </View>
61
42
  </View>
62
43
  );
63
44
  };
@@ -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;
@@ -23,6 +23,7 @@ import { isLast } from "@applicaster/zapp-react-native-utils/arrayUtils";
23
23
  import { withComponentsMapProvider } from "@applicaster/zapp-react-native-ui-components/Decorators/ComponentsMapWrapper";
24
24
  import { useScreenContextV2 } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
25
25
  import { useShallow } from "zustand/react/shallow";
26
+ import { emitScrollEndReached } from "@applicaster/zapp-react-native-ui-components/events";
26
27
 
27
28
  import { isAndroidPlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
28
29
  import { ComponentsMapHeightContext } from "./ContextProviders/ComponentsMapHeightContext";
@@ -73,6 +74,7 @@ function ComponentsMapComponent(props: Props) {
73
74
 
74
75
  const flatListRef = React.useRef<FlatList | null>(null);
75
76
  const flatListWrapperRef = React.useRef<View | null>(null);
77
+ const hasUserScrolledRef = React.useRef(false);
76
78
  const screenConfig = useScreenConfiguration(riverId);
77
79
  const screenData = useScreenData(riverId);
78
80
  const pullToRefreshEnabled = screenData?.rules?.pull_to_refresh_enabled;
@@ -236,6 +238,8 @@ function ComponentsMapComponent(props: Props) {
236
238
  }, []);
237
239
 
238
240
  const onScroll = React.useCallback((event) => {
241
+ hasUserScrolledRef.current = true;
242
+
239
243
  const {
240
244
  nativeEvent: {
241
245
  contentOffset: { y },
@@ -277,6 +281,7 @@ function ComponentsMapComponent(props: Props) {
277
281
  >
278
282
  <ViewportTracker>
279
283
  <FlatList
284
+ testID="components-map-flat-list"
280
285
  ref={(ref) => {
281
286
  flatListRef.current = ref;
282
287
  }}
@@ -308,6 +313,17 @@ function ComponentsMapComponent(props: Props) {
308
313
  onScrollEndDrag={_onScrollEndDrag}
309
314
  scrollEventThrottle={16}
310
315
  {...scrollViewExtraProps}
316
+ onEndReached={
317
+ /* When wrapped in a parent ScrollView (e.g. tabs),
318
+ this FlatList doesn't scroll so onEndReached can fire repeatedly;
319
+ skip it here and let the parent ScrollView emit scroll-end instead. */
320
+ isScreenWrappedInContainer
321
+ ? undefined
322
+ : () => {
323
+ if (!hasUserScrolledRef.current) return;
324
+ emitScrollEndReached();
325
+ }
326
+ }
311
327
  />
312
328
  </ViewportTracker>
313
329
  </ScreenLoadingMeasurements>
@@ -1,4 +1,4 @@
1
- import { renderHook, act } from "@testing-library/react-hooks";
1
+ import { renderHook, act } from "@testing-library/react-native";
2
2
  import { BehaviorSubject } from "rxjs";
3
3
  import { useLoadingState } from "../useLoadingState";
4
4
 
@@ -5,6 +5,8 @@ import {
5
5
  StyleSheet,
6
6
  } from "react-native";
7
7
  import * as R from "ramda";
8
+ import { path } from "@applicaster/zapp-react-native-utils/utils";
9
+ import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
8
10
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
9
11
  import { useLocalizedStrings } from "@applicaster/zapp-react-native-utils/localizationUtils";
10
12
  import { useAnalytics } from "@applicaster/zapp-react-native-utils/analyticsUtils";
@@ -14,6 +16,7 @@ import { useShallow } from "zustand/react/shallow";
14
16
  import { useScreenContextV2 } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
15
17
  import { useSafeAreaInsets } from "react-native-safe-area-context";
16
18
  import { useLoadPipesDataDispatch } from "@applicaster/zapp-react-native-utils/reactHooks";
19
+ import { useGetUrlInflater } from "@applicaster/zapp-react-native-utils/reactHooks/feed/useInflatedUrl";
17
20
 
18
21
  const BRIGHTNESS_THRESHOLD = 160;
19
22
  const ABOVE_DEFAULT_COLOR = "gray";
@@ -62,31 +65,51 @@ export const usePullToRefresh = (
62
65
 
63
66
  const [refreshing, setRefreshing] = React.useState(false);
64
67
 
65
- const feeds: string[] =
66
- riverComponents?.map(R.path(["data", "source"])).filter((feed) => !!feed) ??
67
- [];
68
+ const feedSources = React.useMemo(
69
+ () =>
70
+ (riverComponents || [])
71
+ .map((riverComponent) => ({
72
+ source: path(["data", "source"], riverComponent),
73
+ mapping: path(["data", "mapping"], riverComponent),
74
+ }))
75
+ .filter(({ source }) => !isNilOrEmpty(source)),
76
+ [riverComponents]
77
+ );
68
78
 
69
- const feedsLength = feeds.length;
79
+ const feedsLength = feedSources.length;
70
80
 
71
81
  const [requestsCompletedCounter, setRequestsCompletedCounter] =
72
82
  React.useState(0);
73
83
 
74
84
  const loadPipesDataDispatcher = useLoadPipesDataDispatch();
85
+ const urlInflater = useGetUrlInflater();
75
86
 
76
87
  React.useEffect(() => {
77
88
  // will not work for pipes v1 on 1st level screens
78
89
  if (refreshing && !isPipesV1) {
79
- feeds.forEach((feed) => {
80
- loadPipesDataDispatcher(feed, {
81
- silentRefresh: true,
82
- clearCache: true,
83
- callback: () => {
84
- setRequestsCompletedCounter(R.inc);
85
- },
86
- });
90
+ feedSources.forEach(({ source, mapping }) => {
91
+ const inflatedUrl = urlInflater(source, mapping);
92
+
93
+ if (inflatedUrl) {
94
+ loadPipesDataDispatcher(inflatedUrl, {
95
+ silentRefresh: true,
96
+ clearCache: true,
97
+ callback: () => {
98
+ setRequestsCompletedCounter(R.inc);
99
+ },
100
+ });
101
+ } else {
102
+ setRequestsCompletedCounter(R.inc);
103
+ }
87
104
  });
88
105
  }
89
- }, [refreshing, isPipesV1, feeds, loadPipesDataDispatcher]);
106
+ }, [
107
+ refreshing,
108
+ isPipesV1,
109
+ feedSources,
110
+ loadPipesDataDispatcher,
111
+ urlInflater,
112
+ ]);
90
113
 
91
114
  React.useEffect(() => {
92
115
  if (requestsCompletedCounter === feedsLength) {
@@ -14,6 +14,7 @@ import { tvPluginsWithCellRenderer } from "../../const";
14
14
  import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
15
15
  import type { BehaviorSubject } from "rxjs";
16
16
  import { useCallbackActions } from "@applicaster/zapp-react-native-utils/zappFrameworkUtils/HookCallback/useCallbackActions";
17
+ import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
17
18
 
18
19
  export type RiverItemType = {
19
20
  item: ZappUIComponent;
@@ -112,33 +113,38 @@ function RiverItemComponent(props: RiverItemType) {
112
113
  CellRenderer = undefined;
113
114
  }
114
115
 
115
- React.useEffect(() => {
116
- riverLogger.log({
117
- message: "mounting component",
118
- data: { item, feedUrl, Component, CellRenderer },
119
- jsOnly: true,
120
- });
116
+ const isComponentMissing = isNilOrEmpty(Component);
121
117
 
122
- if (!CellRenderer && !isGroup(item)) {
118
+ /**
119
+ * TODO: Move this plugin existence check further up the stack (before ComponentsMap).
120
+ * Filtering items at the list-rendering or data-processing level would prevent
121
+ * mounting RiverItem entirely for missing components.
122
+ */
123
+ React.useEffect(() => {
124
+ if (isComponentMissing) {
123
125
  riverLogger.warning({
124
- message: "Cell Renderer is null - will fallback to default cell",
125
- data: { item, CellRenderer },
126
+ message: `Component ${item.component_type} is null - skipping rendering`,
127
+ });
128
+
129
+ onLoadFinished(index);
130
+ } else {
131
+ riverLogger.log({
132
+ message: "mounting component",
133
+ data: { item, feedUrl, Component, CellRenderer },
126
134
  jsOnly: true,
127
135
  });
136
+
137
+ if (!CellRenderer && !isGroup(item)) {
138
+ riverLogger.warning({
139
+ message: "Cell Renderer is null - will fallback to default cell",
140
+ data: { item, CellRenderer },
141
+ jsOnly: true,
142
+ });
143
+ }
128
144
  }
129
145
  }, []);
130
146
 
131
- if (!readyToBeDisplayed) {
132
- return null;
133
- }
134
-
135
- if (Component === null || typeof Component === "undefined") {
136
- riverLogger.warning({
137
- message: `Component ${item.component_type} is null - skipping rendering`,
138
- });
139
-
140
- onLoadFinished(index);
141
-
147
+ if (!readyToBeDisplayed || isComponentMissing) {
142
148
  return null;
143
149
  }
144
150