@applicaster/zapp-react-native-ui-components 15.0.0-rc.98 → 15.1.0-alpha.1852552014

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/BackgroundImage/BackgroundImage.tv.android.tsx +28 -0
  2. package/Components/BackgroundImage/BackgroundImage.tv.ios.tsx +24 -0
  3. package/Components/BackgroundImage/BackgroundImage.tv.web.tsx +42 -0
  4. package/Components/BackgroundImage/index.ts +8 -0
  5. package/Components/BaseFocusable/index.ios.ts +2 -12
  6. package/Components/Cell/FocusableWrapper.tsx +0 -3
  7. package/Components/Cell/TvOSCellComponent.tsx +0 -5
  8. package/Components/CellRendererResolver/index.ts +1 -1
  9. package/Components/ComponentResolver/__tests__/componentResolver.test.js +1 -1
  10. package/Components/Focusable/Focusable.tsx +2 -4
  11. package/Components/Focusable/FocusableTvOS.tsx +1 -18
  12. package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +0 -1
  13. package/Components/FocusableGroup/FocusableTvOS.tsx +12 -37
  14. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +39 -28
  15. package/Components/GeneralContentScreen/__tests__/GeneralContentScreen.test.tsx +104 -0
  16. package/Components/GeneralContentScreen/utils/__tests__/getScreenDataSource.test.ts +19 -0
  17. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
  18. package/Components/GeneralContentScreen/utils/getScreenDataSource.ts +9 -0
  19. package/Components/HandlePlayable/HandlePlayable.tsx +24 -42
  20. package/Components/HandlePlayable/utils.ts +31 -0
  21. package/Components/HookRenderer/HookRenderer.tsx +40 -10
  22. package/Components/HookRenderer/__tests__/HookRenderer.test.tsx +60 -0
  23. package/Components/Layout/FullWidthRow.tsx +38 -0
  24. package/Components/Layout/TV/ScreenContainer.tsx +11 -2
  25. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +5 -1
  26. package/Components/Layout/TV/index.tsx +6 -6
  27. package/Components/Layout/TV/index.web.tsx +6 -6
  28. package/Components/LinkHandler/LinkHandler.tsx +2 -2
  29. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +10 -4
  30. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +1 -5
  31. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +3 -11
  32. package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +1 -9
  33. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +14 -15
  34. package/Components/MasterCell/DefaultComponents/LiveImage/__tests__/prepareEntry.test.ts +352 -0
  35. package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +136 -0
  36. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +34 -16
  37. package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +6 -7
  38. package/Components/MasterCell/DefaultComponents/Text/index.tsx +2 -6
  39. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +2 -6
  40. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +11 -233
  41. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +15 -19
  42. package/Components/MasterCell/dataAdapter.ts +15 -5
  43. package/Components/MasterCell/index.tsx +10 -1
  44. package/Components/MasterCell/utils/__tests__/resolveColor.test.js +169 -132
  45. package/Components/MasterCell/utils/__tests__/resolveColorForProp.test.js +92 -0
  46. package/Components/MasterCell/utils/index.ts +89 -46
  47. package/Components/Navigator/StackNavigator.tsx +6 -0
  48. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +2 -2
  49. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +18 -17
  50. package/Components/OfflineHandler/__tests__/index.test.tsx +18 -27
  51. package/Components/PlayerContainer/PlayerContainer.tsx +20 -34
  52. package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +286 -0
  53. package/Components/PreloaderWrapper/__tests__/index.test.tsx +26 -0
  54. package/Components/PreloaderWrapper/index.tsx +15 -0
  55. package/Components/River/ComponentsMap/ComponentsMap.tsx +15 -0
  56. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
  57. package/Components/River/RefreshControl.tsx +9 -3
  58. package/Components/River/RiverItem.tsx +26 -20
  59. package/Components/River/TV/River.tsx +14 -31
  60. package/Components/River/TV/index.tsx +4 -8
  61. package/Components/River/TV/withTVEventHandler.tsx +36 -0
  62. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -15
  63. package/Components/Screen/TV/hooks/__tests__/useAfterPaint.test.ts +60 -0
  64. package/Components/Screen/TV/hooks/index.ts +2 -0
  65. package/Components/Screen/TV/hooks/useAfterPaint.ts +23 -0
  66. package/Components/Screen/TV/index.web.tsx +18 -11
  67. package/Components/Screen/__tests__/Screen.test.tsx +43 -65
  68. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +44 -68
  69. package/Components/Screen/hooks.ts +76 -5
  70. package/Components/Screen/index.tsx +18 -7
  71. package/Components/Screen/orientationHandler.ts +3 -3
  72. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +46 -0
  73. package/Components/ScreenFeedLoader/__tests__/ScreenFeedLoader.test.tsx +94 -0
  74. package/Components/ScreenFeedLoader/index.ts +1 -0
  75. package/Components/ScreenResolver/__tests__/screenResolver.test.js +24 -0
  76. package/Components/ScreenResolver/hooks/index.ts +3 -0
  77. package/Components/ScreenResolver/hooks/useGetComponent.ts +15 -0
  78. package/Components/ScreenResolver/hooks/useScreenComponentResolver.tsx +90 -0
  79. package/Components/ScreenResolver/index.tsx +9 -115
  80. package/Components/ScreenResolver/utils/__tests__/getScreenTypeProps.test.ts +45 -0
  81. package/Components/ScreenResolver/utils/getScreenTypeProps.ts +43 -0
  82. package/Components/ScreenResolver/utils/index.ts +1 -0
  83. package/Components/ScreenResolver/withDefaultScreenContext.tsx +16 -0
  84. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +25 -0
  85. package/Components/ScreenResolverFeedProvider/__tests__/ScreenResolverFeedProvider.test.tsx +44 -0
  86. package/Components/ScreenResolverFeedProvider/index.ts +1 -0
  87. package/Components/ScreenRevealManager/Overlay.tsx +34 -0
  88. package/Components/ScreenRevealManager/ScreenRevealManager.ts +8 -40
  89. package/Components/ScreenRevealManager/__tests__/Overlay.test.tsx +88 -0
  90. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +69 -86
  91. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +10 -18
  92. package/Components/Tabs/TabContent.tsx +4 -7
  93. package/Components/Transitioner/Scene.tsx +9 -15
  94. package/Components/Transitioner/index.js +3 -3
  95. package/Components/VideoLive/LiveImageManager.ts +209 -53
  96. package/Components/VideoLive/PlayerLiveImageComponent.tsx +32 -32
  97. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +2 -17
  98. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +5 -5
  99. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +7 -15
  100. package/Components/VideoModal/utils.ts +16 -13
  101. package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
  102. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +12 -5
  103. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
  104. package/Components/ZappUIComponent/index.tsx +12 -6
  105. package/Components/index.js +1 -1
  106. package/Contexts/CachedDimensionsContext/index.ts +17 -2
  107. package/Contexts/ScreenContext/__tests__/index.test.tsx +57 -0
  108. package/Contexts/ScreenContext/index.tsx +64 -26
  109. package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
  110. package/Contexts/ZappPipesContext/ZappPipesContextFactory.tsx +18 -7
  111. package/Decorators/Analytics/index.tsx +5 -6
  112. package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +0 -1
  113. package/Decorators/ConfigurationWrapper/const.ts +0 -1
  114. package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -7
  115. package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +212 -5
  116. package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
  117. package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
  118. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
  119. package/Helpers/ComponentCellSelectionHelper/index.js +0 -6
  120. package/Helpers/DataSourceHelper/index.js +19 -0
  121. package/Helpers/index.js +7 -40
  122. package/events/index.ts +2 -0
  123. package/events/scrollEndReached.ts +15 -0
  124. package/package.json +5 -5
  125. package/Components/Layout/TV/LayoutBackground.tsx +0 -31
  126. package/Components/MasterCell/DefaultComponents/Text/utils/__tests__/withAdjustedLineHeight.test.ts +0 -46
  127. package/Components/MasterCell/DefaultComponents/Text/utils/index.ts +0 -21
  128. package/Components/PlayerContainer/ErrorDisplay/ErrorDisplay.tsx +0 -57
  129. package/Components/PlayerContainer/ErrorDisplay/index.ts +0 -9
  130. package/Components/PlayerContainer/useRestrictMobilePlayback.tsx +0 -101
  131. package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +0 -30
  132. package/Components/River/TV/utils/index.ts +0 -4
  133. package/Components/River/TV/withFocusableGroupForContent.tsx +0 -71
  134. package/Components/Screen/utils.ts +0 -16
  135. package/Helpers/Analytics/index.js +0 -95
  136. package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +0 -80
  137. package/Helpers/DataSourceHelper/index.ts +0 -19
  138. /package/Components/HookRenderer/{index.tsx → index.ts} +0 -0
@@ -1,22 +1,13 @@
1
1
  import * as React from "react";
2
- import {
3
- useAppData,
4
- useContentTypes,
5
- usePlugins,
6
- } from "@applicaster/zapp-react-native-redux/hooks";
7
- import {
8
- useDimensions,
9
- useIsTablet as isTablet,
10
- useNavigation,
11
- useRivers,
12
- } from "@applicaster/zapp-react-native-utils/reactHooks";
2
+ import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
3
+ import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
13
4
 
14
5
  import { BufferAnimation } from "../PlayerContainer/BufferAnimation";
15
6
  import { PlayerContainer } from "../PlayerContainer";
16
7
  import { useModalSize } from "../VideoModal/hooks";
17
8
  import { ViewStyle } from "react-native";
18
- import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
19
9
  import { findCastPlugin, getPlayer } from "./utils";
10
+ import { useWaitForValidOrientation } from "../Screen/hooks";
20
11
 
21
12
  type Props = {
22
13
  item: ZappEntry;
@@ -31,23 +22,18 @@ type PlayableComponent = {
31
22
  Component: React.ComponentType<any>;
32
23
  };
33
24
 
34
- const dimensionsContext: "window" | "screen" = platformSelect({
35
- android_tv: "window",
36
- amazon: "window",
37
- // eslint-disable-next-line react-hooks/rules-of-hooks
38
- default: isTablet() ? "window" : "screen", // on tablet, window represents correct values, on phone it's not as the screen could be rotated
39
- });
40
-
41
25
  export function HandlePlayable({
42
26
  item,
43
27
  isModal,
44
28
  mode,
45
29
  groupId,
46
30
  }: Props): React.ReactElement | null {
47
- const plugins = usePlugins();
48
- const contentTypes = useContentTypes();
49
- const rivers = useRivers();
50
- const appData = useAppData();
31
+ const { plugins, contentTypes, rivers, appData } = usePickFromState([
32
+ "plugins",
33
+ "contentTypes",
34
+ "rivers",
35
+ "appData",
36
+ ]);
51
37
 
52
38
  const { closeVideoModal } = useNavigation();
53
39
 
@@ -97,27 +83,23 @@ export function HandlePlayable({
97
83
  });
98
84
  }, [casting]);
99
85
 
100
- const { width: screenWidth, height: screenHeight } =
101
- useDimensions(dimensionsContext);
102
-
103
86
  const modalSize = useModalSize();
104
87
 
105
- const style = React.useMemo(
106
- () =>
107
- ({
108
- width: isModal
109
- ? modalSize.width
110
- : mode === "PIP"
111
- ? "100%"
112
- : screenWidth,
113
- height: isModal
114
- ? modalSize.height
115
- : mode === "PIP"
116
- ? "100%"
117
- : screenHeight,
118
- }) as ViewStyle,
119
- [screenWidth, screenHeight, modalSize, isModal, mode]
120
- );
88
+ const isOrientationReady = useWaitForValidOrientation();
89
+
90
+ const style = React.useMemo(() => {
91
+ const isFullScreenReady =
92
+ mode === "PIP" || (mode === "FULLSCREEN" && isOrientationReady);
93
+
94
+ const getDimensionValue = (value: string | number) => {
95
+ return isModal ? value : isFullScreenReady ? "100%" : 0; // do not show player, until full screen mode is ready
96
+ };
97
+
98
+ return {
99
+ width: getDimensionValue(modalSize.width),
100
+ height: getDimensionValue(modalSize.height),
101
+ } as ViewStyle;
102
+ }, [modalSize, isModal, mode, isOrientationReady]);
121
103
 
122
104
  const Component = playable?.Component;
123
105
 
@@ -5,6 +5,14 @@ import {
5
5
 
6
6
  import { CHROMECAST_PLUGIN_ID, YOUTUBE_PLUGIN_ID } from "./const";
7
7
  import { omit } from "@applicaster/zapp-react-native-utils/utils";
8
+ import { getXray } from "@applicaster/zapp-react-native-utils/logger";
9
+
10
+ const { Logger } = getXray();
11
+
12
+ const logger = new Logger(
13
+ "QuickBrick",
14
+ "packages/zapp-react-native-ui-components/Components/HandlePlayable"
15
+ );
8
16
 
9
17
  const getPlayerModuleProperties = (PlayerModule: ZappPlugin) => {
10
18
  if (PlayerModule?.Component && typeof PlayerModule.Component === "object") {
@@ -52,10 +60,25 @@ export const getPlayer = (
52
60
  if (type) {
53
61
  PlayerModule = findPluginByIdentifier(type, plugins)?.module;
54
62
 
63
+ if (!PlayerModule) {
64
+ logger.error({
65
+ message:
66
+ "PlayerModule is undefined – type mapping may be wrong or type not set for player",
67
+ data: {
68
+ type,
69
+ screen_id,
70
+ item_type_value: item?.type?.value,
71
+ },
72
+ });
73
+
74
+ return [null, {}];
75
+ }
76
+
55
77
  return getPlayerWithModuleProperties(PlayerModule);
56
78
  }
57
79
  }
58
80
 
81
+ // TODO: Probably should be removed, Youtube plugin is deprecated
59
82
  if (item?.content?.type === "youtube-id") {
60
83
  PlayerModule = findYoutubePlugin(plugins)?.module;
61
84
 
@@ -70,5 +93,13 @@ export const getPlayer = (
70
93
  )
71
94
  );
72
95
 
96
+ if (!PlayerModule) {
97
+ logger.error({
98
+ message: "PlayerModule is undefined – playable plugin not found",
99
+ });
100
+
101
+ return [null, {}];
102
+ }
103
+
73
104
  return getPlayerWithModuleProperties(PlayerModule);
74
105
  };
@@ -6,7 +6,12 @@ import {
6
6
  } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
7
7
  import { useHookAnalytics } from "@applicaster/zapp-react-native-utils/analyticsUtils/helpers/hooks";
8
8
  import { useSetNavbarState } from "@applicaster/zapp-react-native-utils/reactHooks";
9
- import { PresentationType } from "../ScreenResolver";
9
+
10
+ import { componentsLogger } from "../../Helpers/logger";
11
+
12
+ const logger = componentsLogger.addSubsystem("HookRenderer");
13
+
14
+ const HOOK_PRESENTATION_TYPE = "Hook";
10
15
 
11
16
  type Props = {
12
17
  focused?: boolean;
@@ -15,20 +20,17 @@ type Props = {
15
20
  callback: hookCallback;
16
21
  };
17
22
 
18
- export const HookRenderer = (props: Props) => {
19
- const {
20
- focused,
21
- screenData: { payload, hookPlugin },
22
- callback,
23
- } = props;
24
-
25
- const { setVisible: showNavBar } = useSetNavbarState();
23
+ const HookRenderer = (props: Props) => {
24
+ const { focused, screenData, callback } = props;
25
+ const { payload, hookPlugin } = screenData;
26
26
 
27
27
  const {
28
28
  module: { Component: HookComponent, presentFullScreen },
29
29
  configuration,
30
30
  } = hookPlugin;
31
31
 
32
+ const { setVisible: showNavBar } = useSetNavbarState();
33
+
32
34
  useHookAnalytics(props);
33
35
 
34
36
  const isNavBarVisible = useIsNavBarVisible();
@@ -63,8 +65,36 @@ export const HookRenderer = (props: Props) => {
63
65
  hookPlugin,
64
66
  focused,
65
67
  parentFocus,
66
- presentationType: PresentationType.Hook,
68
+ presentationType: HOOK_PRESENTATION_TYPE,
67
69
  }}
68
70
  />
69
71
  );
70
72
  };
73
+
74
+ /**
75
+ * Guard component to prevent rendering HookRenderer when screenData or hookPlugin is missing. This is to avoid potential crashes due to missing data.
76
+ */
77
+ const HookRendererGuard = (props: Props) => {
78
+ React.useEffect(() => {
79
+ if (!props.screenData) {
80
+ logger.error(
81
+ "HookRenderer received no screenData, screen cannot be rendered"
82
+ );
83
+ } else if (!props.screenData.hookPlugin) {
84
+ logger.error(
85
+ "HookRenderer received screenData with no hookPlugin, screen cannot be rendered",
86
+ {
87
+ screenData: props.screenData,
88
+ }
89
+ );
90
+ }
91
+ }, [props.screenData]);
92
+
93
+ if (!props.screenData || !props.screenData.hookPlugin) {
94
+ return null;
95
+ }
96
+
97
+ return <HookRenderer {...props} />;
98
+ };
99
+
100
+ export { HookRendererGuard as HookRenderer };
@@ -0,0 +1,60 @@
1
+ import React from "react";
2
+ import { View } from "react-native";
3
+ import { render } from "@testing-library/react-native";
4
+ import { HookRenderer } from "..";
5
+
6
+ jest.mock("@applicaster/zapp-react-native-utils/reactUtils", () => ({
7
+ isTV: jest.fn(() => false),
8
+ }));
9
+
10
+ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
11
+ useBackHandler: jest.fn(),
12
+ useIsNavBarVisible: jest.fn(() => true),
13
+ }));
14
+
15
+ jest.mock(
16
+ "@applicaster/zapp-react-native-utils/analyticsUtils/helpers/hooks",
17
+ () => ({
18
+ useHookAnalytics: jest.fn(),
19
+ })
20
+ );
21
+
22
+ jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
23
+ useSetNavbarState: jest.fn(() => ({
24
+ setVisible: jest.fn(),
25
+ })),
26
+ }));
27
+
28
+ describe("HookRenderer", () => {
29
+ it("returns null when hookPlugin is missing", () => {
30
+ const { toJSON } = render(
31
+ <HookRenderer callback={jest.fn()} screenData={{ payload: {} } as any} />
32
+ );
33
+
34
+ expect(toJSON()).toBeNull();
35
+ });
36
+
37
+ it("passes Hook presentationType to rendered hook component", () => {
38
+ const HookComponent = (props) => (
39
+ <View testID="hook-component" {...props} />
40
+ );
41
+
42
+ const { getByTestId } = render(
43
+ <HookRenderer
44
+ callback={jest.fn()}
45
+ screenData={{
46
+ payload: { foo: "bar" },
47
+ hookPlugin: {
48
+ module: {
49
+ Component: HookComponent,
50
+ presentFullScreen: false,
51
+ },
52
+ configuration: {},
53
+ },
54
+ }}
55
+ />
56
+ );
57
+
58
+ expect(getByTestId("hook-component").props.presentationType).toBe("Hook");
59
+ });
60
+ });
@@ -0,0 +1,38 @@
1
+ import * as React from "react";
2
+ import { StyleSheet, View } from "react-native";
3
+
4
+ const styles = StyleSheet.create({
5
+ row: { flexDirection: "row" },
6
+ basis: {
7
+ maxWidth: "100%",
8
+ flexBasis: "100%",
9
+ },
10
+ });
11
+
12
+ type Props = {
13
+ children: React.ReactNode;
14
+ };
15
+
16
+ /**
17
+ * Constrains its child to exactly the available parent width.
18
+ *
19
+ * UI components such as Hero and List render their items inside a parent
20
+ * `ScrollView` (e.g. when wrapped by tabs). A child placed directly in a
21
+ * scroll container can size itself to its own content rather than to the
22
+ * viewport, which causes items to be measured at a stale width and resize
23
+ * unexpectedly after layout changes such as device rotation.
24
+ *
25
+ * Wrapping the content in a `flexDirection: "row"` parent with a
26
+ * `flexBasis: "100%"` / `maxWidth: "100%"` child forces the child to
27
+ * re-derive its width from the parent on every layout pass instead of
28
+ * caching a measured pixel width, keeping it full-width and stable.
29
+ *
30
+ * @param children - The content to render at full parent width.
31
+ */
32
+ export function FullWidthRow({ children }: Props) {
33
+ return (
34
+ <View style={styles.row}>
35
+ <View style={styles.basis}>{children}</View>
36
+ </View>
37
+ );
38
+ }
@@ -18,7 +18,7 @@ import {
18
18
  routeIsPlayerScreen,
19
19
  } from "@applicaster/zapp-react-native-utils/navigationUtils";
20
20
  import { isApplePlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
21
- import { usePlugins } from "@applicaster/zapp-react-native-redux/hooks";
21
+ import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
22
22
  import { NavBarContainer } from "./NavBarContainer";
23
23
 
24
24
  type ComponentsExtraProps = {
@@ -111,7 +111,11 @@ export const ScreenContainer = React.memo(function ScreenContainer({
111
111
  const { activeRiver } = navigator;
112
112
  const { title, visible } = useNavbarState();
113
113
 
114
- const plugins = usePlugins();
114
+ const { plugins = [] } = usePickFromState([
115
+ "appState",
116
+ "remoteConfigurations",
117
+ "plugins",
118
+ ]);
115
119
 
116
120
  const navigationProps = getNavigationProps({
117
121
  navigator,
@@ -129,6 +133,11 @@ export const ScreenContainer = React.memo(function ScreenContainer({
129
133
  []
130
134
  );
131
135
 
136
+ // We need to render menu first and then proceed with screen content,
137
+ // otherwise screen will stay black until everything is loaded and screen
138
+ // rendering put huge load the CPU pushing rendering even further.
139
+ // With this approach, menu will be rendered immediately and screen content
140
+ // will be rendered after paint, which makes it more responsive and prevents black screen.
132
141
  const [navBarReady, setNavBarReady] = React.useState(false);
133
142
 
134
143
  const navBarContainer = (
@@ -2,13 +2,17 @@
2
2
 
3
3
  exports[`Layout TV renders 1`] = `
4
4
  <View
5
- backgroundColor="#000000"
6
5
  testID="background-component"
7
6
  >
8
7
  <View
9
8
  excludeFromFocusSearching={true}
10
9
  id="/river/A1234"
11
10
  preferredFocus={true}
11
+ style={
12
+ {
13
+ "flex": 1,
14
+ }
15
+ }
12
16
  >
13
17
  <View
14
18
  Components={
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import { useAppSelector } from "@applicaster/zapp-react-native-redux/hooks";
2
+ import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
3
3
  import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
4
4
 
5
5
  import { LayoutContainer } from "./LayoutContainer";
@@ -9,8 +9,6 @@ import { ScreenLayoutContextProvider } from "./ScreenLayoutContextProvider";
9
9
  import { PathnameContext } from "../../../Contexts/PathnameContext";
10
10
  import { ScreenDataContext } from "../../../Contexts/ScreenDataContext";
11
11
  import { ScreenContextProvider } from "../../../Contexts/ScreenContext";
12
- import { LayoutBackground } from "./LayoutBackground";
13
- import { selectAppReady } from "@applicaster/zapp-react-native-redux";
14
12
 
15
13
  type Components = {
16
14
  NavBar: React.ComponentType<any>;
@@ -30,7 +28,9 @@ type Props = {
30
28
  const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
31
29
  const navigator = useNavigation();
32
30
 
33
- const appReady = useAppSelector(selectAppReady);
31
+ const { appState: { appReady = false } = {} } = usePickFromState([
32
+ "appState",
33
+ ]);
34
34
 
35
35
  if (!appReady) {
36
36
  return null;
@@ -39,7 +39,7 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
39
39
  return (
40
40
  <LayoutContainer>
41
41
  <ScreenLayoutContextProvider>
42
- <LayoutBackground Background={Components.Background}>
42
+ <Components.Background>
43
43
  <ScreenDataContext.Provider value={navigator.data}>
44
44
  <PathnameContext.Provider value={navigator.currentRoute}>
45
45
  <ScreenContextProvider pathname={navigator.currentRoute}>
@@ -52,7 +52,7 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
52
52
  </ScreenContextProvider>
53
53
  </PathnameContext.Provider>
54
54
  </ScreenDataContext.Provider>
55
- </LayoutBackground>
55
+ </Components.Background>
56
56
  </ScreenLayoutContextProvider>
57
57
  </LayoutContainer>
58
58
  );
@@ -1,11 +1,10 @@
1
1
  import * as React from "react";
2
+ import { pathOr } from "ramda";
2
3
 
3
- import { useAppSelector } from "@applicaster/zapp-react-native-redux/hooks";
4
+ import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
4
5
 
5
6
  import { ScreenLayoutContextProvider } from "./ScreenLayoutContextProvider";
6
7
  import { StackNavigator } from "../../Navigator";
7
- import { LayoutBackground } from "./LayoutBackground";
8
- import { selectAppReady } from "@applicaster/zapp-react-native-redux";
9
8
 
10
9
  type Components = {
11
10
  NavBar: React.ComponentType<any>;
@@ -17,7 +16,8 @@ type Props = {
17
16
  };
18
17
 
19
18
  const Layout = ({ Components }: Props) => {
20
- const appReady = useAppSelector(selectAppReady);
19
+ const { appState } = usePickFromState(["appState"]);
20
+ const appReady = pathOr(false, ["appReady"], appState);
21
21
 
22
22
  if (!appReady) {
23
23
  return null;
@@ -25,9 +25,9 @@ const Layout = ({ Components }: Props) => {
25
25
 
26
26
  return (
27
27
  <ScreenLayoutContextProvider>
28
- <LayoutBackground Background={Components.Background}>
28
+ <Components.Background>
29
29
  <StackNavigator Components={Components} />
30
- </LayoutBackground>
30
+ </Components.Background>
31
31
  </ScreenLayoutContextProvider>
32
32
  );
33
33
  };
@@ -2,7 +2,7 @@ import * as React from "react";
2
2
  import * as R from "ramda";
3
3
 
4
4
  import { findPluginByIdentifier } from "@applicaster/zapp-react-native-utils/pluginUtils";
5
- import { usePlugins } from "@applicaster/zapp-react-native-redux/hooks";
5
+ import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
6
6
  import {
7
7
  inflateString,
8
8
  objectToReadableString,
@@ -40,7 +40,7 @@ export async function inflateUrl(url) {
40
40
 
41
41
  export function LinkHandler(props: Props) {
42
42
  const screenData = props?.screenData;
43
- const plugins = usePlugins();
43
+ const { plugins } = usePickFromState(["rivers", "plugins"]);
44
44
 
45
45
  const ScreenPlugin = findPluginByIdentifier(
46
46
  WEBVIEW_SCREEN_IDENTIFIER,
@@ -135,11 +135,17 @@ export const BorderContainerView = (props: Props) => {
135
135
 
136
136
  const isImageOnlyCell = useMemo(
137
137
  () =>
138
- state === "focused" &&
138
+ !hasFocusableInside(entry) &&
139
139
  !hasTextLabels &&
140
- !isMeasurement?.measuringInProgress &&
141
- !hasFocusableInside(entry),
142
- [entry, hasTextLabels, state, isMeasurement?.measuringInProgress]
140
+ state === "focused" &&
141
+ !isMeasurement?.measuringInProgress,
142
+ [
143
+ hasFocusableInside,
144
+ entry,
145
+ hasTextLabels,
146
+ state,
147
+ isMeasurement?.measuringInProgress,
148
+ ]
143
149
  );
144
150
 
145
151
  useEffect(() => {
@@ -32,7 +32,6 @@ export default function Image({
32
32
  placeholderImage,
33
33
  entry,
34
34
  withDimensions,
35
- source: sourceProp,
36
35
  ...otherProps
37
36
  }: Props) {
38
37
  const [showDefault, setShowDefault] = React.useState(false);
@@ -49,10 +48,7 @@ export default function Image({
49
48
  entry,
50
49
  showDefault,
51
50
  placeholderImage: placeholderImage || "",
52
- otherProps: {
53
- source: sourceProp,
54
- state: otherProps.state,
55
- },
51
+ otherProps,
56
52
  });
57
53
 
58
54
  const onError = React.useCallback(() => {
@@ -1,8 +1,8 @@
1
1
  import * as React from "react";
2
2
  import { Image as RnImage, ImageStyle } from "react-native";
3
+ import { equals, omit } from "ramda";
3
4
 
4
5
  import { useImageSource } from "./hooks";
5
- import { equals } from "@applicaster/zapp-react-native-utils/utils";
6
6
 
7
7
  type Source = {
8
8
  uri: string;
@@ -25,19 +25,11 @@ function Image({
25
25
  placeholderImage,
26
26
  entry,
27
27
  withDimensions,
28
- source: sourceProp,
29
28
  ...otherProps
30
29
  }: Props) {
31
30
  const [error, setErrorState] = React.useState(null);
32
31
 
33
- const source = useImageSource({
34
- uri,
35
- entry,
36
- otherProps: {
37
- source: sourceProp,
38
- state: otherProps.state,
39
- },
40
- });
32
+ const source = useImageSource({ uri, entry, otherProps });
41
33
 
42
34
  React.useEffect(() => {
43
35
  // reset error state on URI change as the error is referencing previous uri
@@ -57,7 +49,7 @@ function Image({
57
49
  onError={React.useCallback(() => setErrorState(true), [])}
58
50
  // as we have defaults as "" for placeholder image, we need to pass undefined to source to not throw warnings
59
51
  source={_source?.uri ? _source : undefined}
60
- {...otherProps}
52
+ {...omit(["source"], otherProps)}
61
53
  />
62
54
  );
63
55
  }
@@ -23,17 +23,9 @@ function Image({
23
23
  placeholderImage,
24
24
  entry,
25
25
  withDimensions,
26
- source: sourceProp,
27
26
  ...otherProps
28
27
  }: Props) {
29
- const source = useImageSource({
30
- uri,
31
- entry,
32
- otherProps: {
33
- source: sourceProp,
34
- state: otherProps.state,
35
- },
36
- });
28
+ const source = useImageSource({ uri, entry, otherProps });
37
29
 
38
30
  const updatedSource = source ? withDimensions(source) : { uri: "" };
39
31
 
@@ -1,21 +1,19 @@
1
1
  import * as React from "react";
2
+ import { path } from "ramda";
2
3
 
3
4
  import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
4
5
  import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
5
6
  import { extractAsset } from "./utils";
6
7
 
7
8
  type Return = { uri: string } | null;
8
- type Source = { context?: string; uri?: string } | null | undefined;
9
9
 
10
- const getSourceContext = (source: Source) => source?.context;
11
- const getSourceUri = (source: Source) => source?.uri;
10
+ const getSourceContext = path(["source", "context"]);
11
+ const getSourceUri = path(["source", "uri"]);
12
+ const getState = path(["state"]);
12
13
 
13
- export const useImageSource = ({
14
- uri,
15
- entry,
16
- otherProps: { source, state: uriState },
17
- }): Return => {
18
- const uriContext = getSourceContext(source);
14
+ export const useImageSource = ({ uri, entry, otherProps }): Return => {
15
+ const uriContext = getSourceContext(otherProps);
16
+ const uriState = getState(otherProps);
19
17
 
20
18
  const action = useActions(uriContext);
21
19
 
@@ -40,7 +38,7 @@ export const useImageSource = ({
40
38
  return { uri };
41
39
  }
42
40
 
43
- const uriFromSource = getSourceUri(source);
41
+ const uriFromSource = getSourceUri(otherProps);
44
42
 
45
43
  if (uriFromSource) {
46
44
  return { uri: uriFromSource };
@@ -49,7 +47,7 @@ export const useImageSource = ({
49
47
  return null;
50
48
  };
51
49
 
52
- const getSource = (uri, showDefault, placeholderImage, source) => {
50
+ const getSource = (uri, showDefault, placeholderImage, otherProps) => {
53
51
  const placeholderName = placeholderImage || "";
54
52
 
55
53
  const defaultPath = {
@@ -62,7 +60,7 @@ const getSource = (uri, showDefault, placeholderImage, source) => {
62
60
  return { uri };
63
61
  }
64
62
 
65
- const uriFromSource = getSourceUri(source);
63
+ const uriFromSource = getSourceUri(otherProps);
66
64
 
67
65
  if (uriFromSource) {
68
66
  return { uri: uriFromSource };
@@ -76,9 +74,10 @@ export const useImageSourceWithDefault = ({
76
74
  entry,
77
75
  showDefault,
78
76
  placeholderImage,
79
- otherProps: { state: uriState, source },
77
+ otherProps,
80
78
  }): Return => {
81
- const uriContext = getSourceContext(source);
79
+ const uriContext = getSourceContext(otherProps);
80
+ const uriState = getState(otherProps);
82
81
 
83
82
  const action = useActions(uriContext);
84
83
 
@@ -99,5 +98,5 @@ export const useImageSourceWithDefault = ({
99
98
  return extractAsset(!isTV(), entryStateLocal.asset, uriState);
100
99
  }
101
100
 
102
- return getSource(uri, showDefault, placeholderImage, source);
101
+ return getSource(uri, showDefault, placeholderImage, otherProps);
103
102
  };