@applicaster/zapp-react-native-ui-components 14.0.0-alpha.4748018412 → 14.0.0-alpha.5114565431

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 (98) hide show
  1. package/Components/AnimatedInOut/index.tsx +5 -3
  2. package/Components/AudioPlayer/index.tsx +15 -0
  3. package/Components/AudioPlayer/mobile/Layout.tsx +66 -0
  4. package/Components/AudioPlayer/{__tests__/__snapshots__/audioPlayer.test.js.snap → mobile/__tests__/__snapshots__/audioPlayerMobileLayout.test.js.snap} +2 -2
  5. package/Components/AudioPlayer/mobile/__tests__/audioPlayerMobileLayout.test.js +18 -0
  6. package/Components/AudioPlayer/mobile/index.tsx +18 -0
  7. package/Components/AudioPlayer/{Artwork.tsx → tv/Artwork.tsx} +3 -2
  8. package/Components/AudioPlayer/{Channel.tsx → tv/Channel.tsx} +7 -7
  9. package/Components/AudioPlayer/tv/Layout.tsx +168 -0
  10. package/Components/AudioPlayer/{Runtime.tsx → tv/Runtime.tsx} +7 -1
  11. package/Components/AudioPlayer/{Summary.tsx → tv/Summary.tsx} +6 -2
  12. package/Components/AudioPlayer/{Title.tsx → tv/Title.tsx} +6 -2
  13. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/Runtime.test.js.snap +2 -2
  14. package/Components/AudioPlayer/tv/__tests__/__snapshots__/audioPlayer.test.js.snap +164 -0
  15. package/Components/AudioPlayer/tv/__tests__/__snapshots__/channel.test.js.snap +19 -0
  16. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/summary.test.js.snap +1 -2
  17. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/title.test.js.snap +1 -2
  18. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/audioPlayer.test.js +7 -3
  19. package/Components/AudioPlayer/{helpers.tsx → tv/helpers.tsx} +11 -5
  20. package/Components/AudioPlayer/{AudioPlayer.tsx → tv/index.tsx} +17 -58
  21. package/Components/AudioPlayer/types.ts +40 -0
  22. package/Components/Cell/index.js +6 -2
  23. package/Components/Focusable/Focusable.tsx +5 -3
  24. package/Components/Focusable/FocusableTvOS.tsx +3 -3
  25. package/Components/Focusable/FocusableiOS.tsx +2 -2
  26. package/Components/Focusable/__tests__/index.android.test.tsx +3 -0
  27. package/Components/Focusable/index.android.tsx +12 -8
  28. package/Components/Focusable/index.tsx +1 -1
  29. package/Components/FocusableList/index.tsx +4 -0
  30. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +0 -2
  31. package/Components/HandlePlayable/HandlePlayable.tsx +25 -9
  32. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -27
  33. package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +1 -1
  34. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +2 -2
  35. package/Components/MasterCell/DefaultComponents/Text/index.tsx +1 -0
  36. package/Components/MasterCell/elementMapper.tsx +1 -2
  37. package/Components/MasterCell/index.tsx +1 -1
  38. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +13 -18
  39. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +9 -0
  40. package/Components/PlayerContainer/PlayerContainer.tsx +43 -30
  41. package/Components/PlayerImageBackground/index.tsx +1 -1
  42. package/Components/River/ComponentsMap/ComponentsMap.tsx +0 -1
  43. package/Components/River/TV/River.tsx +2 -20
  44. package/Components/River/TV/index.tsx +3 -1
  45. package/Components/River/TV/withPipesV1DataLoader.tsx +43 -0
  46. package/Components/River/TV/withRiverDataLoader.tsx +17 -0
  47. package/Components/River/TV/withTVEventHandler.tsx +1 -1
  48. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -0
  49. package/Components/ScreenRevealManager/ScreenRevealManager.ts +76 -0
  50. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +107 -0
  51. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +96 -0
  52. package/Components/ScreenRevealManager/index.ts +1 -0
  53. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +79 -0
  54. package/Components/Tabs/TV/Tabs.android.tsx +0 -2
  55. package/Components/TopMarginApplicator/TopMarginApplicator.tsx +16 -15
  56. package/Components/Touchable/__tests__/__snapshots__/touchable.test.tsx.snap +34 -0
  57. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +15 -9
  58. package/Components/VideoLive/animationUtils.ts +3 -3
  59. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +32 -8
  60. package/Components/VideoModal/PlayerDetails.tsx +24 -2
  61. package/Components/VideoModal/PlayerWrapper.tsx +26 -142
  62. package/Components/VideoModal/VideoModal.tsx +3 -17
  63. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -7
  64. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +44 -180
  65. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +17 -55
  66. package/Components/VideoModal/hooks/index.ts +0 -2
  67. package/Components/VideoModal/hooks/useModalSize.ts +18 -2
  68. package/Components/VideoModal/utils.ts +6 -0
  69. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +12 -16
  70. package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +84 -24
  71. package/Components/Viewport/VisibilitySensor/VisibilitySensor.tsx +3 -3
  72. package/Components/default-cell-renderer/viewTrees/tv/DefaultCell/index.ts +3 -3
  73. package/Decorators/ConfigurationWrapper/withConfigurationProvider.tsx +2 -2
  74. package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -0
  75. package/Decorators/ZappPipesDataConnector/__tests__/NullFeedResolver.test.tsx +78 -0
  76. package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +205 -0
  77. package/Decorators/ZappPipesDataConnector/__tests__/StaticFeedResolver.test.tsx +251 -0
  78. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +368 -0
  79. package/Decorators/ZappPipesDataConnector/__tests__/utils.test.ts +39 -0
  80. package/Decorators/ZappPipesDataConnector/index.tsx +26 -293
  81. package/Decorators/ZappPipesDataConnector/resolvers/NullFeedResolver.tsx +25 -0
  82. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +87 -0
  83. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +241 -0
  84. package/Decorators/ZappPipesDataConnector/types.ts +29 -0
  85. package/index.d.ts +0 -1
  86. package/package.json +5 -9
  87. package/Components/AudioPlayer/AudioPlayerLayout.tsx +0 -202
  88. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayerLayout.test.js.snap +0 -66
  89. package/Components/AudioPlayer/__tests__/__snapshots__/channel.test.js.snap +0 -28
  90. package/Components/AudioPlayer/__tests__/audioPlayerLayout.test.js +0 -26
  91. package/Components/AudioPlayer/index.ts +0 -1
  92. package/Components/VideoModal/hooks/useBackgroundColor.ts +0 -10
  93. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/Runtime.test.js +0 -0
  94. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/artWork.test.js.snap +0 -0
  95. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/artWork.test.js +0 -0
  96. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/channel.test.js +0 -0
  97. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/summary.test.js +0 -0
  98. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/title.test.js +0 -0
@@ -0,0 +1,96 @@
1
+ /* eslint-disable react/prop-types */
2
+
3
+ import * as React from "react";
4
+ import { render, screen, act } from "@testing-library/react-native";
5
+ import { View } from "react-native";
6
+ import {
7
+ withScreenRevealManager,
8
+ SHOWN,
9
+ TIMEOUT,
10
+ } from "../withScreenRevealManager";
11
+
12
+ // jest.mock("react-native/Libraries/Animated/NativeAnimatedHelper");
13
+
14
+ const MockComponent = ({
15
+ initialNumberToLoad,
16
+ onLoadFinishedFromScreenRevealManager,
17
+ onLoadFailedFromScreenRevealManager,
18
+ }) => {
19
+ React.useEffect(() => {
20
+ // Simulate loading components
21
+ for (let i = 0; i < initialNumberToLoad; i++) {
22
+ onLoadFinishedFromScreenRevealManager(i);
23
+ }
24
+ }, [initialNumberToLoad, onLoadFinishedFromScreenRevealManager]);
25
+
26
+ return (
27
+ <View
28
+ testID="mock-component"
29
+ initialNumberToLoad={initialNumberToLoad}
30
+ onLoadFinishedFromScreenRevealManager={
31
+ onLoadFinishedFromScreenRevealManager
32
+ }
33
+ onLoadFailedFromScreenRevealManager={onLoadFailedFromScreenRevealManager}
34
+ />
35
+ );
36
+ };
37
+
38
+ const WrappedComponent = withScreenRevealManager(MockComponent);
39
+
40
+ describe.skip("withScreenRevealManager", () => {
41
+ beforeEach(() => {
42
+ jest.clearAllMocks();
43
+ jest.useFakeTimers();
44
+ });
45
+
46
+ afterEach(() => {
47
+ jest.runOnlyPendingTimers();
48
+ jest.useRealTimers();
49
+ });
50
+
51
+ it("should render the wrapped component", () => {
52
+ render(
53
+ <WrappedComponent
54
+ componentsToRender={[{ id: "1" }, { id: "2" }, { id: "3" }]}
55
+ />
56
+ );
57
+
58
+ expect(screen.getByTestId("mock-component")).toBeTruthy();
59
+ });
60
+
61
+ it("should animate opacity when ready to show", () => {
62
+ render(
63
+ <WrappedComponent
64
+ componentsToRender={[{ id: "1" }, { id: "2" }, { id: "3" }]}
65
+ />
66
+ );
67
+
68
+ const animatedView = screen.getByTestId("animated-component");
69
+
70
+ act(() => {
71
+ jest.advanceTimersByTime(TIMEOUT + 100);
72
+ });
73
+
74
+ expect(animatedView.props.style.opacity).toBe(SHOWN);
75
+ });
76
+
77
+ it("should pass initialNumberToLoad, onLoadFinishedFromScreenRevealManager, and onLoadFailedFromScreenRevealManager to the wrapped component", () => {
78
+ render(
79
+ <WrappedComponent
80
+ componentsToRender={[{ id: "1" }, { id: "2" }, { id: "3" }]}
81
+ />
82
+ );
83
+
84
+ const mockComponent = screen.getByTestId("mock-component");
85
+
86
+ expect(mockComponent.props.initialNumberToLoad).toBe(3);
87
+
88
+ expect(
89
+ mockComponent.props.onLoadFinishedFromScreenRevealManager
90
+ ).toBeInstanceOf(Function);
91
+
92
+ expect(
93
+ mockComponent.props.onLoadFailedFromScreenRevealManager
94
+ ).toBeInstanceOf(Function);
95
+ });
96
+ });
@@ -0,0 +1 @@
1
+ export { withScreenRevealManager } from "./withScreenRevealManager";
@@ -0,0 +1,79 @@
1
+ import * as React from "react";
2
+ import { Animated } from "react-native";
3
+ import { isFirstComponentScreenPicker } from "@applicaster/zapp-react-native-utils/componentsUtils";
4
+ import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
5
+ import { useRefWithInitialValue } from "@applicaster/zapp-react-native-utils/reactHooks/state/useRefWithInitialValue";
6
+
7
+ import { ScreenRevealManager } from "./ScreenRevealManager";
8
+
9
+ const flex = platformSelect({
10
+ tvos: 1,
11
+ android_tv: 1,
12
+ web: undefined,
13
+ samsung_tv: undefined,
14
+ lg_tv: undefined,
15
+ default: undefined,
16
+ });
17
+
18
+ export const TIMEOUT = 500; // 500 ms
19
+
20
+ const HIDDEN = 0; // opacity = 0
21
+
22
+ export const SHOWN = 1; // opacity = 1
23
+
24
+ type Props = {
25
+ componentsToRender: ZappUIComponent[];
26
+ };
27
+
28
+ export const withScreenRevealManager = (Component) => {
29
+ return function WithScreenRevealManager(props: Props) {
30
+ const { componentsToRender } = props;
31
+
32
+ const [isReadyToShow, setIsReadyToShow] = React.useState(false);
33
+
34
+ const handleSetIsReadyToShow = React.useCallback(() => {
35
+ setIsReadyToShow(true);
36
+ }, []);
37
+
38
+ const managerRef = useRefWithInitialValue<ScreenRevealManager>(
39
+ () => new ScreenRevealManager(componentsToRender, handleSetIsReadyToShow)
40
+ );
41
+
42
+ const opacityRef = useRefWithInitialValue<Animated.Value>(
43
+ () => new Animated.Value(HIDDEN)
44
+ );
45
+
46
+ React.useEffect(() => {
47
+ if (isReadyToShow) {
48
+ Animated.timing(opacityRef.current, {
49
+ toValue: SHOWN,
50
+ duration: TIMEOUT,
51
+ useNativeDriver: true,
52
+ }).start();
53
+ }
54
+ }, [isReadyToShow]);
55
+
56
+ if (isFirstComponentScreenPicker(componentsToRender)) {
57
+ // for screen-picker with have additional internal ComponentsMap, no need to add this wrapper
58
+ return <Component {...props} />;
59
+ }
60
+
61
+ return (
62
+ <Animated.View
63
+ style={{ opacity: opacityRef.current, flex }}
64
+ testID="animated-component"
65
+ >
66
+ <Component
67
+ {...props}
68
+ initialNumberToLoad={
69
+ managerRef.current.numberOfComponentsWaitToLoadBeforePresent
70
+ }
71
+ onLoadFinishedFromScreenRevealManager={
72
+ managerRef.current.onLoadFinished
73
+ }
74
+ onLoadFailedFromScreenRevealManager={managerRef.current.onLoadFailed}
75
+ />
76
+ </Animated.View>
77
+ );
78
+ };
79
+ };
@@ -7,7 +7,6 @@ import { isEmptyOrNil } from "@applicaster/zapp-react-native-utils/cellUtils";
7
7
 
8
8
  import Tab from "./Tab";
9
9
  import { Gutter } from "../Gutter";
10
- import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
11
10
  import { ImageBackground, View } from "react-native";
12
11
  import { getStyles } from "./styles";
13
12
  import {
@@ -100,7 +99,6 @@ const TabsComponent = ({
100
99
  >
101
100
  <FocusableList
102
101
  horizontal
103
- onScrollToIndexFailed={noop}
104
102
  onLayout={onLayoutChange}
105
103
  contentContainerStyle={tabsListContentContainer}
106
104
  ref={flatListRef}
@@ -1,17 +1,17 @@
1
1
  import React from "react";
2
- import { View, ViewProps, ViewStyle } from "react-native";
2
+ import { View, 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
5
  import { isFirstComponentScreenPicker } from "@applicaster/zapp-react-native-utils/componentsUtils";
6
+ import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
6
7
 
7
8
  interface IProps {
8
9
  targetScreenId?: string;
9
10
  children?: React.ReactNode;
10
11
  style?: ViewStyle;
11
- extraOffset?: number;
12
+ extraVerticalOffset: Option<number>;
12
13
  }
13
14
 
14
- type CombinedProps = IProps & ViewProps;
15
15
  /**
16
16
  * The MarginTop is essentially a feature used for managing the visibility of components on your screen.
17
17
  * A more accurate term for this might be something like a 'component visibility threshold' or 'cut-off point'.
@@ -47,7 +47,7 @@ export const useMarginTop = (targetScreenId: string): number => {
47
47
 
48
48
  // Empty string means that value is blank in the CMS. Fallback to theme
49
49
  if (String(screenData?.styles?.screen_margin_top) === "") {
50
- return Number(theme.screen_margin_top);
50
+ return toNumberWithDefault(0, theme.screen_margin_top);
51
51
  }
52
52
 
53
53
  /**
@@ -58,28 +58,29 @@ export const useMarginTop = (targetScreenId: string): number => {
58
58
  */
59
59
  if (screenData?.styles?.screen_margin_top === undefined) {
60
60
  if (isGeneralContentScreen || supportsUiComponents) {
61
- return Number(theme.screen_margin_top);
61
+ return toNumberWithDefault(0, theme.screen_margin_top);
62
62
  }
63
63
 
64
64
  return 0;
65
65
  }
66
66
 
67
- return Number(screenData?.styles?.screen_margin_top);
67
+ return toNumberWithDefault(0, screenData?.styles?.screen_margin_top);
68
68
  };
69
69
 
70
- export const TopMarginApplicator: React.FC<CombinedProps> = (
71
- props: CombinedProps
72
- ) => {
73
- const extraOffset = props?.extraOffset ?? 0;
70
+ export const TopMarginApplicator: React.FC<IProps> = ({
71
+ targetScreenId,
72
+ style,
73
+ children,
74
+ extraVerticalOffset,
75
+ }: IProps) => {
76
+ const extraOffset = toNumberWithDefault(0, extraVerticalOffset);
74
77
 
75
78
  // HACK: Remove extraOffset when focusIssue with absolute elements is fixed on tvos
76
- const marginTop = useMarginTop(props.targetScreenId) + extraOffset;
77
- const style = { ...((props.style as {}) || {}), marginTop };
79
+ const marginTop = useMarginTop(targetScreenId);
78
80
 
79
- // Then, spread the rest of the props on your returned JSX.
80
81
  return (
81
- <View {...props} style={style}>
82
- {props.children}
82
+ <View style={[style, { marginTop: marginTop + extraOffset }]}>
83
+ {children}
83
84
  </View>
84
85
  );
85
86
  };
@@ -3,6 +3,23 @@
3
3
  exports[`<Touchable /> when not running in automated tests environment renders correctly 1`] = `
4
4
  <View
5
5
  accessibilityLabel="some-test-id"
6
+ accessibilityState={
7
+ {
8
+ "busy": undefined,
9
+ "checked": undefined,
10
+ "disabled": undefined,
11
+ "expanded": undefined,
12
+ "selected": undefined,
13
+ }
14
+ }
15
+ accessibilityValue={
16
+ {
17
+ "max": undefined,
18
+ "min": undefined,
19
+ "now": undefined,
20
+ "text": undefined,
21
+ }
22
+ }
6
23
  accessible={true}
7
24
  collapsable={false}
8
25
  focusable={true}
@@ -29,6 +46,23 @@ exports[`<Touchable /> when not running in automated tests environment renders c
29
46
  exports[`<Touchable /> when running in automated tests environment has accessible flag set to false 1`] = `
30
47
  <View
31
48
  accessibilityLabel="some-test-id"
49
+ accessibilityState={
50
+ {
51
+ "busy": undefined,
52
+ "checked": undefined,
53
+ "disabled": undefined,
54
+ "expanded": undefined,
55
+ "selected": undefined,
56
+ }
57
+ }
58
+ accessibilityValue={
59
+ {
60
+ "max": undefined,
61
+ "min": undefined,
62
+ "now": undefined,
63
+ "text": undefined,
64
+ }
65
+ }
32
66
  accessible={false}
33
67
  collapsable={false}
34
68
  focusable={true}
@@ -6,15 +6,21 @@ exports[`<Scene /> renders correctly 1`] = `
6
6
  collapsable={false}
7
7
  pointerEvents="auto"
8
8
  style={
9
- {
10
- "flex": 1,
11
- "fontScale": 2,
12
- "height": 1334,
13
- "paddingBottom": 49,
14
- "scale": 2,
15
- "statusBarHeight": null,
16
- "width": 750,
17
- }
9
+ [
10
+ {
11
+ "flex": 1,
12
+ },
13
+ {
14
+ "paddingBottom": 49,
15
+ },
16
+ {
17
+ "fontScale": 2,
18
+ "height": 1334,
19
+ "scale": 2,
20
+ "statusBarHeight": null,
21
+ "width": 750,
22
+ },
23
+ ]
18
24
  }
19
25
  />
20
26
  </View>
@@ -1,8 +1,8 @@
1
1
  import { Animated, Easing, EasingFunction, StyleProp } from "react-native";
2
2
 
3
3
  type AnimatedInterpolatedStyle =
4
- | Animated.AnimatedInterpolation
5
- | [{ [Key: string]: Animated.AnimatedInterpolation }];
4
+ | Animated.AnimatedInterpolation<number>
5
+ | [{ [Key: string]: Animated.AnimatedInterpolation<number> }];
6
6
 
7
7
  type AnimationConfig = {
8
8
  duration: number;
@@ -31,7 +31,7 @@ const interpolate = (
31
31
  animatedValue: Animated.Value,
32
32
  from: number = 0,
33
33
  to: number = 1
34
- ): Animated.AnimatedInterpolation =>
34
+ ): Animated.AnimatedInterpolation<number> =>
35
35
  animatedValue.interpolate({
36
36
  inputRange: [0, 1],
37
37
  outputRange: [from, to],
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect } from "react";
2
- import { Animated } from "react-native";
2
+ import { Animated, Dimensions } from "react-native";
3
3
 
4
4
  import {
5
5
  useSafeAreaInsets,
@@ -11,6 +11,7 @@ import { isLive } from "@applicaster/zapp-react-native-utils/playerUtils";
11
11
 
12
12
  import { PROGRESS_BAR_HEIGHT } from "./utils";
13
13
  import { useConfiguration } from "../utils";
14
+ import { useIsTabletLandscape } from "@applicaster/zapp-react-native-utils/reactHooks/device/useMemoizedIsTablet";
14
15
 
15
16
  export enum PlayerAnimationStateEnum {
16
17
  minimize = "minimize",
@@ -23,6 +24,7 @@ export enum PlayerAnimationStateEnum {
23
24
  export type PlayerAnimationStateT = number | PlayerAnimationStateEnum | null;
24
25
 
25
26
  export type ModalAnimationContextT = {
27
+ yTranslate: React.MutableRefObject<Animated.Value | null>;
26
28
  isActiveGesture: boolean;
27
29
  playerAnimationState: PlayerAnimationStateT;
28
30
  setPlayerAnimationState: (value: PlayerAnimationStateT) => void;
@@ -48,6 +50,7 @@ export type ModalAnimationContextT = {
48
50
  };
49
51
 
50
52
  export const ReactContext = React.createContext<ModalAnimationContextT>({
53
+ yTranslate: React.createRef<Animated.Value | null>(),
51
54
  isActiveGesture: false,
52
55
  playerAnimationState: null,
53
56
  setPlayerAnimationState: () => null,
@@ -73,6 +76,10 @@ export const ReactContext = React.createContext<ModalAnimationContextT>({
73
76
  });
74
77
 
75
78
  const Provider = ({ children }: { children: React.ReactNode }) => {
79
+ const yTranslate = React.useRef(
80
+ new Animated.Value(Dimensions.get("window").height)
81
+ );
82
+
76
83
  const [playerAnimationState, setPlayerAnimationState] =
77
84
  React.useState<PlayerAnimationStateT>(null);
78
85
 
@@ -96,13 +103,6 @@ const Provider = ({ children }: { children: React.ReactNode }) => {
96
103
  setStartComponentsAnimation(false);
97
104
  }, []);
98
105
 
99
- useEffect(() => {
100
- // Reset player animation state when video modal is closed
101
- if (!visible) {
102
- resetPlayerAnimationState();
103
- }
104
- }, [visible, resetPlayerAnimationState]);
105
-
106
106
  // Animated values
107
107
  const lastScrollY = React.useRef(new Animated.Value(0)).current;
108
108
  const dragScrollY = React.useRef(new Animated.Value(0)).current;
@@ -115,6 +115,29 @@ const Provider = ({ children }: { children: React.ReactNode }) => {
115
115
  const { bottom: bottomSafeArea } = useSafeAreaInsets();
116
116
  const bottomTabBarHeight = useGetBottomTabBarHeight();
117
117
  const startComponentsAnimationDistance = Math.round((height * 60) / 100);
118
+ const isTabletLandscape = useIsTabletLandscape();
119
+ const windowDimensions = Dimensions.get("window");
120
+
121
+ useEffect(() => {
122
+ // Reset player animation state when video modal is closed
123
+ if (!visible) {
124
+ resetPlayerAnimationState();
125
+
126
+ if (!isTabletLandscape) {
127
+ // restore to portrait ( in portrait mode height is bigger)
128
+ if (windowDimensions.height > windowDimensions.width) {
129
+ yTranslate.current?.setValue(windowDimensions.height);
130
+ }
131
+ } else {
132
+ yTranslate.current?.setValue(windowDimensions.height);
133
+ }
134
+ }
135
+ }, [
136
+ visible,
137
+ resetPlayerAnimationState,
138
+ windowDimensions.height,
139
+ isTabletLandscape,
140
+ ]);
118
141
 
119
142
  React.useEffect(() => {
120
143
  if (visible && mode === "MAXIMIZED" && height !== safeAreaFrameHeight) {
@@ -141,6 +164,7 @@ const Provider = ({ children }: { children: React.ReactNode }) => {
141
164
  return (
142
165
  <ReactContext.Provider
143
166
  value={{
167
+ yTranslate,
144
168
  startComponentsAnimation,
145
169
  setStartComponentsAnimation,
146
170
  isActiveGesture: playerAnimationState !== null,
@@ -11,6 +11,8 @@ import { useTargetScreenData } from "@applicaster/zapp-react-native-utils/reactH
11
11
  import { ComponentsMap } from "@applicaster/zapp-react-native-ui-components/Components/River/ComponentsMap";
12
12
  import { useSafeAreaInsets } from "react-native-safe-area-context";
13
13
  import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
14
+ import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
15
+ import { useDelayedPlayerDetails } from "./hooks";
14
16
 
15
17
  const { width: SCREEN_WIDTH } = Dimensions.get("screen");
16
18
 
@@ -26,6 +28,10 @@ type Props = {
26
28
  isTabletLandscape?: boolean;
27
29
  isAudioPlayer?: boolean;
28
30
  isTablet?: boolean;
31
+ inline?: any;
32
+ docked?: boolean;
33
+ isModal?: boolean;
34
+ pip?: boolean;
29
35
  };
30
36
 
31
37
  const containerStyle = ({
@@ -42,8 +48,24 @@ export const PlayerDetails = ({
42
48
  configuration,
43
49
  isTabletLandscape = false,
44
50
  isAudioPlayer,
45
- isTablet = false,
51
+ inline,
52
+ docked,
53
+ isModal,
54
+ pip,
46
55
  }: Props) => {
56
+ const isInlineModal = inline && isModal;
57
+
58
+ // Mounting the PlayerDetails component is a resource-intensive process.
59
+ // Therefore, for performance reasons, we mount it with a delay to make the rotation process as smooth as possible.
60
+ // The flow is as follows: the rotation occurs first, and then, after a short delay, we mount the PlayerDetails component.
61
+ // This helps to avoid blocking the rotation and any animations related to the rotation.
62
+ const isShowPlayerDetails = useDelayedPlayerDetails({
63
+ isInline: isInlineModal,
64
+ isDocked: docked,
65
+ isPip: pip,
66
+ });
67
+
68
+ const isTablet = useIsTablet();
47
69
  const screenData = useTargetScreenData(entry);
48
70
  const insets = useSafeAreaInsets();
49
71
 
@@ -79,7 +101,7 @@ export const PlayerDetails = ({
79
101
  }
80
102
  }, [isAudioPlayer]);
81
103
 
82
- if (isNilOrEmpty(screenData?.ui_components)) {
104
+ if (isNilOrEmpty(screenData?.ui_components) || !isShowPlayerDetails) {
83
105
  return null;
84
106
  }
85
107