@applicaster/zapp-react-native-ui-components 13.0.0-alpha.2833001702 → 13.0.0-alpha.2886020207

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/AudioPlayer/AudioPlayer.tsx +7 -4
  2. package/Components/AudioPlayer/helpers.tsx +1 -1
  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 +34 -86
  11. package/Components/FocusableGroup/index.tsx +0 -3
  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/NavBarContainer.tsx +2 -1
  18. package/Components/Layout/TV/ScreenContainer.tsx +8 -2
  19. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +5 -0
  20. package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +2 -1
  21. package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +1 -0
  22. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +1 -0
  23. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +15 -10
  24. package/Components/Layout/TV/__tests__/index.test.tsx +8 -2
  25. package/Components/Layout/TV/index.tsx +6 -20
  26. package/Components/Layout/TV/index.web.tsx +4 -1
  27. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -3
  28. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +4 -3
  29. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +11 -7
  30. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/const.ts +3 -0
  31. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +6 -6
  32. package/Components/MasterCell/SharedUI/ProgressBar/hooks/useFillInPercent.ts +2 -8
  33. package/Components/MasterCell/utils/behaviorProvider.ts +136 -0
  34. package/Components/MasterCell/utils/index.ts +8 -134
  35. package/Components/ModalComponent/BottomSheetModalContent.tsx +59 -43
  36. package/Components/ModalComponent/Button/Item.tsx +6 -5
  37. package/Components/ModalComponent/Button/assets.ts +1 -1
  38. package/Components/ModalComponent/Button/index.tsx +45 -62
  39. package/Components/ModalComponent/Header/index.tsx +9 -8
  40. package/Components/ModalComponent/utils.ts +55 -7
  41. package/Components/OfflineHandler/utils/index.ts +1 -1
  42. package/Components/PlayerContainer/PlayerContainer.tsx +34 -7
  43. package/Components/PlayerImageBackground/index.tsx +1 -1
  44. package/Components/River/ComponentsMap/ComponentsMap.tsx +14 -3
  45. package/Components/River/ComponentsMap/hooks/useLoadingState.ts +3 -3
  46. package/Components/River/RiverItem.tsx +8 -3
  47. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +7 -1
  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/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +1 -1
  59. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +35 -19
  60. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +3 -3
  61. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +38 -14
  62. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +10 -3
  63. package/Components/VideoModal/ModalAnimation/__tests__/getMoveUpValue.test.ts +108 -0
  64. package/Components/VideoModal/ModalAnimation/const.ts +5 -0
  65. package/Components/VideoModal/ModalAnimation/index.ts +2 -0
  66. package/Components/VideoModal/ModalAnimation/utils.ts +30 -0
  67. package/Components/VideoModal/PlayerDetails.tsx +13 -5
  68. package/Components/VideoModal/PlayerWrapper.tsx +35 -22
  69. package/Components/VideoModal/VideoModal.tsx +19 -4
  70. package/Components/VideoModal/__tests__/__snapshots__/PlayerDetails.test.tsx.snap +2 -40
  71. package/Components/VideoModal/utils.ts +3 -4
  72. package/Components/ZappUIComponent/index.tsx +4 -4
  73. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +4 -10
  74. package/Contexts/HeaderOffsetContext/index.tsx +4 -6
  75. package/Contexts/ScreenContext/index.tsx +3 -10
  76. package/Contexts/ScreenLayoutContext/index.tsx +5 -3
  77. package/Decorators/RiverResolver/index.tsx +5 -7
  78. package/Decorators/ZappPipesDataConnector/index.tsx +4 -30
  79. package/package.json +5 -9
  80. package/tsconfig.json +2 -3
  81. package/.babelrc +0 -8
  82. package/Components/Cell/CellStyles/FallbackCellStyle/index.js +0 -157
  83. package/Components/Cell/CellStyles/Hero/index.js +0 -111
  84. package/Components/Cell/CellStyles/ScreenSelector/index.js +0 -68
  85. package/Components/Cell/CellStyles/cellStylesResolver.ts +0 -19
  86. package/Components/Cell/CellStyles/colors.js +0 -40
  87. package/Components/Cell/CellStyles/index.js +0 -15
  88. package/Components/Cell/CellWithFocusable.ios.tsx +0 -126
@@ -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
  >
@@ -39,7 +39,7 @@ const PlayerImageBackgroundComponent = ({
39
39
  return (
40
40
  <View
41
41
  style={
42
- playerAnimationState === PlayerAnimationStateEnum.maximaze
42
+ playerAnimationState === PlayerAnimationStateEnum.maximize
43
43
  ? defaultImageDimensions
44
44
  : style
45
45
  }
@@ -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
 
@@ -12,6 +12,7 @@ type TrackedViewProps = {
12
12
  onPositionUpdated: (props: { rect?: Record<string, number> }) => void;
13
13
  testId?: string | undefined;
14
14
  groupId?: string;
15
+ clipThreshold?: number;
15
16
  };
16
17
 
17
18
  export const TrackedView = memo(function TrackedView(props: TrackedViewProps) {
@@ -2,21 +2,21 @@ import { Animated } from "react-native";
2
2
 
3
3
  import { NAV_ACTION_PUSH, NAV_ACTION_BACK } from "./Transitioner";
4
4
 
5
- type TransitionConfig = {
6
- duration: number;
7
- easing: any;
8
- from: {
9
- style: any;
10
- };
11
- to: {
12
- style: any;
13
- };
14
- };
5
+ // type TransitionConfig = {
6
+ // duration: number;
7
+ // easing: any;
8
+ // from: {
9
+ // style: any;
10
+ // };
11
+ // to: {
12
+ // style: any;
13
+ // };
14
+ // };
15
15
 
16
- type Props = {
17
- transitionConfig: TransitionConfig;
18
- contentStyle: { [string]: any };
19
- };
16
+ // type Props = {
17
+ // transitionConfig: TransitionConfig;
18
+ // contentStyle: { [string]: any };
19
+ // };
20
20
 
21
21
  /**
22
22
  * Manages animation for the Transitioner,
@@ -25,7 +25,7 @@ type Props = {
25
25
  * which must have a proper structure, see types above ^.
26
26
  */
27
27
  export class AnimationManager {
28
- constructor(props: Props) {
28
+ constructor(props) {
29
29
  this.animatedValue = new Animated.Value(0.0);
30
30
 
31
31
  this.config = props.transitionConfig(
@@ -32,6 +32,9 @@ const { log_error, log_debug } = loggerLiveImageComponent;
32
32
  const isMeasurement = (item: ZappEntry) =>
33
33
  isString(item.id) && item.id.startsWith("pre-measurement-");
34
34
 
35
+ // Pixels by which the view can slightly extend outside the viewport and still be considered fully visible.
36
+ const CLIP_THRESHOLD = 10;
37
+
35
38
  type Props = {
36
39
  item: ZappEntry;
37
40
  style: Record<string, any>;
@@ -328,6 +331,7 @@ const PlayerLiveImageComponent = (props: Props) => {
328
331
  <TrackedView
329
332
  testId={`tracked-view-${playerId}-${item.title}`}
330
333
  onPositionUpdated={onPositionUpdated}
334
+ clipThreshold={CLIP_THRESHOLD}
331
335
  >
332
336
  <View ref={trackViewRef}>
333
337
  {isVideoMode ? (
@@ -2,6 +2,7 @@
2
2
 
3
3
  exports[`PlayerLiveImageComponent should render correctly with default props 1`] = `
4
4
  <TrackedView
5
+ clipThreshold={10}
5
6
  onPositionUpdated={[Function]}
6
7
  testId="tracked-view-player1-Test"
7
8
  >
@@ -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
 
@@ -23,10 +23,12 @@ import {
23
23
  setScrollModalAnimatedValue,
24
24
  } from "./utils";
25
25
 
26
+ import { DURATION_TO_MINIMIZE } from "./const";
27
+
26
28
  const getAnimatedConfig = (toValue) => {
27
29
  return {
28
30
  toValue,
29
- duration: 200,
31
+ duration: DURATION_TO_MINIMIZE,
30
32
  useNativeDriver: true,
31
33
  };
32
34
  };
@@ -70,14 +72,19 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
70
72
  const { maximiseVideoModal, minimiseVideoModal, videoModalState } =
71
73
  useNavigation();
72
74
 
73
- const { mode: videoModalMode, item: videoModalItem } = videoModalState;
75
+ const {
76
+ mode: videoModalMode,
77
+ previousMode: previousVideoModalMode,
78
+ item: videoModalItem,
79
+ } = videoModalState;
80
+
74
81
  const isMaximizedModal: boolean = videoModalMode === "MAXIMIZED";
75
82
  const isMinimizedModal: boolean = videoModalMode === "MINIMIZED";
76
83
  const previousItemId = usePrevious(videoModalItem?.id);
77
84
 
78
85
  const isNotMinimizeMaximazeAnimation =
79
86
  playerAnimationState !== PlayerAnimationStateEnum.minimize &&
80
- playerAnimationState !== PlayerAnimationStateEnum.maximaze;
87
+ playerAnimationState !== PlayerAnimationStateEnum.maximize;
81
88
 
82
89
  const isEnablePanGesture =
83
90
  enableGesture &&
@@ -121,7 +128,7 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
121
128
  const preparedTranslationY =
122
129
  Math.abs(translationY) - lastScrollYValue.current;
123
130
 
124
- if (videoModalMode === "MAXIMIZED") {
131
+ if (isMaximizedModal) {
125
132
  const endOffsetY =
126
133
  lastSnap + preparedTranslationY + dragToss * velocityY + 150;
127
134
 
@@ -143,8 +150,8 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
143
150
  translateYOffset.flattenOffset();
144
151
  dragScrollY.setValue(0);
145
152
 
146
- setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
147
- } else {
153
+ setPlayerAnimationState(PlayerAnimationStateEnum.maximize);
154
+ } else if (destSnapPoint !== modalSnapPoints[0] && isMaximizedModal) {
148
155
  setPlayerAnimationState(PlayerAnimationStateEnum.minimize);
149
156
  }
150
157
  } else {
@@ -158,7 +165,7 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
158
165
 
159
166
  dragScrollY.setValue(0);
160
167
 
161
- setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
168
+ setPlayerAnimationState(PlayerAnimationStateEnum.maximize);
162
169
  } else {
163
170
  resetPlayerAnimationState();
164
171
  }
@@ -168,7 +175,13 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
168
175
  resetPlayerAnimationState();
169
176
  }
170
177
  },
171
- [lastSnap, modalSnapPoints, playerAnimationState, videoModalMode]
178
+ [
179
+ lastSnap,
180
+ modalSnapPoints,
181
+ playerAnimationState,
182
+ isMinimizedModal,
183
+ isMaximizedModal,
184
+ ]
172
185
  );
173
186
 
174
187
  const onScroll = React.useCallback(({ nativeEvent }) => {
@@ -219,17 +232,18 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
219
232
  }, []);
220
233
 
221
234
  React.useEffect(() => {
222
- const { mode, previousMode, item } = videoModalState;
223
-
224
235
  if (
225
- mode === "MAXIMIZED" &&
236
+ videoModalMode === "MAXIMIZED" &&
226
237
  !enableGesture &&
227
238
  scrollPosition.current === 0
228
239
  ) {
229
240
  setIEnableGesture(true);
230
241
  }
231
242
 
232
- if (mode === "MINIMIZED" && previousMode === "MAXIMIZED") {
243
+ if (
244
+ videoModalMode === "MINIMIZED" &&
245
+ previousVideoModalMode === "MAXIMIZED"
246
+ ) {
233
247
  // set animation to the minimize values if moving from the player to another screen
234
248
  if (playerAnimationState === null) {
235
249
  setScrollModalAnimatedValue(
@@ -247,14 +261,16 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
247
261
  }
248
262
  } else if (
249
263
  playerAnimationState === null &&
250
- ((previousItemId === item?.id &&
251
- mode === "MAXIMIZED" &&
252
- (previousMode === "MINIMIZED" || previousMode === "MAXIMIZED")) ||
253
- (previousItemId !== item?.id && mode !== "FULLSCREEN"))
264
+ ((previousItemId === videoModalItem?.id &&
265
+ videoModalMode === "MAXIMIZED" &&
266
+ (previousVideoModalMode === "MINIMIZED" ||
267
+ previousVideoModalMode === "MAXIMIZED")) ||
268
+ (previousItemId !== videoModalItem?.id &&
269
+ videoModalMode !== "FULLSCREEN"))
254
270
  ) {
255
- setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
271
+ setPlayerAnimationState(PlayerAnimationStateEnum.maximize);
256
272
  }
257
- }, [videoModalState]);
273
+ }, [videoModalMode, previousVideoModalMode, videoModalItem]);
258
274
 
259
275
  React.useEffect(() => {
260
276
  if (playerAnimationState === PlayerAnimationStateEnum.minimize) {
@@ -296,7 +312,7 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
296
312
 
297
313
  resetPlayerAnimationState();
298
314
  });
299
- } else if (playerAnimationState === PlayerAnimationStateEnum.maximaze) {
315
+ } else if (playerAnimationState === PlayerAnimationStateEnum.maximize) {
300
316
  Animated.timing(translateYOffset, getAnimatedConfig(0)).start(() => {
301
317
  maximiseVideoModal();
302
318
  setScrollModalAnimatedValue(translateYOffset, 0, setLastSnap);