@applicaster/zapp-react-native-ui-components 13.0.0-rc.13 → 13.0.0-rc.130

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 (212) hide show
  1. package/Components/AppContainer/__tests__/AppContainer.test.js +4 -6
  2. package/Components/AppContainer/__tests__/__snapshots__/AppContainer.test.js.snap +1 -1
  3. package/Components/AudioPlayer/AudioPlayer.tsx +5 -9
  4. package/Components/AudioPlayer/AudioPlayerLayout.tsx +6 -7
  5. package/Components/AudioPlayer/__tests__/Runtime.test.js +5 -8
  6. package/Components/AudioPlayer/__tests__/__snapshots__/Runtime.test.js.snap +4 -4
  7. package/Components/AudioPlayer/__tests__/__snapshots__/artWork.test.js.snap +3 -3
  8. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayer.test.js.snap +56 -113
  9. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayerLayout.test.js.snap +39 -23
  10. package/Components/AudioPlayer/__tests__/__snapshots__/channel.test.js.snap +3 -3
  11. package/Components/AudioPlayer/__tests__/__snapshots__/summary.test.js.snap +2 -2
  12. package/Components/AudioPlayer/__tests__/__snapshots__/title.test.js.snap +2 -2
  13. package/Components/AudioPlayer/__tests__/artWork.test.js +3 -5
  14. package/Components/AudioPlayer/__tests__/audioPlayer.test.js +3 -5
  15. package/Components/AudioPlayer/__tests__/audioPlayerLayout.test.js +7 -8
  16. package/Components/AudioPlayer/__tests__/channel.test.js +3 -5
  17. package/Components/AudioPlayer/__tests__/summary.test.js +3 -5
  18. package/Components/AudioPlayer/__tests__/title.test.js +3 -5
  19. package/Components/AudioPlayer/helpers.tsx +1 -2
  20. package/Components/BaseFocusable/index.ios.ts +1 -1
  21. package/Components/BaseFocusable/index.tsx +67 -31
  22. package/Components/Cell/Cell.tsx +14 -14
  23. package/Components/Cell/CellWithFocusable.tsx +50 -59
  24. package/Components/Cell/CellWrapper.tsx +18 -0
  25. package/Components/Cell/TvOSCellComponent.tsx +20 -26
  26. package/Components/Cell/__tests__/CellWIthFocusable.test.js +13 -6
  27. package/Components/Cell/index.js +3 -1
  28. package/Components/Cell/styles.ts +17 -0
  29. package/Components/CellRendererResolver/__tests__/cellRendererResolver.test.js +50 -50
  30. package/Components/CellRendererResolver/index.ts +2 -1
  31. package/Components/ComponentResolver/__tests__/__snapshots__/componentResolver.test.js.snap +1 -42
  32. package/Components/ComponentResolver/__tests__/componentResolver.test.js +26 -30
  33. package/Components/ContentScreen/__tests__/contentScreen.test.js +3 -3
  34. package/Components/DisplayState/__tests__/__snapshots__/displayState.test.js.snap +2 -2
  35. package/Components/ErrorScreen/index.tsx +14 -6
  36. package/Components/Focusable/Focusable.tsx +1 -54
  37. package/Components/Focusable/FocusableTvOS.tsx +6 -2
  38. package/Components/Focusable/Touchable.tsx +19 -20
  39. package/Components/Focusable/index.android.tsx +10 -3
  40. package/Components/FocusableCell/index.tsx +1 -1
  41. package/Components/FocusableGroup/FocusableTvOS.tsx +34 -86
  42. package/Components/FocusableGroup/index.tsx +0 -3
  43. package/Components/FocusableList/index.tsx +26 -10
  44. package/Components/FocusableScrollView/index.tsx +43 -13
  45. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +3 -0
  46. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +9 -0
  47. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +5 -3
  48. package/Components/GeneralContentScreen/utils/useEventAlerts.ts +30 -0
  49. package/Components/HandlePlayable/HandlePlayable.tsx +21 -27
  50. package/Components/Layout/TV/LayoutBackground.tsx +28 -0
  51. package/Components/Layout/TV/NavBarContainer.tsx +19 -23
  52. package/Components/Layout/TV/ScreenContainer.tsx +8 -2
  53. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +5 -0
  54. package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +3 -1
  55. package/Components/Layout/TV/__tests__/__snapshots__/LayoutContainer.test.tsx.snap +1 -1
  56. package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +6 -5
  57. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +12 -11
  58. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +15 -10
  59. package/Components/Layout/TV/__tests__/index.test.tsx +14 -4
  60. package/Components/Layout/TV/index.tsx +6 -20
  61. package/Components/Layout/TV/index.web.tsx +4 -1
  62. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -3
  63. package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +66 -0
  64. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +4 -1
  65. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +10 -15
  66. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +11 -7
  67. package/Components/MasterCell/DefaultComponents/ImageBorderContainer/__tests__/index.test.ts +93 -0
  68. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +2 -2
  69. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +3 -3
  70. package/Components/MasterCell/DefaultComponents/SecondaryImage/utils.ts +1 -1
  71. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
  72. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/const.ts +3 -0
  73. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +6 -6
  74. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/__snapshots__/index.test.tsx.snap +1 -1
  75. package/Components/MasterCell/SharedUI/ProgressBar/hooks/useFillInPercent.ts +2 -8
  76. package/Components/MasterCell/__tests__/__snapshots__/dataAdapter.test.js.snap +180 -180
  77. package/Components/MasterCell/__tests__/__snapshots__/elementMapper.test.js.snap +11 -19
  78. package/Components/MasterCell/__tests__/__snapshots__/masterCell.test.js.snap +6 -235
  79. package/Components/MasterCell/__tests__/elementMapper.test.js +18 -5
  80. package/Components/MasterCell/__tests__/masterCell.test.js +26 -26
  81. package/Components/MasterCell/hooks/useAsyncRendering/MasterCellAsyncRenderManager.ts +2 -2
  82. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +2 -2
  83. package/Components/MasterCell/utils/behaviorProvider.ts +136 -0
  84. package/Components/MasterCell/utils/index.ts +9 -135
  85. package/Components/ModalComponent/BottomSheetModalContent.tsx +66 -54
  86. package/Components/ModalComponent/Button/Item.tsx +6 -5
  87. package/Components/ModalComponent/Button/assets.ts +1 -1
  88. package/Components/ModalComponent/Button/index.tsx +45 -62
  89. package/Components/ModalComponent/Header/index.tsx +12 -11
  90. package/Components/ModalComponent/utils.ts +55 -7
  91. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +2 -2
  92. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +2 -2
  93. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -5
  94. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +7 -7
  95. package/Components/OfflineHandler/__tests__/index.test.tsx +28 -25
  96. package/Components/OfflineHandler/utils/index.ts +1 -1
  97. package/Components/PlayerContainer/PlayerContainer.tsx +49 -29
  98. package/Components/PlayerContainer/PlayerContainerContext.tsx +9 -0
  99. package/Components/PlayerContainer/ProgramInfo/index.tsx +2 -2
  100. package/Components/PlayerImageBackground/index.tsx +1 -1
  101. package/Components/River/ComponentsMap/ComponentsMap.tsx +53 -68
  102. package/Components/River/ComponentsMap/hooks/useLoadingState.ts +78 -51
  103. package/Components/River/RefreshControl.tsx +1 -1
  104. package/Components/River/RiverFooter.tsx +39 -9
  105. package/Components/River/RiverItem.tsx +43 -3
  106. package/Components/River/TV/index.tsx +6 -1
  107. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +121 -46
  108. package/Components/River/__tests__/__snapshots__/river.test.js.snap +21 -83
  109. package/Components/River/__tests__/componentsMap.test.js +64 -17
  110. package/Components/River/__tests__/river.test.js +75 -40
  111. package/Components/RouteManager/TestId.tsx +1 -5
  112. package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +11 -5
  113. package/Components/RouteManager/__tests__/routeManager.test.js +29 -14
  114. package/Components/RouteManager/__tests__/testId.test.js +4 -6
  115. package/Components/Screen/TV/__tests__/index.web.test.tsx +26 -0
  116. package/Components/Screen/TV/index.web.tsx +5 -6
  117. package/Components/Screen/__tests__/Screen.test.tsx +53 -31
  118. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +6 -6
  119. package/Components/Screen/hooks.ts +53 -0
  120. package/Components/Screen/index.tsx +16 -42
  121. package/Components/ScreenResolver/__tests__/screenResolver.test.js +95 -70
  122. package/Components/Tabs/Tab.tsx +12 -9
  123. package/Components/TextInputTv/__tests__/TextInputTv.test.js +42 -42
  124. package/Components/TextInputTv/__tests__/__snapshots__/TextInputTv.test.js.snap +7 -0
  125. package/Components/TextInputTv/index.tsx +6 -5
  126. package/Components/TopMarginApplicator/TopMarginApplicator.tsx +2 -3
  127. package/Components/Touchable/__tests__/__snapshots__/touchable.test.tsx.snap +2 -2
  128. package/Components/TrackedView/index.tsx +1 -0
  129. package/Components/Transitioner/AnimationManager.js +15 -15
  130. package/Components/Transitioner/Scene.tsx +53 -24
  131. package/Components/Transitioner/Transitioner.tsx +3 -3
  132. package/Components/Transitioner/__tests__/Scene.test.js +25 -14
  133. package/Components/Transitioner/__tests__/__snapshots__/AnimationManager.test.js.snap +26 -26
  134. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +10 -50
  135. package/Components/Transitioner/__tests__/__snapshots__/config.test.js.snap +20 -20
  136. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +4 -4
  137. package/Components/Transitioner/__tests__/transitioner.test.js +13 -4
  138. package/Components/Transitioner/index.js +8 -4
  139. package/Components/VideoLive/LiveImageManager.ts +27 -1
  140. package/Components/VideoLive/PlayerLiveImageComponent.tsx +36 -24
  141. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +63 -8
  142. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +6 -10
  143. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +1 -1
  144. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +51 -31
  145. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +8 -4
  146. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +48 -23
  147. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +16 -15
  148. package/Components/VideoModal/ModalAnimation/__tests__/getMoveUpValue.test.ts +108 -0
  149. package/Components/VideoModal/ModalAnimation/const.ts +5 -0
  150. package/Components/VideoModal/ModalAnimation/index.ts +4 -1
  151. package/Components/VideoModal/ModalAnimation/useModalAnimationContext.ts +6 -0
  152. package/Components/VideoModal/ModalAnimation/utils.ts +32 -2
  153. package/Components/VideoModal/OpaqueLayer.tsx +33 -0
  154. package/Components/VideoModal/PlayerDetails.tsx +18 -10
  155. package/Components/VideoModal/PlayerWrapper.tsx +51 -57
  156. package/Components/VideoModal/VideoModal.tsx +17 -11
  157. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +5 -2
  158. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +13 -10
  159. package/Components/VideoModal/__tests__/__snapshots__/PlayerDetails.test.tsx.snap +2 -40
  160. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +66 -156
  161. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +89 -0
  162. package/Components/VideoModal/hooks/index.ts +7 -0
  163. package/Components/VideoModal/hooks/useDelayedPlayerDetails.ts +49 -0
  164. package/Components/VideoModal/hooks/utils/__tests__/showDetails.test.ts +91 -0
  165. package/Components/VideoModal/hooks/utils/index.ts +33 -0
  166. package/Components/VideoModal/utils.ts +6 -9
  167. package/Components/Viewport/ViewportAware/__tests__/__snapshots__/viewportAware.test.js.snap +24 -36
  168. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +12 -18
  169. package/Components/Viewport/ViewportTracker/__tests__/__snapshots__/viewportTracker.test.js.snap +1 -1
  170. package/Components/Viewport/__tests__/utils.test.js +5 -4
  171. package/Components/ZappUIComponent/Placeholder.tsx +3 -1
  172. package/Components/ZappUIComponent/__tests__/{ZappUIComponent.test.js → ZappUIComponent.test.tsx} +26 -10
  173. package/Components/ZappUIComponent/index.tsx +4 -17
  174. package/Components/default-cell-renderer/__tests__/defaultCellRenderer.test.tsx +20 -17
  175. package/Components/default-cell-renderer/viewTrees/tv/DefaultCell/__tests__/DefaultCell.test.tsx +15 -11
  176. package/Contexts/ComponentsMapOffsetContext/index.tsx +49 -0
  177. package/Contexts/ConfigutaionContext/__tests__/__snapshots__/ConfigurationProvider.test.tsx.snap +1 -1
  178. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +4 -10
  179. package/Contexts/HeaderOffsetContext/index.tsx +4 -6
  180. package/Contexts/ScreenContext/index.tsx +6 -12
  181. package/Contexts/ScreenLayoutContext/index.tsx +5 -3
  182. package/Decorators/Navigator/__tests__/navigator.test.js +16 -16
  183. package/Decorators/Navigator/index.tsx +1 -1
  184. package/Decorators/RiverFeedLoader/__tests__/__snapshots__/riverFeedLoader.test.tsx.snap +77 -77
  185. package/Decorators/RiverFeedLoader/__tests__/riverFeedLoader.test.tsx +34 -39
  186. package/Decorators/RiverFeedLoader/__tests__/utils.test.ts +0 -9
  187. package/Decorators/RiverFeedLoader/index.tsx +1 -1
  188. package/Decorators/RiverFeedLoader/utils/getDatasourceUrl.ts +39 -0
  189. package/Decorators/RiverFeedLoader/utils/index.ts +29 -0
  190. package/Decorators/RiverFeedLoader/utils/usePipesContexts.ts +40 -0
  191. package/Decorators/RiverResolver/__tests__/riverResolver.test.tsx +27 -27
  192. package/Decorators/RiverResolver/index.tsx +6 -8
  193. package/Decorators/ZappPipesDataConnector/__tests__/Hero.js +1 -1
  194. package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +29 -22
  195. package/Decorators/ZappPipesDataConnector/index.tsx +7 -3
  196. package/package.json +13 -11
  197. package/tsconfig.json +2 -3
  198. package/.babelrc +0 -8
  199. package/Components/Cell/CellStyles/FallbackCellStyle/index.js +0 -157
  200. package/Components/Cell/CellStyles/Hero/index.js +0 -111
  201. package/Components/Cell/CellStyles/ScreenSelector/index.js +0 -68
  202. package/Components/Cell/CellStyles/cellStylesResolver.ts +0 -19
  203. package/Components/Cell/CellStyles/colors.js +0 -40
  204. package/Components/Cell/CellStyles/index.js +0 -15
  205. package/Components/CellRendererResolver/__tests__/__snapshots__/cellRendererResolver.test.js.snap +0 -53
  206. package/Components/RouteManager/__tests__/__snapshots__/testId.test.js.snap +0 -17
  207. package/Components/ScreenResolver/__tests__/__snapshots__/screenResolver.test.js.snap +0 -17
  208. package/Components/ZappUIComponent/__tests__/__snapshots__/ZappUIComponent.test.js.snap +0 -91
  209. package/Components/default-cell-renderer/__tests__/__snapshots__/defaultCellRenderer.test.tsx.snap +0 -338
  210. package/Decorators/Navigator/__tests__/__snapshots__/navigator.test.js.snap +0 -18
  211. package/Decorators/RiverFeedLoader/utils.ts +0 -100
  212. package/Decorators/RiverResolver/__tests__/__snapshots__/riverResolver.test.tsx.snap +0 -28
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import * as R from "ramda";
3
- import { View, StyleSheet, Platform, FlatList } from "react-native";
3
+ import { View, StyleSheet, FlatList } from "react-native";
4
4
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
5
5
  import { RiverItem } from "../RiverItem";
6
6
  import { RiverFooter } from "../RiverFooter";
@@ -24,6 +24,10 @@ import { withComponentsMapProvider } from "@applicaster/zapp-react-native-ui-com
24
24
  import { useScreenContextV2 } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
25
25
  import { useShallow } from "zustand/react/shallow";
26
26
 
27
+ import { isAndroidPlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
28
+
29
+ const isAndroid = isAndroidPlatform();
30
+
27
31
  type Props = {
28
32
  feed: ZappFeed;
29
33
  groupId?: string;
@@ -34,6 +38,7 @@ type Props = {
34
38
  getStaticComponentFeed: any;
35
39
  pullToRefreshPipesV1RefreshingStateUpdater: () => boolean;
36
40
  refreshingPipesV1?: boolean;
41
+ stickyHeaderIndices?: number[];
37
42
  };
38
43
 
39
44
  const styles = StyleSheet.create({
@@ -61,6 +66,7 @@ function ComponentsMapComponent(props: Props) {
61
66
  // TODO: Remove when pipes v1 is deprecated.
62
67
  pullToRefreshPipesV1RefreshingStateUpdater,
63
68
  refreshingPipesV1,
69
+ stickyHeaderIndices,
64
70
  } = props;
65
71
 
66
72
  const flatListRef = React.useRef<FlatList | null>(null);
@@ -70,21 +76,6 @@ function ComponentsMapComponent(props: Props) {
70
76
 
71
77
  const [flatListHeight, setFlatListHeight] = React.useState(null);
72
78
 
73
- const {
74
- isAnyLoading,
75
- isAllLoaded,
76
- waitForAllComponents,
77
- onLoadFinished,
78
- onLoadFailed,
79
- shouldShowLoadingError,
80
- isAnyLoaded,
81
- arePreviousComponentsLoaded,
82
- } = useLoadingState(feed?.entry?.length || components.length);
83
-
84
- const theme = useTheme();
85
-
86
- const logTimestamp = useProfilerLogging();
87
-
88
79
  const riverComponents = React.useMemo(() => {
89
80
  if (feed?.entry?.length < components.length) {
90
81
  return R.slice(0, feed?.entry?.length, components);
@@ -93,55 +84,42 @@ function ComponentsMapComponent(props: Props) {
93
84
  return components;
94
85
  }, [components, feed]);
95
86
 
87
+ const logTimestamp = useProfilerLogging();
88
+
89
+ const onLoadDone = React.useCallback(() => {
90
+ logTimestamp(riverId?.toString());
91
+ }, [logTimestamp]);
92
+
93
+ const { loadingState, onLoadFinished, onLoadFailed, shouldShowLoadingError } =
94
+ useLoadingState(riverComponents.length, onLoadDone);
95
+
96
+ const theme = useTheme();
97
+
96
98
  const renderRiverItem = React.useCallback(
97
99
  ({ item, index }) => {
98
- const readyToBeDisplayed = arePreviousComponentsLoaded(index);
99
-
100
- const styles = {
101
- display: !readyToBeDisplayed ? "none" : ("flex" as "none" | "flex"),
100
+ const riverItemProps = {
101
+ riverId,
102
+ item,
103
+ index,
104
+ isScreenWrappedInContainer,
105
+ feed,
106
+ groupId,
107
+ onLoadFailed,
108
+ onLoadFinished,
109
+ getStaticComponentFeed,
110
+ isLast: isLast(index, riverComponents.length),
111
+ loadingState,
102
112
  };
103
113
 
104
114
  return (
105
- <View style={styles}>
106
- <ScreenLoadingMeasurementsListItemWrapper index={index}>
107
- <RiverItem
108
- {...{
109
- readyToBeDisplayed,
110
- riverId,
111
- item,
112
- index,
113
- isScreenWrappedInContainer,
114
- feed,
115
- groupId,
116
- onLoadFailed,
117
- onLoadFinished,
118
- getStaticComponentFeed,
119
- isLast: isLast(index, riverComponents.length),
120
- }}
121
- />
122
- </ScreenLoadingMeasurementsListItemWrapper>
123
- </View>
115
+ <ScreenLoadingMeasurementsListItemWrapper index={index}>
116
+ <RiverItem {...riverItemProps} />
117
+ </ScreenLoadingMeasurementsListItemWrapper>
124
118
  );
125
119
  },
126
- [
127
- feed,
128
- getStaticComponentFeed,
129
- arePreviousComponentsLoaded,
130
- onLoadFailed,
131
- onLoadFinished,
132
- ]
120
+ [feed, getStaticComponentFeed, onLoadFailed, onLoadFinished]
133
121
  );
134
122
 
135
- const renderFooter = React.useCallback(() => {
136
- return (
137
- <RiverFooter
138
- visible={isAnyLoading}
139
- flatListHeight={flatListHeight}
140
- isAnyLoaded={isAnyLoaded || waitForAllComponents}
141
- />
142
- );
143
- }, [flatListHeight, isAnyLoading, isAnyLoaded]);
144
-
145
123
  const screenStyle = React.useMemo(
146
124
  () => ({
147
125
  paddingTop: ifEmptyUseFallback(
@@ -179,12 +157,6 @@ function ComponentsMapComponent(props: Props) {
179
157
 
180
158
  usePipesCacheReset(riverId, riverComponents);
181
159
 
182
- React.useEffect(() => {
183
- if (isAllLoaded) {
184
- logTimestamp(riverId?.toString());
185
- }
186
- }, [isAllLoaded]);
187
-
188
160
  const refreshControl = React.useMemo(
189
161
  () =>
190
162
  pullToRefreshEnabled ? (
@@ -280,6 +252,11 @@ function ComponentsMapComponent(props: Props) {
280
252
  }
281
253
  }, []);
282
254
 
255
+ const contentContainerStyle = React.useMemo(
256
+ () => (isScreenWrappedInContainer ? {} : screenStyle),
257
+ [isScreenWrappedInContainer, screenStyle]
258
+ );
259
+
283
260
  if (shouldShowLoadingError) {
284
261
  return <RiverError />;
285
262
  }
@@ -291,7 +268,7 @@ function ComponentsMapComponent(props: Props) {
291
268
  <View style={styles.container}>
292
269
  <ScreenLoadingMeasurements
293
270
  riverId={riverId}
294
- numberOfComponents={feed?.entry?.length || components.length}
271
+ numberOfComponents={riverComponents.length}
295
272
  >
296
273
  <ViewportTracker>
297
274
  <FlatList
@@ -300,11 +277,12 @@ function ComponentsMapComponent(props: Props) {
300
277
  }}
301
278
  // Fix for WebView rerender crashes on Android API 28+
302
279
  // https://github.com/react-native-webview/react-native-webview/issues/1915#issuecomment-964035468
303
- overScrollMode={Platform.OS === "android" ? "never" : "auto"}
280
+ overScrollMode={isAndroid ? "never" : "auto"}
304
281
  scrollIndicatorInsets={scrollIndicatorInsets}
305
282
  extraData={feed}
283
+ stickyHeaderIndices={stickyHeaderIndices}
306
284
  onLayout={handleOnLayout}
307
- removeClippedSubviews
285
+ removeClippedSubviews={isAndroid}
308
286
  initialNumToRender={3}
309
287
  maxToRenderPerBatch={10}
310
288
  windowSize={12}
@@ -312,16 +290,23 @@ function ComponentsMapComponent(props: Props) {
312
290
  keyExtractor={keyExtractor}
313
291
  renderItem={renderRiverItem}
314
292
  data={riverComponents}
315
- contentContainerStyle={
316
- isScreenWrappedInContainer ? {} : screenStyle
293
+ contentContainerStyle={contentContainerStyle}
294
+ ListFooterComponent={
295
+ <RiverFooter
296
+ flatListHeight={flatListHeight}
297
+ loadingState={loadingState}
298
+ />
317
299
  }
318
- ListFooterComponent={renderFooter}
319
300
  refreshControl={refreshControl}
320
301
  onScrollBeginDrag={onScrollBeginDrag}
321
302
  onScroll={onScroll}
322
303
  onMomentumScrollEnd={_onMomentumScrollEnd}
323
304
  onScrollEndDrag={_onScrollEndDrag}
324
305
  scrollEventThrottle={16}
306
+ maintainVisibleContentPosition={{
307
+ minIndexForVisible: 0,
308
+ autoscrollToTopThreshold: 10,
309
+ }}
325
310
  {...scrollViewExtraProps}
326
311
  />
327
312
  </ViewportTracker>
@@ -1,50 +1,86 @@
1
1
  import * as React from "react";
2
- import * as R from "ramda";
3
-
4
- const allTrue = R.all(R.equals(true));
5
- const anyFalse = R.any(R.equals(false));
6
- const anyTrue = R.any(R.equals(true));
2
+ import { isNil, set, lensIndex, T, slice } from "ramda";
3
+ import { BehaviorSubject } from "rxjs";
4
+ import { useRefWithInitialValue } from "@applicaster/zapp-react-native-utils/reactHooks/state/useRefWithInitialValue";
7
5
 
8
6
  const reducer = (state, { payload }) => {
9
- if (!R.isNil(payload) && !state[payload]) {
10
- return R.set(R.lensIndex(payload), true)(state);
7
+ if (!isNil(payload) && !state[payload]) {
8
+ return set(lensIndex(payload), true)(state);
11
9
  }
12
10
 
13
11
  return state;
14
12
  };
15
13
 
16
- type Return = {
17
- isAnyLoading: boolean;
18
- isAllLoaded: boolean;
14
+ type LoadingState = {
15
+ index: number;
16
+ done: boolean;
19
17
  waitForAllComponents: boolean;
18
+ };
19
+
20
+ type Return = {
21
+ loadingState: BehaviorSubject<LoadingState>;
20
22
  onLoadFinished: (index: number) => void;
21
23
  onLoadFailed: ({ error, index }: { error: Error; index: number }) => void;
22
24
  shouldShowLoadingError: boolean;
23
- isAnyLoaded: boolean;
24
25
  arePreviousComponentsLoaded: (index: number) => boolean;
25
26
  };
26
27
 
27
- type Action = { payload: { index: number } };
28
-
29
- type Loaded = true;
30
- type Loading = false;
31
-
32
- type LoadingState = Array<Loaded | Loading>;
33
-
34
28
  // TODO: Take this value from Zapp configuration, when feature is added to GeneralScreen
35
29
  const SHOULD_FAIL_ON_COMPONENT_LOADING = false;
36
30
 
37
- export const useLoadingState = (count: number): Return => {
31
+ const createLoadingStateObservable = (count: number) =>
32
+ new BehaviorSubject<LoadingState>({
33
+ index: -1,
34
+ done: count === 0,
35
+ waitForAllComponents: SHOULD_FAIL_ON_COMPONENT_LOADING,
36
+ });
37
+
38
+ export const useLoadingState = (
39
+ count: number,
40
+ onLoadDone: () => void
41
+ ): Return => {
42
+ const componentStateRef = React.useRef(new Array(count).fill(false));
38
43
  const [loadingError, setLoadingError] = React.useState(null);
39
44
 
40
- const [componentsState, dispatch] = React.useReducer<
41
- React.Reducer<LoadingState, Action>
42
- >(reducer, new Array(count).fill(false));
45
+ const loadingState = useRefWithInitialValue<BehaviorSubject<LoadingState>>(
46
+ () => createLoadingStateObservable(count)
47
+ );
48
+
49
+ const arePreviousComponentsLoaded = React.useCallback((index) => {
50
+ if (index === 0) {
51
+ return true;
52
+ }
43
53
 
44
- const handleComponentLoaded = React.useCallback((index) => {
45
- dispatch({ payload: index });
54
+ const componentsBefore = slice(0, index, componentStateRef.current);
55
+
56
+ return componentsBefore.every(T);
46
57
  }, []);
47
58
 
59
+ const dispatch = React.useCallback(({ payload }) => {
60
+ const newState = reducer(componentStateRef.current, { payload });
61
+ componentStateRef.current = newState;
62
+ const isDone = arePreviousComponentsLoaded(count - 1);
63
+
64
+ const state = loadingState.current.getValue();
65
+
66
+ const newLoadingState = {
67
+ ...state,
68
+ index: state.index < payload ? payload : state.index,
69
+ done: isDone,
70
+ };
71
+
72
+ loadingState.current.next(newLoadingState);
73
+
74
+ if (isDone) {
75
+ onLoadDone();
76
+ }
77
+ }, []);
78
+
79
+ const handleComponentLoaded = React.useCallback(
80
+ (index) => dispatch({ payload: index }),
81
+ []
82
+ );
83
+
48
84
  const handleComponentLoadErrorWhenNeedToFail = React.useCallback(
49
85
  ({ error }) => {
50
86
  if (error !== loadingError) {
@@ -55,35 +91,26 @@ export const useLoadingState = (count: number): Return => {
55
91
  );
56
92
 
57
93
  const handleComponentLoadErrorWhenNoNeedToFail = React.useCallback(
58
- ({ index }) => {
59
- handleComponentLoaded(index);
60
- },
94
+ ({ index }) => handleComponentLoaded(index),
61
95
  []
62
96
  );
63
97
 
64
- const arePreviousComponentsLoaded = React.useCallback(
65
- (index) => {
66
- if (index === 0) {
67
- return true;
68
- }
69
-
70
- const componentsBefore = R.slice(0, index, componentsState);
71
-
72
- return allTrue(componentsBefore);
73
- },
74
- [componentsState]
98
+ return React.useMemo(
99
+ () => ({
100
+ loadingState: loadingState.current,
101
+ onLoadFinished: handleComponentLoaded,
102
+ onLoadFailed: SHOULD_FAIL_ON_COMPONENT_LOADING
103
+ ? handleComponentLoadErrorWhenNeedToFail
104
+ : handleComponentLoadErrorWhenNoNeedToFail,
105
+ shouldShowLoadingError: SHOULD_FAIL_ON_COMPONENT_LOADING && loadingError,
106
+ arePreviousComponentsLoaded,
107
+ }),
108
+ [
109
+ loadingError,
110
+ handleComponentLoaded,
111
+ handleComponentLoadErrorWhenNeedToFail,
112
+ handleComponentLoadErrorWhenNoNeedToFail,
113
+ arePreviousComponentsLoaded,
114
+ ]
75
115
  );
76
-
77
- return {
78
- isAnyLoading: anyFalse(componentsState),
79
- isAllLoaded: allTrue(componentsState),
80
- onLoadFinished: handleComponentLoaded,
81
- onLoadFailed: SHOULD_FAIL_ON_COMPONENT_LOADING
82
- ? handleComponentLoadErrorWhenNeedToFail
83
- : handleComponentLoadErrorWhenNoNeedToFail,
84
- shouldShowLoadingError: SHOULD_FAIL_ON_COMPONENT_LOADING && loadingError,
85
- isAnyLoaded: anyTrue(componentsState),
86
- waitForAllComponents: SHOULD_FAIL_ON_COMPONENT_LOADING,
87
- arePreviousComponentsLoaded,
88
- };
89
116
  };
@@ -9,7 +9,7 @@ import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
9
9
  import { useLocalizedStrings } from "@applicaster/zapp-react-native-utils/localizationUtils";
10
10
  import { useAnalytics } from "@applicaster/zapp-react-native-utils/analyticsUtils";
11
11
  import { useSendAnalyticsEventWithFunction } from "@applicaster/zapp-react-native-utils/analyticsUtils/helpers/hooks";
12
- import { useCurrentScreenData } from "@applicaster/zapp-react-native-utils/reactHooks/screen";
12
+ import { useCurrentScreenData } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useCurrentScreenData";
13
13
  import { loadPipesData } from "@applicaster/zapp-react-native-redux/ZappPipes";
14
14
  import { useDispatch } from "react-redux";
15
15
  import { useShallow } from "zustand/react/shallow";
@@ -1,11 +1,23 @@
1
- import React from "react";
1
+ import React, { useCallback } from "react";
2
2
  import { StyleSheet, View } from "react-native";
3
3
  import { Spinner } from "@applicaster/zapp-react-native-ui-components/Components/Spinner";
4
+ import type { Subject } from "rxjs";
5
+ import { useStateFromSubscribe } from "@applicaster/zapp-react-native-utils/reactHooks/state/useStateFromSubscribe";
6
+
7
+ type LoadingState = {
8
+ waitForAllComponents: boolean;
9
+ index: number;
10
+ done: boolean;
11
+ };
12
+
13
+ type State = {
14
+ visible: boolean;
15
+ isAnyLoaded: boolean;
16
+ };
4
17
 
5
18
  type Props = {
6
- visible?: boolean;
7
19
  flatListHeight?: number;
8
- isAnyLoaded: boolean;
20
+ loadingState: Subject<LoadingState>;
9
21
  };
10
22
 
11
23
  const FOOTER_COMPONENT_HEIGHT = 200;
@@ -19,17 +31,35 @@ const footerStyles = StyleSheet.create({
19
31
  });
20
32
 
21
33
  function RiverFooterComponent(props: Props) {
22
- const { visible = true, flatListHeight, isAnyLoaded } = props;
34
+ const { flatListHeight, loadingState } = props;
35
+
36
+ const { visible, isAnyLoaded } = useStateFromSubscribe<LoadingState, State>(
37
+ loadingState,
38
+ useCallback(
39
+ ({ index, done, waitForAllComponents }, setState) =>
40
+ setState(() => ({
41
+ visible: !done,
42
+ isAnyLoaded: index >= 0 || waitForAllComponents,
43
+ })),
44
+ []
45
+ ),
46
+ {
47
+ visible: true,
48
+ isAnyLoaded: false,
49
+ }
50
+ );
23
51
 
24
52
  if (!visible) return null;
25
53
 
26
54
  return (
27
55
  <View
28
- renderToHardwareTextureAndroid={true}
29
- style={{
30
- ...footerStyles.container,
31
- height: isAnyLoaded ? FOOTER_COMPONENT_HEIGHT : flatListHeight,
32
- }}
56
+ renderToHardwareTextureAndroid
57
+ style={[
58
+ footerStyles.container,
59
+ {
60
+ height: isAnyLoaded ? FOOTER_COMPONENT_HEIGHT : flatListHeight,
61
+ },
62
+ ]}
33
63
  >
34
64
  <Spinner size={isAnyLoaded ? "small" : "large"} />
35
65
  </View>
@@ -1,5 +1,6 @@
1
- import React from "react";
1
+ import React, { useEffect } from "react";
2
2
  import * as R from "ramda";
3
+ import { isGroup } from "@applicaster/zapp-react-native-utils/componentsUtils";
3
4
 
4
5
  import { applyDecorators } from "../../Decorators";
5
6
 
@@ -11,6 +12,7 @@ import {
11
12
  import { riverLogger } from "./logger";
12
13
  import { tvPluginsWithCellRenderer } from "../../const";
13
14
  import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
15
+ import type { BehaviorSubject } from "rxjs";
14
16
 
15
17
  export type RiverItemType = {
16
18
  item: ZappUIComponent;
@@ -24,6 +26,7 @@ export type RiverItemType = {
24
26
  getStaticComponentFeed: GeneralContentScreenProps["getStaticComponentFeed"];
25
27
  readyToBeDisplayed?: boolean;
26
28
  isLast: boolean;
29
+ loadingState: BehaviorSubject<{ index: number }>;
27
30
  };
28
31
 
29
32
  function getFeedUrl(feed: ZappFeed, index: number) {
@@ -36,6 +39,37 @@ function getFeedUrl(feed: ZappFeed, index: number) {
36
39
  }
37
40
  }
38
41
 
42
+ /**
43
+ * useLoadingState for RiverItemComponent
44
+ * takes currentIndex and loadingState as arguments
45
+ **/
46
+ const useLoadingState = (
47
+ currentIndex: number,
48
+ loadingState: RiverItemType["loadingState"]
49
+ ) => {
50
+ const [readyToBeDisplayed, setReadyToBeDisplayed] = React.useState(
51
+ loadingState.getValue().index + 1 >= currentIndex
52
+ );
53
+
54
+ useEffect(() => {
55
+ const subscription = loadingState.subscribe(({ index }) => {
56
+ if (index + 1 >= currentIndex) {
57
+ setReadyToBeDisplayed(true);
58
+ }
59
+ });
60
+
61
+ if (loadingState.getValue().index + 1 >= currentIndex) {
62
+ setReadyToBeDisplayed(true);
63
+ }
64
+
65
+ return () => {
66
+ subscription.unsubscribe();
67
+ };
68
+ }, [loadingState, currentIndex]);
69
+
70
+ return readyToBeDisplayed;
71
+ };
72
+
39
73
  function RiverItemComponent(props: RiverItemType) {
40
74
  const {
41
75
  item,
@@ -47,10 +81,12 @@ function RiverItemComponent(props: RiverItemType) {
47
81
  onLoadFinished,
48
82
  onLoadFailed,
49
83
  getStaticComponentFeed,
50
- readyToBeDisplayed,
51
84
  isLast,
85
+ loadingState,
52
86
  } = props;
53
87
 
88
+ const readyToBeDisplayed = useLoadingState(index, loadingState);
89
+
54
90
  const feedUrl = getFeedUrl(feed, index);
55
91
 
56
92
  const Component = useComponentResolver(
@@ -81,7 +117,7 @@ function RiverItemComponent(props: RiverItemType) {
81
117
  jsOnly: true,
82
118
  });
83
119
 
84
- if (!CellRenderer && item.component_type !== "group-qb") {
120
+ if (!CellRenderer && !isGroup(item)) {
85
121
  riverLogger.warning({
86
122
  message: "Cell Renderer is null - will fallback to default cell",
87
123
  data: { item, CellRenderer },
@@ -90,6 +126,10 @@ function RiverItemComponent(props: RiverItemType) {
90
126
  }
91
127
  }, []);
92
128
 
129
+ if (!readyToBeDisplayed) {
130
+ return null;
131
+ }
132
+
93
133
  if (Component === null || typeof Component === "undefined") {
94
134
  riverLogger.warning({
95
135
  message: `Component ${item.component_type} is null - skipping rendering`,
@@ -1,4 +1,9 @@
1
+ import { compose } from "ramda";
1
2
  import { River as RiverComponent } from "./River";
2
3
  import { withTvEventHandler } from "./withTVEventHandler";
4
+ import { withComponentsMapOffsetContext } from "../../../Contexts/ComponentsMapOffsetContext";
3
5
 
4
- export const River = withTvEventHandler(RiverComponent);
6
+ export const River = compose(
7
+ withTvEventHandler,
8
+ withComponentsMapOffsetContext
9
+ )(RiverComponent);