@applicaster/zapp-react-native-ui-components 15.0.0-rc.99 → 15.1.0-rc.2

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 (114) hide show
  1. package/Components/BaseFocusable/index.ios.ts +2 -12
  2. package/Components/Cell/FocusableWrapper.tsx +0 -3
  3. package/Components/Cell/TvOSCellComponent.tsx +0 -5
  4. package/Components/Focusable/Focusable.tsx +2 -4
  5. package/Components/Focusable/FocusableTvOS.tsx +1 -18
  6. package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +0 -1
  7. package/Components/FocusableGroup/FocusableTvOS.tsx +1 -30
  8. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +39 -28
  9. package/Components/GeneralContentScreen/__tests__/GeneralContentScreen.test.tsx +104 -0
  10. package/Components/GeneralContentScreen/utils/__tests__/getScreenDataSource.test.ts +19 -0
  11. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
  12. package/Components/GeneralContentScreen/utils/getScreenDataSource.ts +9 -0
  13. package/Components/HandlePlayable/HandlePlayable.tsx +24 -42
  14. package/Components/HandlePlayable/utils.ts +31 -0
  15. package/Components/HookRenderer/HookRenderer.tsx +40 -10
  16. package/Components/HookRenderer/__tests__/HookRenderer.test.tsx +60 -0
  17. package/Components/Layout/TV/LayoutBackground.tsx +2 -5
  18. package/Components/Layout/TV/ScreenContainer.tsx +6 -2
  19. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +5 -0
  20. package/Components/Layout/TV/index.tsx +4 -3
  21. package/Components/Layout/TV/index.web.tsx +4 -3
  22. package/Components/LinkHandler/LinkHandler.tsx +2 -2
  23. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +10 -4
  24. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +1 -5
  25. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +3 -11
  26. package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +1 -9
  27. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +14 -15
  28. package/Components/MasterCell/DefaultComponents/LiveImage/__tests__/prepareEntry.test.ts +352 -0
  29. package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +136 -0
  30. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +34 -16
  31. package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +6 -7
  32. package/Components/MasterCell/DefaultComponents/Text/index.tsx +2 -6
  33. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +2 -6
  34. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +11 -233
  35. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +15 -19
  36. package/Components/Navigator/StackNavigator.tsx +6 -0
  37. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +2 -2
  38. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +18 -17
  39. package/Components/OfflineHandler/__tests__/index.test.tsx +18 -27
  40. package/Components/PlayerContainer/PlayerContainer.tsx +14 -32
  41. package/Components/PreloaderWrapper/__tests__/index.test.tsx +26 -0
  42. package/Components/PreloaderWrapper/index.tsx +15 -0
  43. package/Components/River/ComponentsMap/ComponentsMap.tsx +3 -4
  44. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
  45. package/Components/River/RefreshControl.tsx +9 -3
  46. package/Components/River/RiverItem.tsx +26 -20
  47. package/Components/River/TV/River.tsx +14 -31
  48. package/Components/River/TV/index.tsx +4 -8
  49. package/Components/River/TV/withTVEventHandler.tsx +36 -0
  50. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +0 -1
  51. package/Components/River/__tests__/componentsMap.test.js +0 -38
  52. package/Components/Screen/TV/index.web.tsx +2 -4
  53. package/Components/Screen/__tests__/Screen.test.tsx +43 -65
  54. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +44 -68
  55. package/Components/Screen/hooks.ts +76 -5
  56. package/Components/Screen/index.tsx +10 -3
  57. package/Components/Screen/orientationHandler.ts +3 -3
  58. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +46 -0
  59. package/Components/ScreenFeedLoader/__tests__/ScreenFeedLoader.test.tsx +94 -0
  60. package/Components/ScreenFeedLoader/index.ts +1 -0
  61. package/Components/ScreenResolver/__tests__/screenResolver.test.js +24 -0
  62. package/Components/ScreenResolver/hooks/index.ts +3 -0
  63. package/Components/ScreenResolver/hooks/useGetComponent.ts +15 -0
  64. package/Components/ScreenResolver/hooks/useScreenComponentResolver.tsx +90 -0
  65. package/Components/ScreenResolver/index.tsx +9 -115
  66. package/Components/ScreenResolver/utils/__tests__/getScreenTypeProps.test.ts +45 -0
  67. package/Components/ScreenResolver/utils/getScreenTypeProps.ts +43 -0
  68. package/Components/ScreenResolver/utils/index.ts +1 -0
  69. package/Components/ScreenResolver/withDefaultScreenContext.tsx +16 -0
  70. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +25 -0
  71. package/Components/ScreenResolverFeedProvider/__tests__/ScreenResolverFeedProvider.test.tsx +44 -0
  72. package/Components/ScreenResolverFeedProvider/index.ts +1 -0
  73. package/Components/ScreenRevealManager/ScreenRevealManager.ts +8 -40
  74. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +69 -86
  75. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +4 -1
  76. package/Components/Tabs/TabContent.tsx +4 -7
  77. package/Components/Transitioner/Scene.tsx +9 -15
  78. package/Components/Transitioner/index.js +3 -3
  79. package/Components/VideoLive/LiveImageManager.ts +199 -54
  80. package/Components/VideoLive/PlayerLiveImageComponent.tsx +31 -33
  81. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +2 -17
  82. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +5 -5
  83. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +7 -15
  84. package/Components/VideoModal/utils.ts +9 -12
  85. package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
  86. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +6 -4
  87. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
  88. package/Components/ZappUIComponent/index.tsx +12 -6
  89. package/Components/index.js +1 -1
  90. package/Contexts/ScreenContext/__tests__/index.test.tsx +57 -0
  91. package/Contexts/ScreenContext/index.tsx +64 -26
  92. package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
  93. package/Contexts/ZappPipesContext/ZappPipesContextFactory.tsx +18 -7
  94. package/Decorators/Analytics/index.tsx +5 -6
  95. package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +0 -1
  96. package/Decorators/ConfigurationWrapper/const.ts +0 -1
  97. package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -7
  98. package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +212 -5
  99. package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
  100. package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
  101. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
  102. package/Helpers/DataSourceHelper/index.js +19 -0
  103. package/package.json +5 -5
  104. package/Components/MasterCell/DefaultComponents/Text/utils/__tests__/withAdjustedLineHeight.test.ts +0 -46
  105. package/Components/MasterCell/DefaultComponents/Text/utils/index.ts +0 -21
  106. package/Components/PlayerContainer/ErrorDisplay/ErrorDisplay.tsx +0 -57
  107. package/Components/PlayerContainer/ErrorDisplay/index.ts +0 -9
  108. package/Components/PlayerContainer/useRestrictMobilePlayback.tsx +0 -101
  109. package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +0 -30
  110. package/Components/River/TV/utils/index.ts +0 -4
  111. package/Components/River/TV/withFocusableGroupForContent.tsx +0 -71
  112. package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +0 -80
  113. package/Helpers/DataSourceHelper/index.ts +0 -19
  114. /package/Components/HookRenderer/{index.tsx → index.ts} +0 -0
@@ -22,7 +22,6 @@ type Props = {
22
22
  onFocus?: FocusManager.FocusEventCB;
23
23
  onBlur?: FocusManager.FocusEventCB;
24
24
  selected?: boolean;
25
- skipFocusManagerRegistration?: boolean;
26
25
  };
27
26
 
28
27
  export class BaseFocusable<
@@ -62,14 +61,10 @@ export class BaseFocusable<
62
61
  }
63
62
 
64
63
  componentDidMount() {
65
- const { id, skipFocusManagerRegistration } = this.props;
64
+ const { id } = this.props;
66
65
  const component = this;
67
66
  this.node = this.ref.current;
68
67
 
69
- if (skipFocusManagerRegistration) {
70
- return;
71
- }
72
-
73
68
  focusManager.register({
74
69
  id,
75
70
  component: component,
@@ -123,12 +118,7 @@ export class BaseFocusable<
123
118
 
124
119
  componentWillUnmount() {
125
120
  this._isMounted = false;
126
- const { id, skipFocusManagerRegistration } = this.props;
127
-
128
- if (skipFocusManagerRegistration) {
129
- return;
130
- }
131
-
121
+ const { id } = this.props;
132
122
  focusManager.unregister(id, { group: this.isGroup || false });
133
123
  }
134
124
 
@@ -10,7 +10,6 @@ type Props = {
10
10
  children: (focused: boolean) => React.ReactNode;
11
11
  onFocus: (arg1: any, index?: number) => void;
12
12
  onBlur: Callback;
13
- skipFocusManagerRegistration?: boolean;
14
13
  };
15
14
 
16
15
  export const FocusableWrapper = ({
@@ -21,7 +20,6 @@ export const FocusableWrapper = ({
21
20
  applyWrapper,
22
21
  onFocus,
23
22
  onBlur,
24
- skipFocusManagerRegistration,
25
23
  }: Props) => {
26
24
  if (applyWrapper) {
27
25
  return (
@@ -36,7 +34,6 @@ export const FocusableWrapper = ({
36
34
  // @ts-ignore
37
35
  offsetUpdater={noop}
38
36
  isFocusable
39
- skipFocusManagerRegistration={skipFocusManagerRegistration}
40
37
  >
41
38
  {(focused) => children(focused)}
42
39
  </Focusable>
@@ -80,7 +80,6 @@ type Props = {
80
80
  componentsMapOffset: number;
81
81
  applyFocusableWrapper: boolean;
82
82
  hasFocusableInside: boolean;
83
- skipFocusManagerRegistration?: boolean;
84
83
  };
85
84
 
86
85
  type State = {
@@ -267,7 +266,6 @@ class TvOSCell extends React.Component<Props, State> {
267
266
  behavior,
268
267
  applyFocusableWrapper,
269
268
  hasFocusableInside,
270
- skipFocusManagerRegistration,
271
269
  } = this.props;
272
270
 
273
271
  const { id } = item;
@@ -293,7 +291,6 @@ class TvOSCell extends React.Component<Props, State> {
293
291
  onFocus={handleFocus}
294
292
  onBlur={onBlur || this.onBlur}
295
293
  applyWrapper={applyFocusableWrapper}
296
- skipFocusManagerRegistration={skipFocusManagerRegistration}
297
294
  >
298
295
  {(focused) => (
299
296
  <CellWithFocusable
@@ -308,7 +305,6 @@ class TvOSCell extends React.Component<Props, State> {
308
305
  focused={focused || this.props.focused}
309
306
  behavior={behavior}
310
307
  isFocusable={isFocusable}
311
- skipFocusManagerRegistration={skipFocusManagerRegistration}
312
308
  />
313
309
  )}
314
310
  </FocusableWrapper>
@@ -331,7 +327,6 @@ class TvOSCell extends React.Component<Props, State> {
331
327
  offsetUpdater={offsetUpdater}
332
328
  style={baseCellStyles}
333
329
  isFocusable={isFocusable}
334
- skipFocusManagerRegistration={skipFocusManagerRegistration}
335
330
  >
336
331
  {(focused) => (
337
332
  <FocusableCell
@@ -8,8 +8,6 @@ import { withFocusableContext } from "../../Contexts/FocusableGroupContext/withF
8
8
  import { StyleSheet, ViewStyle } from "react-native";
9
9
  import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager";
10
10
 
11
- import { isSearchInputId } from "@applicaster/zapp-react-native-utils/searchUtils";
12
-
13
11
  type Props = {
14
12
  initialFocus?: boolean;
15
13
  id: string;
@@ -108,7 +106,7 @@ class Focusable extends BaseFocusable<Props> {
108
106
  onMouseEnter() {
109
107
  const { id } = this.props;
110
108
 
111
- if (!isSearchInputId(id)) {
109
+ if (id !== "search_input_group_id") {
112
110
  this.mouse = true;
113
111
  this.props?.handleFocus?.({ mouse: true });
114
112
 
@@ -122,7 +120,7 @@ class Focusable extends BaseFocusable<Props> {
122
120
  onMouseLeave() {
123
121
  const { id } = this.props;
124
122
 
125
- if (!isSearchInputId(id)) {
123
+ if (id !== "search_input_group_id") {
126
124
  this.mouse = false;
127
125
  this.blur(null);
128
126
  }
@@ -10,12 +10,8 @@ import {
10
10
  forceFocusableFocus,
11
11
  } from "@applicaster/zapp-react-native-utils/appUtils/focusManager/index.ios";
12
12
  import { findNodeHandle, ViewStyle } from "react-native";
13
- import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
14
13
 
15
- import {
16
- emitFocused,
17
- emitNativeRegistered,
18
- } from "@applicaster/zapp-react-native-utils/appUtils/focusManagerAux/utils/utils.ios";
14
+ function noop() {}
19
15
 
20
16
  type Props = {
21
17
  id: string;
@@ -43,7 +39,6 @@ type Props = {
43
39
  hasReceivedFocus: () => void;
44
40
  offsetUpdater: (arg1: string, arg2: number) => number;
45
41
  style: ViewStyle;
46
- skipFocusManagerRegistration?: boolean;
47
42
  };
48
43
 
49
44
  export class Focusable extends BaseFocusable<Props> {
@@ -58,7 +53,6 @@ export class Focusable extends BaseFocusable<Props> {
58
53
  this.nextFocusableReactTags = {};
59
54
  this.preferredFocus = this.preferredFocus.bind(this);
60
55
  this.measureView = this.measureView.bind(this);
61
- this.onRegistered = this.onRegistered.bind(this);
62
56
  }
63
57
 
64
58
  /**
@@ -90,9 +84,6 @@ export class Focusable extends BaseFocusable<Props> {
90
84
  });
91
85
  }
92
86
 
93
- const id: string = nativeEvent.itemID;
94
- emitFocused(id);
95
-
96
87
  onFocus(nativeEvent);
97
88
  }
98
89
 
@@ -178,13 +169,6 @@ export class Focusable extends BaseFocusable<Props> {
178
169
  });
179
170
  }
180
171
 
181
- onRegistered({ nativeEvent }) {
182
- const groupId = nativeEvent?.groupId;
183
- const id = nativeEvent?.itemId;
184
-
185
- emitNativeRegistered({ id, groupId, isGroup: false });
186
- }
187
-
188
172
  render() {
189
173
  const {
190
174
  children,
@@ -219,7 +203,6 @@ export class Focusable extends BaseFocusable<Props> {
219
203
  focusable={isFocusable}
220
204
  {...this.nextFocusableReactTags}
221
205
  {...otherProps}
222
- onRegistered={this.onRegistered}
223
206
  >
224
207
  {typeof children === "function" ? children(focused) : children}
225
208
  </FocusableItemNative>
@@ -5,7 +5,6 @@ exports[`FocusableTvOS should render correctly 1`] = `
5
5
  groupId={null}
6
6
  itemId={null}
7
7
  onLayout={[Function]}
8
- onRegistered={[Function]}
9
8
  onViewBlur={[Function]}
10
9
  onViewFocus={[Function]}
11
10
  onViewPress={[Function]}
@@ -2,10 +2,6 @@ import * as React from "react";
2
2
  import { FocusableGroupNative } from "@applicaster/zapp-react-native-ui-components/Components/NativeFocusables";
3
3
  import { BaseFocusable } from "@applicaster/zapp-react-native-ui-components/Components/BaseFocusable";
4
4
  import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
5
- import { LayoutContext } from "@applicaster/zapp-react-native-tvos-app/Context/LayoutContext";
6
- import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useRoute";
7
- import { isScreenPlayable } from "@applicaster/zapp-react-native-utils/navigationUtils/itemTypes";
8
- import { emitNativeRegistered } from "@applicaster/zapp-react-native-utils/appUtils/focusManagerAux/utils/utils.ios";
9
5
 
10
6
  const { log_verbose } = createLogger({
11
7
  subsystem: "General",
@@ -37,16 +33,9 @@ type Props = {
37
33
  screenData: { screenId: string; parentScreenId: string };
38
34
  };
39
35
 
40
- class FocusableGroupComponent extends BaseFocusable<Props> {
36
+ export class FocusableGroup extends BaseFocusable<Props> {
41
37
  public readonly isGroup: boolean = true;
42
38
 
43
- onRegistered = ({ nativeEvent }) => {
44
- const groupId = nativeEvent?.groupId;
45
- const id = nativeEvent?.itemId;
46
-
47
- emitNativeRegistered({ id, groupId, isGroup: true });
48
- };
49
-
50
39
  render() {
51
40
  const {
52
41
  children,
@@ -79,27 +68,9 @@ class FocusableGroupComponent extends BaseFocusable<Props> {
79
68
  onGroupBlur={onGroupBlur}
80
69
  style={style}
81
70
  {...otherProps}
82
- onRegistered={this.onRegistered}
83
71
  >
84
72
  {children}
85
73
  </FocusableGroupNative>
86
74
  );
87
75
  }
88
76
  }
89
-
90
- export const withFocusDisabled = (Component) => {
91
- return function WithFocusDisabled(props) {
92
- // @ts-ignore
93
- const { screenFocusBlocked } = React.useContext(LayoutContext.ReactContext);
94
-
95
- const { pathname } = useRoute();
96
-
97
- const isPlayerPresented = isScreenPlayable(pathname);
98
-
99
- const blockScreenFocus = isPlayerPresented === false && screenFocusBlocked;
100
-
101
- return <Component {...props} isFocusDisabled={blockScreenFocus} />;
102
- };
103
- };
104
-
105
- export const FocusableGroup = withFocusDisabled(FocusableGroupComponent);
@@ -12,12 +12,26 @@ import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
12
12
  import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
13
13
  import { ScreenTrackedViewPositionsContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenTrackedViewPositionsContext";
14
14
  import { useEventAlerts } from "./utils/useEventAlerts";
15
-
16
- const { log_info } = createLogger({
15
+ import {
16
+ selectRiverById,
17
+ useAppSelector,
18
+ } from "@applicaster/zapp-react-native-redux";
19
+ import { getScreenDataSource } from "./utils/getScreenDataSource";
20
+ import { ScreenResolverFeedProvider } from "../ScreenResolverFeedProvider/ScreenResolverFeedProvider";
21
+
22
+ const { log_debug } = createLogger({
17
23
  category: "ScreenContainer",
18
24
  subsystem: "General",
19
25
  });
20
26
 
27
+ /** Provides screen-feed from general-screen configuration (if defined) */
28
+ const useFeedData = (id) => {
29
+ const river = useAppSelector((state) => selectRiverById(state, id));
30
+ const feedData = getScreenDataSource(river);
31
+
32
+ return feedData;
33
+ };
34
+
21
35
  export const GeneralContentScreen = ({
22
36
  feed,
23
37
  screenId,
@@ -54,20 +68,15 @@ export const GeneralContentScreen = ({
54
68
  useEffect(() => {
55
69
  if (!riverActionProvidersReady) {
56
70
  if (actionsInitialStateSetters.length > 0) {
57
- log_info(
58
- "ScreenContainer: starting to check river action providers to initialize",
59
- { actionsInitialStateSetters }
60
- );
61
-
62
71
  allSettled(actionsInitialStateSetters).finally(() => {
63
- log_info(
72
+ log_debug(
64
73
  "ScreenContainer: action provider ready, completed. Starting to present screen"
65
74
  );
66
75
 
67
76
  setRiverActionProvidersReady(true);
68
77
  });
69
78
  } else {
70
- log_info(
79
+ log_debug(
71
80
  "ScreenContainer: no action provider to check, completed. Starting to present screen"
72
81
  );
73
82
 
@@ -108,24 +117,26 @@ export const GeneralContentScreen = ({
108
117
  if (!isReady || isNilOrEmpty(components || uiComponents)) return null;
109
118
 
110
119
  return (
111
- <ScreenTrackedViewPositionsContext.Provider>
112
- <CellTapContext.Provider value={contextValue}>
113
- <ComponentsMap
114
- feed={feed}
115
- riverId={screenId}
116
- groupId={groupId || `general-content-screen-${screenId}`}
117
- riverComponents={components || uiComponents}
118
- scrollViewExtraProps={scrollViewExtraProps}
119
- getStaticComponentFeed={getStaticComponentFeed}
120
- extraAnchorPointYOffset={extraAnchorPointYOffset}
121
- isScreenWrappedInContainer={isScreenWrappedInContainer}
122
- parentFocus={parentFocus}
123
- focused={focused}
124
- containerHeight={containerHeight}
125
- preferredFocus={preferredFocus}
126
- {...componentsMapExtraProps}
127
- />
128
- </CellTapContext.Provider>
129
- </ScreenTrackedViewPositionsContext.Provider>
120
+ <ScreenResolverFeedProvider id={screenId} useFeedData={useFeedData}>
121
+ <ScreenTrackedViewPositionsContext.Provider>
122
+ <CellTapContext.Provider value={contextValue}>
123
+ <ComponentsMap
124
+ feed={feed}
125
+ riverId={screenId}
126
+ groupId={groupId || `general-content-screen-${screenId}`}
127
+ riverComponents={components || uiComponents}
128
+ scrollViewExtraProps={scrollViewExtraProps}
129
+ getStaticComponentFeed={getStaticComponentFeed}
130
+ extraAnchorPointYOffset={extraAnchorPointYOffset}
131
+ isScreenWrappedInContainer={isScreenWrappedInContainer}
132
+ parentFocus={parentFocus}
133
+ focused={focused}
134
+ containerHeight={containerHeight}
135
+ preferredFocus={preferredFocus}
136
+ {...componentsMapExtraProps}
137
+ />
138
+ </CellTapContext.Provider>
139
+ </ScreenTrackedViewPositionsContext.Provider>
140
+ </ScreenResolverFeedProvider>
130
141
  );
131
142
  };
@@ -0,0 +1,104 @@
1
+ import React from "react";
2
+ import { render } from "@testing-library/react-native";
3
+ import { GeneralContentScreen } from "../GeneralContentScreen";
4
+
5
+ const mockUseAppSelector = jest.fn();
6
+ const mockSelectRiverById = jest.fn();
7
+ const mockProviderSpy = jest.fn();
8
+
9
+ jest.mock("../../River/ComponentsMap", () => ({
10
+ ComponentsMap: () => {
11
+ const React = require("react");
12
+ const { View } = require("react-native");
13
+
14
+ return <View testID="components-map" />;
15
+ },
16
+ }));
17
+
18
+ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/actions", () => ({
19
+ useActions: jest.fn(() => jest.fn()),
20
+ }));
21
+
22
+ jest.mock("../utils", () => ({
23
+ logger: { warn: jest.fn() },
24
+ whenMatchingType: jest.fn((_type, value) => value),
25
+ }));
26
+
27
+ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/layout", () => ({
28
+ useLayoutVersion: jest.fn(() => false),
29
+ }));
30
+
31
+ jest.mock(
32
+ "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenData",
33
+ () => ({
34
+ useScreenData: jest.fn(() => ({
35
+ ui_components: [{ id: "ui-component" }],
36
+ general: {},
37
+ })),
38
+ })
39
+ );
40
+
41
+ jest.mock("../utils/useCurationAPI", () => ({
42
+ useCurationAPI: jest.fn(() => [{ id: "curation-component" }]),
43
+ }));
44
+
45
+ jest.mock("@applicaster/quick-brick-core/App/ActionSetters", () => ({
46
+ useRiverInitialState: jest.fn(() => []),
47
+ }));
48
+
49
+ jest.mock("../utils/useEventAlerts", () => ({
50
+ useEventAlerts: jest.fn(),
51
+ }));
52
+
53
+ jest.mock("@applicaster/zapp-react-native-redux", () => ({
54
+ useAppSelector: (...args) => mockUseAppSelector(...args),
55
+ selectRiverById: (...args) => mockSelectRiverById(...args),
56
+ }));
57
+
58
+ jest.mock("../utils/getScreenDataSource", () => ({
59
+ getScreenDataSource: jest.fn(() => ({
60
+ source: "https://feed",
61
+ mapping: {},
62
+ })),
63
+ }));
64
+
65
+ jest.mock(
66
+ "../../ScreenResolverFeedProvider/ScreenResolverFeedProvider",
67
+ () => ({
68
+ ScreenResolverFeedProvider: ({ id, useFeedData, children }) => {
69
+ const React = require("react");
70
+ const { View } = require("react-native");
71
+
72
+ mockProviderSpy(id, useFeedData);
73
+ useFeedData(id);
74
+
75
+ return <View testID="screen-resolver-feed-provider">{children}</View>;
76
+ },
77
+ })
78
+ );
79
+
80
+ describe("GeneralContentScreen", () => {
81
+ beforeEach(() => {
82
+ jest.clearAllMocks();
83
+ mockUseAppSelector.mockImplementation((selector) => selector({}));
84
+ mockSelectRiverById.mockReturnValue({ id: "screen-1" });
85
+ });
86
+
87
+ it("wraps content with ScreenResolverFeedProvider and renders ComponentsMap", () => {
88
+ const { getByTestId } = render(
89
+ <GeneralContentScreen
90
+ screenId="screen-1"
91
+ feed={null}
92
+ components={[{ id: "component-1" }]}
93
+ />
94
+ );
95
+
96
+ expect(getByTestId("screen-resolver-feed-provider")).toBeDefined();
97
+ expect(getByTestId("components-map")).toBeDefined();
98
+
99
+ expect(mockProviderSpy).toHaveBeenCalledWith(
100
+ "screen-1",
101
+ expect.any(Function)
102
+ );
103
+ });
104
+ });
@@ -0,0 +1,19 @@
1
+ import { getScreenDataSource } from "../getScreenDataSource";
2
+
3
+ describe("getScreenDataSource", () => {
4
+ it("returns screen_feed data when present", () => {
5
+ const result = getScreenDataSource({
6
+ data: {
7
+ screen_feed: {
8
+ source: "https://feed",
9
+ },
10
+ },
11
+ });
12
+
13
+ expect(result).toEqual({ source: "https://feed" });
14
+ });
15
+
16
+ it("returns undefined when screen_feed is missing", () => {
17
+ expect(getScreenDataSource({ data: {} })).toBeUndefined();
18
+ });
19
+ });
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import { renderHook } from "@testing-library/react-native";
2
+ import { renderHook } from "@testing-library/react-hooks";
3
3
 
4
4
  import {
5
5
  getTransformedPreset,
@@ -0,0 +1,9 @@
1
+ import { get } from "@applicaster/zapp-react-native-utils/utils";
2
+
3
+ const lookupPath = ["data", "screen_feed"];
4
+
5
+ export const getScreenDataSource = (
6
+ screenData: any
7
+ ): Option<ZappDataSource> => {
8
+ return get(screenData, lookupPath) as ZappDataSource | undefined;
9
+ };
@@ -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
  };