@applicaster/zapp-react-native-ui-components 15.0.0-alpha.2413435535 → 15.0.0-alpha.2502322043

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 (208) hide show
  1. package/Components/BaseFocusable/index.ios.ts +12 -2
  2. package/Components/Cell/Cell.tsx +6 -0
  3. package/Components/Cell/CellWithFocusable.tsx +9 -0
  4. package/Components/Cell/FocusableWrapper.tsx +3 -0
  5. package/Components/Cell/TvOSCellComponent.tsx +17 -7
  6. package/Components/Focusable/Focusable.tsx +4 -2
  7. package/Components/Focusable/FocusableTvOS.tsx +18 -1
  8. package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +1 -0
  9. package/Components/FocusableGroup/FocusableTvOS.tsx +32 -1
  10. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +39 -28
  11. package/Components/GeneralContentScreen/__tests__/GeneralContentScreen.test.tsx +104 -0
  12. package/Components/GeneralContentScreen/utils/__tests__/getScreenDataSource.test.ts +19 -0
  13. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
  14. package/Components/GeneralContentScreen/utils/getScreenDataSource.ts +9 -0
  15. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +22 -6
  16. package/Components/HandlePlayable/HandlePlayable.tsx +26 -36
  17. package/Components/HandlePlayable/utils.ts +31 -0
  18. package/Components/HookRenderer/HookRenderer.tsx +40 -10
  19. package/Components/HookRenderer/__tests__/HookRenderer.test.tsx +60 -0
  20. package/Components/Layout/TV/LayoutBackground.tsx +5 -2
  21. package/Components/Layout/TV/NavBarContainer.tsx +1 -10
  22. package/Components/Layout/TV/ScreenContainer.tsx +2 -6
  23. package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +7 -12
  24. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +7 -12
  25. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +5 -0
  26. package/Components/Layout/TV/index.tsx +3 -4
  27. package/Components/Layout/TV/index.web.tsx +3 -4
  28. package/Components/LinkHandler/LinkHandler.tsx +2 -2
  29. package/Components/MasterCell/CONFIG_BUILDER_TO_REACT_COMPONENT.md +144 -0
  30. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/model.test.ts +80 -0
  31. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +187 -0
  32. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/selectors.test.ts +45 -0
  33. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/style.test.ts +49 -0
  34. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +165 -0
  35. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +405 -0
  36. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/index.ts +1 -0
  37. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/model.ts +47 -0
  38. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +170 -0
  39. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/selectors.ts +26 -0
  40. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/style.ts +29 -0
  41. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/types.ts +37 -0
  42. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +4 -4
  43. package/Components/MasterCell/DefaultComponents/Button.tsx +0 -15
  44. package/Components/MasterCell/DefaultComponents/ButtonContainerView/components/HorizontalSeparator.tsx +8 -0
  45. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tsx +15 -0
  46. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tv.android.tsx +58 -0
  47. package/Components/MasterCell/DefaultComponents/{tv/ButtonContainerView/index.tsx → ButtonContainerView/index.tv.tsx} +3 -11
  48. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.web.ts +1 -0
  49. package/Components/MasterCell/DefaultComponents/ButtonContainerView/types.ts +40 -0
  50. package/Components/MasterCell/DefaultComponents/DataProvider/index.tsx +163 -0
  51. package/Components/MasterCell/DefaultComponents/FocusableView/index.android.tsx +2 -23
  52. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -22
  53. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +8 -2
  54. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +11 -3
  55. package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +9 -1
  56. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +15 -14
  57. package/Components/MasterCell/DefaultComponents/LiveImage/__tests__/prepareEntry.test.ts +352 -0
  58. package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +136 -0
  59. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +36 -21
  60. package/Components/MasterCell/DefaultComponents/PressableView.tsx +34 -0
  61. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +40 -39
  62. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/Image.test.tsx +95 -0
  63. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +86 -0
  64. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/index.test.ts +141 -0
  65. package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +7 -6
  66. package/Components/MasterCell/DefaultComponents/SecondaryImage/index.ts +1 -1
  67. package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +11 -0
  68. package/Components/MasterCell/DefaultComponents/Text/index.tsx +2 -6
  69. package/Components/MasterCell/DefaultComponents/__tests__/DataProvider.test.tsx +141 -0
  70. package/Components/MasterCell/DefaultComponents/index.ts +9 -3
  71. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +135 -0
  72. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Asset.ts +33 -0
  73. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +22 -0
  74. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +125 -0
  75. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +16 -0
  76. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabel.ts +67 -0
  77. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +37 -0
  78. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +393 -0
  79. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/builders.test.ts +141 -0
  80. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +343 -0
  81. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/helpers.ts +105 -0
  82. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +122 -0
  83. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +118 -0
  84. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +238 -0
  85. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Asset.ts +4 -18
  86. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Button.ts +24 -73
  87. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TextLabelsContainer.ts +37 -18
  88. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TvActionButton.tsx +27 -0
  89. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/index.test.ts +89 -0
  90. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/renderedTree.test.tsx +231 -0
  91. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +47 -48
  92. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +115 -29
  93. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +101 -144
  94. package/Components/MasterCell/MappingFunctions/index.js +3 -2
  95. package/Components/MasterCell/README.md +4 -0
  96. package/Components/MasterCell/__tests__/__snapshots__/dataAdapter.test.js.snap +24 -0
  97. package/Components/MasterCell/__tests__/configInflater.test.js +1 -0
  98. package/Components/MasterCell/__tests__/elementMapper.test.js +46 -0
  99. package/Components/MasterCell/dataAdapter.ts +4 -1
  100. package/Components/MasterCell/elementMapper.tsx +52 -7
  101. package/Components/MasterCell/hoc/__tests__/withAsyncRender.test.tsx +219 -0
  102. package/Components/MasterCell/hoc/withAsyncRender.tsx +9 -7
  103. package/Components/MasterCell/utils/__tests__/cloneChildrenWithIds.test.tsx +43 -0
  104. package/Components/MasterCell/utils/__tests__/resolveColor.test.js +82 -3
  105. package/Components/MasterCell/utils/__tests__/useFilterChildren.test.tsx +80 -0
  106. package/Components/MasterCell/utils/index.ts +146 -46
  107. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +102 -87
  108. package/Components/MeasurmentsPortal/__tests__/MeasurementsPortal.test.tsx +355 -0
  109. package/Components/Navigator/StackNavigator.tsx +6 -0
  110. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +17 -9
  111. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +16 -8
  112. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +2 -2
  113. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +17 -18
  114. package/Components/OfflineHandler/NotificationView/utils.ts +34 -0
  115. package/Components/OfflineHandler/__tests__/index.test.tsx +27 -18
  116. package/Components/PlayerContainer/PlayerContainer.tsx +44 -50
  117. package/Components/PreloaderWrapper/__tests__/index.test.tsx +26 -0
  118. package/Components/PreloaderWrapper/index.tsx +15 -0
  119. package/Components/River/ComponentsMap/ComponentsMap.tsx +18 -16
  120. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
  121. package/Components/River/RefreshControl.tsx +19 -82
  122. package/Components/River/River.tsx +9 -82
  123. package/Components/River/RiverItem.tsx +26 -20
  124. package/Components/River/TV/River.tsx +31 -14
  125. package/Components/River/TV/index.tsx +8 -4
  126. package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +30 -0
  127. package/Components/River/TV/utils/index.ts +4 -0
  128. package/Components/River/TV/withFocusableGroupForContent.tsx +71 -0
  129. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -0
  130. package/Components/River/__tests__/componentsMap.test.js +38 -0
  131. package/Components/River/hooks/__tests__/usePullToRefresh.test.ts +132 -0
  132. package/Components/River/hooks/index.ts +1 -0
  133. package/Components/River/hooks/usePullToRefresh.ts +51 -0
  134. package/Components/Screen/TV/index.web.tsx +4 -2
  135. package/Components/Screen/__tests__/Screen.test.tsx +66 -42
  136. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +68 -44
  137. package/Components/Screen/hooks.ts +75 -6
  138. package/Components/Screen/index.tsx +9 -4
  139. package/Components/Screen/navigationHandler.ts +49 -24
  140. package/Components/Screen/orientationHandler.ts +10 -13
  141. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +46 -0
  142. package/Components/ScreenFeedLoader/__tests__/ScreenFeedLoader.test.tsx +94 -0
  143. package/Components/ScreenFeedLoader/index.ts +1 -0
  144. package/Components/ScreenResolver/__tests__/screenResolver.test.js +24 -0
  145. package/Components/ScreenResolver/hooks/index.ts +3 -0
  146. package/Components/ScreenResolver/hooks/useGetComponent.ts +15 -0
  147. package/Components/ScreenResolver/hooks/useScreenComponentResolver.tsx +90 -0
  148. package/Components/ScreenResolver/index.tsx +15 -111
  149. package/Components/ScreenResolver/utils/__tests__/getScreenTypeProps.test.ts +45 -0
  150. package/Components/ScreenResolver/utils/getScreenTypeProps.ts +43 -0
  151. package/Components/ScreenResolver/utils/index.ts +1 -0
  152. package/Components/ScreenResolver/withDefaultScreenContext.tsx +16 -0
  153. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +25 -0
  154. package/Components/ScreenResolverFeedProvider/__tests__/ScreenResolverFeedProvider.test.tsx +44 -0
  155. package/Components/ScreenResolverFeedProvider/index.ts +1 -0
  156. package/Components/ScreenRevealManager/ScreenRevealManager.ts +40 -8
  157. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +86 -69
  158. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +4 -1
  159. package/Components/Tabs/TabContent.tsx +7 -4
  160. package/Components/TopCutoffOverlay/__tests__/TopCutoffOverlay.test.tsx +201 -0
  161. package/Components/TopCutoffOverlay/hooks/__tests__/useMarginTop.test.ts +130 -0
  162. package/Components/TopCutoffOverlay/hooks/index.ts +1 -0
  163. package/Components/TopCutoffOverlay/hooks/useMarginTop.ts +59 -0
  164. package/Components/TopCutoffOverlay/index.tsx +55 -0
  165. package/Components/Transitioner/Scene.tsx +10 -3
  166. package/Components/Transitioner/index.js +3 -3
  167. package/Components/VideoLive/LiveImageManager.ts +199 -54
  168. package/Components/VideoLive/PlayerLiveImageComponent.tsx +31 -33
  169. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +2 -17
  170. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +13 -9
  171. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +15 -7
  172. package/Components/VideoModal/utils.ts +12 -9
  173. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +0 -2
  174. package/Components/Viewport/ViewportAware/index.tsx +16 -7
  175. package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
  176. package/Components/ZappUIComponent/index.tsx +12 -6
  177. package/Components/default-cell-renderer/viewTrees/mobile/index.ts +0 -3
  178. package/Components/index.js +1 -1
  179. package/Contexts/ScreenContext/__tests__/index.test.tsx +57 -0
  180. package/Contexts/ScreenContext/index.tsx +71 -19
  181. package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
  182. package/Contexts/ZappHookModalContext/index.tsx +37 -61
  183. package/Contexts/ZappPipesContext/ZappPipesContextFactory.tsx +18 -7
  184. package/Contexts/index.ts +0 -2
  185. package/Decorators/Analytics/index.tsx +6 -5
  186. package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +1 -0
  187. package/Decorators/ConfigurationWrapper/const.ts +1 -0
  188. package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -7
  189. package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +212 -5
  190. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +39 -21
  191. package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
  192. package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
  193. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
  194. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +18 -7
  195. package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +80 -0
  196. package/Helpers/DataSourceHelper/index.ts +19 -0
  197. package/events/index.ts +3 -0
  198. package/events/scrollEndReached.ts +15 -0
  199. package/package.json +6 -5
  200. package/Components/MasterCell/DefaultComponents/Text/utils/__tests__/withAdjustedLineHeight.test.ts +0 -46
  201. package/Components/MasterCell/DefaultComponents/Text/utils/index.ts +0 -21
  202. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +0 -135
  203. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/types.ts +0 -25
  204. package/Components/PlayerContainer/ErrorDisplay/ErrorDisplay.tsx +0 -57
  205. package/Components/PlayerContainer/ErrorDisplay/index.ts +0 -9
  206. package/Components/River/TV/withTVEventHandler.tsx +0 -27
  207. package/Helpers/DataSourceHelper/index.js +0 -19
  208. /package/Components/HookRenderer/{index.tsx → index.ts} +0 -0
@@ -22,6 +22,7 @@ type Props = {
22
22
  onFocus?: FocusManager.FocusEventCB;
23
23
  onBlur?: FocusManager.FocusEventCB;
24
24
  selected?: boolean;
25
+ skipFocusManagerRegistration?: boolean;
25
26
  };
26
27
 
27
28
  export class BaseFocusable<
@@ -61,10 +62,14 @@ export class BaseFocusable<
61
62
  }
62
63
 
63
64
  componentDidMount() {
64
- const { id } = this.props;
65
+ const { id, skipFocusManagerRegistration } = this.props;
65
66
  const component = this;
66
67
  this.node = this.ref.current;
67
68
 
69
+ if (skipFocusManagerRegistration) {
70
+ return;
71
+ }
72
+
68
73
  focusManager.register({
69
74
  id,
70
75
  component: component,
@@ -118,7 +123,12 @@ export class BaseFocusable<
118
123
 
119
124
  componentWillUnmount() {
120
125
  this._isMounted = false;
121
- const { id } = this.props;
126
+ const { id, skipFocusManagerRegistration } = this.props;
127
+
128
+ if (skipFocusManagerRegistration) {
129
+ return;
130
+ }
131
+
122
132
  focusManager.unregister(id, { group: this.isGroup || false });
123
133
  }
124
134
 
@@ -26,11 +26,15 @@ type Props = {
26
26
  componentAnchorPointY: number;
27
27
  headerOffset?: number;
28
28
  extraAnchorPointYOffset?: number;
29
+ componentPaddingTop?: number;
29
30
  }) => void;
30
31
  offsetUpdater: (arg1: string, arg2: number, arg3: number) => number;
31
32
  componentId: string;
32
33
  component: {
33
34
  id: string;
35
+ styles?: {
36
+ component_padding_top?: number;
37
+ };
34
38
  };
35
39
  selected?: boolean;
36
40
  CellRenderer: React.FunctionComponent<any> & {
@@ -178,6 +182,8 @@ export class CellComponent extends React.Component<Props, State> {
178
182
  componentAnchorPointY,
179
183
  headerOffset,
180
184
  extraAnchorPointYOffset,
185
+ componentPaddingTop:
186
+ this.props?.component?.styles?.component_padding_top,
181
187
  });
182
188
  }
183
189
  }
@@ -2,6 +2,7 @@ import * as React from "react";
2
2
 
3
3
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
4
4
  import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
5
+ import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
5
6
 
6
7
  import { useCellState } from "../MasterCell/utils";
7
8
  import { FocusableGroup } from "../FocusableGroup";
@@ -26,6 +27,13 @@ type Props = {
26
27
 
27
28
  const addPrefix = (id: string) => `focusable-cell-wrapper-${id}`;
28
29
 
30
+ const wrapperStyles = {
31
+ flex: platformSelect({
32
+ tvos: 1,
33
+ default: undefined,
34
+ }),
35
+ };
36
+
29
37
  export function CellWithFocusable(props: Props) {
30
38
  const {
31
39
  index,
@@ -94,6 +102,7 @@ export function CellWithFocusable(props: Props) {
94
102
  onFocus={onGroupFocus}
95
103
  onBlur={onGroupBlur}
96
104
  skipFocusManagerRegistration={skipFocusManagerRegistration}
105
+ style={wrapperStyles}
97
106
  >
98
107
  <CellWrapper style={styles.cellWrapper}>
99
108
  <CellRenderer
@@ -10,6 +10,7 @@ type Props = {
10
10
  children: (focused: boolean) => React.ReactNode;
11
11
  onFocus: (arg1: any, index?: number) => void;
12
12
  onBlur: Callback;
13
+ skipFocusManagerRegistration?: boolean;
13
14
  };
14
15
 
15
16
  export const FocusableWrapper = ({
@@ -20,6 +21,7 @@ export const FocusableWrapper = ({
20
21
  applyWrapper,
21
22
  onFocus,
22
23
  onBlur,
24
+ skipFocusManagerRegistration,
23
25
  }: Props) => {
24
26
  if (applyWrapper) {
25
27
  return (
@@ -34,6 +36,7 @@ export const FocusableWrapper = ({
34
36
  // @ts-ignore
35
37
  offsetUpdater={noop}
36
38
  isFocusable
39
+ skipFocusManagerRegistration={skipFocusManagerRegistration}
37
40
  >
38
41
  {(focused) => children(focused)}
39
42
  </Focusable>
@@ -40,6 +40,7 @@ type Props = {
40
40
  component_type: string;
41
41
  styles?: {
42
42
  component_margin_top?: number;
43
+ component_padding_top?: number;
43
44
  };
44
45
  };
45
46
  selected: boolean;
@@ -79,6 +80,7 @@ type Props = {
79
80
  componentsMapOffset: number;
80
81
  applyFocusableWrapper: boolean;
81
82
  hasFocusableInside: boolean;
83
+ skipFocusManagerRegistration?: boolean;
82
84
  };
83
85
 
84
86
  type State = {
@@ -193,7 +195,6 @@ class TvOSCell extends React.Component<Props, State> {
193
195
  groupId,
194
196
  component,
195
197
  index,
196
- componentsMapOffset,
197
198
  } = this.props;
198
199
 
199
200
  this.setScreenLayout(componentAnchorPointY, screenLayout);
@@ -205,19 +206,24 @@ class TvOSCell extends React.Component<Props, State> {
205
206
  ) {
206
207
  const { headerOffset } = getHeaderOffset();
207
208
 
208
- const extraAnchorPointYOffset =
209
- screenLayout?.extraAnchorPointYOffset || 0;
209
+ const extraAnchorPointYOffset = toNumberWithDefaultZero(
210
+ screenLayout?.extraAnchorPointYOffset
211
+ );
210
212
 
211
213
  const componentMarginTop = toNumberWithDefaultZero(
212
214
  component?.styles?.component_margin_top
213
215
  );
214
216
 
217
+ const componentPaddingTop = toNumberWithDefaultZero(
218
+ component?.styles?.component_padding_top
219
+ );
220
+
215
221
  const totalOffset =
216
222
  headerOffset +
217
- (componentAnchorPointY || 0) +
218
- extraAnchorPointYOffset -
219
- (componentsMapOffset || 0) +
220
- componentMarginTop;
223
+ toNumberWithDefaultZero(componentAnchorPointY) +
224
+ extraAnchorPointYOffset +
225
+ componentMarginTop +
226
+ componentPaddingTop;
221
227
 
222
228
  mainOffsetUpdater?.(
223
229
  { tag: this.target },
@@ -259,6 +265,7 @@ class TvOSCell extends React.Component<Props, State> {
259
265
  behavior,
260
266
  applyFocusableWrapper,
261
267
  hasFocusableInside,
268
+ skipFocusManagerRegistration,
262
269
  } = this.props;
263
270
 
264
271
  const { id } = item;
@@ -284,6 +291,7 @@ class TvOSCell extends React.Component<Props, State> {
284
291
  onFocus={handleFocus}
285
292
  onBlur={onBlur || this.onBlur}
286
293
  applyWrapper={applyFocusableWrapper}
294
+ skipFocusManagerRegistration={skipFocusManagerRegistration}
287
295
  >
288
296
  {(focused) => (
289
297
  <CellWithFocusable
@@ -298,6 +306,7 @@ class TvOSCell extends React.Component<Props, State> {
298
306
  focused={focused || this.props.focused}
299
307
  behavior={behavior}
300
308
  isFocusable={isFocusable}
309
+ skipFocusManagerRegistration={skipFocusManagerRegistration}
301
310
  />
302
311
  )}
303
312
  </FocusableWrapper>
@@ -320,6 +329,7 @@ class TvOSCell extends React.Component<Props, State> {
320
329
  offsetUpdater={offsetUpdater}
321
330
  style={baseCellStyles}
322
331
  isFocusable={isFocusable}
332
+ skipFocusManagerRegistration={skipFocusManagerRegistration}
323
333
  >
324
334
  {(focused) => (
325
335
  <FocusableCell
@@ -8,6 +8,8 @@ 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
+
11
13
  type Props = {
12
14
  initialFocus?: boolean;
13
15
  id: string;
@@ -106,7 +108,7 @@ class Focusable extends BaseFocusable<Props> {
106
108
  onMouseEnter() {
107
109
  const { id } = this.props;
108
110
 
109
- if (id !== "search_input_group_id") {
111
+ if (!isSearchInputId(id)) {
110
112
  this.mouse = true;
111
113
  this.props?.handleFocus?.({ mouse: true });
112
114
 
@@ -120,7 +122,7 @@ class Focusable extends BaseFocusable<Props> {
120
122
  onMouseLeave() {
121
123
  const { id } = this.props;
122
124
 
123
- if (id !== "search_input_group_id") {
125
+ if (!isSearchInputId(id)) {
124
126
  this.mouse = false;
125
127
  this.blur(null);
126
128
  }
@@ -10,8 +10,12 @@ 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";
13
14
 
14
- function noop() {}
15
+ import {
16
+ emitFocused,
17
+ emitNativeRegistered,
18
+ } from "@applicaster/zapp-react-native-utils/appUtils/focusManagerAux/utils/utils.ios";
15
19
 
16
20
  type Props = {
17
21
  id: string;
@@ -39,6 +43,7 @@ type Props = {
39
43
  hasReceivedFocus: () => void;
40
44
  offsetUpdater: (arg1: string, arg2: number) => number;
41
45
  style: ViewStyle;
46
+ skipFocusManagerRegistration?: boolean;
42
47
  };
43
48
 
44
49
  export class Focusable extends BaseFocusable<Props> {
@@ -53,6 +58,7 @@ export class Focusable extends BaseFocusable<Props> {
53
58
  this.nextFocusableReactTags = {};
54
59
  this.preferredFocus = this.preferredFocus.bind(this);
55
60
  this.measureView = this.measureView.bind(this);
61
+ this.onRegistered = this.onRegistered.bind(this);
56
62
  }
57
63
 
58
64
  /**
@@ -84,6 +90,9 @@ export class Focusable extends BaseFocusable<Props> {
84
90
  });
85
91
  }
86
92
 
93
+ const id: string = nativeEvent.itemID;
94
+ emitFocused(id);
95
+
87
96
  onFocus(nativeEvent);
88
97
  }
89
98
 
@@ -169,6 +178,13 @@ export class Focusable extends BaseFocusable<Props> {
169
178
  });
170
179
  }
171
180
 
181
+ onRegistered({ nativeEvent }) {
182
+ const groupId = nativeEvent?.groupId;
183
+ const id = nativeEvent?.itemId;
184
+
185
+ emitNativeRegistered({ id, groupId, isGroup: false });
186
+ }
187
+
172
188
  render() {
173
189
  const {
174
190
  children,
@@ -203,6 +219,7 @@ export class Focusable extends BaseFocusable<Props> {
203
219
  focusable={isFocusable}
204
220
  {...this.nextFocusableReactTags}
205
221
  {...otherProps}
222
+ onRegistered={this.onRegistered}
206
223
  >
207
224
  {typeof children === "function" ? children(focused) : children}
208
225
  </FocusableItemNative>
@@ -5,6 +5,7 @@ exports[`FocusableTvOS should render correctly 1`] = `
5
5
  groupId={null}
6
6
  itemId={null}
7
7
  onLayout={[Function]}
8
+ onRegistered={[Function]}
8
9
  onViewBlur={[Function]}
9
10
  onViewFocus={[Function]}
10
11
  onViewPress={[Function]}
@@ -2,6 +2,10 @@ 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";
5
9
 
6
10
  const { log_verbose } = createLogger({
7
11
  subsystem: "General",
@@ -33,7 +37,16 @@ type Props = {
33
37
  screenData: { screenId: string; parentScreenId: string };
34
38
  };
35
39
 
36
- export class FocusableGroup extends BaseFocusable<Props> {
40
+ class FocusableGroupComponent extends BaseFocusable<Props> {
41
+ public readonly isGroup: boolean = true;
42
+
43
+ onRegistered = ({ nativeEvent }) => {
44
+ const groupId = nativeEvent?.groupId;
45
+ const id = nativeEvent?.itemId;
46
+
47
+ emitNativeRegistered({ id, groupId, isGroup: true });
48
+ };
49
+
37
50
  render() {
38
51
  const {
39
52
  children,
@@ -66,9 +79,27 @@ export class FocusableGroup extends BaseFocusable<Props> {
66
79
  onGroupBlur={onGroupBlur}
67
80
  style={style}
68
81
  {...otherProps}
82
+ onRegistered={this.onRegistered}
69
83
  >
70
84
  {children}
71
85
  </FocusableGroupNative>
72
86
  );
73
87
  }
74
88
  }
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-hooks";
2
+ import { renderHook } from "@testing-library/react-native";
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,4 +1,4 @@
1
- import { all, equals, path, prop, isEmpty, pluck, values } from "ramda";
1
+ import { all, equals, isEmpty, path, pluck, prop, values } from "ramda";
2
2
 
3
3
  import { useEffect, useMemo } from "react";
4
4
 
@@ -9,10 +9,9 @@ import {
9
9
  import { isEmptyOrNil } from "@applicaster/zapp-react-native-utils/cellUtils";
10
10
  import { Categories } from "./logger";
11
11
  import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
12
- import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useRoute";
12
+ import { useScreenContext } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
13
13
 
14
14
  import {
15
- ZappPipesEntryContext,
16
15
  ZappPipesScreenContext,
17
16
  ZappPipesSearchContext,
18
17
  } from "@applicaster/zapp-react-native-ui-components/Contexts";
@@ -24,6 +23,7 @@ import {
24
23
 
25
24
  import { produce } from "immer";
26
25
  import { useLoadPipesDataDispatch } from "@applicaster/zapp-react-native-utils/reactHooks";
26
+
27
27
  // types reference
28
28
 
29
29
  declare interface CurationEntry {
@@ -35,6 +35,8 @@ type Feeds = Record<string, ZappPipesData>;
35
35
 
36
36
  type LayoutPresets = PresetsMapping["presets_mappings"];
37
37
 
38
+ const TABS_SCREEN_TYPE = "tabs_screen";
39
+ const QB_TABS_SCREEN_TYPE = "quick-brick-tabs";
38
40
  const SMART_COMPONENT_TYPE = "quick-brick-smart-component";
39
41
  const SOURCE_PATH = ["data", "source"];
40
42
  const MAPPING_PATH = ["data", "mapping"];
@@ -53,7 +55,10 @@ export const getTransformedPreset = (
53
55
  const presetComponent = layoutPresets?.[preset?.preset_name];
54
56
 
55
57
  if (!presetComponent) {
56
- logger.log_error("Preset missing or wrong data format", { entry: preset });
58
+ logger.log_error(
59
+ `Preset "${preset?.preset_name}" missing or wrong data format`,
60
+ { entry: preset }
61
+ );
57
62
 
58
63
  return;
59
64
  }
@@ -130,11 +135,22 @@ export const useCurationAPI = (
130
135
  [components]
131
136
  );
132
137
 
133
- const { pathname } = useRoute();
134
- const [entryContext] = ZappPipesEntryContext.useZappPipesContext(pathname);
135
138
  const [searchContext] = ZappPipesSearchContext.useZappPipesContext();
136
139
  const [screenContext] = ZappPipesScreenContext.useZappPipesContext();
137
140
 
141
+ const screenContextType = screenContext?.type;
142
+
143
+ const isNestedScreen =
144
+ screenContextType === TABS_SCREEN_TYPE ||
145
+ screenContextType === QB_TABS_SCREEN_TYPE;
146
+
147
+ const screenContextData = useScreenContext();
148
+
149
+ const entryContext = ((isNestedScreen && screenContextData?.nested?.entry
150
+ ? screenContextData?.nested?.entry
151
+ : (screenContextData?.entry?.payload ?? screenContextData?.entry)) ||
152
+ {}) as ZappEntry;
153
+
138
154
  const urlsMap = useMemo<{ [key: string]: string }>(() => {
139
155
  const map = {};
140
156