@applicaster/zapp-react-native-ui-components 13.0.0-alpha.1753452014 → 13.0.0-alpha.1756731121

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 (71) hide show
  1. package/Components/AudioPlayer/AudioPlayer.tsx +5 -9
  2. package/Components/AudioPlayer/helpers.tsx +1 -2
  3. package/Components/Cell/Cell.tsx +3 -13
  4. package/Components/Cell/CellWithFocusable.tsx +21 -15
  5. package/Components/Cell/CellWrapper.tsx +18 -0
  6. package/Components/Cell/styles.ts +17 -0
  7. package/Components/CellRendererResolver/index.ts +2 -1
  8. package/Components/ErrorScreen/index.tsx +8 -0
  9. package/Components/FocusableCell/index.tsx +1 -1
  10. package/Components/FocusableGroup/FocusableTvOS.tsx +3 -6
  11. package/Components/FocusableGroup/index.tsx +0 -1
  12. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +3 -0
  13. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +4 -2
  14. package/Components/GeneralContentScreen/utils/useEventAlerts.ts +30 -0
  15. package/Components/HandlePlayable/HandlePlayable.tsx +21 -27
  16. package/Components/Layout/TV/LayoutBackground.tsx +28 -0
  17. package/Components/Layout/TV/ScreenContainer.tsx +0 -1
  18. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +15 -10
  19. package/Components/Layout/TV/__tests__/index.test.tsx +8 -2
  20. package/Components/Layout/TV/index.tsx +6 -20
  21. package/Components/Layout/TV/index.web.tsx +4 -1
  22. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +4 -3
  23. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +11 -7
  24. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/const.ts +3 -0
  25. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +6 -6
  26. package/Components/MasterCell/utils/behaviorProvider.ts +4 -1
  27. package/Components/MasterCell/utils/index.ts +6 -6
  28. package/Components/ModalComponent/BottomSheetModalContent.tsx +49 -41
  29. package/Components/ModalComponent/Button/Item.tsx +6 -5
  30. package/Components/ModalComponent/Button/assets.ts +1 -1
  31. package/Components/ModalComponent/Button/index.tsx +45 -62
  32. package/Components/ModalComponent/Header/index.tsx +9 -8
  33. package/Components/ModalComponent/utils.ts +55 -7
  34. package/Components/OfflineHandler/utils/index.ts +1 -1
  35. package/Components/PlayerContainer/PlayerContainer.tsx +34 -7
  36. package/Components/PlayerImageBackground/index.tsx +4 -24
  37. package/Components/River/ComponentsMap/ComponentsMap.tsx +14 -3
  38. package/Components/River/ComponentsMap/hooks/useLoadingState.ts +3 -3
  39. package/Components/River/RiverItem.tsx +8 -3
  40. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +7 -1
  41. package/Components/Screen/TV/__tests__/index.web.test.tsx +26 -0
  42. package/Components/Screen/TV/index.web.tsx +5 -6
  43. package/Components/Screen/__tests__/Screen.test.tsx +22 -14
  44. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -2
  45. package/Components/Screen/hooks.ts +3 -6
  46. package/Components/TopMarginApplicator/TopMarginApplicator.tsx +2 -3
  47. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +1 -1
  48. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +35 -19
  49. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +3 -3
  50. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +38 -14
  51. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +11 -3
  52. package/Components/VideoModal/ModalAnimation/__tests__/getMoveUpValue.test.ts +108 -0
  53. package/Components/VideoModal/ModalAnimation/const.ts +5 -0
  54. package/Components/VideoModal/ModalAnimation/index.ts +2 -0
  55. package/Components/VideoModal/ModalAnimation/utils.ts +30 -0
  56. package/Components/VideoModal/PlayerDetails.tsx +9 -4
  57. package/Components/VideoModal/PlayerWrapper.tsx +35 -22
  58. package/Components/VideoModal/VideoModal.tsx +19 -4
  59. package/Components/VideoModal/__tests__/__snapshots__/PlayerDetails.test.tsx.snap +2 -40
  60. package/Components/VideoModal/utils.ts +3 -4
  61. package/Components/ZappUIComponent/index.tsx +4 -4
  62. package/Decorators/RiverResolver/index.tsx +5 -7
  63. package/Decorators/ZappPipesDataConnector/index.tsx +2 -1
  64. package/package.json +5 -6
  65. package/.babelrc +0 -8
  66. package/Components/Cell/CellStyles/FallbackCellStyle/index.js +0 -157
  67. package/Components/Cell/CellStyles/Hero/index.js +0 -111
  68. package/Components/Cell/CellStyles/ScreenSelector/index.js +0 -68
  69. package/Components/Cell/CellStyles/cellStylesResolver.ts +0 -19
  70. package/Components/Cell/CellStyles/colors.js +0 -40
  71. package/Components/Cell/CellStyles/index.js +0 -15
@@ -1,15 +1,18 @@
1
1
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
2
- import { PluginConfiguration } from "./index";
2
+ import { PluginConfiguration } from "./";
3
+ import { ItemIconProps } from "./Button/ItemIcon";
4
+ import { ItemLabelProps } from "./Button/ItemLabel";
5
+ import { ItemProps } from "./Button/Item";
3
6
 
4
7
  export function defaultItemProps(
5
8
  config: PluginConfiguration,
6
9
  maxWidth: number
7
- ) {
10
+ ): ItemProps {
8
11
  const {
9
12
  modal_bottom_sheet_item_background_color,
10
13
  modal_bottom_sheet_item_focus_background_color,
11
14
  modal_bottom_sheet_item_selected_background_color,
12
- modal_bottom_sheet_item_focusected_background_color,
15
+ modal_bottom_sheet_item_focused_selected_background_color,
13
16
  modal_bottom_sheet_item_corner_radius,
14
17
  modal_bottom_sheet_item_margin_bottom,
15
18
  modal_bottom_sheet_item_margin_left,
@@ -25,8 +28,8 @@ export function defaultItemProps(
25
28
  backgroundColor: modal_bottom_sheet_item_background_color,
26
29
  focusedBackgroundColor: modal_bottom_sheet_item_focus_background_color,
27
30
  selectedBackgroundColor: modal_bottom_sheet_item_selected_background_color,
28
- focusectedBackgroundColor:
29
- modal_bottom_sheet_item_focusected_background_color,
31
+ focusedSelectedBackgroundColor:
32
+ modal_bottom_sheet_item_focused_selected_background_color,
30
33
  borderRadius: modal_bottom_sheet_item_corner_radius,
31
34
  marginBottom: modal_bottom_sheet_item_margin_bottom,
32
35
  marginTop: modal_bottom_sheet_item_margin_top,
@@ -43,7 +46,7 @@ export function defaultItemProps(
43
46
  export function defaultItemLabelProps(
44
47
  config: PluginConfiguration,
45
48
  sheetWidth: number
46
- ) {
49
+ ): ItemLabelProps {
47
50
  const {
48
51
  modal_bottom_sheet_item_label_android_letter_spacing,
49
52
  modal_bottom_sheet_item_label_ios_letter_spacing,
@@ -103,7 +106,9 @@ export function defaultItemLabelProps(
103
106
  };
104
107
  }
105
108
 
106
- export function defaultItemIconProps(config: PluginConfiguration) {
109
+ export function defaultItemIconProps(
110
+ config: PluginConfiguration
111
+ ): ItemIconProps {
107
112
  const {
108
113
  modal_bottom_sheet_item_icon_height,
109
114
  modal_bottom_sheet_item_icon_width,
@@ -124,3 +129,46 @@ export function defaultItemIconProps(config: PluginConfiguration) {
124
129
  marginRight: modal_bottom_sheet_item_icon_margin_right,
125
130
  };
126
131
  }
132
+
133
+ export function getItemIconProps(
134
+ theme: PluginConfiguration,
135
+ iconProps?: ((theme: PluginConfiguration) => ItemIconProps) | ItemIconProps
136
+ ) {
137
+ if (iconProps) {
138
+ return typeof iconProps === "function" ? iconProps(theme) : iconProps;
139
+ }
140
+
141
+ return defaultItemIconProps(theme);
142
+ }
143
+
144
+ export function getItemLabelProps(
145
+ theme: PluginConfiguration,
146
+ width: number,
147
+ labelProps?:
148
+ | ((theme: PluginConfiguration, width: number) => ItemLabelProps)
149
+ | ItemLabelProps
150
+ ) {
151
+ if (labelProps) {
152
+ return typeof labelProps === "function"
153
+ ? labelProps(theme, width)
154
+ : labelProps;
155
+ }
156
+
157
+ return defaultItemLabelProps(theme, width);
158
+ }
159
+
160
+ export function getItemProps(
161
+ theme: PluginConfiguration,
162
+ width: number,
163
+ itemProps?:
164
+ | ((theme: PluginConfiguration, width: number) => ItemProps)
165
+ | ItemProps
166
+ ) {
167
+ if (itemProps) {
168
+ return typeof itemProps === "function"
169
+ ? itemProps(theme, width)
170
+ : itemProps;
171
+ }
172
+
173
+ return defaultItemProps(theme, width);
174
+ }
@@ -57,7 +57,7 @@ export const useNotificationHeight = () => {
57
57
 
58
58
  const navBarHeight = platformSelect({ ios: 44, android: 56 });
59
59
 
60
- const statusHeight = Platform.OS === "android" ? 0 : insets.top;
60
+ const statusHeight = insets.top;
61
61
  const notificationHeight = statusHeight + navBarHeight;
62
62
 
63
63
  return { statusHeight, notificationHeight };
@@ -9,6 +9,7 @@ import {
9
9
  isApplePlatform,
10
10
  isTV,
11
11
  platformSelect,
12
+ isAndroidTVPlatform,
12
13
  } from "@applicaster/zapp-react-native-utils/reactUtils";
13
14
 
14
15
  import { TVEventHandlerComponent } from "@applicaster/zapp-react-native-tvos-ui-components/Components/TVEventHandlerComponent";
@@ -93,6 +94,20 @@ export type PlayNextData = {
93
94
 
94
95
  const focusableBottomContainerId = "player-container-bottom";
95
96
 
97
+ const isAndroidTV = isAndroidTVPlatform();
98
+
99
+ const withBorderHack = () => {
100
+ if (isAndroidTV) {
101
+ /* @HACK: see GH#7269 */
102
+ return {
103
+ borderWidth: 1,
104
+ borderColor: "transparent",
105
+ };
106
+ }
107
+
108
+ return {};
109
+ };
110
+
96
111
  // Styles
97
112
  const webStyles = {
98
113
  focusableGroup: {
@@ -335,9 +350,9 @@ const PlayerContainerComponent = (props: Props) => {
335
350
 
336
351
  const resumeTime = Number(item?.extensions?.resumeTime);
337
352
 
338
- // TODO: This is temp hack, will be removed on next pr with player refactoring
353
+ // Сhecking that the player itself knows where to start playing, and there is no need to call seekTo after returning from the Сhromecast
339
354
  if (
340
- !!playerManager.getActivePlayer()?.getContinueWatchingOffset === false &&
355
+ !playerManager.getActivePlayer()?.hasResumePosition() &&
341
356
  !isNaN(resumeTime)
342
357
  ) {
343
358
  player?.seekTo(resumeTime);
@@ -514,6 +529,12 @@ const PlayerContainerComponent = (props: Props) => {
514
529
  useEffect(() => {
515
530
  playerEvent("source_changed", { item });
516
531
 
532
+ return () => {
533
+ playerEvent("player_did_close", { item });
534
+ };
535
+ }, [item?.id, Player]);
536
+
537
+ useEffect(() => {
517
538
  if (!isModal) {
518
539
  showNavBar(false);
519
540
  }
@@ -523,13 +544,11 @@ const PlayerContainerComponent = (props: Props) => {
523
544
  }
524
545
 
525
546
  return () => {
526
- playerEvent("player_did_close", { item });
527
-
528
547
  if (!isModal) {
529
548
  showNavBar(true);
530
549
  }
531
550
  };
532
- }, [showNavBar, /* added as suggested: */ item?.id, isModal, Player]);
551
+ }, [showNavBar, isModal, Player]);
533
552
 
534
553
  useEffect(() => {
535
554
  if (prevItemId && !R.equals(prevItemId, item?.id)) {
@@ -578,7 +597,7 @@ const PlayerContainerComponent = (props: Props) => {
578
597
  );
579
598
  }
580
599
 
581
- if (screen_background_color) {
600
+ if (screen_background_color && mode !== VideoModalMode.FULLSCREEN) {
582
601
  updatedStyles.playerScreen.backgroundColor = screen_background_color;
583
602
  }
584
603
 
@@ -596,6 +615,7 @@ const PlayerContainerComponent = (props: Props) => {
596
615
  tv_component_container_height,
597
616
  screen_background_color,
598
617
  isInlineTV,
618
+ mode,
599
619
  ]);
600
620
 
601
621
  const applePlayerProps = {
@@ -640,7 +660,14 @@ const PlayerContainerComponent = (props: Props) => {
640
660
  testID={"player-screen-container"}
641
661
  >
642
662
  {/* Player container */}
643
- <View style={styles.playerWrapper} testID={"player-wrapper"}>
663
+ <View
664
+ style={[
665
+ styles.playerWrapper,
666
+ // eslint-disable-next-line react-native/no-inline-styles, react-native/no-color-literals
667
+ withBorderHack(),
668
+ ]}
669
+ testID={"player-wrapper"}
670
+ >
644
671
  <PlayerFocusableWrapperView
645
672
  nextFocusDown={context.bottomFocusableId}
646
673
  >
@@ -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);
@@ -271,11 +277,12 @@ function ComponentsMapComponent(props: Props) {
271
277
  }}
272
278
  // Fix for WebView rerender crashes on Android API 28+
273
279
  // https://github.com/react-native-webview/react-native-webview/issues/1915#issuecomment-964035468
274
- overScrollMode={Platform.OS === "android" ? "never" : "auto"}
280
+ overScrollMode={isAndroid ? "never" : "auto"}
275
281
  scrollIndicatorInsets={scrollIndicatorInsets}
276
282
  extraData={feed}
283
+ stickyHeaderIndices={stickyHeaderIndices}
277
284
  onLayout={handleOnLayout}
278
- removeClippedSubviews
285
+ removeClippedSubviews={isAndroid}
279
286
  initialNumToRender={3}
280
287
  maxToRenderPerBatch={10}
281
288
  windowSize={12}
@@ -296,6 +303,10 @@ function ComponentsMapComponent(props: Props) {
296
303
  onMomentumScrollEnd={_onMomentumScrollEnd}
297
304
  onScrollEndDrag={_onScrollEndDrag}
298
305
  scrollEventThrottle={16}
306
+ maintainVisibleContentPosition={{
307
+ minIndexForVisible: 0,
308
+ autoscrollToTopThreshold: 10,
309
+ }}
299
310
  {...scrollViewExtraProps}
300
311
  />
301
312
  </ViewportTracker>
@@ -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
 
@@ -47,16 +48,20 @@ const useLoadingState = (
47
48
  loadingState: RiverItemType["loadingState"]
48
49
  ) => {
49
50
  const [readyToBeDisplayed, setReadyToBeDisplayed] = React.useState(
50
- loadingState.getValue().index + 1 === currentIndex
51
+ loadingState.getValue().index + 1 >= currentIndex
51
52
  );
52
53
 
53
54
  useEffect(() => {
54
55
  const subscription = loadingState.subscribe(({ index }) => {
55
- if (index + 1 === currentIndex) {
56
+ if (index + 1 >= currentIndex) {
56
57
  setReadyToBeDisplayed(true);
57
58
  }
58
59
  });
59
60
 
61
+ if (loadingState.getValue().index + 1 >= currentIndex) {
62
+ setReadyToBeDisplayed(true);
63
+ }
64
+
60
65
  return () => {
61
66
  subscription.unsubscribe();
62
67
  };
@@ -112,7 +117,7 @@ function RiverItemComponent(props: RiverItemType) {
112
117
  jsOnly: true,
113
118
  });
114
119
 
115
- if (!CellRenderer && item.component_type !== "group-qb") {
120
+ if (!CellRenderer && !isGroup(item)) {
116
121
  riverLogger.warning({
117
122
  message: "Cell Renderer is null - will fallback to default cell",
118
123
  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={
@@ -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;
@@ -2,6 +2,7 @@ import React from "react";
2
2
  import { View, ViewProps, ViewStyle } from "react-native";
3
3
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
4
4
  import { useCurrentScreenData } from "@applicaster/zapp-react-native-utils/reactHooks";
5
+ import { isFirstComponentScreenPicker } from "@applicaster/zapp-react-native-utils/componentsUtils";
5
6
 
6
7
  interface IProps {
7
8
  targetScreenId?: string;
@@ -31,11 +32,9 @@ export const useMarginTop = (targetScreenId: string): number => {
31
32
  * ScreenPicker is a component but should really be a screen.
32
33
  * We need to skip margin top for it as it's already applied to the target screen
33
34
  **/
34
- const isScreenPicker =
35
- screenData?.ui_components?.[0]?.component_type === "screen-picker-qb-tv";
36
35
 
37
36
  // ignore margin on screenPicker
38
- if (isScreenPicker) {
37
+ if (isFirstComponentScreenPicker(screenData?.ui_components)) {
39
38
  return 0;
40
39
  }
41
40
 
@@ -46,7 +46,7 @@ export const AnimatedPlayerModalWrapper = (props: Props) => {
46
46
 
47
47
  React.useEffect(() => {
48
48
  (playerAnimationState === PlayerAnimationStateEnum.minimize ||
49
- playerAnimationState === PlayerAnimationStateEnum.maximaze) &&
49
+ playerAnimationState === PlayerAnimationStateEnum.maximize) &&
50
50
  setStartComponentsAnimation(true);
51
51
  }, [playerAnimationState]);
52
52