@applicaster/zapp-react-native-ui-components 14.0.0-alpha.9119252693 → 14.0.0-alpha.9203091422

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 (35) hide show
  1. package/Components/AnimatedInOut/index.tsx +5 -3
  2. package/Components/AudioPlayer/mobile/__tests__/__snapshots__/audioPlayerMobileLayout.test.js.snap +0 -6
  3. package/Components/Focusable/Focusable.tsx +5 -3
  4. package/Components/Focusable/FocusableTvOS.tsx +3 -3
  5. package/Components/Focusable/FocusableiOS.tsx +1 -1
  6. package/Components/FocusableList/index.tsx +4 -0
  7. package/Components/HandlePlayable/HandlePlayable.tsx +14 -8
  8. package/Components/MasterCell/elementMapper.tsx +1 -2
  9. package/Components/MasterCell/utils/behaviorProvider.ts +82 -14
  10. package/Components/MasterCell/utils/index.ts +23 -3
  11. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +13 -18
  12. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +9 -0
  13. package/Components/PlayerContainer/PlayerContainer.tsx +26 -22
  14. package/Components/River/ComponentsMap/ComponentsMap.tsx +0 -1
  15. package/Components/River/TV/withTVEventHandler.tsx +1 -1
  16. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -0
  17. package/Components/ScreenRevealManager/ScreenRevealManager.ts +76 -0
  18. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +107 -0
  19. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +96 -0
  20. package/Components/ScreenRevealManager/index.ts +1 -0
  21. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +79 -0
  22. package/Components/Tabs/TV/Tabs.android.tsx +0 -2
  23. package/Components/Touchable/__tests__/__snapshots__/touchable.test.tsx.snap +34 -0
  24. package/Components/Transitioner/Scene.tsx +1 -0
  25. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +15 -9
  26. package/Components/VideoLive/animationUtils.ts +3 -3
  27. package/Components/VideoModal/hooks/useModalSize.ts +3 -1
  28. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +12 -16
  29. package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +84 -24
  30. package/Decorators/ConfigurationWrapper/withConfigurationProvider.tsx +2 -2
  31. package/Decorators/RiverFeedLoader/index.tsx +8 -2
  32. package/Decorators/RiverFeedLoader/utils/index.ts +7 -2
  33. package/Decorators/ZappPipesDataConnector/index.tsx +20 -2
  34. package/package.json +5 -6
  35. package/Components/AudioPlayer/AudioPlayerLayout.tsx +0 -202
@@ -4,9 +4,11 @@ import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/uti
4
4
  import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
5
5
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
6
6
 
7
- type AnimatedInterpolatedStyle =
8
- | Animated.AnimatedInterpolation
9
- | [{ [Key: string]: Animated.AnimatedInterpolation }];
7
+ type AnimatedInterpolatedStyle = any;
8
+
9
+ // type AnimatedInterpolatedStyle =
10
+ // | Animated.AnimatedInterpolation
11
+ // | [{ [Key: string]: Animated.AnimatedInterpolation }];
10
12
 
11
13
  type AnimationConfig = {
12
14
  duration: number;
@@ -6,9 +6,7 @@ exports[`<AudioPlayerMobileLayout /> renders correctly 1`] = `
6
6
  style={
7
7
  {
8
8
  "backgroundColor": "transparent",
9
- "height": "100%",
10
9
  "overflow": "hidden",
11
- "width": "100%",
12
10
  }
13
11
  }
14
12
  >
@@ -17,10 +15,8 @@ exports[`<AudioPlayerMobileLayout /> renders correctly 1`] = `
17
15
  style={
18
16
  {
19
17
  "backgroundColor": "transparent",
20
- "height": "100%",
21
18
  "opacity": 0,
22
19
  "overflow": "hidden",
23
- "width": "100%",
24
20
  }
25
21
  }
26
22
  >
@@ -60,9 +56,7 @@ exports[`<AudioPlayerMobileLayout /> renders correctly 1`] = `
60
56
  style={
61
57
  {
62
58
  "backgroundColor": "transparent",
63
- "height": "100%",
64
59
  "overflow": "hidden",
65
- "width": "100%",
66
60
  }
67
61
  }
68
62
  />
@@ -5,6 +5,7 @@ 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
7
  import { withFocusableContext } from "../../Contexts/FocusableGroupContext/withFocusableContext";
8
+ import { StyleSheet, ViewStyle } from "react-native";
8
9
 
9
10
  type Props = {
10
11
  initialFocus?: boolean;
@@ -21,7 +22,7 @@ type Props = {
21
22
  handleFocus?: ({ mouse }: { mouse: boolean }) => void;
22
23
  children: (boolean, string) => React.ComponentType<any>;
23
24
  selected?: boolean;
24
- style?: React.CSSProperties;
25
+ style?: ViewStyle[] | ViewStyle;
25
26
  };
26
27
 
27
28
  class Focusable extends BaseFocusable<Props> {
@@ -122,7 +123,7 @@ class Focusable extends BaseFocusable<Props> {
122
123
  }
123
124
 
124
125
  render() {
125
- const { children, style } = this.props;
126
+ const { children, style, ...otherProps } = this.props;
126
127
  const { focused } = this.state;
127
128
 
128
129
  const id = this.getId();
@@ -139,7 +140,8 @@ class Focusable extends BaseFocusable<Props> {
139
140
  onMouseUp={this.pressOut}
140
141
  data-testid={focusableId}
141
142
  focused-teststate={focused ? "focused" : "default"}
142
- style={style}
143
+ style={StyleSheet.flatten(style) as any as React.CSSProperties}
144
+ {...otherProps}
143
145
  >
144
146
  {children(focused, { mouse: this.mouse })}
145
147
  </div>
@@ -16,9 +16,9 @@ function noop() {}
16
16
  type Props = {
17
17
  id: string;
18
18
  groupId: string;
19
- onPress?: (nativeEvent: React.SyntheticEvent) => void;
20
- onFocus?: (nativeEvent: React.SyntheticEvent) => void;
21
- onBlur?: (nativeEvent: React.SyntheticEvent) => void;
19
+ onPress?: (nativeEvent: any) => void;
20
+ onFocus?: (nativeEvent: any) => void;
21
+ onBlur?: (nativeEvent: any) => void;
22
22
  children: (focused?: boolean) => React.ReactNode;
23
23
  isParallaxDisabled: boolean;
24
24
  preferredFocus?: boolean;
@@ -12,4 +12,4 @@ function FocusableiOSComponent({ children }: Props) {
12
12
  return children;
13
13
  }
14
14
 
15
- export const FocusableiOS = React.forwardRef(FocusableiOSComponent);
15
+ export const FocusableiOS = FocusableiOSComponent;
@@ -91,6 +91,7 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
91
91
  // eslint-disable-next-line unused-imports/no-unused-vars
92
92
  omitPropsPropagation = [],
93
93
  useScrollView = false,
94
+ onScrollToIndexFailed = noop,
94
95
  } = props;
95
96
 
96
97
  useCheckItemIdsForUnique({ componentId: props.id, items: data });
@@ -277,6 +278,7 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
277
278
  "withStateMemory",
278
279
  "useSequentialLoading",
279
280
  "useScrollView",
281
+ "onScrollToIndexFailed",
280
282
  ...omitPropsPropagation,
281
283
  ],
282
284
  R.__
@@ -305,6 +307,7 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
305
307
  {...getFlatListProps(props)}
306
308
  onEndReached={onEndReached}
307
309
  initialNumToRender={initialNumToRender}
310
+ onScrollToIndexFailed={onScrollToIndexFailed}
308
311
  renderItem={renderItem}
309
312
  focused={focused}
310
313
  data={data}
@@ -319,6 +322,7 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
319
322
  renderItem={renderItem}
320
323
  onEndReached={onEndReached}
321
324
  initialNumToRender={initialNumToRender}
325
+ onScrollToIndexFailed={onScrollToIndexFailed}
322
326
  />
323
327
  )}
324
328
  </ChildrenFocusDeactivatorView>
@@ -14,6 +14,7 @@ import {
14
14
  import { BufferAnimation } from "../PlayerContainer/BufferAnimation";
15
15
  import { PlayerContainer } from "../PlayerContainer";
16
16
  import { useModalSize } from "../VideoModal/hooks";
17
+ import { ViewStyle } from "react-native";
17
18
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
18
19
 
19
20
  type Props = {
@@ -150,14 +151,19 @@ export function HandlePlayable({
150
151
  const modalSize = useModalSize();
151
152
 
152
153
  const style = React.useMemo(
153
- () => ({
154
- width: isModal ? modalSize.width : mode === "PIP" ? "100%" : screenWidth,
155
- height: isModal
156
- ? modalSize.height
157
- : mode === "PIP"
158
- ? "100%"
159
- : screenHeight,
160
- }),
154
+ () =>
155
+ ({
156
+ width: isModal
157
+ ? modalSize.width
158
+ : mode === "PIP"
159
+ ? "100%"
160
+ : screenWidth,
161
+ height: isModal
162
+ ? modalSize.height
163
+ : mode === "PIP"
164
+ ? "100%"
165
+ : screenHeight,
166
+ }) as ViewStyle,
161
167
  [screenWidth, screenHeight, modalSize, isModal, mode]
162
168
  );
163
169
 
@@ -73,7 +73,6 @@ export function elementMapper(
73
73
  : {};
74
74
 
75
75
  const componentProps = {
76
- key,
77
76
  style,
78
77
  skipButtons: otherProps?.skipButtons,
79
78
  emitAsyncElementRegistrate: otherProps?.emitAsyncElementRegistrate,
@@ -91,7 +90,7 @@ export function elementMapper(
91
90
  const fn = mapElementWithKey(elementMapper(components, otherProps));
92
91
 
93
92
  return (
94
- <Component {...componentProps}>
93
+ <Component key={key} {...componentProps}>
95
94
  {focusableTypes.has(type) && elements.length > 0
96
95
  ? elements.map(fn)
97
96
  : null}
@@ -1,28 +1,58 @@
1
1
  import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
2
- import { StorageSingleValueProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/StorageSingleSelectProvider";
2
+ import { StorageSingleValueProvider } from "@applicaster/zapp-react-native-utils/storage/StorageSingleSelectProvider";
3
3
  import { PushTopicManager } from "@applicaster/zapp-react-native-bridge/PushNotifications/PushTopicManager";
4
- import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/StorageMultiSelectProvider";
4
+ import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-utils/storage/StorageMultiSelectProvider";
5
5
  import React, { useEffect } from "react";
6
6
  import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
7
7
  import { BehaviorSubject } from "rxjs";
8
8
  import { masterCellLogger } from "../logger";
9
9
  import get from "lodash/get";
10
-
11
- const parseContextKey = (key: string): string | null => {
12
- if (!key?.startsWith("@{ctx/")) return null;
13
-
14
- return key.substring("@{ctx/".length, key.length - 1);
10
+ import { ScreenMultiSelectProvider } from "@applicaster/zapp-react-native-utils/storage/ScreenStateMultiSelectProvider";
11
+ import { ScreenSingleValueProvider } from "@applicaster/zapp-react-native-utils/storage/ScreenSingleValueProvider";
12
+ import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks";
13
+ import { useScreenStateStore } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useScreenStateStore";
14
+
15
+ const parseContextKey = (
16
+ key: string,
17
+ context: string = "ctx"
18
+ ): string | null => {
19
+ if (!key?.startsWith(`@{${context}/`)) return null;
20
+
21
+ return key.substring(`@{${context}/`.length, key.length - 1);
15
22
  };
16
23
 
17
24
  const getDataSourceProvider = (
18
- behavior: Behavior
25
+ behavior: Behavior,
26
+ screenRoute: string,
27
+ screenStateStore: ScreenStateStore
19
28
  ): BehaviorSubject<string[] | string> | null => {
20
29
  if (!behavior) return null;
21
30
 
22
31
  const selection = String(behavior.current_selection);
32
+ const screenKey = parseContextKey(selection, "screen");
33
+
34
+ if (screenKey) {
35
+ if (behavior.select_mode === "multi") {
36
+ return ScreenMultiSelectProvider.getProvider(
37
+ screenKey,
38
+ screenRoute,
39
+ screenStateStore
40
+ ).getObservable();
41
+ }
42
+
43
+ if (behavior.select_mode === "single") {
44
+ return ScreenSingleValueProvider.getProvider(
45
+ screenKey,
46
+ screenRoute,
47
+ screenStateStore
48
+ ).getObservable();
49
+ }
50
+ }
51
+
23
52
  const contextKey = parseContextKey(selection);
24
53
 
25
54
  if (contextKey) {
55
+ // TODO: Add storage scope to behavior
26
56
  if (behavior.select_mode === "multi") {
27
57
  return StorageMultiSelectProvider.getProvider(contextKey).getObservable();
28
58
  }
@@ -41,6 +71,8 @@ const getDataSourceProvider = (
41
71
 
42
72
  export const useBehaviorUpdate = (behavior: Behavior) => {
43
73
  const [lastUpdate, setLastUpdate] = React.useState<number | null>(null);
74
+ const screenRoute = useRoute()?.pathname || "";
75
+ const screenStateStore = useScreenStateStore();
44
76
  const player = usePlayer();
45
77
 
46
78
  const triggerUpdate = () => setLastUpdate(Date.now());
@@ -48,7 +80,11 @@ export const useBehaviorUpdate = (behavior: Behavior) => {
48
80
  useEffect(() => {
49
81
  if (!behavior) return;
50
82
 
51
- const dataSource = getDataSourceProvider(behavior);
83
+ const dataSource = getDataSourceProvider(
84
+ behavior,
85
+ screenRoute,
86
+ screenStateStore
87
+ );
52
88
 
53
89
  if (dataSource) {
54
90
  const subscription = dataSource.subscribe(triggerUpdate);
@@ -72,10 +108,17 @@ export const useBehaviorUpdate = (behavior: Behavior) => {
72
108
 
73
109
  // We cant use async in this function (its inside render),
74
110
  // so we rely on useBehaviorUpdate to update current value and trigger re-render
75
- export const isCellSelected = (
76
- item: ZappEntry,
77
- behavior?: Behavior
78
- ): boolean => {
111
+ export const isCellSelected = ({
112
+ item,
113
+ screenRoute,
114
+ screenStateStore,
115
+ behavior,
116
+ }: {
117
+ item: ZappEntry;
118
+ screenRoute: string;
119
+ screenStateStore: ScreenStateStore;
120
+ behavior?: Behavior;
121
+ }): boolean => {
79
122
  if (!behavior) return false;
80
123
 
81
124
  const id = behavior.selector ? get(item, behavior.selector) : item.id;
@@ -99,7 +142,32 @@ export const isCellSelected = (
99
142
  }
100
143
 
101
144
  const selection = String(behavior.current_selection);
102
- const contextKey = parseContextKey(selection);
145
+
146
+ const screenKey = parseContextKey(selection, "screen");
147
+
148
+ if (screenKey) {
149
+ if (behavior.select_mode === "single") {
150
+ const selectedItem = ScreenSingleValueProvider.getProvider(
151
+ screenKey,
152
+ screenRoute,
153
+ screenStateStore
154
+ ).getValue();
155
+
156
+ return selectedItem === String(id);
157
+ }
158
+
159
+ if (behavior.select_mode === "multi") {
160
+ const selectedItems = ScreenMultiSelectProvider.getProvider(
161
+ screenKey,
162
+ screenRoute,
163
+ screenStateStore
164
+ ).getSelectedItems();
165
+
166
+ return selectedItems?.includes(String(id));
167
+ }
168
+ }
169
+
170
+ const contextKey = parseContextKey(selection, "ctx");
103
171
 
104
172
  if (contextKey) {
105
173
  if (behavior.select_mode === "single") {
@@ -8,6 +8,8 @@ import { masterCellLogger } from "../logger";
8
8
  import { getCellState } from "../../Cell/utils";
9
9
  import { getColorFromData } from "@applicaster/zapp-react-native-utils/cellUtils";
10
10
  import { isCellSelected, useBehaviorUpdate } from "./behaviorProvider";
11
+ import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks";
12
+ import { useScreenStateStore } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useScreenStateStore";
11
13
 
12
14
  const hasElementSpecificViewType = (viewType) => (element) => {
13
15
  if (R.isNil(element)) {
@@ -190,8 +192,18 @@ export const getFocusedButtonId = (focusable) => {
190
192
  });
191
193
  };
192
194
 
193
- export const isSelected = (item: ZappEntry, behavior?: Behavior) => {
194
- return isCellSelected(item, behavior);
195
+ export const isSelected = ({
196
+ item,
197
+ screenRoute,
198
+ screenStateStore,
199
+ behavior,
200
+ }: {
201
+ item: ZappEntry;
202
+ screenRoute: string;
203
+ screenStateStore: ScreenStateStore;
204
+ behavior?: Behavior;
205
+ }) => {
206
+ return isCellSelected({ item, screenRoute, screenStateStore, behavior });
195
207
  };
196
208
 
197
209
  export const useCellState = ({
@@ -204,9 +216,17 @@ export const useCellState = ({
204
216
  focused: boolean;
205
217
  }): CellState => {
206
218
  const lastUpdate = useBehaviorUpdate(behavior);
219
+ const router = useRoute();
220
+ const screenStateStore = useScreenStateStore();
207
221
 
208
222
  const _isSelected = useMemo(
209
- () => isSelected(item, behavior),
223
+ () =>
224
+ isSelected({
225
+ item,
226
+ screenRoute: router?.pathname,
227
+ screenStateStore,
228
+ behavior,
229
+ }),
210
230
  [behavior, item, lastUpdate]
211
231
  );
212
232
 
@@ -1,9 +1,12 @@
1
1
  import React from "react";
2
2
  import { Text, Animated } from "react-native";
3
+ import { render } from "@testing-library/react-native";
3
4
 
4
- import renderer from "react-test-renderer";
5
-
6
- jest.useFakeTimers();
5
+ import {
6
+ NotificationView,
7
+ onlinePhrase,
8
+ offlinePhrase,
9
+ } from "../NotificationView";
7
10
 
8
11
  jest.mock("@applicaster/zapp-react-native-redux/hooks", () => ({
9
12
  usePickFromState: () => ({
@@ -32,39 +35,31 @@ jest.mock("react-native-safe-area-context", () => ({
32
35
 
33
36
  const dismiss = jest.fn();
34
37
 
35
- const {
36
- NotificationView,
37
- onlinePhrase,
38
- offlinePhrase,
39
- } = require("../NotificationView");
40
-
41
38
  describe("NotificationView", () => {
42
39
  it("Show online message when Online", () => {
43
- const component = renderer.create(
44
- <NotificationView online dismiss={dismiss} />
45
- );
40
+ const component = render(<NotificationView online dismiss={dismiss} />);
46
41
 
47
- expect(component.root.findByType(Text).props.children).toBe(onlinePhrase);
42
+ expect(component.UNSAFE_getByType(Text).props.children).toBe(onlinePhrase);
48
43
  });
49
44
 
50
45
  it("Show offline message when Online", () => {
51
- const component = renderer.create(
46
+ const component = render(
52
47
  <NotificationView online={false} dismiss={dismiss} />
53
48
  );
54
49
 
55
- expect(component.root.findByType(Text).props.children).toBe(offlinePhrase);
50
+ expect(component.UNSAFE_getByType(Text).props.children).toBe(offlinePhrase);
56
51
  });
57
52
 
58
53
  it("When hidden is false to true notification is visible", () => {
59
- const component = renderer.create(
54
+ const component = render(
60
55
  <NotificationView online={false} hidden={false} dismiss={dismiss} />
61
56
  );
62
57
 
63
- component.update(
58
+ component.rerender(
64
59
  <NotificationView online={false} hidden={true} dismiss={dismiss} />
65
60
  );
66
61
 
67
- const animatedView = component.root.findByType(Animated.View);
62
+ const animatedView = component.UNSAFE_getByType(Animated.View);
68
63
  const animatedViewStyles = animatedView.props.style;
69
64
 
70
65
  expect(animatedViewStyles.opacity).toBe(1);
@@ -21,6 +21,15 @@ exports[`OfflineHandler renders 1`] = `
21
21
  }
22
22
  >
23
23
  <View
24
+ accessibilityState={
25
+ {
26
+ "busy": undefined,
27
+ "checked": undefined,
28
+ "disabled": undefined,
29
+ "expanded": undefined,
30
+ "selected": undefined,
31
+ }
32
+ }
24
33
  accessible={true}
25
34
  collapsable={false}
26
35
  focusable={true}
@@ -1,7 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { useEffect, useReducer } from "react";
3
- // @ts-ignore
4
- import { TVMenuControl, View, ViewStyle } from "react-native";
3
+ import { View, ViewStyle } from "react-native";
5
4
  import * as R from "ramda";
6
5
  import uuid from "uuid/v4";
7
6
  import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager";
@@ -62,6 +61,11 @@ import {
62
61
  useModalAnimationContext,
63
62
  } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
64
63
 
64
+ import {
65
+ PlayerNativeCommandTypes,
66
+ PlayerNativeSendCommand,
67
+ } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/playerNativeCommand";
68
+
65
69
  type Props = {
66
70
  Player: React.ComponentType<any>;
67
71
  PlayerLoadingView?: React.ComponentType<any>; // 👀 we are not receiving this prop
@@ -259,9 +263,15 @@ const PlayerContainerComponent = (props: Props) => {
259
263
  return;
260
264
  }
261
265
 
266
+ // send command to clear and stop player
267
+ PlayerNativeSendCommand(
268
+ PlayerNativeCommandTypes.clearPlayerData,
269
+ state.playerId
270
+ );
271
+
262
272
  showNavBar(true);
263
273
  navigator.goBack();
264
- }, [isModal, navigator.goBack, showNavBar]);
274
+ }, [isModal, navigator.goBack, state.playerId, showNavBar]);
265
275
 
266
276
  const playEntry = (entry) => navigator.replaceTop(entry, { mode });
267
277
 
@@ -389,13 +399,17 @@ const PlayerContainerComponent = (props: Props) => {
389
399
  }
390
400
  };
391
401
 
392
- const playerRemoteHandler = (event, isLanguageOverlayVisible) => {
393
- const { eventType } = event;
402
+ const playerRemoteHandler = React.useCallback(
403
+ (isLanguageOverlayVisible = false) =>
404
+ (event) => {
405
+ const { eventType } = event;
394
406
 
395
- if (!isLanguageOverlayVisible && eventType === "menu") {
396
- close();
397
- }
398
- };
407
+ if (!isLanguageOverlayVisible && eventType === "menu") {
408
+ close();
409
+ }
410
+ },
411
+ [close]
412
+ );
399
413
 
400
414
  // Effects
401
415
  useEffect(() => {
@@ -508,16 +522,6 @@ const PlayerContainerComponent = (props: Props) => {
508
522
  }
509
523
  }, [isAudioContent]);
510
524
 
511
- // Needs to handle back button on Apple TV
512
- // https://github.com/facebook/react-native/issues/18930
513
- useEffect(() => {
514
- TVMenuControl?.enableTVMenuKey();
515
-
516
- return () => {
517
- TVMenuControl?.disableTVMenuKey();
518
- };
519
- }, []);
520
-
521
525
  useEffect(() => {
522
526
  playerEvent("source_changed", { item });
523
527
 
@@ -633,9 +637,9 @@ const PlayerContainerComponent = (props: Props) => {
633
637
  <PlayerContainerContext.Consumer>
634
638
  {(context) => (
635
639
  <TVEventHandlerComponent
636
- tvEventHandler={(_component, event) =>
637
- playerRemoteHandler(event, context.isLanguageOverlayVisible)
638
- }
640
+ tvEventHandler={playerRemoteHandler(
641
+ context.isLanguageOverlayVisible
642
+ )}
639
643
  >
640
644
  <FocusableGroup
641
645
  id={FocusableGroupMainContainerId}
@@ -286,7 +286,6 @@ function ComponentsMapComponent(props: Props) {
286
286
  initialNumToRender={3}
287
287
  maxToRenderPerBatch={10}
288
288
  windowSize={12}
289
- listKey={riverId}
290
289
  keyExtractor={keyExtractor}
291
290
  renderItem={renderRiverItem}
292
291
  data={riverComponents}
@@ -8,7 +8,7 @@ export const withTvEventHandler = (Component) => {
8
8
  return function WithTVEventHandler(props) {
9
9
  const navigator = useNavigation();
10
10
 
11
- const remoteHandler = (_, event) => {
11
+ const remoteHandler = (event) => {
12
12
  const { eventType } = event;
13
13
 
14
14
  const canGoBack = navigator.canGoBack();
@@ -159,6 +159,7 @@ exports[`componentsMap renders renders components map correctly 1`] = `
159
159
  >
160
160
  <View>
161
161
  <View
162
+ onFocusCapture={[Function]}
162
163
  onLayout={[Function]}
163
164
  style={null}
164
165
  >
@@ -174,6 +175,7 @@ exports[`componentsMap renders renders components map correctly 1`] = `
174
175
  </View>
175
176
  </View>
176
177
  <View
178
+ onFocusCapture={[Function]}
177
179
  onLayout={[Function]}
178
180
  style={null}
179
181
  >
@@ -0,0 +1,76 @@
1
+ import { makeListOf } from "@applicaster/zapp-react-native-utils/arrayUtils";
2
+ import { isFirstComponentGallery } from "@applicaster/zapp-react-native-utils/componentsUtils";
3
+ import { once } from "ramda";
4
+
5
+ const INITIAL_NUMBER_TO_LOAD = 3;
6
+
7
+ // Infer the values of COMPONENT_LOADING_STATE as a type
8
+ type ComponentLoadingState =
9
+ (typeof COMPONENT_LOADING_STATE)[keyof typeof COMPONENT_LOADING_STATE];
10
+
11
+ export const COMPONENT_LOADING_STATE = {
12
+ UNKNOWN: "UNKNOWN",
13
+ LOADED_WITH_SUCCESS: "LOADED_WITH_SUCCESS",
14
+ LOADED_WITH_FAILURE: "LOADED_WITH_FAILURE",
15
+ } as const;
16
+
17
+ // Function to get the number of loaded components
18
+ const getNumberOfLoaded = (states: ComponentLoadingState[]): number => {
19
+ return states.filter((value) => value !== COMPONENT_LOADING_STATE.UNKNOWN)
20
+ .length;
21
+ };
22
+
23
+ const getNumberOfComponentsWaitToLoadBeforePresent = (
24
+ componentsToRender: ZappUIComponent[]
25
+ ): number => {
26
+ // when Gallery is the first component, no need to wait the others
27
+ if (isFirstComponentGallery(componentsToRender)) {
28
+ return 1;
29
+ }
30
+
31
+ return Math.min(INITIAL_NUMBER_TO_LOAD, componentsToRender.length);
32
+ };
33
+
34
+ export class ScreenRevealManager {
35
+ public numberOfComponentsWaitToLoadBeforePresent: number;
36
+ private renderingState: Array<ComponentLoadingState>;
37
+ private callback: Callback;
38
+
39
+ constructor(componentsToRender: ZappUIComponent[], callback: Callback) {
40
+ this.numberOfComponentsWaitToLoadBeforePresent =
41
+ getNumberOfComponentsWaitToLoadBeforePresent(componentsToRender);
42
+
43
+ this.renderingState = makeListOf<ComponentLoadingState>(
44
+ COMPONENT_LOADING_STATE.UNKNOWN,
45
+ this.numberOfComponentsWaitToLoadBeforePresent
46
+ );
47
+
48
+ this.callback = once(callback);
49
+ }
50
+
51
+ onLoadFinished = (index: number): void => {
52
+ this.renderingState[index] = COMPONENT_LOADING_STATE.LOADED_WITH_SUCCESS;
53
+
54
+ if (
55
+ getNumberOfLoaded(this.renderingState) >=
56
+ this.numberOfComponentsWaitToLoadBeforePresent
57
+ ) {
58
+ this.setIsReadyToShow();
59
+ }
60
+ };
61
+
62
+ onLoadFailed = (index: number): void => {
63
+ this.renderingState[index] = COMPONENT_LOADING_STATE.LOADED_WITH_FAILURE;
64
+
65
+ if (
66
+ getNumberOfLoaded(this.renderingState) >=
67
+ this.numberOfComponentsWaitToLoadBeforePresent
68
+ ) {
69
+ this.setIsReadyToShow();
70
+ }
71
+ };
72
+
73
+ setIsReadyToShow = (): void => {
74
+ this.callback();
75
+ };
76
+ }