@applicaster/zapp-react-native-ui-components 12.0.0-alpha.5251355865 → 12.0.0-alpha.5347482137

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 (112) hide show
  1. package/Components/BaseFocusable/index.tsx +3 -3
  2. package/Components/Cell/Cell.tsx +7 -7
  3. package/Components/Cell/CellWithFocusable.tsx +40 -19
  4. package/Components/Cell/TvOSCellComponent.tsx +5 -9
  5. package/Components/ErrorScreen/index.tsx +1 -1
  6. package/Components/Focusable/Focusable.tsx +6 -1
  7. package/Components/FocusableCell/index.tsx +7 -7
  8. package/Components/FocusableGroup/index.tsx +11 -2
  9. package/Components/FocusableList/FocusableChild.ts +24 -14
  10. package/Components/FocusableList/FocusableItem.tsx +1 -1
  11. package/Components/FocusableList/FocusableListItemWrapper.tsx +76 -0
  12. package/Components/FocusableList/hooks/useCellState.android.ts +3 -1
  13. package/Components/FocusableList/index.tsx +26 -25
  14. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +6 -3
  15. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
  16. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
  17. package/Components/HandlePlayable/HandlePlayable.tsx +3 -0
  18. package/Components/Image/Image.android.tsx +3 -25
  19. package/Components/Image/Image.tsx +18 -2
  20. package/Components/Image/utils/index.ts +39 -0
  21. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
  22. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -10
  23. package/Components/Layout/TV/__tests__/index.test.tsx +19 -75
  24. package/Components/Layout/TV/index.tsx +12 -11
  25. package/Components/Layout/TV/index.web.tsx +33 -0
  26. package/Components/Layout/index.tsx +15 -11
  27. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -0
  28. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +88 -112
  29. package/Components/MasterCell/DefaultComponents/DynamicBadge.tsx +4 -2
  30. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -1
  31. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +9 -26
  32. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -3
  33. package/Components/MasterCell/DefaultComponents/Image/hooks/useImageSize.ts +65 -0
  34. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -1
  35. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +17 -2
  36. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +3 -2
  37. package/Components/MasterCell/DefaultComponents/Text/index.tsx +14 -2
  38. package/Components/MasterCell/DefaultComponents/Toggle.tsx +5 -1
  39. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
  40. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +24 -9
  41. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
  42. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
  43. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -3
  44. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
  45. package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
  46. package/Components/MasterCell/utils/index.ts +33 -35
  47. package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
  48. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
  49. package/Components/Navigator/StackNavigator.tsx +54 -0
  50. package/Components/Navigator/index.ts +1 -0
  51. package/Components/PlayerContainer/PlayerContainer.tsx +69 -31
  52. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -1
  53. package/Components/PlayerImageBackground/index.tsx +34 -8
  54. package/Components/River/ComponentsMap/ComponentsMap.tsx +92 -22
  55. package/Components/River/RefreshControl.tsx +55 -1
  56. package/Components/River/TV/River.tsx +2 -25
  57. package/Components/River/__tests__/componentsMap.test.js +7 -0
  58. package/Components/River/useScreenConfiguration.ts +2 -2
  59. package/Components/Screen/TV/hooks/index.ts +3 -0
  60. package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
  61. package/Components/Screen/TV/hooks/useRestoreFocus.ts +22 -0
  62. package/Components/Screen/TV/index.web.tsx +186 -0
  63. package/Components/Screen/__tests__/Screen.test.tsx +8 -1
  64. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +14 -0
  65. package/Components/Screen/index.tsx +78 -30
  66. package/Components/ScreenResolver/index.tsx +3 -1
  67. package/Components/Tabs/TV/styles.ts +1 -1
  68. package/Components/Transitioner/Scene.tsx +24 -19
  69. package/Components/Transitioner/Transitioner.tsx +21 -28
  70. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +6 -2
  71. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +1 -0
  72. package/Components/VideoLive/LiveImageManager.ts +28 -1
  73. package/Components/VideoLive/PlayerLiveImageComponent.tsx +31 -28
  74. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +45 -3
  75. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +18 -28
  76. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +78 -0
  77. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +352 -0
  78. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +158 -0
  79. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +455 -0
  80. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +162 -0
  81. package/Components/VideoModal/ModalAnimation/index.ts +15 -0
  82. package/Components/VideoModal/ModalAnimation/utils.ts +301 -0
  83. package/Components/VideoModal/PlayerDetails.tsx +3 -3
  84. package/Components/VideoModal/PlayerWrapper.tsx +77 -47
  85. package/Components/VideoModal/VideoModal.tsx +23 -40
  86. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +13 -1
  87. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +157 -105
  88. package/Components/VideoModal/utils.ts +8 -5
  89. package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +31 -40
  90. package/Components/Viewport/ViewportTracker/index.tsx +1 -0
  91. package/Components/Viewport/utils.js +1 -1
  92. package/Components/ZappUIComponent/index.tsx +22 -12
  93. package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
  94. package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
  95. package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
  96. package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
  97. package/Contexts/FocusableGroupContext/index.ts +7 -0
  98. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
  99. package/Contexts/MenuToggleContext/{index.js → index.ts} +16 -0
  100. package/Contexts/ScreenContext/__tests__/__snapshots__/index.test.tsx.snap +3 -0
  101. package/Contexts/ScreenContext/__tests__/index.test.tsx +27 -0
  102. package/Contexts/ScreenContext/index.tsx +94 -28
  103. package/Contexts/ScreenDataContext/index.tsx +20 -0
  104. package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
  105. package/Decorators/ComponentsMapWrapper/withComponentsMapProvider.tsx +7 -2
  106. package/events/index.ts +1 -0
  107. package/package.json +5 -5
  108. package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
  109. package/Components/VideoModal/hooks/usePosition.ts +0 -50
  110. /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
  111. /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
  112. /package/Contexts/{index.js → index.ts} +0 -0
@@ -66,14 +66,14 @@ export class BaseFocusable<
66
66
  const { initialFocus } = this.props;
67
67
 
68
68
  if (initialFocus) {
69
- focusManager.setupInitialFocus(this.getId());
69
+ focusManager.setFocus(this.getId());
70
70
  }
71
71
  }
72
72
 
73
73
  getId(props?: { id: string; groupId: string }) {
74
- const { id, groupId = null } = props || this.props;
74
+ const { id } = props || this.props;
75
75
 
76
- return groupId ? `${groupId}-${id}` : id;
76
+ return id;
77
77
  }
78
78
 
79
79
  getRect() {
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import * as R from "ramda";
2
+ import { join, path, merge } from "ramda";
3
3
  import { View } from "react-native";
4
4
 
5
5
  import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable";
@@ -105,8 +105,8 @@ export class CellComponent extends React.Component<Props> {
105
105
  onPress(item, index);
106
106
  } else {
107
107
  const { item, navigator, component, appData } = this.props;
108
- const itemTargetScreen = R.path(["screen_type"], item);
109
- const componentTargetScreen = R.path(["data", "target"], component);
108
+ const itemTargetScreen = path(["screen_type"], item);
109
+ const componentTargetScreen = path(["data", "target"], component);
110
110
  const itemType = getItemType(item, appData?.layoutVersion);
111
111
 
112
112
  sendSelectCellEvent(item, component, item.title, index);
@@ -116,7 +116,7 @@ export class CellComponent extends React.Component<Props> {
116
116
  componentTargetScreen &&
117
117
  !itemTargetScreen
118
118
  ) {
119
- navigator.push(R.merge(item, { screen_type: componentTargetScreen }));
119
+ navigator.push(merge(item, { screen_type: componentTargetScreen }));
120
120
 
121
121
  return;
122
122
  }
@@ -202,12 +202,12 @@ export class CellComponent extends React.Component<Props> {
202
202
  groupId,
203
203
  isFocusable = true,
204
204
  skipFocusManagerRegistration,
205
- behavior
205
+ behavior,
206
206
  } = this.props;
207
207
 
208
208
  const { id } = item;
209
209
 
210
- const focusableId = R.join("-", [component?.id, id, index]);
210
+ const focusableId = join("-", [component?.id, id, index]);
211
211
 
212
212
  const handleFocus = (focusable, mouse) => {
213
213
  const focusFn = onFocus || noop;
@@ -263,7 +263,7 @@ export class CellComponent extends React.Component<Props> {
263
263
  item,
264
264
  focused: this.isCellFocused(focused),
265
265
  scrollTo: this.scrollToIndex(event),
266
- behavior
266
+ behavior,
267
267
  }}
268
268
  />
269
269
  );
@@ -3,15 +3,25 @@ import * as React from "react";
3
3
  import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
4
4
  import * as FOCUS_EVENTS from "@applicaster/zapp-react-native-utils/appUtils/focusManager/events";
5
5
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
6
+ import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
7
+
6
8
  import { isAppleTV } from "../../Helpers/Platform";
7
9
  import { useCellState } from "../MasterCell/utils";
8
10
 
9
- const useCellFocusedState = (groupId: string, id: string) => {
11
+ const useCellFocusedState = (
12
+ skipFocusManagerRegistration: boolean,
13
+ groupId: string,
14
+ id: string
15
+ ) => {
10
16
  const [currentCellFocused, setCurrentCellFocused] = React.useState(false);
11
17
 
12
18
  React.useEffect(() => {
13
- const isGroupItemFocused = () =>
14
- setCurrentCellFocused(focusManager.isGroupItemFocused(groupId, id));
19
+ const isGroupItemFocused = () => {
20
+ if (!skipFocusManagerRegistration) {
21
+ const isFocused = focusManager.isGroupItemFocused(groupId, id);
22
+ setCurrentCellFocused(isFocused);
23
+ }
24
+ };
15
25
 
16
26
  const handler = () => {
17
27
  // tvOS hack for properly checking focus
@@ -29,24 +39,24 @@ const useCellFocusedState = (groupId: string, id: string) => {
29
39
  return () => {
30
40
  focusManager.removeHandler(FOCUS_EVENTS.FOCUS, handler);
31
41
  };
32
- }, [groupId]);
42
+ }, [groupId, skipFocusManagerRegistration]);
33
43
 
34
44
  return currentCellFocused;
35
45
  };
36
46
 
37
47
  type Props = {
38
- item: ZappEntry,
39
- CellRenderer: React.ComponentType<{ item: Object, state: string }>,
40
- id: string,
41
- groupId: string,
42
- onFocus: Function,
43
- index: number,
44
- scrollTo: Function,
45
- preferredFocus?: boolean,
46
- skipFocusManagerRegistration?: boolean,
47
- isFocusable?: boolean,
48
- behavior: Behavior,
49
- selected?: boolean,
48
+ item: ZappEntry;
49
+ CellRenderer: React.ComponentType<{ item: Object; state: string }>;
50
+ id: string;
51
+ groupId: string;
52
+ onFocus: Function;
53
+ index: number;
54
+ scrollTo: Function;
55
+ preferredFocus?: boolean;
56
+ skipFocusManagerRegistration?: boolean;
57
+ isFocusable?: boolean;
58
+ behavior: Behavior;
59
+ focused?: boolean;
50
60
  };
51
61
 
52
62
  export function CellWithFocusable(props: Props) {
@@ -62,11 +72,21 @@ export function CellWithFocusable(props: Props) {
62
72
  skipFocusManagerRegistration,
63
73
  isFocusable,
64
74
  behavior,
65
- selected
75
+ focused,
66
76
  } = props;
67
77
 
68
- const isFocused = useCellFocusedState(groupId, id);
69
- const state = useCellState({ id: item.id, behavior, focused: isFocused, selected });
78
+ const isFocused = useCellFocusedState(
79
+ skipFocusManagerRegistration,
80
+ groupId,
81
+ id
82
+ );
83
+
84
+ const state = useCellState({
85
+ id: item.id,
86
+ behavior,
87
+ focused: isFocused || toBooleanWithDefaultFalse(focused),
88
+ });
89
+
70
90
  const [focusedButtonId, setFocusedButtonId] = React.useState(undefined);
71
91
 
72
92
  // for horizontal scrolling
@@ -95,6 +115,7 @@ export function CellWithFocusable(props: Props) {
95
115
  preferredFocus={preferredFocus}
96
116
  skipFocusManagerRegistration={skipFocusManagerRegistration}
97
117
  isFocusable={isFocusable}
118
+ focused={focused}
98
119
  />
99
120
  );
100
121
  }
@@ -226,13 +226,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
226
226
 
227
227
  hasReceivedFocus() {}
228
228
 
229
- scrollTo(index) {
230
- // Workaround for HL autoscroll/autoloop feature, we scroll manually on tvOS.
231
- // Should be removed after refactoring autoscroll/autoloop feature
232
- if (this.props.component?.component_type !== "horizontal_list_qb") {
233
- this.props?.scrollToIndex?.(index);
234
- }
235
- }
229
+ scrollTo() {}
236
230
 
237
231
  render() {
238
232
  const {
@@ -249,7 +243,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
249
243
  hasReceivedFocus,
250
244
  groupId,
251
245
  isFocusable,
252
- behavior
246
+ behavior,
253
247
  } = this.props;
254
248
 
255
249
  const { id } = item;
@@ -279,7 +273,9 @@ export class TvOSCellComponent extends React.Component<Props, State> {
279
273
  index={index}
280
274
  scrollTo={this.scrollTo}
281
275
  preferredFocus={preferredFocus}
276
+ focused={this.props.focused}
282
277
  behavior={behavior}
278
+ isFocusable={isFocusable}
283
279
  />
284
280
  </View>
285
281
  );
@@ -310,7 +306,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
310
306
  item,
311
307
  focused: this.props.focused || selectedCell || focused,
312
308
  scrollTo: this.scrollTo,
313
- behavior
309
+ behavior,
314
310
  }}
315
311
  />
316
312
  );
@@ -3,7 +3,6 @@ import * as R from "ramda";
3
3
  import {
4
4
  View,
5
5
  StyleSheet,
6
- Image,
7
6
  Text,
8
7
  ViewStyle,
9
8
  TextStyle,
@@ -13,6 +12,7 @@ import {
13
12
  import { defaultAsset } from "./defaultAsset";
14
13
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
15
14
  import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
15
+ import { QBImage as Image } from "@applicaster/zapp-react-native-ui-components/Components/Image";
16
16
  import { Button } from "./Button";
17
17
 
18
18
  type ButtonProp = {
@@ -4,6 +4,7 @@ import * as R from "ramda";
4
4
  import { BaseFocusable } from "../BaseFocusable";
5
5
  import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
6
6
  import { LONG_KEY_PRESS_TIMEOUT } from "@applicaster/quick-brick-core/const";
7
+ import { withFocusableContext } from "../../Contexts/FocusableGroupContext/withFocusableContext";
7
8
 
8
9
  type Props = {
9
10
  initialFocus?: boolean;
@@ -23,7 +24,7 @@ type Props = {
23
24
  style?: React.CSSProperties;
24
25
  };
25
26
 
26
- export class Focusable extends BaseFocusable<Props> {
27
+ class Focusable extends BaseFocusable<Props> {
27
28
  isGroup: boolean;
28
29
  mouse: boolean;
29
30
  longPressTimeout = null;
@@ -198,3 +199,7 @@ export class Focusable extends BaseFocusable<Props> {
198
199
  );
199
200
  }
200
201
  }
202
+
203
+ const FocusableWithFocusableContext = withFocusableContext(Focusable);
204
+
205
+ export { FocusableWithFocusableContext as Focusable };
@@ -3,12 +3,12 @@ import { Platform } from "react-native";
3
3
  import { useCellState } from "../MasterCell/utils";
4
4
 
5
5
  type Props = {
6
- index: number,
7
- item: ZappEntry,
8
- focused: boolean,
9
- scrollTo?: (index: number) => void,
10
- CellRenderer: React.ComponentType<{ item: Object, state: string }>,
11
- behavior: Behavior
6
+ index: number;
7
+ item: ZappEntry;
8
+ focused: boolean;
9
+ scrollTo?: (index: number) => void;
10
+ CellRenderer: React.ComponentType<{ item: Object; state: string }>;
11
+ behavior: Behavior;
12
12
  };
13
13
 
14
14
  const isAndroid = Platform.OS === "android";
@@ -20,7 +20,7 @@ export function FocusableCell(props: Props) {
20
20
  CellRenderer,
21
21
  focused,
22
22
  scrollTo = null,
23
- behavior
23
+ behavior,
24
24
  } = props;
25
25
 
26
26
  const state = useCellState({ id: item.id, behavior, focused });
@@ -1,6 +1,8 @@
1
1
  import * as React from "react";
2
2
 
3
3
  import { BaseFocusable } from "../BaseFocusable";
4
+ import { FocusableGroupContextProvider } from "../../Contexts/FocusableGroupContext";
5
+ import { withFocusableContext } from "../../Contexts/FocusableGroupContext/withFocusableContext";
4
6
 
5
7
  type Props = {
6
8
  testID?: string;
@@ -26,9 +28,10 @@ type Props = {
26
28
  nextFocusLeft?: any;
27
29
  nextFocusRight?: any;
28
30
  onLayout?: any;
31
+ excludeFromFocusSearching?: boolean;
29
32
  };
30
33
 
31
- export class FocusableGroup extends BaseFocusable<Props> {
34
+ class FocusableGroup extends BaseFocusable<Props> {
32
35
  constructor(props) {
33
36
  super(props);
34
37
  this.isGroup = true;
@@ -81,8 +84,14 @@ export class FocusableGroup extends BaseFocusable<Props> {
81
84
  style={style}
82
85
  ref={this.ref}
83
86
  >
84
- {children}
87
+ <FocusableGroupContextProvider id={id}>
88
+ {children}
89
+ </FocusableGroupContextProvider>
85
90
  </div>
86
91
  );
87
92
  }
88
93
  }
94
+
95
+ const FocusableGroupWithFocusableContext = withFocusableContext(FocusableGroup);
96
+
97
+ export { FocusableGroupWithFocusableContext as FocusableGroup };
@@ -1,28 +1,38 @@
1
1
  import * as React from "react";
2
2
  import { isEqual } from "@applicaster/zapp-react-native-utils/equalUtils";
3
- import { FocusableContext } from "../Focusable/index.android";
3
+ import { useFocusable } from "../Focusable/index.android";
4
4
  import { useCellState } from "./hooks/useCellState.android";
5
5
 
6
- function FocusableChildComponent(props) {
7
- const context = React.useContext(FocusableContext);
8
-
9
- const { setIsFocusable } = context || {};
10
-
11
- const nextFocusDown = props.nextFocusDown ?? context.nextFocusDown;
12
- const nextFocusUp = props.nextFocusUp ?? context.nextFocusUp;
13
- const nextFocusRight = props.nextFocusRight ?? context.nextFocusRight;
14
- const nextFocusLeft = props.nextFocusLeft ?? context.nextFocusLeft;
15
-
16
- const parentFocus = React.useMemo(
6
+ /**
7
+ * returns parent focus from prop or from context
8
+ * */
9
+ const useParentFocus = (
10
+ { nextFocusDown, nextFocusUp, nextFocusRight, nextFocusLeft },
11
+ context
12
+ ) =>
13
+ React.useMemo(
17
14
  () => ({
15
+ nextFocusRight: nextFocusRight ?? context.nextFocusRight,
16
+ nextFocusDown: nextFocusDown ?? context.nextFocusDown,
17
+ nextFocusLeft: nextFocusLeft ?? context.nextFocusLeft,
18
+ nextFocusUp: nextFocusUp ?? context.nextFocusUp,
19
+ }),
20
+ [
18
21
  nextFocusRight,
19
22
  nextFocusDown,
20
23
  nextFocusLeft,
21
24
  nextFocusUp,
22
- }),
23
- [nextFocusRight, nextFocusDown, nextFocusLeft, nextFocusUp]
25
+ context.nextFocusLeft,
26
+ context.nextFocusRight,
27
+ context.nextFocusUp,
28
+ context.nextFocusDown,
29
+ ]
24
30
  );
25
31
 
32
+ function FocusableChildComponent(props) {
33
+ const { setIsFocusable, ...focusableContext } = useFocusable();
34
+ const parentFocus = useParentFocus(props, focusableContext);
35
+
26
36
  const onLoadFailed = React.useCallback(() => {
27
37
  setIsFocusable(false);
28
38
  }, []);
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import { Focusable } from "../Focusable";
2
+ import { Focusable } from "../Focusable/index.android";
3
3
  import { FocusableChild } from "./FocusableChild";
4
4
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
5
5
  import { IListRenderItem } from "./index";
@@ -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,
@@ -103,8 +106,8 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
103
106
  });
104
107
 
105
108
  const getFocusableEntryId = React.useCallback(
106
- (_entry: ZappEntry, index?: number) => {
107
- const entry = R.isNil(index) ? _entry : data[index];
109
+ (_entry: ZappEntry | null, index?: number) => {
110
+ const entry = R.isNil(index) ? _entry : data[index as number];
108
111
 
109
112
  return entry ? getFocusableId(props.id, index) : undefined;
110
113
  },
@@ -143,7 +146,6 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
143
146
  const getNextFocus = React.useCallback(
144
147
  (index) => {
145
148
  const itemCount = R.length(data);
146
- const { numColumns } = props;
147
149
 
148
150
  const getFocusableEntryIdForIndex = (entryIndex) =>
149
151
  getFocusableEntryId(null, entryIndex);
@@ -160,22 +162,23 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
160
162
  });
161
163
 
162
164
  const getGridFocus = () => {
163
- const numberOfRows = Math.ceil(itemCount / numColumns);
164
- const currentRowIndex = Math.floor(index / numColumns);
165
+ const _numColumns = toFiniteNumberWithDefault(1, numColumns);
166
+ const numberOfRows = Math.ceil(itemCount / _numColumns);
167
+ const currentRowIndex = Math.floor(index / _numColumns);
165
168
  const isFirstRow = currentRowIndex === 0;
166
169
  const isLastRow = currentRowIndex + 1 === numberOfRows;
167
- const isLeftEdge = index % numColumns === 0;
170
+ const isLeftEdge = index % _numColumns === 0;
168
171
 
169
172
  const isRightEdge =
170
- (index + 1) % numColumns === 0 || itemCount === index + 1;
173
+ (index + 1) % _numColumns === 0 || itemCount === index + 1;
171
174
 
172
175
  return {
173
176
  nextFocusUp: isFirstRow
174
177
  ? nextFocusUp
175
- : getFocusableEntryIdForIndex(index - numColumns),
178
+ : getFocusableEntryIdForIndex(index - _numColumns),
176
179
  nextFocusDown: isLastRow
177
180
  ? nextFocusDown
178
- : getFocusableEntryIdForIndex(index + numColumns),
181
+ : getFocusableEntryIdForIndex(index + _numColumns),
179
182
  nextFocusLeft: isLeftEdge
180
183
  ? nextFocusLeft
181
184
  : getFocusableEntryIdForIndex(index - 1),
@@ -215,26 +218,24 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
215
218
  const renderItem = React.useCallback(
216
219
  ({ item, index }) => {
217
220
  const id = getFocusableId(props.id, index);
218
- const nextFocus = getNextFocus(index);
219
221
 
220
222
  return (
221
- <FocusableItem
222
- nextFocusDown={nextFocus.nextFocusDown}
223
- nextFocusRight={nextFocus.nextFocusRight}
224
- nextFocusLeft={nextFocus.nextFocusLeft}
225
- nextFocusUp={nextFocus.nextFocusUp}
226
- id={id}
223
+ <FocusableListItemWrapper
224
+ {...{
225
+ onFocus,
226
+ onListElementBlur,
227
+ onListElementPress,
228
+ onListElementLongPress,
229
+ onListElementPressOut,
230
+ }}
231
+ renderItem={props.renderItem}
227
232
  focusableItemProps={focusableItemProps}
228
- onFocus={onFocus}
233
+ getNextFocus={getNextFocus}
234
+ id={id}
229
235
  item={item}
230
236
  index={index}
231
- renderItem={props.renderItem}
232
- extraChildrenData={props.extraChildrenData}
233
237
  extraData={props.extraData}
234
- onListElementBlur={onListElementBlur}
235
- onListElementPress={onListElementPress}
236
- onListElementLongPress={onListElementLongPress}
237
- onListElementPressOut={onListElementPressOut}
238
+ extraChildrenData={props.extraChildrenData}
238
239
  />
239
240
  );
240
241
  },
@@ -287,7 +288,7 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
287
288
  );
288
289
 
289
290
  const initialNumToRender = toFiniteNumberWithDefault(
290
- data.length,
291
+ 10,
291
292
  flatListProps.initialNumToRender
292
293
  );
293
294
 
@@ -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) {