@applicaster/zapp-react-native-ui-components 13.0.0-alpha.7223526501 → 13.0.0-alpha.7429912110

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 (88) hide show
  1. package/Components/AnimatedInOut/index.tsx +3 -5
  2. package/Components/AudioPlayer/AudioPlayer.tsx +5 -9
  3. package/Components/AudioPlayer/AudioPlayerLayout.tsx +8 -9
  4. package/Components/AudioPlayer/helpers.tsx +1 -2
  5. package/Components/Cell/Cell.tsx +3 -13
  6. package/Components/Cell/CellWithFocusable.tsx +21 -15
  7. package/Components/Cell/CellWrapper.tsx +18 -0
  8. package/Components/Cell/styles.ts +17 -0
  9. package/Components/CellRendererResolver/index.ts +2 -1
  10. package/Components/ErrorScreen/index.tsx +8 -0
  11. package/Components/FocusableCell/index.tsx +1 -1
  12. package/Components/FocusableGroup/FocusableTvOS.tsx +3 -6
  13. package/Components/FocusableGroup/index.tsx +0 -1
  14. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +3 -0
  15. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +4 -2
  16. package/Components/GeneralContentScreen/utils/useEventAlerts.ts +30 -0
  17. package/Components/HandlePlayable/HandlePlayable.tsx +21 -27
  18. package/Components/Layout/TV/LayoutBackground.tsx +28 -0
  19. package/Components/Layout/TV/ScreenContainer.tsx +8 -2
  20. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +5 -0
  21. package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +2 -1
  22. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +15 -10
  23. package/Components/Layout/TV/__tests__/index.test.tsx +8 -2
  24. package/Components/Layout/TV/index.tsx +6 -20
  25. package/Components/Layout/TV/index.web.tsx +4 -1
  26. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +4 -3
  27. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +11 -7
  28. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/const.ts +3 -0
  29. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +6 -6
  30. package/Components/MasterCell/utils/behaviorProvider.ts +136 -0
  31. package/Components/MasterCell/utils/index.ts +8 -134
  32. package/Components/ModalComponent/BottomSheetModalContent.tsx +66 -54
  33. package/Components/ModalComponent/Button/Item.tsx +6 -5
  34. package/Components/ModalComponent/Button/assets.ts +1 -1
  35. package/Components/ModalComponent/Button/index.tsx +45 -62
  36. package/Components/ModalComponent/Header/index.tsx +9 -8
  37. package/Components/ModalComponent/utils.ts +55 -7
  38. package/Components/OfflineHandler/utils/index.ts +1 -1
  39. package/Components/PlayerContainer/PlayerContainer.tsx +55 -46
  40. package/Components/PlayerImageBackground/index.tsx +4 -24
  41. package/Components/River/ComponentsMap/ComponentsMap.tsx +18 -3
  42. package/Components/River/ComponentsMap/hooks/useLoadingState.ts +3 -3
  43. package/Components/River/RiverItem.tsx +25 -5
  44. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +7 -1
  45. package/Components/RouteManager/TestId.tsx +1 -5
  46. package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +0 -1
  47. package/Components/RouteManager/__tests__/testId.test.js +0 -4
  48. package/Components/Screen/TV/__tests__/index.web.test.tsx +26 -0
  49. package/Components/Screen/TV/index.web.tsx +5 -6
  50. package/Components/Screen/__tests__/Screen.test.tsx +22 -14
  51. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -2
  52. package/Components/Screen/hooks.ts +3 -6
  53. package/Components/TopMarginApplicator/TopMarginApplicator.tsx +2 -3
  54. package/Components/TrackedView/index.tsx +1 -0
  55. package/Components/Transitioner/AnimationManager.js +15 -15
  56. package/Components/VideoLive/PlayerLiveImageComponent.tsx +4 -0
  57. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +1 -0
  58. package/Components/VideoLive/animationUtils.ts +4 -7
  59. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +1 -1
  60. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +35 -19
  61. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +3 -3
  62. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +38 -14
  63. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +11 -3
  64. package/Components/VideoModal/ModalAnimation/__tests__/getMoveUpValue.test.ts +108 -0
  65. package/Components/VideoModal/ModalAnimation/const.ts +5 -0
  66. package/Components/VideoModal/ModalAnimation/index.ts +2 -0
  67. package/Components/VideoModal/ModalAnimation/utils.ts +30 -0
  68. package/Components/VideoModal/PlayerDetails.tsx +9 -4
  69. package/Components/VideoModal/PlayerWrapper.tsx +35 -22
  70. package/Components/VideoModal/VideoModal.tsx +19 -4
  71. package/Components/VideoModal/__tests__/__snapshots__/PlayerDetails.test.tsx.snap +2 -40
  72. package/Components/VideoModal/utils.ts +3 -4
  73. package/Components/ZappUIComponent/index.tsx +4 -4
  74. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +4 -10
  75. package/Contexts/HeaderOffsetContext/index.tsx +4 -6
  76. package/Contexts/ScreenContext/index.tsx +3 -10
  77. package/Contexts/ScreenLayoutContext/index.tsx +5 -3
  78. package/Decorators/RiverResolver/index.tsx +5 -7
  79. package/Decorators/ZappPipesDataConnector/index.tsx +4 -30
  80. package/package.json +5 -9
  81. package/tsconfig.json +2 -3
  82. package/.babelrc +0 -8
  83. package/Components/Cell/CellStyles/FallbackCellStyle/index.js +0 -157
  84. package/Components/Cell/CellStyles/Hero/index.js +0 -111
  85. package/Components/Cell/CellStyles/ScreenSelector/index.js +0 -68
  86. package/Components/Cell/CellStyles/cellStylesResolver.ts +0 -19
  87. package/Components/Cell/CellStyles/colors.js +0 -40
  88. package/Components/Cell/CellStyles/index.js +0 -15
@@ -6,10 +6,15 @@ import * as R from "ramda";
6
6
  import uuid from "uuid/v4";
7
7
  import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager";
8
8
  import {
9
- isApplePlatform,
10
9
  isTV,
11
10
  platformSelect,
11
+ isAndroidTVPlatform,
12
+ isTvOSPlatform,
12
13
  } from "@applicaster/zapp-react-native-utils/reactUtils";
14
+ import {
15
+ isAudioItem,
16
+ isInlineTV as isInlineTVUtil,
17
+ } from "@applicaster/zapp-react-native-utils/playerUtils";
13
18
 
14
19
  import { TVEventHandlerComponent } from "@applicaster/zapp-react-native-tvos-ui-components/Components/TVEventHandlerComponent";
15
20
  import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";
@@ -20,7 +25,7 @@ import {
20
25
  } from "@applicaster/zapp-react-native-utils/reactHooks";
21
26
 
22
27
  import { PlayerStateContext } from "../../Contexts/PlayerStateContext";
23
- import { isAppleTV } from "../../Helpers/Platform";
28
+
24
29
  import {
25
30
  QUICK_BRICK_EVENTS,
26
31
  QuickBrickEvent,
@@ -93,6 +98,25 @@ export type PlayNextData = {
93
98
 
94
99
  const focusableBottomContainerId = "player-container-bottom";
95
100
 
101
+ const isAndroidTV = isAndroidTVPlatform();
102
+
103
+ const isTvOS = isTvOSPlatform();
104
+
105
+ // height for container with additional content below player
106
+ const INLINE_CONTAINER_CONTENT_HEIGHT = 400;
107
+
108
+ const withBorderHack = () => {
109
+ if (isAndroidTV) {
110
+ /* @HACK: see GH#7269 */
111
+ return {
112
+ borderWidth: 1,
113
+ borderColor: "transparent",
114
+ };
115
+ }
116
+
117
+ return {};
118
+ };
119
+
96
120
  // Styles
97
121
  const webStyles = {
98
122
  focusableGroup: {
@@ -110,7 +134,7 @@ const webStyles = {
110
134
  flex: "initial",
111
135
  },
112
136
  inlineRiver: {
113
- height: 400,
137
+ height: INLINE_CONTAINER_CONTENT_HEIGHT,
114
138
  },
115
139
  };
116
140
 
@@ -128,7 +152,7 @@ const nativeStyles = {
128
152
  flex: 1,
129
153
  },
130
154
  inlineRiver: {
131
- height: 400,
155
+ height: INLINE_CONTAINER_CONTENT_HEIGHT,
132
156
  },
133
157
  };
134
158
 
@@ -148,12 +172,11 @@ const renderApplePlayer = ({
148
172
  videoStyle,
149
173
  playNextData,
150
174
  }) => {
151
- const { title, summary } = item || {};
152
-
153
- if (!isAppleTV() || !player) {
175
+ if (!isTvOS || !player) {
154
176
  return null;
155
177
  }
156
178
 
179
+ // render audio_player for tvos
157
180
  if (isAudioContent) {
158
181
  return (
159
182
  <AudioPlayer
@@ -167,6 +190,8 @@ const renderApplePlayer = ({
167
190
  return null;
168
191
  }
169
192
 
193
+ const { title, summary } = item || {};
194
+
170
195
  return (
171
196
  <ProgramInfo
172
197
  title={title}
@@ -177,21 +202,6 @@ const renderApplePlayer = ({
177
202
  );
178
203
  };
179
204
 
180
- const renderRestPlayers = ({ item, showAudioPlayer, pluginConfiguration }) => {
181
- if (isApplePlatform()) {
182
- return null;
183
- }
184
-
185
- return (
186
- showAudioPlayer && (
187
- <AudioPlayer
188
- audio_item={item}
189
- plugin_configuration={pluginConfiguration}
190
- />
191
- )
192
- );
193
- };
194
-
195
205
  const PlayerContainerComponent = (props: Props) => {
196
206
  const {
197
207
  Player,
@@ -321,7 +331,7 @@ const PlayerContainerComponent = (props: Props) => {
321
331
 
322
332
  setState({ error: errorObj });
323
333
 
324
- if (!isAppleTV()) {
334
+ if (!isTvOS) {
325
335
  setTimeout(() => {
326
336
  close();
327
337
  }, 800);
@@ -335,9 +345,9 @@ const PlayerContainerComponent = (props: Props) => {
335
345
 
336
346
  const resumeTime = Number(item?.extensions?.resumeTime);
337
347
 
338
- // TODO: This is temp hack, will be removed on next pr with player refactoring
348
+ // Сhecking that the player itself knows where to start playing, and there is no need to call seekTo after returning from the Сhromecast
339
349
  if (
340
- !!playerManager.getActivePlayer()?.getContinueWatchingOffset === false &&
350
+ !playerManager.getActivePlayer()?.hasResumePosition() &&
341
351
  !isNaN(resumeTime)
342
352
  ) {
343
353
  player?.seekTo(resumeTime);
@@ -478,9 +488,7 @@ const PlayerContainerComponent = (props: Props) => {
478
488
  // TODO: Skip hook call on TV platform
479
489
  useBackHandler(backHandler);
480
490
 
481
- const isAudioContent =
482
- typeof item?.content?.type === "string" &&
483
- item?.content?.type.includes("audio");
491
+ const isAudioContent = isAudioItem(item);
484
492
 
485
493
  useEffect(() => {
486
494
  if (!isAudioContent) {
@@ -514,6 +522,12 @@ const PlayerContainerComponent = (props: Props) => {
514
522
  useEffect(() => {
515
523
  playerEvent("source_changed", { item });
516
524
 
525
+ return () => {
526
+ playerEvent("player_did_close", { item });
527
+ };
528
+ }, [item?.id, Player]);
529
+
530
+ useEffect(() => {
517
531
  if (!isModal) {
518
532
  showNavBar(false);
519
533
  }
@@ -523,13 +537,11 @@ const PlayerContainerComponent = (props: Props) => {
523
537
  }
524
538
 
525
539
  return () => {
526
- playerEvent("player_did_close", { item });
527
-
528
540
  if (!isModal) {
529
541
  showNavBar(true);
530
542
  }
531
543
  };
532
- }, [showNavBar, /* added as suggested: */ item?.id, isModal, Player]);
544
+ }, [showNavBar, isModal, Player]);
533
545
 
534
546
  useEffect(() => {
535
547
  if (prevItemId && !R.equals(prevItemId, item?.id)) {
@@ -550,10 +562,7 @@ const PlayerContainerComponent = (props: Props) => {
550
562
 
551
563
  const uri = item?.content ? item.content?.src : null;
552
564
 
553
- const isInlineTV =
554
- screenData?.ui_components &&
555
- screenData?.ui_components?.length > 0 &&
556
- isTV();
565
+ const isInlineTV = isInlineTVUtil(screenData);
557
566
 
558
567
  const inline =
559
568
  [VideoModalMode.MAXIMIZED, VideoModalMode.MINIMIZED].includes(mode) ||
@@ -578,7 +587,7 @@ const PlayerContainerComponent = (props: Props) => {
578
587
  );
579
588
  }
580
589
 
581
- if (screen_background_color) {
590
+ if (screen_background_color && mode !== VideoModalMode.FULLSCREEN) {
582
591
  updatedStyles.playerScreen.backgroundColor = screen_background_color;
583
592
  }
584
593
 
@@ -596,6 +605,7 @@ const PlayerContainerComponent = (props: Props) => {
596
605
  tv_component_container_height,
597
606
  screen_background_color,
598
607
  isInlineTV,
608
+ mode,
599
609
  ]);
600
610
 
601
611
  const applePlayerProps = {
@@ -607,12 +617,6 @@ const PlayerContainerComponent = (props: Props) => {
607
617
  playNextData,
608
618
  };
609
619
 
610
- const restPlayerProps = {
611
- item,
612
- showAudioPlayer: isAudioContent,
613
- pluginConfiguration,
614
- };
615
-
616
620
  return (
617
621
  <PlayerStateContext.Provider value={value}>
618
622
  <PlayerContainerContextProvider
@@ -640,7 +644,14 @@ const PlayerContainerComponent = (props: Props) => {
640
644
  testID={"player-screen-container"}
641
645
  >
642
646
  {/* Player container */}
643
- <View style={styles.playerWrapper} testID={"player-wrapper"}>
647
+ <View
648
+ style={[
649
+ styles.playerWrapper,
650
+ // eslint-disable-next-line react-native/no-inline-styles, react-native/no-color-literals
651
+ withBorderHack(),
652
+ ]}
653
+ testID={"player-wrapper"}
654
+ >
644
655
  <PlayerFocusableWrapperView
645
656
  nextFocusDown={context.bottomFocusableId}
646
657
  >
@@ -681,8 +692,6 @@ const PlayerContainerComponent = (props: Props) => {
681
692
  </Player>
682
693
  </PlayerFocusableWrapperView>
683
694
 
684
- {renderRestPlayers(restPlayerProps)}
685
-
686
695
  {state.error ? <ErrorDisplay error={state.error} /> : null}
687
696
  </View>
688
697
  {/* Components container */}
@@ -706,7 +715,7 @@ const PlayerContainerComponent = (props: Props) => {
706
715
  screenId={screenData.id}
707
716
  key={item.id}
708
717
  groupId={FocusableGroupMainContainerId}
709
- cellTapAction={(event) => onCellTap(event)}
718
+ cellTapAction={onCellTap}
710
719
  extraAnchorPointYOffset={-600}
711
720
  isScreenWrappedInContainer={true}
712
721
  containerHeight={styles.inlineRiver.height}
@@ -1,4 +1,4 @@
1
- import React, { PropsWithChildren, useEffect, useState } from "react";
1
+ import React, { PropsWithChildren } from "react";
2
2
  import { ImageBackground, View } from "react-native";
3
3
 
4
4
  import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
@@ -12,7 +12,6 @@ import {
12
12
  type Props = PropsWithChildren<{
13
13
  entry: ZappEntry;
14
14
  style?: { [K: string]: any };
15
- docked?: boolean;
16
15
  imageStyle?: { [K: string]: any };
17
16
  imageKey?: string;
18
17
  defaultImageDimensions?: { [K: string]: any };
@@ -24,7 +23,6 @@ const PlayerImageBackgroundComponent = ({
24
23
  entry,
25
24
  children,
26
25
  style,
27
- docked,
28
26
  imageStyle,
29
27
  imageKey,
30
28
  defaultImageDimensions,
@@ -36,42 +34,24 @@ const PlayerImageBackgroundComponent = ({
36
34
 
37
35
  const { playerAnimationState } = useModalAnimationContext();
38
36
 
39
- const [lastNonNullAnimationState, setLastNonNullAnimationState] =
40
- useState(playerAnimationState);
41
-
42
- useEffect(() => {
43
- if (playerAnimationState !== null) {
44
- setLastNonNullAnimationState(playerAnimationState);
45
- }
46
- }, [playerAnimationState]);
47
-
48
37
  if (!source) return <>{children}</>;
49
38
 
50
- const imageBackgroundStyle =
51
- lastNonNullAnimationState !== PlayerAnimationStateEnum.minimize && !docked
52
- ? defaultImageDimensions
53
- : imageSize;
54
-
55
39
  return (
56
40
  <View
57
41
  style={
58
- playerAnimationState === PlayerAnimationStateEnum.maximaze
42
+ playerAnimationState === PlayerAnimationStateEnum.maximize
59
43
  ? defaultImageDimensions
60
44
  : style
61
45
  }
62
46
  >
63
47
  <AnimationComponent
64
- style={
65
- playerAnimationState === PlayerAnimationStateEnum.maximaze
66
- ? defaultImageDimensions
67
- : style
68
- }
48
+ style={style}
69
49
  animationType={ComponentAnimationType.player}
70
50
  additionalData={defaultImageDimensions}
71
51
  >
72
52
  <ImageBackground
73
53
  resizeMode="cover"
74
- style={imageBackgroundStyle}
54
+ style={imageSize}
75
55
  imageStyle={imageStyle}
76
56
  source={source}
77
57
  >
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import * as R from "ramda";
3
- import { View, StyleSheet, Platform, FlatList } from "react-native";
3
+ import { View, StyleSheet, FlatList } from "react-native";
4
4
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
5
5
  import { RiverItem } from "../RiverItem";
6
6
  import { RiverFooter } from "../RiverFooter";
@@ -24,6 +24,10 @@ import { withComponentsMapProvider } from "@applicaster/zapp-react-native-ui-com
24
24
  import { useScreenContextV2 } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
25
25
  import { useShallow } from "zustand/react/shallow";
26
26
 
27
+ import { isAndroidPlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
28
+
29
+ const isAndroid = isAndroidPlatform();
30
+
27
31
  type Props = {
28
32
  feed: ZappFeed;
29
33
  groupId?: string;
@@ -34,6 +38,7 @@ type Props = {
34
38
  getStaticComponentFeed: any;
35
39
  pullToRefreshPipesV1RefreshingStateUpdater: () => boolean;
36
40
  refreshingPipesV1?: boolean;
41
+ stickyHeaderIndices?: number[];
37
42
  };
38
43
 
39
44
  const styles = StyleSheet.create({
@@ -61,6 +66,7 @@ function ComponentsMapComponent(props: Props) {
61
66
  // TODO: Remove when pipes v1 is deprecated.
62
67
  pullToRefreshPipesV1RefreshingStateUpdater,
63
68
  refreshingPipesV1,
69
+ stickyHeaderIndices,
64
70
  } = props;
65
71
 
66
72
  const flatListRef = React.useRef<FlatList | null>(null);
@@ -87,6 +93,13 @@ function ComponentsMapComponent(props: Props) {
87
93
  const { loadingState, onLoadFinished, onLoadFailed, shouldShowLoadingError } =
88
94
  useLoadingState(riverComponents.length, onLoadDone);
89
95
 
96
+ // console.log("RiverItem ComponentsMapComponent useLoadingState", {
97
+ // riverId,
98
+ // riverComponents,
99
+ // loadingState: loadingState.getValue(),
100
+ // arePreviousComponentsLoaded,
101
+ // });
102
+
90
103
  const theme = useTheme();
91
104
 
92
105
  const renderRiverItem = React.useCallback(
@@ -271,14 +284,16 @@ function ComponentsMapComponent(props: Props) {
271
284
  }}
272
285
  // Fix for WebView rerender crashes on Android API 28+
273
286
  // https://github.com/react-native-webview/react-native-webview/issues/1915#issuecomment-964035468
274
- overScrollMode={Platform.OS === "android" ? "never" : "auto"}
287
+ overScrollMode={isAndroid ? "never" : "auto"}
275
288
  scrollIndicatorInsets={scrollIndicatorInsets}
276
289
  extraData={feed}
290
+ stickyHeaderIndices={stickyHeaderIndices}
291
+ removeClippedSubviews={isAndroid}
277
292
  onLayout={handleOnLayout}
278
- removeClippedSubviews
279
293
  initialNumToRender={3}
280
294
  maxToRenderPerBatch={10}
281
295
  windowSize={12}
296
+ listKey={riverId}
282
297
  keyExtractor={keyExtractor}
283
298
  renderItem={renderRiverItem}
284
299
  data={riverComponents}
@@ -28,10 +28,10 @@ type Return = {
28
28
  // TODO: Take this value from Zapp configuration, when feature is added to GeneralScreen
29
29
  const SHOULD_FAIL_ON_COMPONENT_LOADING = false;
30
30
 
31
- const createLoadingStateObservable = () =>
31
+ const createLoadingStateObservable = (count: number) =>
32
32
  new BehaviorSubject<LoadingState>({
33
33
  index: -1,
34
- done: false,
34
+ done: count === 0,
35
35
  waitForAllComponents: SHOULD_FAIL_ON_COMPONENT_LOADING,
36
36
  });
37
37
 
@@ -43,7 +43,7 @@ export const useLoadingState = (
43
43
  const [loadingError, setLoadingError] = React.useState(null);
44
44
 
45
45
  const loadingState = useRefWithInitialValue<BehaviorSubject<LoadingState>>(
46
- createLoadingStateObservable
46
+ () => createLoadingStateObservable(count)
47
47
  );
48
48
 
49
49
  const arePreviousComponentsLoaded = React.useCallback((index) => {
@@ -1,5 +1,6 @@
1
1
  import React, { useEffect } from "react";
2
2
  import * as R from "ramda";
3
+ import { isGroup } from "@applicaster/zapp-react-native-utils/componentsUtils";
3
4
 
4
5
  import { applyDecorators } from "../../Decorators";
5
6
 
@@ -38,6 +39,10 @@ function getFeedUrl(feed: ZappFeed, index: number) {
38
39
  }
39
40
  }
40
41
 
42
+ const isNextIndex = (index, readyIndex) => {
43
+ return readyIndex + 1 >= index;
44
+ };
45
+
41
46
  /**
42
47
  * useLoadingState for RiverItemComponent
43
48
  * takes currentIndex and loadingState as arguments
@@ -47,12 +52,18 @@ const useLoadingState = (
47
52
  loadingState: RiverItemType["loadingState"]
48
53
  ) => {
49
54
  const [readyToBeDisplayed, setReadyToBeDisplayed] = React.useState(
50
- loadingState.getValue().index + 1 === currentIndex
55
+ isNextIndex(currentIndex, loadingState.getValue().index)
51
56
  );
52
57
 
53
58
  useEffect(() => {
54
59
  const subscription = loadingState.subscribe(({ index }) => {
55
- if (index + 1 === currentIndex) {
60
+ // console.log("RiverItem useLoadingState subscription", {
61
+ // currentIndex,
62
+ // index,
63
+ // readyToBeDisplayed,
64
+ // });
65
+
66
+ if (isNextIndex(currentIndex, index)) {
56
67
  setReadyToBeDisplayed(true);
57
68
  }
58
69
  });
@@ -60,9 +71,18 @@ const useLoadingState = (
60
71
  return () => {
61
72
  subscription.unsubscribe();
62
73
  };
63
- }, [loadingState, currentIndex]);
74
+ }, [currentIndex]);
64
75
 
65
- return readyToBeDisplayed;
76
+ // console.log("RiverItem useLoadingState", {
77
+ // currentIndex,
78
+ // readyToBeDisplayed,
79
+ // loadingState: loadingState.getValue(),
80
+ // });
81
+
82
+ return (
83
+ readyToBeDisplayed ||
84
+ isNextIndex(loadingState.getValue().index, currentIndex)
85
+ );
66
86
  };
67
87
 
68
88
  function RiverItemComponent(props: RiverItemType) {
@@ -112,7 +132,7 @@ function RiverItemComponent(props: RiverItemType) {
112
132
  jsOnly: true,
113
133
  });
114
134
 
115
- if (!CellRenderer && item.component_type !== "group-qb") {
135
+ if (!CellRenderer && !isGroup(item)) {
116
136
  riverLogger.warning({
117
137
  message: "Cell Renderer is null - will fallback to default cell",
118
138
  data: { item, CellRenderer },
@@ -135,6 +135,12 @@ exports[`componentsMap renders renders components map correctly 1`] = `
135
135
  getItemCount={[Function]}
136
136
  initialNumToRender={3}
137
137
  keyExtractor={[Function]}
138
+ maintainVisibleContentPosition={
139
+ {
140
+ "autoscrollToTopThreshold": 10,
141
+ "minIndexForVisible": 0,
142
+ }
143
+ }
138
144
  maxToRenderPerBatch={10}
139
145
  onContentSizeChange={[Function]}
140
146
  onLayout={[Function]}
@@ -145,7 +151,7 @@ exports[`componentsMap renders renders components map correctly 1`] = `
145
151
  onScrollEndDrag={[Function]}
146
152
  overScrollMode="auto"
147
153
  refreshControl={null}
148
- removeClippedSubviews={true}
154
+ removeClippedSubviews={false}
149
155
  renderItem={[Function]}
150
156
  scrollEventThrottle={16}
151
157
  scrollIndicatorInsets={
@@ -12,11 +12,7 @@ const styles = StyleSheet.create({
12
12
 
13
13
  export function TestId({ screenId, children }: Props) {
14
14
  return (
15
- <View
16
- testID={screenId}
17
- accessibilityLabel={screenId}
18
- style={styles.container}
19
- >
15
+ <View testID={screenId} style={styles.container}>
20
16
  {children}
21
17
  </View>
22
18
  );
@@ -2,7 +2,6 @@
2
2
 
3
3
  exports[`<RouteManager /> renders correctly 1`] = `
4
4
  <View
5
- accessibilityLabel="A1234"
6
5
  style={
7
6
  {
8
7
  "flex": 1,
@@ -24,9 +24,5 @@ describe("TestId", () => {
24
24
  );
25
25
 
26
26
  expect(wrapper.getByTestId(screenId)).toBeTruthy();
27
-
28
- expect(wrapper.getByTestId(screenId).props.accessibilityLabel).toEqual(
29
- screenId
30
- );
31
27
  });
32
28
  });
@@ -0,0 +1,26 @@
1
+ const {
2
+ useScreenBackgroundColor,
3
+ } = require("@applicaster/zapp-react-native-utils/reactHooks/screen");
4
+
5
+ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/screen", () => ({
6
+ useScreenBackgroundColor: jest.fn(),
7
+ useNavbarState: jest.fn().mockReturnValue({ visible: true }),
8
+ }));
9
+
10
+ describe("TV Screen Component", () => {
11
+ it("uses the useScreenBackgroundColor hook with the correct screen ID", () => {
12
+ useScreenBackgroundColor.mockReturnValue("#FF0000");
13
+
14
+ expect(useScreenBackgroundColor("test-screen-id")).toBe("#FF0000");
15
+
16
+ // Verify the hook was called with the correct screen ID
17
+ expect(useScreenBackgroundColor).toHaveBeenCalledWith("test-screen-id");
18
+ });
19
+
20
+ it("returns 'transparent' when the screen background color is not defined", () => {
21
+ useScreenBackgroundColor.mockReturnValue("transparent");
22
+
23
+ // Verify the hook returns 'transparent'
24
+ expect(useScreenBackgroundColor("test-screen-id")).toBe("transparent");
25
+ });
26
+ });
@@ -92,6 +92,10 @@ const getNavigations = ({
92
92
  return fallbackNavigations;
93
93
  };
94
94
 
95
+ const onRelease = () => {
96
+ focusManager.setInitialFocus();
97
+ };
98
+
95
99
  export const Screen = ({ route, Components }: Props) => {
96
100
  if (isNilOrEmpty(route)) {
97
101
  throw Error("Required props: route is missing");
@@ -152,12 +156,7 @@ export const Screen = ({ route, Components }: Props) => {
152
156
  const isScreenActive = useIsScreenActive();
153
157
 
154
158
  return (
155
- <FreezeWithCallback
156
- freeze={!isScreenActive}
157
- onRelease={() => {
158
- focusManager.setInitialFocus();
159
- }}
160
- >
159
+ <FreezeWithCallback freeze={!isScreenActive} onRelease={onRelease}>
161
160
  <View style={[styles.container, { backgroundColor }]}>
162
161
  <NavBarContainer isVisible={isNavBarVisible} onReady={noop}>
163
162
  <NavBar
@@ -68,21 +68,21 @@ jest.mock(
68
68
  })
69
69
  );
70
70
 
71
- jest.mock(
72
- "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenData",
73
- () => ({
74
- useScreenData: jest.fn(() => ({
75
- id: "testId",
76
- navigations: [{ id: "testId", category: "nav_bar" }],
77
- })),
78
- })
79
- );
80
-
81
71
  jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
82
72
  isNavBarVisible: mockIsNavBarVisible,
83
- useRoute: jest.fn(() => ({
84
- pathname: "/river/testId",
85
- screenData: { id: "testId" },
73
+ useIsScreenActive: jest.fn().mockReturnValue(true),
74
+ }));
75
+
76
+ jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
77
+ useCurrentScreenData: jest.fn(() => ({
78
+ id: "testId",
79
+ })),
80
+ useNavbarState: jest.fn(() => ({
81
+ title: "Test Title",
82
+ })),
83
+ useScreenData: jest.fn(() => ({
84
+ id: "testId",
85
+ navigations: [{ id: "testId", category: "nav_bar" }],
86
86
  })),
87
87
  useNavigation: jest.fn(() => ({
88
88
  canGoBack: () => false,
@@ -91,7 +91,15 @@ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
91
91
  screenData: { id: "testId" },
92
92
  data: { screen: { id: "testId" } },
93
93
  })),
94
- useIsScreenActive: jest.fn().mockReturnValue(true),
94
+ useRoute: jest.fn(() => ({
95
+ pathname: "/river/testId",
96
+ screenData: { id: "testId" },
97
+ })),
98
+ useDimensions: jest.fn(() => ({
99
+ width: 1920,
100
+ height: 1080,
101
+ })),
102
+ useIsTablet: jest.fn(() => false),
95
103
  }));
96
104
 
97
105
  jest.mock("@applicaster/zapp-react-native-redux/hooks/usePickFromState", () => {
@@ -16,7 +16,7 @@ exports[`<Screen Component /> when the navbar should be hidden renders correctly
16
16
  pathname="/river/testId"
17
17
  selected="testId"
18
18
  testID="navBar"
19
- title=""
19
+ title="Test Title"
20
20
  />
21
21
  <View>
22
22
  <View
@@ -48,7 +48,7 @@ exports[`<Screen Component /> when the navbar should show renders correctly 1`]
48
48
  pathname="/river/testId"
49
49
  selected="testId"
50
50
  testID="navBar"
51
- title=""
51
+ title="Test Title"
52
52
  />
53
53
  <View>
54
54
  <View
@@ -7,15 +7,12 @@ import {
7
7
  useCurrentScreenData,
8
8
  useDimensions,
9
9
  useRoute,
10
+ useIsTablet,
10
11
  } from "@applicaster/zapp-react-native-utils/reactHooks";
11
12
  import { useMemo, useEffect, useState } from "react";
12
13
 
13
14
  export const useWaitForValidOrientation = () => {
14
- const {
15
- width: screenWidth,
16
- height,
17
- deviceInfo,
18
- } = useDimensions("screen", {
15
+ const { width: screenWidth, height } = useDimensions("screen", {
19
16
  fullDimensions: true,
20
17
  updateForInactiveScreens: false,
21
18
  });
@@ -26,7 +23,7 @@ export const useWaitForValidOrientation = () => {
26
23
 
27
24
  const [readyState, setReadyState] = useState(false);
28
25
 
29
- const isTablet = deviceInfo?.isTablet;
26
+ const isTablet = useIsTablet();
30
27
 
31
28
  const { appData } = usePickFromState(["appData"]);
32
29
  const isTabletPortrait = appData?.isTabletPortrait;