@applicaster/zapp-react-native-ui-components 16.0.0-rc.1 → 16.0.0-rc.100

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 (169) hide show
  1. package/Components/AudioPlayer/tv/__tests__/audioPlayer.test.js +19 -0
  2. package/Components/AudioPlayer/tv/__tests__/index.test.tsx +120 -0
  3. package/Components/AudioPlayer/tv/index.tsx +5 -1
  4. package/Components/BackgroundImage/BackgroundImage.android.tv.tsx +28 -0
  5. package/Components/BackgroundImage/BackgroundImage.ios.tv.tsx +24 -0
  6. package/Components/BackgroundImage/BackgroundImage.tsx +42 -0
  7. package/Components/BackgroundImage/index.ts +1 -0
  8. package/Components/Cell/Cell.tsx +3 -6
  9. package/Components/Cell/TvOSCellComponent.tsx +0 -1
  10. package/Components/CellRendererResolver/index.ts +2 -2
  11. package/Components/ComponentResolver/__tests__/componentResolver.test.js +1 -1
  12. package/Components/FocusableGroup/FocusableTvOS.tsx +11 -7
  13. package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +54 -0
  14. package/Components/GeneralContentScreen/__tests__/GeneralContentScreenHookAdapter.test.tsx +64 -0
  15. package/Components/GeneralContentScreen/__tests__/HookContentFocusGroup.web.test.tsx +91 -0
  16. package/Components/GeneralContentScreen/hookAdapter/__tests__/networkService.test.ts +74 -0
  17. package/Components/GeneralContentScreen/hookAdapter/__tests__/runInBackground.test.ts +139 -0
  18. package/Components/GeneralContentScreen/hookAdapter/__tests__/skipExpression.test.ts +294 -0
  19. package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +188 -0
  20. package/Components/GeneralContentScreen/hookAdapter/logger.ts +6 -0
  21. package/Components/GeneralContentScreen/hookAdapter/networkService.ts +53 -0
  22. package/Components/GeneralContentScreen/hookAdapter/runInBackground.ts +48 -0
  23. package/Components/GeneralContentScreen/hookAdapter/skipExpression.ts +252 -0
  24. package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +90 -0
  25. package/Components/GeneralContentScreen/hookFocus/index.tsx +13 -0
  26. package/Components/GeneralContentScreen/hookFocus/index.web.tsx +69 -0
  27. package/Components/GeneralContentScreen/index.ts +2 -0
  28. package/Components/Layout/FullWidthRow.tsx +38 -0
  29. package/Components/Layout/TV/ScreenContainer.tsx +5 -0
  30. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +0 -1
  31. package/Components/Layout/TV/index.tsx +3 -4
  32. package/Components/Layout/TV/index.web.tsx +2 -3
  33. package/Components/LinkHandler/LinkHandler.tsx +2 -7
  34. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +21 -6
  35. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +47 -21
  36. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +19 -4
  37. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +43 -1
  38. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +34 -6
  39. package/Components/MasterCell/DefaultComponents/Button.tsx +1 -0
  40. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tsx +1 -1
  41. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/__tests__/withDimensions.test.ts +420 -125
  42. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/index.ts +114 -19
  43. package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +19 -8
  44. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -3
  45. package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +2 -4
  46. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +2 -3
  47. package/Components/MasterCell/DefaultComponents/PressableView.tsx +29 -9
  48. package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +4 -0
  49. package/Components/MasterCell/DefaultComponents/Toggle.tsx +36 -14
  50. package/Components/MasterCell/DefaultComponents/__tests__/Toggle.test.tsx +167 -0
  51. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +154 -100
  52. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +15 -4
  53. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +56 -22
  54. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +6 -4
  55. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +3 -1
  56. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +32 -21
  57. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +15 -4
  58. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +23 -16
  59. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +8 -3
  60. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +2 -2
  61. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
  62. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +2 -2
  63. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/factory.test.ts +32 -0
  64. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -2
  65. package/Components/MasterCell/contexts/PressedStateContext.ts +3 -0
  66. package/Components/MasterCell/dataAdapter.ts +15 -5
  67. package/Components/MasterCell/hooks/index.ts +2 -0
  68. package/Components/MasterCell/hooks/usePressedState.ts +4 -0
  69. package/Components/MasterCell/index.tsx +30 -5
  70. package/Components/MasterCell/utils/__tests__/resolveColor.test.js +169 -132
  71. package/Components/MasterCell/utils/__tests__/resolveColorForProp.test.js +92 -0
  72. package/Components/MasterCell/utils/index.ts +85 -46
  73. package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +56 -0
  74. package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
  75. package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +24 -0
  76. package/Components/ModalComponent/ActionsBottomSheet/index.ts +9 -0
  77. package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +61 -0
  78. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +392 -0
  79. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +772 -0
  80. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +728 -0
  81. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +1007 -0
  82. package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +205 -0
  83. package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +113 -0
  84. package/Components/ModalComponent/AudioPlayer/Components/__tests__/NowPlayingHeaderSection.test.tsx +36 -0
  85. package/Components/ModalComponent/AudioPlayer/Components/index.ts +9 -0
  86. package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +356 -0
  87. package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +521 -0
  88. package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
  89. package/Components/ModalComponent/BottomSheetModalContent.tsx +505 -50
  90. package/Components/ModalComponent/Button/index.tsx +5 -3
  91. package/Components/ModalComponent/Header/CloseButton.tsx +12 -0
  92. package/Components/ModalComponent/Header/__tests__/CloseButton.test.tsx +72 -0
  93. package/Components/ModalComponent/Header/index.tsx +20 -7
  94. package/Components/ModalComponent/Header/utils.ts +1 -1
  95. package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
  96. package/Components/ModalComponent/SortableList.tsx +156 -0
  97. package/Components/ModalComponent/SortableList.web.tsx +34 -0
  98. package/Components/ModalComponent/TextInputContent.tsx +135 -0
  99. package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
  100. package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +343 -0
  101. package/Components/ModalComponent/__tests__/SortableList.web.test.tsx +16 -0
  102. package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +169 -0
  103. package/Components/ModalComponent/__tests__/showTextInput.test.ts +95 -0
  104. package/Components/ModalComponent/index.tsx +18 -9
  105. package/Components/ModalComponent/presets/__tests__/getCell.test.ts +90 -0
  106. package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
  107. package/Components/ModalComponent/presets/index.ts +27 -0
  108. package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
  109. package/Components/ModalComponent/presets/standardCell.tsx +41 -0
  110. package/Components/ModalComponent/presets/types.ts +35 -0
  111. package/Components/ModalComponent/showTextInput.ts +52 -0
  112. package/Components/ModalComponent/utils.ts +82 -21
  113. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +4 -85
  114. package/Components/OfflineHandler/__tests__/index.test.tsx +78 -18
  115. package/Components/OfflineHandler/hooks.ts +111 -0
  116. package/Components/OfflineHandler/index.tsx +51 -73
  117. package/Components/OfflineHandler/utils/index.ts +1 -13
  118. package/Components/PlayerContainer/PlayerContainer.tsx +25 -17
  119. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -4
  120. package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +337 -0
  121. package/Components/PreloaderWrapper/index.tsx +1 -1
  122. package/Components/River/ComponentsMap/ComponentsMap.tsx +87 -61
  123. package/Components/River/River.tsx +4 -0
  124. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +140 -89
  125. package/Components/River/__tests__/componentsMap.test.js +8 -3
  126. package/Components/River/__tests__/resultCallback.test.tsx +47 -0
  127. package/Components/Screen/TV/hooks/__tests__/useAfterPaint.test.ts +60 -0
  128. package/Components/Screen/TV/hooks/index.ts +2 -0
  129. package/Components/Screen/TV/hooks/useAfterPaint.ts +23 -0
  130. package/Components/Screen/TV/index.web.tsx +16 -7
  131. package/Components/Screen/__tests__/Screen.test.tsx +24 -0
  132. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
  133. package/Components/Screen/index.tsx +13 -4
  134. package/Components/Screen/navigationHandler.ts +8 -8
  135. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +5 -3
  136. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +1 -1
  137. package/Components/ScreenRevealManager/Overlay.tsx +34 -0
  138. package/Components/ScreenRevealManager/ScreenRevealManager.ts +18 -2
  139. package/Components/ScreenRevealManager/__tests__/Overlay.test.tsx +88 -0
  140. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +36 -0
  141. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +56 -0
  142. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +29 -23
  143. package/Components/TopCutoffOverlay/index.tsx +2 -2
  144. package/Components/Transitioner/Transitioner.tsx +28 -17
  145. package/Components/VideoLive/LiveImageManager.ts +65 -48
  146. package/Components/VideoLive/PlayerLiveImageComponent.tsx +4 -2
  147. package/Components/VideoLive/__tests__/LiveImageManager.test.ts +59 -0
  148. package/Components/VideoModal/utils.ts +6 -1
  149. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +12 -5
  150. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
  151. package/Contexts/CachedDimensionsContext/__tests__/index.test.ts +154 -0
  152. package/Contexts/CachedDimensionsContext/index.ts +17 -2
  153. package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
  154. package/Contexts/ModalNavigationContext/index.tsx +2 -2
  155. package/Helpers/ComponentCellSelectionHelper/index.js +0 -6
  156. package/Helpers/index.js +7 -40
  157. package/Hooks/useEntryActionsExpander.ts +63 -0
  158. package/assets/empty-Info.png +0 -0
  159. package/package.json +6 -5
  160. package/Components/Layout/TV/LayoutBackground.tsx +0 -31
  161. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +0 -112
  162. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +0 -107
  163. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +0 -153
  164. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -66
  165. package/Components/OfflineHandler/NotificationView/utils.ts +0 -34
  166. package/Components/Screen/utils.ts +0 -16
  167. package/Helpers/Analytics/index.js +0 -95
  168. /package/Components/MasterCell/DefaultComponents/ButtonContainerView/{index.tv.android.tsx → index.android.tv.tsx} +0 -0
  169. /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
@@ -42,9 +42,25 @@ export class ScreenRevealManager {
42
42
  private subject$ = new Subject<void>();
43
43
  private subscription: Subscription;
44
44
 
45
- constructor(componentsToRender: ZappUIComponent[], callback: Callback) {
45
+ /**
46
+ * @param initialNumberToLoad how many components to wait for before revealing
47
+ * the screen. Staggering exists so a screen full of network-backed components
48
+ * does not load them all at once. A caller that has nothing to stagger - see
49
+ * `disableIncrementalLoading` on the HOC - passes the full count here so the
50
+ * reveal waits for exactly what gets rendered. Omitted, the count is derived
51
+ * from the components themselves.
52
+ */
53
+ constructor(
54
+ componentsToRender: ZappUIComponent[],
55
+ callback: Callback,
56
+ initialNumberToLoad?: number
57
+ ) {
46
58
  this.numberOfComponentsWaitToLoadBeforePresent =
47
- getNumberOfComponentsWaitToLoadBeforePresent(componentsToRender);
59
+ initialNumberToLoad == null
60
+ ? getNumberOfComponentsWaitToLoadBeforePresent(componentsToRender)
61
+ : // Clamped like the computed path: waiting for components that do not
62
+ // exist would hold the screen behind its overlay until the timeout.
63
+ Math.min(initialNumberToLoad, componentsToRender.length);
48
64
 
49
65
  this.renderingState = makeListOf<ComponentLoadingState>(
50
66
  COMPONENT_LOADING_STATE.UNKNOWN,
@@ -0,0 +1,88 @@
1
+ import React from "react";
2
+ import { Animated } from "react-native";
3
+ import { render } from "@testing-library/react-native";
4
+
5
+ jest.mock(
6
+ "@applicaster/zapp-react-native-ui-components/Components/BackgroundImage",
7
+ () => {
8
+ const { Text } = require("react-native");
9
+
10
+ return {
11
+ BackgroundImage: () => (
12
+ <Text testID="background-image">BackgroundImage</Text>
13
+ ),
14
+ };
15
+ }
16
+ );
17
+
18
+ import { Overlay } from "../Overlay";
19
+
20
+ const flattenStyle = (style: unknown): Record<string, unknown> => {
21
+ if (!style) {
22
+ return {};
23
+ }
24
+
25
+ if (Array.isArray(style)) {
26
+ return style.reduce<Record<string, unknown>>(
27
+ (acc, item) => ({ ...acc, ...flattenStyle(item) }),
28
+ {}
29
+ );
30
+ }
31
+
32
+ if (typeof style === "object") {
33
+ return style as Record<string, unknown>;
34
+ }
35
+
36
+ return {};
37
+ };
38
+
39
+ describe("Overlay", () => {
40
+ it("renders the animated overlay container", () => {
41
+ const opacity = new Animated.Value(1);
42
+
43
+ const { getByTestId } = render(
44
+ <Overlay opacity={opacity} backgroundColor="#000000" />
45
+ );
46
+
47
+ expect(getByTestId("animated-component")).toBeTruthy();
48
+ });
49
+
50
+ it("applies opacity and backgroundColor to the overlay", () => {
51
+ const opacity = new Animated.Value(0.5);
52
+
53
+ const { getByTestId } = render(
54
+ <Overlay opacity={opacity} backgroundColor="#ff0000" />
55
+ );
56
+
57
+ const style = flattenStyle(getByTestId("animated-component").props.style);
58
+
59
+ expect(style.backgroundColor).toBe("#ff0000");
60
+ expect(style.opacity).toBe(0.5);
61
+ });
62
+
63
+ it("positions the overlay with absolute fill layout", () => {
64
+ const opacity = new Animated.Value(1);
65
+
66
+ const { getByTestId } = render(
67
+ <Overlay opacity={opacity} backgroundColor="#000000" />
68
+ );
69
+
70
+ const style = flattenStyle(getByTestId("animated-component").props.style);
71
+
72
+ expect(style.position).toBe("absolute");
73
+ expect(style.top).toBe(0);
74
+ expect(style.left).toBe(0);
75
+ expect(style.right).toBe(0);
76
+ expect(style.bottom).toBe(0);
77
+ });
78
+
79
+ it("renders BackgroundImage inside the overlay", () => {
80
+ const opacity = new Animated.Value(1);
81
+
82
+ const { getByTestId } = render(
83
+ <Overlay opacity={opacity} backgroundColor="#000000" />
84
+ );
85
+
86
+ expect(getByTestId("background-image")).toBeTruthy();
87
+ });
88
+ });
@@ -22,6 +22,42 @@ import { makeListOf } from "@applicaster/zapp-react-native-utils/arrayUtils";
22
22
  import { isFirstComponentGallery } from "@applicaster/zapp-react-native-utils/componentsUtils";
23
23
  import { withTimeout$ } from "@applicaster/zapp-react-native-utils/idleUtils";
24
24
 
25
+ describe("ScreenRevealManager explicit component count", () => {
26
+ beforeEach(() => {
27
+ (isFirstComponentGallery as jest.Mock).mockReturnValue(false);
28
+
29
+ (makeListOf as jest.Mock).mockImplementation((value, length) =>
30
+ Array(length).fill(value)
31
+ );
32
+
33
+ (withTimeout$ as jest.Mock).mockReturnValue(new Subject());
34
+ });
35
+
36
+ it("waits for the number of components it is given, not the computed default", () => {
37
+ const components = Array(6).fill({}) as any;
38
+
39
+ const manager = new ScreenRevealManager(components, jest.fn(), 6);
40
+
41
+ expect(manager.numberOfComponentsWaitToLoadBeforePresent).toBe(6);
42
+ });
43
+
44
+ it("never waits for more components than it was handed", () => {
45
+ const components = Array(3).fill({}) as any;
46
+
47
+ const manager = new ScreenRevealManager(components, jest.fn(), 10);
48
+
49
+ expect(manager.numberOfComponentsWaitToLoadBeforePresent).toBe(3);
50
+ });
51
+
52
+ it("falls back to the computed default when given no explicit count", () => {
53
+ const components = Array(6).fill({}) as any;
54
+
55
+ const manager = new ScreenRevealManager(components, jest.fn());
56
+
57
+ expect(manager.numberOfComponentsWaitToLoadBeforePresent).toBe(3);
58
+ });
59
+ });
60
+
25
61
  describe("ScreenRevealManager", () => {
26
62
  let mockCallback: jest.Mock;
27
63
  let timeout$: Subject<void>;
@@ -9,6 +9,12 @@ import {
9
9
  TIMEOUT,
10
10
  } from "../withScreenRevealManager";
11
11
 
12
+ jest.mock("@applicaster/zapp-react-native-utils/theme", () => ({
13
+ useTheme: () => ({ app_background_color: "#000000" }),
14
+ }));
15
+
16
+ jest.mock("../Overlay", () => ({ Overlay: () => null }));
17
+
12
18
  // jest.mock("react-native/Libraries/Animated/NativeAnimatedHelper");
13
19
 
14
20
  const MockComponent = ({
@@ -37,6 +43,56 @@ const MockComponent = ({
37
43
 
38
44
  const WrappedComponent = withScreenRevealManager(MockComponent);
39
45
 
46
+ describe("withScreenRevealManager disableIncrementalLoading", () => {
47
+ const components = (count: number) =>
48
+ Array(count).fill({ component_type: "grid-qb" });
49
+
50
+ beforeEach(() => {
51
+ jest.clearAllMocks();
52
+ });
53
+
54
+ it("asks for every component at once when incremental loading is disabled", () => {
55
+ render(
56
+ <WrappedComponent
57
+ componentsToRender={components(6)}
58
+ disableIncrementalLoading
59
+ />
60
+ );
61
+
62
+ expect(screen.getByTestId("mock-component").props.initialNumberToLoad).toBe(
63
+ 6
64
+ );
65
+ });
66
+
67
+ it("still tracks the component count after it changes", () => {
68
+ const { rerender } = render(
69
+ <WrappedComponent
70
+ componentsToRender={components(6)}
71
+ disableIncrementalLoading
72
+ />
73
+ );
74
+
75
+ rerender(
76
+ <WrappedComponent
77
+ componentsToRender={components(9)}
78
+ disableIncrementalLoading
79
+ />
80
+ );
81
+
82
+ expect(screen.getByTestId("mock-component").props.initialNumberToLoad).toBe(
83
+ 9
84
+ );
85
+ });
86
+
87
+ it("loads incrementally when the flag is not set", () => {
88
+ render(<WrappedComponent componentsToRender={components(6)} />);
89
+
90
+ expect(screen.getByTestId("mock-component").props.initialNumberToLoad).toBe(
91
+ 3
92
+ );
93
+ });
94
+ });
95
+
40
96
  describe.skip("withScreenRevealManager", () => {
41
97
  beforeEach(() => {
42
98
  jest.clearAllMocks();
@@ -1,8 +1,10 @@
1
1
  import * as React from "react";
2
- import { Animated, StyleSheet } from "react-native";
2
+ import { Animated } from "react-native";
3
+
3
4
  import { isFirstComponentScreenPicker } from "@applicaster/zapp-react-native-utils/componentsUtils";
4
5
  import { useRefWithInitialValue } from "@applicaster/zapp-react-native-utils/reactHooks/state/useRefWithInitialValue";
5
6
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
7
+ import { Overlay } from "./Overlay";
6
8
 
7
9
  import { ScreenRevealManager } from "./ScreenRevealManager";
8
10
  import {
@@ -10,13 +12,6 @@ import {
10
12
  emitScreenRevealManagerIsNotReadyToShow,
11
13
  } from "./utils";
12
14
 
13
- const styles = StyleSheet.create({
14
- container: {
15
- ...StyleSheet.absoluteFillObject,
16
- position: "absolute",
17
- },
18
- });
19
-
20
15
  export const TIMEOUT = 300; // 300 ms
21
16
 
22
17
  const HIDDEN = 0; // opacity = 0
@@ -26,11 +21,23 @@ export const SHOWN = 1; // opacity = 1
26
21
  type Props = {
27
22
  componentsToRender: ZappUIComponent[];
28
23
  backgroundColor?: string;
24
+ /**
25
+ * Renders every component at once instead of revealing them a few at a time.
26
+ *
27
+ * For a screen that builds its own components and holds all of them already -
28
+ * a form, say - staggering buys nothing and costs: each rebuild of the list
29
+ * tears it back down to the first few and lets the rest crawl in again.
30
+ *
31
+ * This is a flag rather than a count on purpose. A count would have to be
32
+ * re-read whenever the number of components changes, while "do not stagger"
33
+ * stays true whatever the screen ends up holding.
34
+ */
35
+ disableIncrementalLoading?: boolean;
29
36
  };
30
37
 
31
38
  export const withScreenRevealManager = (Component) => {
32
39
  return function WithScreenRevealManager(props: Props) {
33
- const { componentsToRender } = props;
40
+ const { componentsToRender, disableIncrementalLoading } = props;
34
41
 
35
42
  const [isContentReadyToBeShown, setIsContentReadyToBeShown] =
36
43
  React.useState(false);
@@ -47,7 +54,8 @@ export const withScreenRevealManager = (Component) => {
47
54
  () =>
48
55
  new ScreenRevealManager(
49
56
  componentsToRender,
50
- handleSetIsContentReadyToBeShown
57
+ handleSetIsContentReadyToBeShown,
58
+ disableIncrementalLoading ? componentsToRender.length : undefined
51
59
  )
52
60
  );
53
61
 
@@ -77,7 +85,7 @@ export const withScreenRevealManager = (Component) => {
77
85
 
78
86
  if (isFirstComponentScreenPicker(componentsToRender)) {
79
87
  // for screen-picker with have additional internal ComponentsMap, no need to add this wrapper
80
- return <Component {...props} />;
88
+ return <Component {...props} initialNumberToLoad={1} />;
81
89
  }
82
90
 
83
91
  return (
@@ -85,7 +93,11 @@ export const withScreenRevealManager = (Component) => {
85
93
  <Component
86
94
  {...props}
87
95
  initialNumberToLoad={
88
- managerRef.current.numberOfComponentsWaitToLoadBeforePresent
96
+ // Recomputed on every render, so the count follows the components
97
+ // even though the manager settled its own expectation at mount.
98
+ disableIncrementalLoading
99
+ ? componentsToRender.length
100
+ : managerRef.current.numberOfComponentsWaitToLoadBeforePresent
89
101
  }
90
102
  onLoadFinishedFromScreenRevealManager={
91
103
  managerRef.current.onLoadFinished
@@ -93,17 +105,11 @@ export const withScreenRevealManager = (Component) => {
93
105
  onLoadFailedFromScreenRevealManager={managerRef.current.onLoadFailed}
94
106
  />
95
107
  {isShowOverlay ? (
96
- <Animated.View
97
- style={[
98
- styles.container,
99
- {
100
- opacity: opacityRef.current,
101
- // TODO: we should support background image as well, but for now we will use background color from theme
102
- backgroundColor:
103
- props.backgroundColor ?? theme.app_background_color,
104
- },
105
- ]}
106
- testID="animated-component"
108
+ <Overlay
109
+ opacity={opacityRef.current}
110
+ backgroundColor={
111
+ props.backgroundColor ?? theme.app_background_color
112
+ }
107
113
  />
108
114
  ) : null}
109
115
  </>
@@ -7,7 +7,7 @@ import { useMarginTop } from "./hooks";
7
7
 
8
8
  interface IProps {
9
9
  targetScreenId?: string;
10
- children?: React.ReactNode;
10
+ children: React.ReactElement | null;
11
11
  applyTopCutoff?: boolean;
12
12
  }
13
13
 
@@ -21,7 +21,7 @@ export const TopCutoffOverlay: React.FC<IProps> = ({
21
21
  targetScreenId,
22
22
  children,
23
23
  applyTopCutoff = true,
24
- }: IProps) => {
24
+ }) => {
25
25
  const cutoffHeight = useMarginTop(targetScreenId);
26
26
 
27
27
  const { backgroundColor: screenBackgroundColor } =
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { View } from "react-native";
3
- import { append, compose, identity, isNil, tail, take, update } from "ramda";
3
+ import { append, compose, identity, tail, take, update } from "ramda";
4
4
 
5
5
  import { AnimationManager } from "./AnimationManager";
6
6
  import { Scene } from "./Scene";
@@ -10,6 +10,7 @@ import {
10
10
  shouldSkipAnimationForPreviousWebViewScene,
11
11
  } from "./utils";
12
12
  import { v4 as uuid_v4 } from "uuid";
13
+ import { isScreenActiveForRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useIsScreenActive";
13
14
 
14
15
  export const NAV_ACTION_PUSH = "PUSH";
15
16
 
@@ -50,16 +51,16 @@ function scenesForBackTransition(props: SceneProps, { scenes }) {
50
51
  : [prepareScene(props.children), scenes[0]];
51
52
  }
52
53
 
53
- const isModalClosed = (navigator) => {
54
- return !!isNil(navigator?.modalData);
55
- };
56
-
57
54
  type Props = {
58
55
  transitionConfig: any;
59
56
  children: React.ReactNode;
60
57
  navigator: {
61
58
  previousAction: string;
62
59
  currentRoute: string;
60
+ videoModalState?: {
61
+ visible?: boolean;
62
+ mode?: string;
63
+ };
63
64
  screenData: {
64
65
  [key: string]: any;
65
66
  };
@@ -283,8 +284,10 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
283
284
  // so only a single scene should be rendered,
284
285
  // instead of dangerous nulls or duplicates.
285
286
  renderSingleScene({ scenes, to }) {
286
- const { contentStyle } = this.props;
287
- const screenData = this.props.children.props.screenData;
287
+ const { contentStyle, navigator, children } = this.props;
288
+
289
+ const { currentRoute, videoModalState } = navigator;
290
+ const screenData = children.props.screenData;
288
291
  const pathname = scenes[to.index].key;
289
292
 
290
293
  return (
@@ -293,7 +296,11 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
293
296
  <Scene
294
297
  {...{ style: to?.style || {} }}
295
298
  pathname={pathname}
296
- isActive={pathname === this.props.navigator.currentRoute}
299
+ isActive={isScreenActiveForRoute(
300
+ pathname,
301
+ currentRoute,
302
+ videoModalState
303
+ )}
297
304
  contentStyle={contentStyle}
298
305
  key={scenes[to.index].key}
299
306
  screenUniqueId={scenes[to.index].screenUniqueId}
@@ -311,16 +318,19 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
311
318
  // Looks redundant, but when written like this
312
319
  // we do not need to pass or generate keys.
313
320
  renderScenes({ scenes, from, to }) {
314
- const { contentStyle } = this.props;
321
+ const { contentStyle, navigator } = this.props;
322
+
323
+ const { currentRoute, videoModalState } = navigator;
315
324
 
316
325
  const fromScene = scenes[from.index] && (
317
326
  <Scene
318
327
  key={scenes[from.index].key}
319
328
  pathname={scenes[from.index].key}
320
- isActive={
321
- scenes[from.index].key === this.props.navigator.currentRoute &&
322
- isModalClosed(this.props.navigator)
323
- }
329
+ isActive={isScreenActiveForRoute(
330
+ scenes[from.index].key,
331
+ currentRoute,
332
+ videoModalState
333
+ )}
324
334
  style={from.style}
325
335
  pointerEvents="none"
326
336
  overlayStyle={from.overlayStyle}
@@ -336,10 +346,11 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
336
346
  const toScene = scenes[to.index] && (
337
347
  <Scene
338
348
  pathname={scenes[to.index].key}
339
- isActive={
340
- scenes[to.index].key === this.props.navigator.currentRoute &&
341
- isModalClosed(this.props.navigator)
342
- }
349
+ isActive={isScreenActiveForRoute(
350
+ scenes[to.index].key,
351
+ currentRoute,
352
+ videoModalState
353
+ )}
343
354
  key={scenes[to.index].key}
344
355
  style={to.style}
345
356
  overlayStyle={to.overlayStyle}
@@ -5,7 +5,7 @@ import {
5
5
  } from "@applicaster/zapp-react-native-utils/appUtils/playerManager";
6
6
  import { setUserCellPlayerMutedPreference } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/userCellPlayerMutedPreference";
7
7
  import { playerFactory } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/playerFactory";
8
- import { PlayerRole } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/conts";
8
+ import { PlayerRole } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/const";
9
9
  import { loggerLiveImageManager } from "./loggerHelper";
10
10
  import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
11
11
  import {
@@ -48,7 +48,6 @@ type Position = {
48
48
  type PlayerFactoryConfig = {
49
49
  player: any; // React ref for native view
50
50
  playerId: string;
51
- muted: boolean;
52
51
  playerPluginId: string;
53
52
  screenConfig: Record<string, any>;
54
53
  entry: ZappEntry; // original entry, used as fallback if no hooks
@@ -214,10 +213,16 @@ export class LiveImageManager implements PlayerLifecycleListener {
214
213
  log_debug(
215
214
  `pauseItem: live-image not ready, ${item.playerId} - ${item.tag}`
216
215
  );
217
- } else {
218
- item.player.pause();
219
216
  }
220
217
 
218
+ // Pause even when the player is not ready yet: the underlying player may
219
+ // still be loading and would otherwise start playing on its own once it
220
+ // has buffered, leaving the live-image audible underneath the primary
221
+ // player. Note this is best effort - the web controller's pause() only
222
+ // logs while the player component is unmounted - so the player itself also
223
+ // refuses buffer-driven starts when it was never asked to play.
224
+ item.player.pause();
225
+
221
226
  // Fake close event, because we unmount native view
222
227
  item.player.onPlayerClose();
223
228
  item.setMode?.(LiveImageType.Image);
@@ -296,7 +301,7 @@ export class LiveImageManager implements PlayerLifecycleListener {
296
301
 
297
302
  setUserCellPlayerMutedPreference(true);
298
303
 
299
- this.items.forEach((liveImage) => liveImage.player?.mute());
304
+ this.items.forEach((liveImage) => liveImage.mute());
300
305
  };
301
306
 
302
307
  public unmuteAll = () => {
@@ -304,7 +309,7 @@ export class LiveImageManager implements PlayerLifecycleListener {
304
309
 
305
310
  setUserCellPlayerMutedPreference(false);
306
311
 
307
- this.items.forEach((liveImage) => liveImage.player?.unmute());
312
+ this.items.forEach((liveImage) => liveImage.unmute());
308
313
  };
309
314
 
310
315
  public onViewPositionChanged = (item: LiveImage) => {
@@ -574,6 +579,20 @@ export class LiveImage implements QuickBrickPlayer.SharedPlayerCallBacks {
574
579
  left: 0,
575
580
  };
576
581
 
582
+ // Keeps muted state in sync: updates the initial value for deferred
583
+ // player creation and forwards to the player if it already exists.
584
+ public initiallyMuted: boolean = true;
585
+
586
+ mute = () => {
587
+ this.initiallyMuted = true;
588
+ this.player?.mute();
589
+ };
590
+
591
+ unmute = () => {
592
+ this.initiallyMuted = false;
593
+ this.player?.unmute();
594
+ };
595
+
577
596
  positionToString() {
578
597
  if (!this.position) {
579
598
  return "position not set";
@@ -611,48 +630,7 @@ export class LiveImage implements QuickBrickPlayer.SharedPlayerCallBacks {
611
630
  return this._preparePromise;
612
631
  }
613
632
 
614
- this._preparePromise = (async (): Promise<boolean> => {
615
- // 1. Run hooks if configured
616
- let entry = this.factoryConfig.entry;
617
-
618
- if (this.preloadHooks?.length) {
619
- const result = await executePreloadHooks({
620
- preloadHooks: this.preloadHooks,
621
- entry,
622
- });
623
-
624
- if (result) {
625
- this.processedEntry = result;
626
- entry = result;
627
- } else {
628
- return false;
629
- }
630
- }
631
-
632
- // 2. Create the player with the correct entry
633
- const factoryItem = playerFactory({
634
- player: this.factoryConfig.player,
635
- playerId: this.factoryConfig.playerId,
636
- autoplay: false,
637
- entry,
638
- muted: this.factoryConfig.muted,
639
- playerPluginId: this.factoryConfig.playerPluginId,
640
- screenConfig: this.factoryConfig.screenConfig,
641
- playerRole: PlayerRole.Cell,
642
- });
643
-
644
- if (!factoryItem) {
645
- throw new Error("Player factory returned null");
646
- }
647
-
648
- this.player = factoryItem.controller;
649
- this.component = factoryItem.Component;
650
-
651
- // 3. Register callbacks — player now exists
652
- this.player.addListener({ id: "live-image", listener: this });
653
-
654
- return true;
655
- })()
633
+ this._preparePromise = this.createPreparedPlayer()
656
634
  .then((result) => {
657
635
  this._preparePromise = null;
658
636
 
@@ -672,6 +650,45 @@ export class LiveImage implements QuickBrickPlayer.SharedPlayerCallBacks {
672
650
  return this._preparePromise;
673
651
  }
674
652
 
653
+ private async createPreparedPlayer(): Promise<boolean> {
654
+ let entry = this.factoryConfig.entry;
655
+
656
+ if (this.preloadHooks?.length) {
657
+ const result = await executePreloadHooks({
658
+ preloadHooks: this.preloadHooks,
659
+ entry,
660
+ });
661
+
662
+ if (!result) return false;
663
+
664
+ this.processedEntry = result;
665
+ entry = result;
666
+ }
667
+
668
+ const factoryItem = await playerFactory({
669
+ player: this.factoryConfig.player,
670
+ playerId: this.factoryConfig.playerId,
671
+ autoplay: false,
672
+ entry,
673
+ muted: this.initiallyMuted,
674
+ playerPluginId: this.factoryConfig.playerPluginId,
675
+ screenConfig: this.factoryConfig.screenConfig,
676
+ playerRole: PlayerRole.Cell,
677
+ });
678
+
679
+ if (!factoryItem) {
680
+ throw new Error(
681
+ `Player factory returned null (playerId: ${this.factoryConfig.playerId}, playerPluginId: ${this.factoryConfig.playerPluginId})`
682
+ );
683
+ }
684
+
685
+ this.player = factoryItem.controller;
686
+ this.component = factoryItem.Component;
687
+ this.player.addListener({ id: "live-image", listener: this });
688
+
689
+ return true;
690
+ }
691
+
675
692
  public getPlayer = (): Player | null => {
676
693
  return this.player;
677
694
  };
@@ -126,14 +126,16 @@ const PlayerLiveImageComponent = (props: Props) => {
126
126
  factoryConfig: {
127
127
  player: ref,
128
128
  playerId,
129
- muted,
130
129
  playerPluginId: playerPluginId,
131
130
  screenConfig: screenConfig,
132
131
  entry: item,
133
132
  },
134
133
  tag: item.title?.toString(),
135
134
  });
136
- }, [playerId, preloadHooks, muted, playerPluginId, screenConfig, item]);
135
+ }, [playerId, preloadHooks, playerPluginId, screenConfig, item]);
136
+
137
+ // Keep the muted state in sync with user preference and player state
138
+ liveImageItem.initiallyMuted = muted;
137
139
 
138
140
  React.useEffect(() => {
139
141
  liveImageItem.setMode = setModeDebounced;