@applicaster/zapp-react-native-ui-components 12.0.0-rc.13 → 12.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 (171) hide show
  1. package/Components/AudioPlayer/AudioPlayer.tsx +9 -1
  2. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayer.test.js.snap +5 -0
  3. package/Components/AudioPlayer/helpers.tsx +1 -0
  4. package/Components/BaseFocusable/index.tsx +3 -3
  5. package/Components/Cell/Cell.tsx +32 -11
  6. package/Components/Cell/CellWithFocusable.tsx +40 -19
  7. package/Components/Cell/TvOSCellComponent.tsx +5 -9
  8. package/Components/ErrorScreen/index.tsx +1 -1
  9. package/Components/Focusable/Focusable.tsx +6 -1
  10. package/Components/Focusable/__tests__/index.android.test.tsx +72 -0
  11. package/Components/Focusable/index.android.tsx +15 -9
  12. package/Components/FocusableCell/index.tsx +7 -7
  13. package/Components/FocusableGroup/index.tsx +11 -2
  14. package/Components/FocusableList/FocusableChild.ts +26 -18
  15. package/Components/FocusableList/FocusableItem.tsx +1 -1
  16. package/Components/FocusableList/FocusableListItemWrapper.tsx +76 -0
  17. package/Components/FocusableList/hooks/useCellState.android.ts +3 -1
  18. package/Components/FocusableList/index.tsx +37 -37
  19. package/Components/FocusableScrollView/index.tsx +1 -1
  20. package/Components/FreezeWithCallback/Freeze.tsx +31 -0
  21. package/Components/FreezeWithCallback/LICENSE +21 -0
  22. package/Components/FreezeWithCallback/Suspender.tsx +17 -0
  23. package/Components/FreezeWithCallback/__tests__/index.test.tsx +170 -0
  24. package/Components/FreezeWithCallback/index.tsx +3 -0
  25. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +11 -8
  26. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
  27. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
  28. package/Components/HandlePlayable/HandlePlayable.tsx +4 -1
  29. package/Components/HookRenderer/index.tsx +1 -6
  30. package/Components/Image/Image.android.tsx +3 -25
  31. package/Components/Image/Image.tsx +18 -2
  32. package/Components/Image/utils/index.ts +39 -0
  33. package/Components/Layout/TV/ScreenContainer.tsx +79 -5
  34. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
  35. package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +38 -28
  36. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +25 -13
  37. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -50
  38. package/Components/Layout/TV/__tests__/index.test.tsx +20 -108
  39. package/Components/Layout/TV/index.tsx +15 -84
  40. package/Components/Layout/TV/index.web.tsx +33 -0
  41. package/Components/Layout/index.tsx +15 -11
  42. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +4 -1
  43. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +86 -117
  44. package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +21 -8
  45. package/Components/MasterCell/DefaultComponents/DynamicBadge.tsx +4 -2
  46. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +16 -1
  47. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +9 -26
  48. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +3 -0
  49. package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +2 -2
  50. package/Components/MasterCell/DefaultComponents/Image/hooks/useImageSize.ts +65 -0
  51. package/Components/MasterCell/DefaultComponents/ImageBorderContainer/index.ts +71 -0
  52. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -1
  53. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +37 -2
  54. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +3 -2
  55. package/Components/MasterCell/DefaultComponents/Text/index.tsx +14 -2
  56. package/Components/MasterCell/DefaultComponents/Toggle.tsx +5 -1
  57. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +24 -9
  58. package/Components/MasterCell/DefaultComponents/index.ts +2 -0
  59. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
  60. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
  61. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +6 -10
  62. package/Components/MasterCell/SharedUI/ProgressBar/hooks/useFillInPercent.ts +7 -1
  63. package/Components/MasterCell/dataAdapter.ts +21 -7
  64. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
  65. package/Components/MasterCell/index.tsx +5 -3
  66. package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
  67. package/Components/MasterCell/utils/index.ts +33 -35
  68. package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
  69. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
  70. package/Components/ModalComponent/BottomSheetModalContent.tsx +121 -0
  71. package/Components/ModalComponent/Button/Item.tsx +82 -0
  72. package/Components/ModalComponent/Button/ItemIcon.tsx +60 -0
  73. package/Components/ModalComponent/Button/ItemLabel.tsx +101 -0
  74. package/Components/ModalComponent/Button/assets.ts +4 -0
  75. package/Components/ModalComponent/Button/index.tsx +111 -0
  76. package/Components/ModalComponent/Header/CloseButton.tsx +59 -0
  77. package/Components/ModalComponent/Header/Title.tsx +70 -0
  78. package/Components/ModalComponent/Header/assets.ts +4 -0
  79. package/Components/ModalComponent/Header/index.tsx +71 -0
  80. package/Components/ModalComponent/Header/utils.ts +79 -0
  81. package/Components/ModalComponent/index.tsx +38 -0
  82. package/Components/ModalComponent/utils.ts +126 -0
  83. package/Components/Navigator/StackNavigator.tsx +57 -0
  84. package/Components/Navigator/index.ts +1 -0
  85. package/Components/PlayerContainer/PlayerContainer.tsx +73 -34
  86. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -1
  87. package/Components/PlayerImageBackground/index.tsx +34 -8
  88. package/Components/River/ComponentsMap/ComponentsMap.tsx +97 -2
  89. package/Components/River/RefreshControl.tsx +55 -1
  90. package/Components/River/TV/River.tsx +2 -25
  91. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -1
  92. package/Components/River/__tests__/componentsMap.test.js +7 -0
  93. package/Components/River/useScreenConfiguration.ts +2 -2
  94. package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +1 -0
  95. package/Components/RouteManager/__tests__/routeManager.test.js +9 -2
  96. package/Components/RouteManager/index.tsx +4 -2
  97. package/Components/Screen/TV/hooks/index.ts +1 -0
  98. package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
  99. package/Components/Screen/TV/index.web.tsx +178 -0
  100. package/Components/Screen/__tests__/Screen.test.tsx +43 -75
  101. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +24 -6
  102. package/Components/Screen/__tests__/navigationHandler.test.ts +1 -178
  103. package/Components/Screen/index.tsx +104 -65
  104. package/Components/Screen/navigationHandler.ts +6 -85
  105. package/Components/Screen/orientationHandler.ts +18 -7
  106. package/Components/Screen/utils.ts +16 -0
  107. package/Components/ScreenResolver/index.tsx +10 -13
  108. package/Components/Tabs/TV/styles.ts +1 -1
  109. package/Components/TrackedView/index.tsx +6 -3
  110. package/Components/Transitioner/Scene.tsx +39 -26
  111. package/Components/Transitioner/Transitioner.tsx +56 -48
  112. package/Components/Transitioner/__tests__/Scene.test.js +7 -0
  113. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +12 -4
  114. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +2 -0
  115. package/Components/VideoLive/LiveImageManager.ts +70 -14
  116. package/Components/VideoLive/PlayerLiveImageComponent.tsx +132 -95
  117. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +45 -3
  118. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +24 -27
  119. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +66 -0
  120. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +377 -0
  121. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +165 -0
  122. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +481 -0
  123. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +182 -0
  124. package/Components/VideoModal/ModalAnimation/index.ts +15 -0
  125. package/Components/VideoModal/ModalAnimation/utils.ts +302 -0
  126. package/Components/VideoModal/PlayerDetails.tsx +21 -4
  127. package/Components/VideoModal/PlayerWrapper.tsx +115 -55
  128. package/Components/VideoModal/VideoModal.tsx +32 -43
  129. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +6 -1
  130. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +12 -1
  131. package/Components/VideoModal/__tests__/__snapshots__/PlayerDetails.test.tsx.snap +2 -0
  132. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +358 -166
  133. package/Components/VideoModal/hooks/useModalSize.ts +10 -12
  134. package/Components/VideoModal/utils.ts +8 -5
  135. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +26 -21
  136. package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +31 -40
  137. package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +4 -5
  138. package/Components/Viewport/ViewportTracker/index.tsx +1 -0
  139. package/Components/Viewport/utils.js +1 -1
  140. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +7 -3
  141. package/Components/ZappFrameworkComponents/BarView/Buttons/BackButton.tsx +7 -2
  142. package/Components/ZappFrameworkComponents/BarView/Buttons/CloseButton.tsx +7 -2
  143. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +87 -0
  144. package/Components/ZappFrameworkComponents/Buttons/ActionButton.tsx +9 -4
  145. package/Components/ZappUIComponent/index.tsx +22 -12
  146. package/Components/default-cell-renderer/__tests__/__snapshots__/defaultCellRenderer.test.tsx.snap +4 -4
  147. package/Components/index.js +2 -0
  148. package/Contexts/CachedDimensionsContext/index.ts +13 -9
  149. package/Contexts/ConfirmDialogState/index.ts +1 -1
  150. package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
  151. package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
  152. package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
  153. package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
  154. package/Contexts/FocusableGroupContext/index.ts +7 -0
  155. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
  156. package/Contexts/MenuToggleContext/{index.js → index.ts} +16 -0
  157. package/Contexts/ScreenContext/__tests__/__snapshots__/index.test.tsx.snap +3 -0
  158. package/Contexts/ScreenContext/__tests__/index.test.tsx +27 -0
  159. package/Contexts/ScreenContext/index.tsx +101 -30
  160. package/Contexts/ScreenDataContext/index.tsx +20 -0
  161. package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
  162. package/Decorators/ComponentsMapWrapper/withComponentsMapProvider.tsx +7 -2
  163. package/const.ts +6 -0
  164. package/events/index.ts +1 -0
  165. package/index.js +3 -0
  166. package/package.json +5 -5
  167. package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
  168. package/Components/VideoModal/hooks/usePosition.ts +0 -50
  169. /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
  170. /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
  171. /package/Contexts/{index.js → index.ts} +0 -0
@@ -0,0 +1,76 @@
1
+ import * as React from "react";
2
+ import { ListRenderItemInfo as IListRenderItemInfo } from "react-native";
3
+ import { FocusableItem } from "./FocusableItem";
4
+
5
+ export type IListRenderItem<ItemT> = (
6
+ info: IListRenderItemInfo<ItemT> & {
7
+ focused: boolean;
8
+ onLoadFinished: () => void;
9
+ onLoadFailed: () => void;
10
+ }
11
+ ) => React.ReactElement | null;
12
+
13
+ type Item = ZappEntry | ZappUIComponent | any;
14
+
15
+ type Props = {
16
+ item: Item;
17
+ onFocus: (
18
+ element: FocusManager.FocusableRef,
19
+ renderArgs: { item: Item; index: number },
20
+ direction: FocusManager.Android.FocusNavigationDirections
21
+ ) => void;
22
+ onListElementBlur?: (any, RenderItemProps, Direction) => void;
23
+ onListElementPress?: (any, RenderItemProps) => void;
24
+ onListElementLongPress?: (any, RenderItemProps) => void;
25
+ onListElementPressOut?: (any, RenderItemProps) => void;
26
+ focusableItemProps: any;
27
+ id: string;
28
+ index: number;
29
+ extraData: any;
30
+ extraChildrenData: any;
31
+ renderItem: IListRenderItem<any> | null | undefined;
32
+ getNextFocus: (index: number) => ParentFocus;
33
+ } & ParentFocus;
34
+
35
+ export const FocusableListItemWrapper = React.memo(
36
+ ({
37
+ onFocus,
38
+ onListElementBlur,
39
+ onListElementPress,
40
+ onListElementLongPress,
41
+ onListElementPressOut,
42
+ focusableItemProps,
43
+ id,
44
+ getNextFocus,
45
+ item,
46
+ index,
47
+ extraData,
48
+ extraChildrenData,
49
+ renderItem,
50
+ }: Props) => {
51
+ const nextFocus = getNextFocus(index);
52
+
53
+ return (
54
+ <FocusableItem
55
+ nextFocusDown={nextFocus.nextFocusDown}
56
+ nextFocusRight={nextFocus.nextFocusRight}
57
+ nextFocusLeft={nextFocus.nextFocusLeft}
58
+ nextFocusUp={nextFocus.nextFocusUp}
59
+ id={id}
60
+ focusableItemProps={focusableItemProps}
61
+ onFocus={onFocus}
62
+ item={item}
63
+ index={index}
64
+ renderItem={renderItem}
65
+ extraChildrenData={extraChildrenData}
66
+ extraData={extraData}
67
+ onListElementBlur={onListElementBlur}
68
+ onListElementPress={onListElementPress}
69
+ onListElementLongPress={onListElementLongPress}
70
+ onListElementPressOut={onListElementPressOut}
71
+ />
72
+ );
73
+ }
74
+ );
75
+
76
+ FocusableListItemWrapper.displayName = "FocusableListItemWrapper";
@@ -5,7 +5,9 @@ import { useFocusManager } from "@applicaster/zapp-react-native-utils/focusManag
5
5
  export const useCellState = (id: string) => {
6
6
  const focusManager = useFocusManager();
7
7
 
8
- const [currentCellFocused, setCurrentCellFocused] = React.useState(false);
8
+ const [currentCellFocused, setCurrentCellFocused] = React.useState(
9
+ focusManager.isFocused(id)
10
+ );
9
11
 
10
12
  React.useEffect(() => {
11
13
  const handler = (focusable) => {
@@ -12,8 +12,9 @@ import { ChildrenFocusDeactivatorView } from "../ChildrenFocusDeactivatorView";
12
12
  import { useInitialFocus } from "@applicaster/zapp-react-native-utils/focusManager";
13
13
 
14
14
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
15
- import { FocusableItem } from "./FocusableItem";
15
+
16
16
  import { useParentFocus, useCheckItemIdsForUnique } from "./hooks";
17
+ import { FocusableListItemWrapper } from "./FocusableListItemWrapper";
17
18
 
18
19
  const mapIndexed = R.addIndex(R.map);
19
20
 
@@ -74,6 +75,8 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
74
75
  nextFocusLeft: parentNextFocusLeft,
75
76
  nextFocusUp: parentNextFocusUp,
76
77
 
78
+ numColumns,
79
+
77
80
  focused: parentFocused,
78
81
 
79
82
  focusableItemProps,
@@ -85,6 +88,7 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
85
88
  data = [],
86
89
  // eslint-disable-next-line unused-imports/no-unused-vars
87
90
  onAllComponentsLoaded, // TODO: re-implement it
91
+ omitPropsPropagation = [],
88
92
  } = props;
89
93
 
90
94
  useCheckItemIdsForUnique({ componentId: props.id, items: data });
@@ -103,8 +107,8 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
103
107
  });
104
108
 
105
109
  const getFocusableEntryId = React.useCallback(
106
- (_entry: ZappEntry, index?: number) => {
107
- const entry = R.isNil(index) ? _entry : data[index];
110
+ (_entry: ZappEntry | null, index?: number) => {
111
+ const entry = R.isNil(index) ? _entry : data[index as number];
108
112
 
109
113
  return entry ? getFocusableId(props.id, index) : undefined;
110
114
  },
@@ -143,7 +147,6 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
143
147
  const getNextFocus = React.useCallback(
144
148
  (index) => {
145
149
  const itemCount = R.length(data);
146
- const { numColumns } = props;
147
150
 
148
151
  const getFocusableEntryIdForIndex = (entryIndex) =>
149
152
  getFocusableEntryId(null, entryIndex);
@@ -160,22 +163,23 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
160
163
  });
161
164
 
162
165
  const getGridFocus = () => {
163
- const numberOfRows = Math.ceil(itemCount / numColumns);
164
- const currentRowIndex = Math.floor(index / numColumns);
166
+ const _numColumns = toFiniteNumberWithDefault(1, numColumns);
167
+ const numberOfRows = Math.ceil(itemCount / _numColumns);
168
+ const currentRowIndex = Math.floor(index / _numColumns);
165
169
  const isFirstRow = currentRowIndex === 0;
166
170
  const isLastRow = currentRowIndex + 1 === numberOfRows;
167
- const isLeftEdge = index % numColumns === 0;
171
+ const isLeftEdge = index % _numColumns === 0;
168
172
 
169
173
  const isRightEdge =
170
- (index + 1) % numColumns === 0 || itemCount === index + 1;
174
+ (index + 1) % _numColumns === 0 || itemCount === index + 1;
171
175
 
172
176
  return {
173
177
  nextFocusUp: isFirstRow
174
178
  ? nextFocusUp
175
- : getFocusableEntryIdForIndex(index - numColumns),
179
+ : getFocusableEntryIdForIndex(index - _numColumns),
176
180
  nextFocusDown: isLastRow
177
181
  ? nextFocusDown
178
- : getFocusableEntryIdForIndex(index + numColumns),
182
+ : getFocusableEntryIdForIndex(index + _numColumns),
179
183
  nextFocusLeft: isLeftEdge
180
184
  ? nextFocusLeft
181
185
  : getFocusableEntryIdForIndex(index - 1),
@@ -215,26 +219,24 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
215
219
  const renderItem = React.useCallback(
216
220
  ({ item, index }) => {
217
221
  const id = getFocusableId(props.id, index);
218
- const nextFocus = getNextFocus(index);
219
222
 
220
223
  return (
221
- <FocusableItem
222
- nextFocusDown={nextFocus.nextFocusDown}
223
- nextFocusRight={nextFocus.nextFocusRight}
224
- nextFocusLeft={nextFocus.nextFocusLeft}
225
- nextFocusUp={nextFocus.nextFocusUp}
226
- id={id}
224
+ <FocusableListItemWrapper
225
+ {...{
226
+ onFocus,
227
+ onListElementBlur,
228
+ onListElementPress,
229
+ onListElementLongPress,
230
+ onListElementPressOut,
231
+ }}
232
+ renderItem={props.renderItem}
227
233
  focusableItemProps={focusableItemProps}
228
- onFocus={onFocus}
234
+ getNextFocus={getNextFocus}
235
+ id={id}
229
236
  item={item}
230
237
  index={index}
231
- renderItem={props.renderItem}
232
- extraChildrenData={props.extraChildrenData}
233
238
  extraData={props.extraData}
234
- onListElementBlur={onListElementBlur}
235
- onListElementPress={onListElementPress}
236
- onListElementLongPress={onListElementLongPress}
237
- onListElementPressOut={onListElementPressOut}
239
+ extraChildrenData={props.extraChildrenData}
238
240
  />
239
241
  );
240
242
  },
@@ -251,9 +253,9 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
251
253
  ]
252
254
  );
253
255
 
254
- const flatListProps = React.useMemo(
255
- () =>
256
- R.omit([
256
+ const getFlatListProps = React.useCallback(
257
+ R.omit(
258
+ [
257
259
  "onListElementFocus",
258
260
  "onListElementPress",
259
261
  "onListElementPressOut",
@@ -266,17 +268,17 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
266
268
  "focusableItemProps",
267
269
  "renderItem",
268
270
  "extraChildrenData",
269
- "extraData",
270
- "keyExtractor",
271
271
  "onAllComponentsLoaded",
272
272
  "focused",
273
273
  "id",
274
274
  "initialFocusDirection",
275
275
  "withStateMemory",
276
276
  "useSequentialLoading",
277
- ...(props.omitPropsPropagation || []),
278
- ])(props),
279
- [props]
277
+ ...omitPropsPropagation,
278
+ ],
279
+ R.__
280
+ ),
281
+ [omitPropsPropagation]
280
282
  );
281
283
 
282
284
  const onEndReached = React.useCallback(
@@ -287,8 +289,8 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
287
289
  );
288
290
 
289
291
  const initialNumToRender = toFiniteNumberWithDefault(
290
- data.length,
291
- flatListProps.initialNumToRender
292
+ 10,
293
+ props.initialNumToRender
292
294
  );
293
295
 
294
296
  return (
@@ -297,12 +299,10 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
297
299
  <FlatList
298
300
  focusable={false}
299
301
  scrollEnabled={false}
300
- extraData={props.extraData}
301
302
  ref={ref}
302
- {...flatListProps}
303
+ {...getFlatListProps(props)}
303
304
  renderItem={renderItem}
304
305
  onEndReached={onEndReached}
305
- keyExtractor={props.keyExtractor}
306
306
  initialNumToRender={initialNumToRender}
307
307
  />
308
308
  </ChildrenFocusDeactivatorView>
@@ -284,7 +284,7 @@ function FocusableScrollViewComponent(props: Props, ref) {
284
284
  };
285
285
 
286
286
  return (
287
- <View onLayout={onItemLayout}>
287
+ <View key={id} onLayout={onItemLayout}>
288
288
  <Focusable
289
289
  {...nextFocus}
290
290
  onFocus={(element, direction) =>
@@ -0,0 +1,31 @@
1
+ import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";
2
+ import React, { Suspense, useEffect } from "react";
3
+ import { Suspender } from "./Suspender";
4
+
5
+ interface Props {
6
+ freeze: boolean;
7
+ children: React.ReactNode;
8
+ placeholder?: React.ReactNode;
9
+ onRelease?: () => void;
10
+ }
11
+
12
+ export function Freeze({
13
+ freeze,
14
+ children,
15
+ placeholder = null,
16
+ onRelease,
17
+ }: Props) {
18
+ const prevFreeze = usePrevious(freeze);
19
+
20
+ useEffect(() => {
21
+ if (prevFreeze && !freeze && onRelease) {
22
+ onRelease();
23
+ }
24
+ }, [freeze, onRelease, prevFreeze]);
25
+
26
+ return (
27
+ <Suspense fallback={placeholder}>
28
+ <Suspender freeze={freeze}>{children}</Suspender>
29
+ </Suspense>
30
+ );
31
+ }
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Software Mansion
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,17 @@
1
+ import React, { Fragment } from "react";
2
+
3
+ const infiniteThenable = { then() {} };
4
+
5
+ export const Suspender = ({
6
+ freeze,
7
+ children,
8
+ }: {
9
+ freeze: boolean;
10
+ children: React.ReactNode;
11
+ }) => {
12
+ if (freeze) {
13
+ throw infiniteThenable;
14
+ }
15
+
16
+ return <Fragment>{children}</Fragment>;
17
+ };
@@ -0,0 +1,170 @@
1
+ import React, { Dispatch, useEffect, useState } from "react";
2
+ import {
3
+ act,
4
+ create,
5
+ ReactTestRenderer,
6
+ ReactTestRendererJSON,
7
+ } from "react-test-renderer";
8
+ import { FreezeWithCallback } from "../index";
9
+
10
+ const SimpleContent = () => <div />;
11
+
12
+ interface InnerProps {
13
+ value: number;
14
+ }
15
+ // eslint-disable-next-line unused-imports/no-unused-vars
16
+ const Inner = ({ value }: InnerProps) => <div />;
17
+
18
+ interface TestSubscriber {
19
+ renderCount: number;
20
+ subscription: Dispatch<number>;
21
+ }
22
+
23
+ function createSubscriberComponent(): [React.FC, TestSubscriber] {
24
+ const testState: TestSubscriber = {
25
+ renderCount: 0,
26
+ subscription: () => {},
27
+ };
28
+
29
+ const Subscriber: React.FC = () => {
30
+ const [value, setValue] = useState(0);
31
+
32
+ useEffect(() => {
33
+ testState.subscription = setValue;
34
+ }, []);
35
+
36
+ testState.renderCount += 1;
37
+
38
+ return <Inner value={value} />;
39
+ };
40
+
41
+ return [Subscriber, testState];
42
+ }
43
+
44
+ interface ContainerProps {
45
+ freeze: boolean;
46
+ children: React.ReactNode;
47
+ }
48
+
49
+ const Container = ({ freeze, children }: ContainerProps) => (
50
+ <FreezeWithCallback freeze={freeze}>{children}</FreezeWithCallback>
51
+ );
52
+
53
+ function setupTest(initialFreeze: boolean = false) {
54
+ let testRenderer: ReactTestRenderer | undefined;
55
+ const [Subscriber, subscriberState] = createSubscriberComponent();
56
+
57
+ act(() => {
58
+ testRenderer = create(
59
+ <Container freeze={initialFreeze}>
60
+ <Subscriber />
61
+ </Container>
62
+ );
63
+ });
64
+
65
+ return {
66
+ testRenderer,
67
+ testInstance: testRenderer?.root,
68
+ subscriberState,
69
+ updateFreeze: (freeze: boolean) => {
70
+ act(() =>
71
+ testRenderer?.update(
72
+ <Container freeze={freeze}>
73
+ <Subscriber />
74
+ </Container>
75
+ )
76
+ );
77
+ },
78
+ };
79
+ }
80
+
81
+ describe("FreezeWithCallback", () => {
82
+ test("Renders stuff not frozen", () => {
83
+ const testRenderer = create(
84
+ <Container freeze={false}>
85
+ <SimpleContent />
86
+ </Container>
87
+ );
88
+
89
+ expect(testRenderer.root.findByType(SimpleContent)).toBeTruthy();
90
+ });
91
+
92
+ test("Does not render stuff when frozen", () => {
93
+ const testRenderer = create(
94
+ <Container freeze>
95
+ <SimpleContent />
96
+ </Container>
97
+ );
98
+
99
+ expect(testRenderer.root.findAllByType(SimpleContent)).toHaveLength(0);
100
+ });
101
+
102
+ test("Stuff is gone after freeze", () => {
103
+ const testRenderer = create(
104
+ <Container freeze={false}>
105
+ <SimpleContent />
106
+ </Container>
107
+ );
108
+
109
+ expect(testRenderer.root.findByType(SimpleContent)).toBeTruthy();
110
+
111
+ act(() =>
112
+ testRenderer.update(
113
+ <Container freeze>
114
+ <SimpleContent />
115
+ </Container>
116
+ )
117
+ );
118
+
119
+ expect(testRenderer.toJSON()).toBe(null);
120
+ });
121
+
122
+ test("Updates work when not frozen", () => {
123
+ const { testInstance, subscriberState } = setupTest();
124
+
125
+ expect(testInstance?.findByType(Inner).props.value).toEqual(0);
126
+ act(() => subscriberState.subscription(1));
127
+ expect(testInstance?.findByType(Inner).props.value).toEqual(1);
128
+ expect(subscriberState.renderCount).toBe(2);
129
+ });
130
+
131
+ test("Updates does not propagate when frozen", () => {
132
+ const { testInstance, subscriberState, updateFreeze } = setupTest();
133
+
134
+ expect(testInstance?.findByType(Inner).props.value).toEqual(0);
135
+ updateFreeze(true);
136
+ act(() => subscriberState.subscription(1));
137
+ expect(testInstance?.findByType(Inner).props.value).toEqual(0);
138
+ expect(subscriberState.renderCount).toBe(1);
139
+ });
140
+
141
+ test("State persists after defrost", () => {
142
+ const { testInstance, subscriberState, updateFreeze, testRenderer } =
143
+ setupTest();
144
+
145
+ expect(testInstance?.findByType(Inner).props.value).toEqual(0);
146
+ act(() => subscriberState.subscription(1));
147
+ expect(testInstance?.findByType(Inner).props.value).toEqual(1);
148
+
149
+ updateFreeze(true);
150
+ expect(testRenderer?.toJSON()).toBe(null);
151
+
152
+ updateFreeze(false);
153
+ expect((testRenderer?.toJSON() as ReactTestRendererJSON).type).toBe("div");
154
+ expect(testInstance?.findByType(Inner).props.value).toEqual(1);
155
+ });
156
+
157
+ test("Update propagate after defrost", () => {
158
+ const { testInstance, subscriberState, updateFreeze } = setupTest();
159
+
160
+ updateFreeze(true);
161
+ act(() => subscriberState.subscription(1));
162
+ act(() => subscriberState.subscription(2));
163
+ act(() => subscriberState.subscription(3));
164
+ expect(testInstance?.findByType(Inner).props.value).toEqual(0);
165
+
166
+ updateFreeze(false);
167
+ expect(testInstance?.findByType(Inner).props.value).toEqual(3);
168
+ expect(subscriberState.renderCount).toBe(2);
169
+ });
170
+ });
@@ -0,0 +1,3 @@
1
+ import { Freeze } from "./Freeze";
2
+
3
+ export const FreezeWithCallback = Freeze;
@@ -52,13 +52,13 @@ export const GeneralContentScreen = ({
52
52
  React.useState(actionsInitialStateSetters.length === 0);
53
53
 
54
54
  useEffect(() => {
55
- log_info(
56
- `ScreenContainer: starting to check river action provider ready, current status: ${riverActionProvidersReady}`,
57
- { actionsInitialStateSetters }
58
- );
59
-
60
55
  if (!riverActionProvidersReady) {
61
56
  if (actionsInitialStateSetters.length > 0) {
57
+ log_info(
58
+ "ScreenContainer: starting to check river action providers to initialize",
59
+ { actionsInitialStateSetters }
60
+ );
61
+
62
62
  allSettled(actionsInitialStateSetters).finally(() => {
63
63
  log_info(
64
64
  "ScreenContainer: action provider ready, completed. Starting to present screen"
@@ -98,13 +98,16 @@ export const GeneralContentScreen = ({
98
98
 
99
99
  const isReady = useRiverActionProviderReady();
100
100
 
101
+ const contextValue = React.useMemo(
102
+ () => whenMatchingType(Function, cellTapAction) || onCellTapAction,
103
+ [typeof cellTapAction === "function" ? cellTapAction : onCellTapAction]
104
+ );
105
+
101
106
  if (!isReady || isNilOrEmpty(components || uiComponents)) return null;
102
107
 
103
108
  return (
104
109
  <ScreenTrackedViewPositionsContext.Provider>
105
- <CellTapContext.Provider
106
- value={whenMatchingType(Function, cellTapAction) || onCellTapAction}
107
- >
110
+ <CellTapContext.Provider value={contextValue}>
108
111
  <ComponentsMap
109
112
  feed={feed}
110
113
  riverId={screenId}
@@ -50,7 +50,11 @@ describe("getTransformedPreset should return the passed components if smartCompo
50
50
  );
51
51
 
52
52
  expect(transformed_preset).toBeDefined();
53
- expect(transformed_preset.id).toEqual("comp10"); // id was transformed
53
+
54
+ expect(transformed_preset.id).toEqual(
55
+ `${mockComp.id}-${mockPreset.feed_url}-0`
56
+ ); // id was transformed
57
+
54
58
  expect(transformed_preset.data.source).toEqual(mockPreset.feed_url); // source was replaced with preset feed_url.
55
59
  });
56
60
  });
@@ -117,7 +121,10 @@ describe("getTransformedPreset should return the passed components if smartCompo
117
121
 
118
122
  expect(Array.isArray(result)).toBe(true);
119
123
  expect(result).toHaveLength(2);
120
- expect(result[0].id).toEqual("smart10");
124
+
125
+ expect(result[0].id).toEqual(
126
+ `${mockSmartComp.id}-${mockFeeds.preset_source.data.entry[0].feed_url}-0`
127
+ );
121
128
 
122
129
  expect(result[0].data.source).toEqual(
123
130
  mockFeeds.preset_source.data.entry[0].feed_url
@@ -287,7 +294,7 @@ describe("getTransformedPreset should return the passed components if smartCompo
287
294
  mockComponents[0],
288
295
  {
289
296
  ...mockLayoutPresets.preset_one,
290
- id: mockComponents[1].id + 1,
297
+ id: `${mockComponents[1].id}-${mockPresetEntry[0].feed_url}-1`,
291
298
  data: { source: mockPresetEntry[0].feed_url },
292
299
  },
293
300
  ];
@@ -344,12 +351,12 @@ describe("getTransformedPreset should return the passed components if smartCompo
344
351
  const mockTransformedComponents = [
345
352
  {
346
353
  ...mockLayoutPresets.preset_one,
347
- id: mockComponents[0].id + 0,
354
+ id: `${mockComponents[0].id}-${mockPresetEntry1[0].feed_url}-0`,
348
355
  data: { source: mockPresetEntry1[0].feed_url },
349
356
  },
350
357
  {
351
358
  ...mockLayoutPresets.preset_one,
352
- id: mockComponents[1].id + 1,
359
+ id: `${mockComponents[1].id}-${mockPresetEntry2[0].feed_url}-1`,
353
360
  data: { source: mockPresetEntry2[0].feed_url },
354
361
  },
355
362
  ];
@@ -412,7 +419,7 @@ describe("getTransformedPreset should return the passed components if smartCompo
412
419
  const mockTransformedComponents = mockPresetEntry.map((preset, i) => {
413
420
  return {
414
421
  ...mockLayoutPresets[preset.preset_name],
415
- id: mockComponents[0].id + i,
422
+ id: `${mockComponents[0].id}-${preset.feed_url}-${i}`,
416
423
  data: { source: preset.feed_url },
417
424
  };
418
425
  });
@@ -41,7 +41,7 @@ export const getTransformedPreset = (
41
41
  }
42
42
 
43
43
  const updatedPresetComponent = produce(presetComponent, (draft) => {
44
- draft.id = `${componentId}${index}`;
44
+ draft.id = `${componentId}-${preset?.feed_url}-${index}`;
45
45
  draft.data.source = preset?.feed_url;
46
46
 
47
47
  if (draft.ui_components) {
@@ -19,6 +19,7 @@ type Props = {
19
19
  modalHeight?: number;
20
20
  modalWidth?: number;
21
21
  mode: VideoModalMode;
22
+ groupId?: string;
22
23
  };
23
24
 
24
25
  const YOUTUBE_PLUGIN_ID = "youtube-player-qb";
@@ -93,6 +94,7 @@ export function HandlePlayable({
93
94
  modalHeight,
94
95
  modalWidth,
95
96
  mode,
97
+ groupId,
96
98
  }: Props): React.ReactElement | null {
97
99
  const state = usePickFromState();
98
100
 
@@ -104,7 +106,7 @@ export function HandlePlayable({
104
106
  const [Player, playerModuleProperties] = getPlayer(item, state);
105
107
 
106
108
  const { module: CastPlugin } =
107
- findPluginByIdentifier(CHROMECAST_PLUGIN_ID, state.plugins) || {};
109
+ findPluginByIdentifier(CHROMECAST_PLUGIN_ID, state.plugins, true) || {};
108
110
 
109
111
  const [playable, setPlayable] =
110
112
  React.useState<Nullable<PlayableComponent>>(null);
@@ -167,6 +169,7 @@ export function HandlePlayable({
167
169
  isModal={isModal}
168
170
  mode={mode}
169
171
  disableCastAction={playerModuleProperties?.disableCastAction}
172
+ groupId={groupId}
170
173
  />
171
174
  );
172
175
  }
@@ -1,6 +1 @@
1
- import * as R from "ramda";
2
-
3
- import { withNavigator } from "@applicaster/zapp-react-native-ui-components/Decorators/Navigator/";
4
- import { HookRenderer as HookRendererComponent } from "./HookRenderer";
5
-
6
- export const HookRenderer = R.compose(withNavigator)(HookRendererComponent);
1
+ export { HookRenderer } from "./HookRenderer";