@applicaster/zapp-react-native-ui-components 14.0.0-alpha.3552323332 → 14.0.0-alpha.4009339136

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 (44) hide show
  1. package/Components/AudioPlayer/index.tsx +15 -0
  2. package/Components/AudioPlayer/mobile/Layout.tsx +66 -0
  3. package/Components/AudioPlayer/{__tests__/__snapshots__/audioPlayer.test.js.snap → mobile/__tests__/__snapshots__/audioPlayerMobileLayout.test.js.snap} +2 -8
  4. package/Components/AudioPlayer/mobile/__tests__/audioPlayerMobileLayout.test.js +18 -0
  5. package/Components/AudioPlayer/mobile/index.tsx +18 -0
  6. package/Components/AudioPlayer/{Artwork.tsx → tv/Artwork.tsx} +3 -2
  7. package/Components/AudioPlayer/{Channel.tsx → tv/Channel.tsx} +7 -7
  8. package/Components/AudioPlayer/tv/Layout.tsx +168 -0
  9. package/Components/AudioPlayer/{Runtime.tsx → tv/Runtime.tsx} +7 -1
  10. package/Components/AudioPlayer/{Summary.tsx → tv/Summary.tsx} +6 -2
  11. package/Components/AudioPlayer/{Title.tsx → tv/Title.tsx} +6 -2
  12. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/Runtime.test.js.snap +2 -2
  13. package/Components/AudioPlayer/tv/__tests__/__snapshots__/audioPlayer.test.js.snap +164 -0
  14. package/Components/AudioPlayer/tv/__tests__/__snapshots__/channel.test.js.snap +19 -0
  15. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/summary.test.js.snap +1 -2
  16. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/title.test.js.snap +1 -2
  17. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/audioPlayer.test.js +7 -3
  18. package/Components/AudioPlayer/{helpers.tsx → tv/helpers.tsx} +1 -2
  19. package/Components/AudioPlayer/{AudioPlayer.tsx → tv/index.tsx} +17 -58
  20. package/Components/AudioPlayer/types.ts +40 -0
  21. package/Components/MasterCell/index.tsx +1 -1
  22. package/Components/PlayerContainer/PlayerContainer.tsx +7 -17
  23. package/Components/PlayerImageBackground/index.tsx +1 -1
  24. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +1 -9
  25. package/Components/VideoModal/PlayerDetails.tsx +2 -24
  26. package/Components/VideoModal/PlayerWrapper.tsx +142 -26
  27. package/Components/VideoModal/VideoModal.tsx +17 -3
  28. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +7 -1
  29. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +240 -44
  30. package/Components/VideoModal/hooks/index.ts +2 -0
  31. package/Components/VideoModal/hooks/useBackgroundColor.ts +10 -0
  32. package/Components/VideoModal/utils.ts +0 -6
  33. package/package.json +5 -5
  34. package/Components/AudioPlayer/AudioPlayerLayout.tsx +0 -202
  35. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayerLayout.test.js.snap +0 -72
  36. package/Components/AudioPlayer/__tests__/__snapshots__/channel.test.js.snap +0 -28
  37. package/Components/AudioPlayer/__tests__/audioPlayerLayout.test.js +0 -26
  38. package/Components/AudioPlayer/index.ts +0 -1
  39. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/Runtime.test.js +0 -0
  40. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/artWork.test.js.snap +0 -0
  41. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/artWork.test.js +0 -0
  42. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/channel.test.js +0 -0
  43. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/summary.test.js +0 -0
  44. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/title.test.js +0 -0
@@ -4,14 +4,13 @@ exports[`<Summary /> renders correctly 1`] = `
4
4
  <View
5
5
  style={
6
6
  {
7
- "height": 80,
8
7
  "marginBottom": 30,
9
8
  "width": 600,
10
9
  }
11
10
  }
12
11
  >
13
12
  <Text
14
- numberOfLines={2}
13
+ numberOfLines={3}
15
14
  style={
16
15
  {
17
16
  "color": "white",
@@ -4,14 +4,13 @@ exports[`<Title /> renders correctly 1`] = `
4
4
  <View
5
5
  style={
6
6
  {
7
- "height": 100,
8
7
  "marginBottom": 12,
9
8
  "width": 600,
10
9
  }
11
10
  }
12
11
  >
13
12
  <Text
14
- numberOfLines={2}
13
+ numberOfLines={3}
15
14
  style={
16
15
  {
17
16
  "color": "white",
@@ -1,7 +1,11 @@
1
1
  import React from "react";
2
2
  import { render } from "@testing-library/react-native";
3
3
 
4
- import { AudioPlayer } from "../AudioPlayer";
4
+ import { AudioPlayerTV } from "..";
5
+
6
+ jest.mock("@applicaster/zapp-react-native-utils/audioPlayerUtils", () => ({
7
+ useArtworkImage: jest.fn(() => "artwork_url"),
8
+ }));
5
9
 
6
10
  const audioPlayerProps = {
7
11
  audio_item: {
@@ -45,9 +49,9 @@ const audioPlayerProps = {
45
49
  styles: {},
46
50
  };
47
51
 
48
- describe("<AudioPlayer />", () => {
52
+ describe("<AudioPlayerTV />", () => {
49
53
  it("renders correctly", () => {
50
- const { toJSON } = render(<AudioPlayer {...audioPlayerProps} />);
54
+ const { toJSON } = render(<AudioPlayerTV {...audioPlayerProps} />);
51
55
  expect(toJSON()).toMatchSnapshot();
52
56
  });
53
57
  });
@@ -2,9 +2,8 @@ const defaults = {
2
2
  audio_player_title_color: "white",
3
3
  audio_player_summary_color: "white",
4
4
  audio_player_background_color: "black",
5
- audio_player_artwork_aspect_ratio: "1:1",
5
+ audio_player_background_image: undefined,
6
6
  audio_player_rtl: false,
7
- audio_player_background_image_default_color: "",
8
7
  };
9
8
 
10
9
  export function getPropertyFromEntryOrConfig({ entry, plugin_configuration }) {
@@ -1,60 +1,24 @@
1
1
  import React, { useCallback, useMemo } from "react";
2
2
 
3
3
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
4
+ import { useArtworkImage } from "@applicaster/zapp-react-native-utils/audioPlayerUtils";
4
5
 
5
- import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
6
+ import { AudioPlayerTVLayout } from "./Layout";
6
7
 
7
- import { AudioPlayerLayout } from "./AudioPlayerLayout";
8
8
  import { Channel } from "./Channel";
9
9
  import { Title } from "./Title";
10
10
  import { Summary } from "./Summary";
11
11
  import { Runtime } from "./Runtime";
12
12
  import { getPropertyFromEntryOrConfig } from "./helpers";
13
- import { ViewStyle } from "react-native";
14
-
15
- type Props = {
16
- audio_item: ZappEntry & {
17
- extensions?: {
18
- audio_player_artwork_aspect_ratio?: string;
19
- audio_player_background_image?: string;
20
- audio_player_background_color?: string;
21
- audio_player_channel_icon?: string;
22
- audio_player_title_color?: string;
23
- audio_player_summary_color?: string;
24
- audio_player_rtl?: boolean;
25
- audio_player_background_image_default_color?: string;
26
- start_time?: string;
27
- end_time?: string;
28
- };
29
- };
30
- plugin_configuration: {
31
- audio_player_background_color?: string;
32
- audio_player_title_color?: string;
33
- audio_player_summary_color?: string;
34
- audio_player_rtl?: string;
35
- audio_player_background_image_default_color?: string;
36
- audio_player_background_image?: string;
37
- audio_player_artwork_aspect_ratio?: string;
38
- lg_tv_audio_player_title_font_family?: string;
39
- lg_tv_audio_player_title_font_size?: number;
40
- lg_tv_audio_player_summary_font_family?: string;
41
- lg_tv_audio_player_summary_font_size?: number;
42
- samsung_tv_audio_player_title_font_family?: string;
43
- samsung_tv_audio_player_title_font_size?: number;
44
- samsung_tv_audio_player_summary_font_family?: string;
45
- samsung_tv_audio_player_summary_font_size?: number;
46
- tv_os_audio_player_title_font_family?: string;
47
- tv_os_audio_player_title_font_size?: number;
48
- tv_os_audio_player_summary_font_family?: string;
49
- tv_os_audio_player_summary_font_size?: number;
50
- };
51
- style?: ViewStyle;
52
- };
53
-
54
- export function AudioPlayer(props: Props) {
55
- const { audio_item, plugin_configuration } = props;
13
+
14
+ import { Props } from "../types";
15
+
16
+ export function AudioPlayerTV(props: Props) {
17
+ const { audio_item, plugin_configuration, style = {} } = props;
56
18
  const { extensions, title, summary } = audio_item;
57
19
 
20
+ const artwork = useArtworkImage(audio_item);
21
+
58
22
  const getProp = useCallback(
59
23
  getPropertyFromEntryOrConfig({
60
24
  entry: audio_item,
@@ -64,20 +28,20 @@ export function AudioPlayer(props: Props) {
64
28
  );
65
29
 
66
30
  const config = useMemo(() => {
67
- // Checking if we are recieving items from the DSP
31
+ // Checking if we are receiving items from the DSP
68
32
  const titleColor = getProp("audio_player_title_color");
69
33
  const summaryColor = getProp("audio_player_summary_color");
70
34
  const backgroundColor = getProp("audio_player_background_color");
71
35
  const backgroundImage = getProp("audio_player_background_image");
72
- const artworkAspectRatio = getProp("audio_player_artwork_aspect_ratio");
73
36
  const channelIcon = getProp("audio_player_channel_icon");
74
37
  const rtlFlag = getProp("audio_player_rtl");
75
- const artworkBorderRadius = getProp("audio_player_artwork_border_radius");
76
38
 
77
- const audioPlayerBackgroundImageDefaultColor = getProp(
78
- "audio_player_background_image_default_color"
39
+ const backgroundImageOverlay = getProp(
40
+ "audio_player_background_image_overlay"
79
41
  );
80
42
 
43
+ const artworkBorderRadius = getProp("audio_player_artwork_border_radius");
44
+
81
45
  const isRTL = rtlFlag === "1" || rtlFlag === "true" || rtlFlag === true;
82
46
 
83
47
  const titleFontFamily = getProp(
@@ -152,23 +116,18 @@ export function AudioPlayer(props: Props) {
152
116
  summaryFontSize,
153
117
  runTimeFontFamily,
154
118
  runTimeFontSize,
155
- artworkAspectRatio,
156
119
  channelIcon,
157
- audioPlayerBackgroundImageDefaultColor,
158
120
  artworkBorderRadius,
121
+ backgroundImageOverlay,
159
122
  };
160
123
  }, [getProp]);
161
124
 
162
- const artwork = imageSrcFromMediaItem(audio_item, [
163
- config?.artworkAspectRatio,
164
- ]);
165
-
166
125
  return (
167
- <AudioPlayerLayout artwork={artwork} config={config}>
126
+ <AudioPlayerTVLayout artwork={artwork} config={config} style={style}>
168
127
  <Channel srcImage={config?.channelIcon} config={config} />
169
128
  <Title title={title} config={config} />
170
129
  <Summary summary={summary} config={config} />
171
130
  <Runtime {...extensions} config={config} />
172
- </AudioPlayerLayout>
131
+ </AudioPlayerTVLayout>
173
132
  );
174
133
  }
@@ -0,0 +1,40 @@
1
+ import { ViewStyle } from "react-native";
2
+
3
+ export type Props = {
4
+ audio_item: ZappEntry & {
5
+ extensions?: {
6
+ audio_player_artwork_aspect_ratio?: string;
7
+ audio_player_background_image?: string;
8
+ audio_player_background_color?: string;
9
+ audio_player_channel_icon?: string;
10
+ audio_player_title_color?: string;
11
+ audio_player_summary_color?: string;
12
+ audio_player_rtl?: boolean;
13
+ audio_player_background_image_default_color?: string;
14
+ start_time?: string;
15
+ end_time?: string;
16
+ };
17
+ };
18
+ plugin_configuration: {
19
+ audio_player_background_color?: string;
20
+ audio_player_title_color?: string;
21
+ audio_player_summary_color?: string;
22
+ audio_player_rtl?: string;
23
+ audio_player_background_image_default_color?: string;
24
+ audio_player_background_image?: string;
25
+ audio_player_artwork_aspect_ratio?: string;
26
+ lg_tv_audio_player_title_font_family?: string;
27
+ lg_tv_audio_player_title_font_size?: number;
28
+ lg_tv_audio_player_summary_font_family?: string;
29
+ lg_tv_audio_player_summary_font_size?: number;
30
+ samsung_tv_audio_player_title_font_family?: string;
31
+ samsung_tv_audio_player_title_font_size?: number;
32
+ samsung_tv_audio_player_summary_font_family?: string;
33
+ samsung_tv_audio_player_summary_font_size?: number;
34
+ tv_os_audio_player_title_font_family?: string;
35
+ tv_os_audio_player_title_font_size?: number;
36
+ tv_os_audio_player_summary_font_family?: string;
37
+ tv_os_audio_player_summary_font_size?: number;
38
+ };
39
+ style?: ViewStyle;
40
+ };
@@ -86,7 +86,7 @@ export function masterCellBuilder({
86
86
  entry: item,
87
87
  state: getEntryState(state, entryIsSelected),
88
88
  }),
89
- [state, item?.id, entryIsSelected] // Assuming that item won't mutate
89
+ [state, item, entryIsSelected] // Assuming that item won't mutate
90
90
  );
91
91
 
92
92
  const wrapperRef = React.useRef(null);
@@ -88,7 +88,7 @@ export const VideoModalMode = {
88
88
  MAXIMIZED: "MAXIMIZED",
89
89
  MINIMIZED: "MINIMIZED",
90
90
  FULLSCREEN: "FULLSCREEN",
91
- } as const;
91
+ };
92
92
 
93
93
  export type PlayNextData = {
94
94
  state: PlayNextState;
@@ -127,7 +127,7 @@ const webStyles = {
127
127
  playerScreen: {
128
128
  flex: 1,
129
129
  height: "100vh",
130
- backgroundColor: "black",
130
+ background: "black",
131
131
  },
132
132
  playerWrapper: {
133
133
  height: "100%",
@@ -135,9 +135,6 @@ const webStyles = {
135
135
  },
136
136
  inlineRiver: {
137
137
  height: INLINE_CONTAINER_CONTENT_HEIGHT,
138
-
139
- borderWidth: 4,
140
- borderColor: "yellow",
141
138
  },
142
139
  };
143
140
 
@@ -148,6 +145,7 @@ const nativeStyles = {
148
145
  },
149
146
  playerScreen: {
150
147
  flex: 1,
148
+ backgroundColor: "black",
151
149
  overflow: "hidden",
152
150
  },
153
151
  playerWrapper: {
@@ -567,9 +565,8 @@ const PlayerContainerComponent = (props: Props) => {
567
565
  const isInlineTV = isInlineTVUtil(screenData);
568
566
 
569
567
  const inline =
570
- [VideoModalMode.MAXIMIZED, VideoModalMode.MINIMIZED].includes(
571
- mode as any
572
- ) || isInlineTV;
568
+ [VideoModalMode.MAXIMIZED, VideoModalMode.MINIMIZED].includes(mode) ||
569
+ isInlineTV;
573
570
 
574
571
  const value = React.useMemo(
575
572
  () => ({ playerId: state.playerId }),
@@ -590,11 +587,7 @@ const PlayerContainerComponent = (props: Props) => {
590
587
  );
591
588
  }
592
589
 
593
- if (
594
- screen_background_color &&
595
- mode !== VideoModalMode.FULLSCREEN &&
596
- isTV()
597
- ) {
590
+ if (screen_background_color && mode !== VideoModalMode.FULLSCREEN) {
598
591
  updatedStyles.playerScreen.backgroundColor = screen_background_color;
599
592
  }
600
593
 
@@ -644,17 +637,14 @@ const PlayerContainerComponent = (props: Props) => {
644
637
  preferredFocus
645
638
  shouldUsePreferredFocus
646
639
  groupId={groupId}
647
- pointerEvents={mode === "MINIMIZED" ? "box-none" : "auto"}
648
640
  >
649
641
  {/* Video player and components */}
650
642
  <View
651
643
  style={styles.playerScreen}
652
644
  testID={"player-screen-container"}
653
- pointerEvents={mode === "MINIMIZED" ? "box-none" : "auto"}
654
645
  >
655
646
  {/* Player container */}
656
647
  <View
657
- pointerEvents={mode === "MINIMIZED" ? "box-none" : "auto"}
658
648
  style={[
659
649
  styles.playerWrapper,
660
650
  // eslint-disable-next-line react-native/no-inline-styles, react-native/no-color-literals
@@ -726,7 +716,7 @@ const PlayerContainerComponent = (props: Props) => {
726
716
  key={item.id}
727
717
  groupId={FocusableGroupMainContainerId}
728
718
  cellTapAction={onCellTap}
729
- extraAnchorPointYOffset={-600}
719
+ extraAnchorPointYOffset={0}
730
720
  isScreenWrappedInContainer={true}
731
721
  containerHeight={styles.inlineRiver.height}
732
722
  componentsMapExtraProps={{
@@ -28,7 +28,7 @@ const PlayerImageBackgroundComponent = ({
28
28
  defaultImageDimensions,
29
29
  }: Props) => {
30
30
  const source = React.useMemo(
31
- () => ({ uri: imageSrcFromMediaItem(entry, imageKey) }),
31
+ () => ({ uri: imageSrcFromMediaItem(entry, [imageKey]) }),
32
32
  [imageKey, entry]
33
33
  );
34
34
 
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect } from "react";
2
- import { Animated, Dimensions } from "react-native";
2
+ import { Animated } from "react-native";
3
3
 
4
4
  import {
5
5
  useSafeAreaInsets,
@@ -23,7 +23,6 @@ export enum PlayerAnimationStateEnum {
23
23
  export type PlayerAnimationStateT = number | PlayerAnimationStateEnum | null;
24
24
 
25
25
  export type ModalAnimationContextT = {
26
- yTranslate: React.MutableRefObject<Animated.Value | null>;
27
26
  isActiveGesture: boolean;
28
27
  playerAnimationState: PlayerAnimationStateT;
29
28
  setPlayerAnimationState: (value: PlayerAnimationStateT) => void;
@@ -49,7 +48,6 @@ export type ModalAnimationContextT = {
49
48
  };
50
49
 
51
50
  export const ReactContext = React.createContext<ModalAnimationContextT>({
52
- yTranslate: React.createRef<Animated.Value | null>(),
53
51
  isActiveGesture: false,
54
52
  playerAnimationState: null,
55
53
  setPlayerAnimationState: () => null,
@@ -75,10 +73,6 @@ export const ReactContext = React.createContext<ModalAnimationContextT>({
75
73
  });
76
74
 
77
75
  const Provider = ({ children }: { children: React.ReactNode }) => {
78
- const yTranslate = React.useRef(
79
- new Animated.Value(Dimensions.get("window").height)
80
- );
81
-
82
76
  const [playerAnimationState, setPlayerAnimationState] =
83
77
  React.useState<PlayerAnimationStateT>(null);
84
78
 
@@ -106,7 +100,6 @@ const Provider = ({ children }: { children: React.ReactNode }) => {
106
100
  // Reset player animation state when video modal is closed
107
101
  if (!visible) {
108
102
  resetPlayerAnimationState();
109
- yTranslate.current?.setValue(Dimensions.get("window").height);
110
103
  }
111
104
  }, [visible, resetPlayerAnimationState]);
112
105
 
@@ -148,7 +141,6 @@ const Provider = ({ children }: { children: React.ReactNode }) => {
148
141
  return (
149
142
  <ReactContext.Provider
150
143
  value={{
151
- yTranslate,
152
144
  startComponentsAnimation,
153
145
  setStartComponentsAnimation,
154
146
  isActiveGesture: playerAnimationState !== null,
@@ -11,8 +11,6 @@ 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";
16
14
 
17
15
  const { width: SCREEN_WIDTH } = Dimensions.get("screen");
18
16
 
@@ -28,10 +26,6 @@ type Props = {
28
26
  isTabletLandscape?: boolean;
29
27
  isAudioPlayer?: boolean;
30
28
  isTablet?: boolean;
31
- inline?: any;
32
- docked?: boolean;
33
- isModal?: boolean;
34
- pip?: boolean;
35
29
  };
36
30
 
37
31
  const containerStyle = ({
@@ -48,24 +42,8 @@ export const PlayerDetails = ({
48
42
  configuration,
49
43
  isTabletLandscape = false,
50
44
  isAudioPlayer,
51
- inline,
52
- docked,
53
- isModal,
54
- pip,
45
+ isTablet = false,
55
46
  }: 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();
69
47
  const screenData = useTargetScreenData(entry);
70
48
  const insets = useSafeAreaInsets();
71
49
 
@@ -101,7 +79,7 @@ export const PlayerDetails = ({
101
79
  }
102
80
  }, [isAudioPlayer]);
103
81
 
104
- if (isNilOrEmpty(screenData?.ui_components) || !isShowPlayerDetails) {
82
+ if (isNilOrEmpty(screenData?.ui_components)) {
105
83
  return null;
106
84
  }
107
85
 
@@ -9,8 +9,16 @@ import {
9
9
  import { Edge, SafeAreaView } from "react-native-safe-area-context";
10
10
  import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
11
11
  import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
12
+ import { PlayerDetails } from "./PlayerDetails";
12
13
  import { playerDimensionsHack } from "./utils";
13
- import { getTabletWidth } from "@applicaster/zapp-react-native-utils/playerUtils";
14
+ import { useDelayedPlayerDetails } from "./hooks";
15
+
16
+ import {
17
+ AnimatedScrollModal,
18
+ AnimatedVideoPlayerComponent,
19
+ AnimationComponent,
20
+ ComponentAnimationType,
21
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
14
22
 
15
23
  const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get("screen");
16
24
 
@@ -36,15 +44,29 @@ type Props = {
36
44
  isModal?: boolean;
37
45
  fullscreen?: boolean;
38
46
  isTabletPortrait?: boolean;
47
+ children: (playerDimensions: DimensionsT) => React.ReactNode;
39
48
  configuration: Configuration;
40
-
41
- playerContent: (styles: ViewStyle) => React.ReactNode;
42
49
  };
43
50
 
44
51
  const defaultStyles = StyleSheet.create({
45
52
  playerContainer: { position: "relative", alignSelf: "center", zIndex: 200 },
53
+ playerDetails: { flex: 1, paddingTop: 20 },
54
+ flex: { flex: 1 },
55
+ });
56
+
57
+ const orientationStyles = StyleSheet.create({
58
+ landscape: { flexDirection: "row" },
59
+ portrait: { flexDirection: "column" },
46
60
  });
47
61
 
62
+ const directionStyles = (isTabletLandscape: boolean): ViewStyle => {
63
+ if (isTabletLandscape) {
64
+ return orientationStyles.landscape;
65
+ }
66
+
67
+ return orientationStyles.portrait;
68
+ };
69
+
48
70
  const getScreenAspectRatio = () => {
49
71
  const longEdge = Math.max(SCREEN_WIDTH, SCREEN_HEIGHT);
50
72
  const shortEdge = Math.min(SCREEN_WIDTH, SCREEN_HEIGHT);
@@ -64,6 +86,51 @@ const getEdges = (isTablet: boolean, isInlineModal: boolean) => {
64
86
  return ["top"];
65
87
  };
66
88
 
89
+ const isPercentage = (value: string | number): boolean => {
90
+ if (typeof value === "string") {
91
+ return value.includes("%");
92
+ }
93
+
94
+ return false;
95
+ };
96
+
97
+ const getPercentageOf = (percent: string, value: number) => {
98
+ const percentageValue = parseFloat(percent.replace("%", ""));
99
+
100
+ if (isNaN(percentageValue)) {
101
+ return value;
102
+ }
103
+
104
+ return (value * percentageValue) / 100;
105
+ };
106
+
107
+ const getTabletWidth = (
108
+ configuration: Configuration,
109
+ dimensions: DimensionsT
110
+ ) => {
111
+ const tablet_landscape_sidebar_width =
112
+ configuration?.tablet_landscape_sidebar_width;
113
+
114
+ const { width } = dimensions;
115
+ let widthValue = Number(width);
116
+
117
+ if (isPercentage(width)) {
118
+ widthValue = getPercentageOf(width.toString(), SCREEN_WIDTH);
119
+ }
120
+
121
+ const sidebarWidth = Number(tablet_landscape_sidebar_width?.replace("%", ""));
122
+
123
+ if (tablet_landscape_sidebar_width?.includes("%")) {
124
+ return widthValue * (1 - sidebarWidth / 100);
125
+ }
126
+
127
+ if (Number.isNaN(sidebarWidth)) {
128
+ return widthValue * 0.65;
129
+ }
130
+
131
+ return widthValue - sidebarWidth;
132
+ };
133
+
67
134
  const PlayerWrapperComponent = (props: Props) => {
68
135
  const {
69
136
  entry,
@@ -72,26 +139,35 @@ const PlayerWrapperComponent = (props: Props) => {
72
139
  inline,
73
140
  docked,
74
141
  isModal,
142
+ children,
75
143
  isTabletPortrait,
76
144
  configuration,
145
+ fullscreen,
77
146
  pip,
78
- playerContent,
79
147
  } = props;
80
148
 
81
149
  const isTablet = useIsTablet();
82
150
 
83
151
  const isInlineModal = inline && isModal;
84
152
 
153
+ // Mounting the PlayerDetails component is a resource-intensive process.
154
+ // Therefore, for performance reasons, we mount it with a delay to make the rotation process as smooth as possible.
155
+ // The flow is as follows: the rotation occurs first, and then, after a short delay, we mount the PlayerDetails component.
156
+ // This helps to avoid blocking the rotation and any animations related to the rotation.
157
+ const isShowPlayerDetails = useDelayedPlayerDetails({
158
+ isInline: isInlineModal,
159
+ isDocked: docked,
160
+ isPip: pip,
161
+ });
162
+
85
163
  const isTabletLandscape = !isTV() && isTablet && !isTabletPortrait;
86
164
 
87
- const tabletWidth = getTabletWidth(
88
- configuration.tablet_landscape_sidebar_width,
89
- style
90
- );
165
+ const tabletWidth = getTabletWidth(configuration, style);
91
166
 
92
167
  const baseDimensions: DimensionsT = React.useMemo(
93
168
  () => ({
94
- width: isInlineModal && isTabletLandscape ? tabletWidth : "100%",
169
+ width:
170
+ isInlineModal && !docked && isTabletLandscape ? tabletWidth : "100%",
95
171
  height: undefined,
96
172
  }),
97
173
  [isInlineModal, tabletWidth, docked]
@@ -100,7 +176,7 @@ const PlayerWrapperComponent = (props: Props) => {
100
176
  const playerDimensions: DimensionsT = React.useMemo(
101
177
  () => ({
102
178
  ...baseDimensions,
103
- width: baseDimensions.width,
179
+ width: isInlineModal && docked ? undefined : baseDimensions.width,
104
180
  aspectRatio: !isInlineModal && !pip ? getScreenAspectRatio() : 16 / 9,
105
181
  }),
106
182
  [baseDimensions, isInlineModal, pip]
@@ -109,7 +185,8 @@ const PlayerWrapperComponent = (props: Props) => {
109
185
  const containerDimensions: DimensionsT = React.useMemo(
110
186
  () => ({
111
187
  ...baseDimensions,
112
- aspectRatio: playerDimensions.aspectRatio,
188
+ aspectRatio:
189
+ isInlineModal && docked ? undefined : playerDimensions.aspectRatio,
113
190
  }),
114
191
  [baseDimensions, isInlineModal, docked, playerDimensions.aspectRatio]
115
192
  );
@@ -117,29 +194,68 @@ const PlayerWrapperComponent = (props: Props) => {
117
194
  const WrapperView = React.useMemo(() => (isTV() ? View : SafeAreaView), []);
118
195
 
119
196
  const childrenStyles = React.useMemo(
120
- () => ({
121
- ...playerDimensions,
122
- ...playerDimensionsHack,
123
- }),
197
+ () => ({ ...playerDimensions, ...playerDimensionsHack }),
124
198
  [containerDimensions, playerDimensionsHack]
125
199
  );
126
200
 
201
+ const wrapperViewStyle: ViewStyle = {
202
+ backgroundColor:
203
+ isTablet && !fullscreen
204
+ ? configuration?.tablet_landscape_player_container_background_color
205
+ : "transparent",
206
+ };
207
+
127
208
  return (
128
209
  <WrapperView
129
210
  edges={getEdges(isTablet, isInlineModal) as readonly Edge[]}
130
- style={playerDimensionsHack}
211
+ style={[wrapperViewStyle, style, playerDimensionsHack]}
131
212
  >
132
- <View
133
- testID={`${entry?.id}-player-container`}
134
- style={[
135
- defaultStyles.playerContainer,
136
- playerDimensionsHack,
137
- containerDimensions,
138
- containerStyle,
139
- ]}
213
+ <AnimationComponent
214
+ animationType={ComponentAnimationType.moveUpComponent}
215
+ additionalData={{ saveArea: true }}
216
+ style={[directionStyles(isTabletLandscape), defaultStyles.flex]}
140
217
  >
141
- {playerContent(childrenStyles)}
142
- </View>
218
+ <View
219
+ testID={`${entry?.id}-player-container`}
220
+ style={[
221
+ defaultStyles.playerContainer,
222
+ containerDimensions,
223
+ containerStyle,
224
+ playerDimensionsHack,
225
+ ]}
226
+ >
227
+ <AnimationComponent
228
+ animationType={ComponentAnimationType.moveUpComponent}
229
+ style={isTabletLandscape ? defaultStyles.flex : undefined}
230
+ additionalData={{
231
+ useLayoutMeasure: isTabletLandscape,
232
+ disableAnimatedComponent: !isTabletLandscape,
233
+ resetAnimationValue: isTabletLandscape && docked,
234
+ }}
235
+ >
236
+ <AnimatedVideoPlayerComponent>
237
+ {children(childrenStyles)}
238
+ </AnimatedVideoPlayerComponent>
239
+ </AnimationComponent>
240
+ </View>
241
+
242
+ <AnimatedScrollModal>
243
+ {isShowPlayerDetails ? (
244
+ <AnimationComponent
245
+ animationType={ComponentAnimationType.componentFade}
246
+ style={defaultStyles.flex}
247
+ >
248
+ <PlayerDetails
249
+ configuration={configuration}
250
+ style={defaultStyles.playerDetails}
251
+ entry={entry}
252
+ isTabletLandscape={isTabletLandscape}
253
+ isTablet={isTablet}
254
+ />
255
+ </AnimationComponent>
256
+ ) : null}
257
+ </AnimatedScrollModal>
258
+ </AnimationComponent>
143
259
  </WrapperView>
144
260
  );
145
261
  };