@applicaster/quick-brick-player 15.0.0-alpha.3514407021 → 15.0.0-alpha.4069571733

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/quick-brick-player",
3
- "version": "15.0.0-alpha.3514407021",
3
+ "version": "15.0.0-alpha.4069571733",
4
4
  "description": "Quick Brick Player",
5
5
  "main": "./src/index.ts",
6
6
  "types": "index.d.ts",
@@ -35,11 +35,11 @@
35
35
  },
36
36
  "homepage": "https://github.com/applicaster/quickbrick#readme",
37
37
  "dependencies": {
38
- "@applicaster/quick-brick-mobile-transport-controls": "15.0.0-rc.8",
38
+ "@applicaster/quick-brick-mobile-transport-controls": "15.0.0-alpha.4069571733",
39
39
  "@applicaster/quick-brick-tv-transport-controls": "15.0.0-rc.15",
40
- "@applicaster/zapp-react-native-tvos-app": "15.0.0-alpha.3514407021",
41
- "@applicaster/zapp-react-native-ui-components": "15.0.0-alpha.3514407021",
42
- "@applicaster/zapp-react-native-utils": "15.0.0-alpha.3514407021",
40
+ "@applicaster/zapp-react-native-tvos-app": "15.0.0-alpha.4069571733",
41
+ "@applicaster/zapp-react-native-ui-components": "15.0.0-alpha.4069571733",
42
+ "@applicaster/zapp-react-native-utils": "15.0.0-alpha.4069571733",
43
43
  "query-string": "7.1.3",
44
44
  "shaka-player": "4.3.5",
45
45
  "typeface-montserrat": "^0.0.54",
@@ -61,6 +61,7 @@ type Props = {
61
61
  inline: boolean;
62
62
  docked: boolean;
63
63
  isModal: boolean;
64
+ pip: boolean;
64
65
  };
65
66
  playNextData: PlayNextData;
66
67
  isTabletPortrait?: boolean;
@@ -99,11 +100,7 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
99
100
  );
100
101
 
101
102
  useEffect(() => {
102
- if (player) {
103
- setSpeedManager(new SpeedController(player));
104
- } else {
105
- setSpeedManager(null);
106
- }
103
+ setSpeedManager(player ? new SpeedController(player) : null);
107
104
  }, [player]);
108
105
 
109
106
  useEffect(() => {
@@ -217,7 +214,7 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
217
214
  items: speedOptions,
218
215
  current_selection: playbackSpeed,
219
216
  onPress: onSpeedSelect,
220
- title: configuration["playback_speed_title"],
217
+ title: configuration.playback_speed_title,
221
218
  itemProps: getBottomSheetModalItemProps,
222
219
  iconProps: getBottomSheetModalIconProps,
223
220
  labelProps: getBottomSheetModalLabelProps,
@@ -233,7 +230,7 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
233
230
  ],
234
231
  },
235
232
  });
236
- }, [playbackSpeed, onSpeedSelect, configuration["playback_speed_title"]]);
233
+ }, [playbackSpeed, onSpeedSelect, configuration.playback_speed_title]);
237
234
 
238
235
  const onPlayerSeek = React.useCallback(
239
236
  (targetTime: number) => {
@@ -250,13 +247,28 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
250
247
  player?.seekTo(0);
251
248
  }, [player]);
252
249
 
250
+ const layoutStateMemo = React.useMemo(
251
+ () => ({
252
+ inline: layoutState.inline,
253
+ docked: layoutState.docked,
254
+ isModal: layoutState.isModal,
255
+ pip: layoutState.pip,
256
+ }),
257
+ [
258
+ layoutState.inline,
259
+ layoutState.docked,
260
+ layoutState.isModal,
261
+ layoutState.pip,
262
+ ]
263
+ );
264
+
253
265
  return (
254
266
  <SafeAreaView style={[style, Styles.playerContainer]} edges={edges}>
255
267
  <View style={Styles.playerContainer} onLayout={onLayout}>
256
268
  <Layout
257
269
  entry={entry}
258
270
  playerState={playerState}
259
- layoutState={layoutState}
271
+ layoutState={layoutStateMemo}
260
272
  value={value}
261
273
  onPlaybackButtonPress={onPlaybackButtonPress}
262
274
  onRewindButtonPress={onRewindButtonPress}
@@ -1,6 +1,5 @@
1
1
  import * as React from "react";
2
- import { StyleSheet, View, ViewStyle } from "react-native";
3
- import * as R from "ramda";
2
+ import { StyleSheet, View } from "react-native";
4
3
  import {
5
4
  useIsTablet,
6
5
  useZStore,
@@ -16,7 +15,6 @@ import {
16
15
 
17
16
  import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
18
17
 
19
- import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
20
18
  import { useSafeAreaInsets } from "react-native-safe-area-context";
21
19
  import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useNavigation";
22
20
  import { getTabBarHeight } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/getTabBarHeight";
@@ -27,22 +25,17 @@ import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
27
25
  import { PROGRESS_BAR_HEIGHT } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation/utils";
28
26
  import { isMenuVisible } from "@applicaster/zapp-react-native-ui-components/Components/Screen/navigationHandler";
29
27
  import { usePlugins } from "@applicaster/zapp-react-native-redux";
28
+ import { partial } from "@applicaster/zapp-react-native-utils/utils";
30
29
 
31
30
  const bottomTabBarHeight = getTabBarHeight();
32
31
 
33
32
  type Props = {
34
33
  entry: ZappEntry;
35
34
  layoutState: QuickBrickPlayer.LayoutState;
36
- playerState: QuickBrickPlayer.PlayerState;
37
- value: GetValue;
38
- onCloseVideoModal: () => void;
39
- onPlayerSeek: (targetTime: number) => void;
35
+ onPlayerSeek?: (targetTime: number) => void;
40
36
  playNextData: PlayNextData;
41
- title: string | number;
42
- summary: string | number;
43
37
  ImageComponent?: React.ReactNode;
44
38
  isActiveGesture?: boolean;
45
- minimisedHeight: number;
46
39
  aspectRatio?: number;
47
40
  };
48
41
 
@@ -51,7 +44,6 @@ const styles = StyleSheet.create({
51
44
  flex: 1,
52
45
  flexDirection: "column",
53
46
  alignItems: "stretch",
54
- marginTop: PROGRESS_BAR_HEIGHT,
55
47
  },
56
48
  content: {
57
49
  flex: 1,
@@ -94,14 +86,11 @@ const styles = StyleSheet.create({
94
86
  buttons: {
95
87
  marginVertical: 10,
96
88
  },
89
+ liveMarginTop: { marginTop: 0 },
90
+ marginTop: { marginTop: PROGRESS_BAR_HEIGHT },
97
91
  });
98
92
 
99
- const playerDimensionsStyle = (
100
- minimisedHeight: number,
101
- addPadding: boolean
102
- ): ViewStyle => ({
103
- width: addPadding ? toNumberWithDefault(100, minimisedHeight) : 0,
104
- });
93
+ const getValue = (key: string, stylesObj: {}) => stylesObj?.[key] ?? null;
105
94
 
106
95
  const controlButtons = (
107
96
  live: boolean,
@@ -167,7 +156,6 @@ export const DockedControls = (props: Props) => {
167
156
  onPlayerSeek = noop,
168
157
  playNextData,
169
158
  ImageComponent,
170
- isActiveGesture = true,
171
159
  aspectRatio,
172
160
  } = props;
173
161
 
@@ -185,11 +173,6 @@ export const DockedControls = (props: Props) => {
185
173
  const configuration = useZStore("playerConfiguration")();
186
174
  const playerState = usePlayerState("shared-player-wrapper-docked");
187
175
 
188
- const getValue = React.useCallback(
189
- (key: string, stylesObj: {}) => R.propOr(null, key, stylesObj),
190
- []
191
- );
192
-
193
176
  const value = React.useCallback(
194
177
  (key: string) => {
195
178
  const retVal = getValue(key, configuration);
@@ -208,7 +191,7 @@ export const DockedControls = (props: Props) => {
208
191
 
209
192
  return retVal;
210
193
  },
211
- [...Object.values(configuration), playerState.seekableDuration, getValue]
194
+ [...Object.values(configuration), playerState.seekableDuration]
212
195
  );
213
196
 
214
197
  const { title, summary } = useTitleSummaryOverlay(
@@ -225,15 +208,65 @@ export const DockedControls = (props: Props) => {
225
208
 
226
209
  const insets = useSafeAreaInsets();
227
210
 
211
+ const progresBarLayoutState = React.useMemo(
212
+ () => ({
213
+ inline: layoutState.inline,
214
+ docked: true,
215
+ isModal: layoutState.isModal,
216
+ pip: layoutState.pip,
217
+ }),
218
+ [layoutState.inline, layoutState.isModal, layoutState.pip]
219
+ );
220
+
221
+ const _controlButtons = React.useMemo(
222
+ () => partial(controlButtons, playerState.isLive && liveButton),
223
+ [playerState.isLive, liveButton]
224
+ );
225
+
226
+ const containerStyles = React.useMemo(
227
+ () => ({
228
+ height: minimisedHeight,
229
+ maxHeight: minimisedHeight,
230
+ }),
231
+ [minimisedHeight]
232
+ );
233
+
234
+ const imageComponentWrapperStyles = React.useMemo(
235
+ () => ({
236
+ aspectRatio: aspectRatio,
237
+ height: minimisedHeight,
238
+ }),
239
+ [aspectRatio, minimisedHeight]
240
+ );
241
+
242
+ const backgroundColorStyle = React.useMemo(
243
+ () => ({
244
+ backgroundColor: dockedBackgroundColor,
245
+ }),
246
+ [dockedBackgroundColor]
247
+ );
248
+
249
+ const bottomSpacerStyle = React.useMemo(() => {
250
+ const tabBarHeight = menuVisible ? bottomTabBarHeight : 0;
251
+ const safeAreaBottomInset = insets.bottom || 0;
252
+
253
+ return {
254
+ height: safeAreaBottomInset + tabBarHeight,
255
+ };
256
+ }, [menuVisible, insets.bottom]);
257
+
258
+ const contentInfoContent = React.useMemo(
259
+ () => ({ ...entry, title, summary }),
260
+ [title, summary, entry]
261
+ );
262
+
228
263
  return (
229
264
  <>
230
265
  <View
231
266
  style={[
232
267
  styles.container,
233
- {
234
- height: minimisedHeight,
235
- maxHeight: minimisedHeight,
236
- },
268
+ containerStyles,
269
+ playerState.isLive ? styles.liveMarginTop : styles.marginTop,
237
270
  ]}
238
271
  >
239
272
  {!playerState.isLive ? (
@@ -242,42 +275,23 @@ export const DockedControls = (props: Props) => {
242
275
  content={entry}
243
276
  value={value}
244
277
  visible
245
- layoutState={layoutState}
278
+ layoutState={progresBarLayoutState}
246
279
  onPlayerSeek={onPlayerSeek}
247
280
  playerState={playerState}
248
- startComponentsAnimation={noop}
249
281
  docked
250
282
  />
251
283
  ) : null}
252
284
 
253
285
  <View style={styles.content}>
254
286
  <View style={styles.touchableArea}>
255
- {isActiveGesture || layoutState.docked ? (
256
- <View
257
- style={{ aspectRatio: aspectRatio, height: minimisedHeight }}
258
- >
259
- {ImageComponent || null}
260
- </View>
261
- ) : (
262
- <View
263
- style={playerDimensionsStyle(
264
- minimisedHeight,
265
- isActiveGesture || layoutState.docked
266
- )}
267
- />
268
- )}
287
+ <View style={imageComponentWrapperStyles}>
288
+ {ImageComponent || null}
289
+ </View>
269
290
  <View
270
291
  testID="content-info"
271
- style={[
272
- styles.contentInfo,
273
- { backgroundColor: dockedBackgroundColor },
274
- ]}
292
+ style={[styles.contentInfo, backgroundColorStyle]}
275
293
  >
276
- <ContentInfo
277
- content={{ ...entry, title, summary }}
278
- value={value}
279
- docked
280
- />
294
+ <ContentInfo content={contentInfoContent} value={value} docked />
281
295
  </View>
282
296
  </View>
283
297
  <View
@@ -285,15 +299,13 @@ export const DockedControls = (props: Props) => {
285
299
  isTablet
286
300
  ? styles.controlsContainerForTablet
287
301
  : styles.controlsContainerForMobile,
288
- { backgroundColor: dockedBackgroundColor },
302
+ backgroundColorStyle,
289
303
  ]}
290
304
  >
291
305
  <Controls
292
306
  value={value}
293
307
  visible
294
- buttons={R.partial(controlButtons, [
295
- playerState.isLive && liveButton,
296
- ])}
308
+ buttons={_controlButtons}
297
309
  style={styles.playPauseButton}
298
310
  playNextData={playNextData}
299
311
  playerState={playerState}
@@ -308,12 +320,7 @@ export const DockedControls = (props: Props) => {
308
320
  </View>
309
321
  </View>
310
322
  </View>
311
- <View
312
- style={{
313
- height: insets.bottom + (menuVisible ? bottomTabBarHeight : 0),
314
- backgroundColor: dockedBackgroundColor,
315
- }}
316
- />
323
+ <View style={[bottomSpacerStyle, backgroundColorStyle]} />
317
324
  </>
318
325
  );
319
326
  };
@@ -151,12 +151,7 @@ export function MobileLayout({
151
151
  },
152
152
  ]}
153
153
  >
154
- <PlayerImage
155
- entry={entry}
156
- docked={false}
157
- imageWidth={imageSize}
158
- configuration={configuration}
159
- />
154
+ <PlayerImage entry={entry} configuration={configuration} />
160
155
  </View>
161
156
  {/* PlayerImage */}
162
157
 
@@ -4,7 +4,6 @@ import { LayoutChangeEvent, View } from "react-native";
4
4
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
5
5
  import { toBooleanWithDefaultTrue } from "@applicaster/zapp-react-native-utils/booleanUtils";
6
6
 
7
- import { AnimatedImage } from "./AnimatedImage";
8
7
  import { FlexImage } from "./FlexImage";
9
8
 
10
9
  import { styles } from "./styles";
@@ -13,19 +12,12 @@ import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/nu
13
12
  type Props = {
14
13
  entry: ZappEntry;
15
14
  configuration: QuickBrickPlayer.AudioPlayerProps["configuration"];
16
- docked: boolean;
17
- imageWidth?: Option<number>;
15
+ docked?: boolean;
18
16
  onLayoutImage?: (event: LayoutChangeEvent) => void;
19
17
  };
20
18
 
21
19
  export const PlayerImage = (props: Props) => {
22
- const {
23
- entry,
24
- docked,
25
- imageWidth,
26
- configuration,
27
- onLayoutImage = noop,
28
- } = props;
20
+ const { entry, docked = false, configuration, onLayoutImage = noop } = props;
29
21
 
30
22
  const isShadowEnabled = toBooleanWithDefaultTrue(
31
23
  configuration?.audio_player_artwork_shadow_enabled
@@ -43,19 +35,9 @@ export const PlayerImage = (props: Props) => {
43
35
  onLayout={onLayoutImage}
44
36
  style={[shouldShowShadow ? styles.shadow : undefined, { borderRadius }]}
45
37
  >
46
- {!imageWidth ? (
47
- <View style={[styles.defaultImageWrapperView, { borderRadius }]}>
48
- <FlexImage entry={entry} style={styles.backgroundImageContainer} />
49
- </View>
50
- ) : (
51
- // we have imageSize here and now we could animate resizing of this image
52
- <AnimatedImage
53
- entry={entry}
54
- style={[styles.backgroundImageContainer, { borderRadius }]}
55
- imageWidth={imageWidth}
56
- docked={docked}
57
- />
58
- )}
38
+ <View style={[styles.defaultImageWrapperView, { borderRadius }]}>
39
+ <FlexImage entry={entry} style={styles.backgroundImageContainer} />
40
+ </View>
59
41
  </View>
60
42
  </View>
61
43
  );
@@ -117,14 +117,11 @@ export function TabletLandscapeLayout({
117
117
  <View style={[styles.flex, styles.audioPlayerContainerHeightLimit]}>
118
118
  {/* PlayerImage non-docked */}
119
119
  <View style={[styles.flex, withDebug(styles.debugYellow)]}>
120
- {!docked ? (
121
- <PlayerImage
122
- docked={false}
123
- entry={entry}
124
- configuration={configuration}
125
- onLayoutImage={onLayoutImage}
126
- />
127
- ) : null}
120
+ <PlayerImage
121
+ entry={entry}
122
+ configuration={configuration}
123
+ onLayoutImage={onLayoutImage}
124
+ />
128
125
  </View>
129
126
  {/* PlayerImage */}
130
127
 
@@ -144,12 +144,7 @@ export function TabletPortraitLayout({
144
144
  ]}
145
145
  onLayout={onLayout}
146
146
  >
147
- <PlayerImage
148
- entry={entry}
149
- configuration={configuration}
150
- docked={docked}
151
- imageWidth={imageSize}
152
- />
147
+ <PlayerImage entry={entry} configuration={configuration} />
153
148
  </View>
154
149
 
155
150
  <View
@@ -1,10 +1,6 @@
1
1
  import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
2
- import {
3
- PlayerAnimationStateEnum,
4
- PlayerAnimationStateT,
5
- } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation/ModalAnimationContext";
2
+
6
3
  import { parseJsonIfNeeded } from "@applicaster/zapp-react-native-utils/functionUtils";
7
- import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
8
4
 
9
5
  export const NAMESPACE = "audio-player";
10
6
 
@@ -36,25 +32,3 @@ export const formatSpeed = (value: number) => {
36
32
  // Return the formatted string with "speed_" prefix
37
33
  return `speed_${formattedValue}`;
38
34
  };
39
-
40
- export const calculateBorderRadius = (
41
- docked: boolean,
42
- startComponentsAnimation: boolean,
43
- isActiveGesture: boolean,
44
- playerAnimationState: PlayerAnimationStateT,
45
- configuration: Record<string, any>
46
- ): number => {
47
- const shouldRemoveBorderRadius =
48
- (startComponentsAnimation &&
49
- isActiveGesture &&
50
- playerAnimationState !== PlayerAnimationStateEnum.maximize) ||
51
- docked;
52
-
53
- if (shouldRemoveBorderRadius) {
54
- return 0;
55
- }
56
-
57
- return toNumberWithDefaultZero(
58
- configuration.audio_player_artwork_border_radius
59
- );
60
- };
@@ -4,8 +4,9 @@ import { Animated, Pressable } from "react-native";
4
4
  import { PanGestureHandler } from "react-native-gesture-handler";
5
5
  import { useStyles } from "./styles";
6
6
  import { useVideoModalState } from "./hooks";
7
- import { MODAL_COLLAPSE_START, MODAL_RADIUS, SCREEN_HEIGHT } from "./consts";
7
+ import { MODAL_COLLAPSE_RATIO, MODAL_RADIUS } from "./consts";
8
8
  import { useModalAnimationContext } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
9
+ import { getWindowHeight } from "./utils";
9
10
 
10
11
  type AnimatedModalProps = {
11
12
  content?: ReactElement;
@@ -28,21 +29,26 @@ export function AnimatedModal({
28
29
 
29
30
  const styles = useStyles({ height: collapsedHeight, type: "audio" });
30
31
 
32
+ const WINDOW_HEIGHT = getWindowHeight();
33
+ const heightAboveMinimised = WINDOW_HEIGHT - collapsedHeight;
34
+
35
+ const MODAL_COLLAPSE_START = WINDOW_HEIGHT * MODAL_COLLAPSE_RATIO;
36
+
31
37
  // Interpolated opacities for smooth cross-fade
32
38
  const collapsedOpacity = translateY.interpolate({
33
- inputRange: [MODAL_COLLAPSE_START, SCREEN_HEIGHT - collapsedHeight],
39
+ inputRange: [MODAL_COLLAPSE_START, heightAboveMinimised],
34
40
  outputRange: [0, 1],
35
41
  extrapolate: "clamp",
36
42
  });
37
43
 
38
44
  const expandedOpacity = translateY.interpolate({
39
- inputRange: [0, SCREEN_HEIGHT - collapsedHeight],
45
+ inputRange: [0, heightAboveMinimised],
40
46
  outputRange: [1, 0],
41
47
  extrapolate: "clamp",
42
48
  });
43
49
 
44
50
  const borderTopRadiusAnimated = translateY.interpolate({
45
- inputRange: [0, SCREEN_HEIGHT - collapsedHeight],
51
+ inputRange: [0, heightAboveMinimised],
46
52
  outputRange: [MODAL_RADIUS, 0],
47
53
  extrapolate: "clamp",
48
54
  });
@@ -57,7 +63,7 @@ export function AnimatedModal({
57
63
  borderTopLeftRadius: borderTopRadiusAnimated,
58
64
  borderTopRightRadius: borderTopRadiusAnimated,
59
65
  transform: [{ translateY }],
60
- height: SCREEN_HEIGHT,
66
+ height: WINDOW_HEIGHT,
61
67
  },
62
68
  ]}
63
69
  >
@@ -67,7 +73,7 @@ export function AnimatedModal({
67
73
  {
68
74
  borderTopLeftRadius: borderTopRadiusAnimated,
69
75
  borderTopRightRadius: borderTopRadiusAnimated,
70
- height: SCREEN_HEIGHT,
76
+ height: WINDOW_HEIGHT,
71
77
  },
72
78
  ]}
73
79
  >
@@ -1,4 +1,4 @@
1
- import React, { ReactElement, useRef } from "react";
1
+ import React, { ReactElement, useMemo, useRef } from "react";
2
2
 
3
3
  import { Animated, Pressable, StyleSheet, ViewStyle } from "react-native";
4
4
  import { PanGestureHandler } from "react-native-gesture-handler";
@@ -7,10 +7,8 @@ import { useConfiguration } from "@applicaster/zapp-react-native-ui-components/C
7
7
  import { useVideoModalState } from "./hooks";
8
8
  import {
9
9
  DEFAULT_IMAGE_RATIO_VIDEO,
10
- MODAL_COLLAPSE_START,
10
+ MODAL_COLLAPSE_RATIO,
11
11
  MODAL_RADIUS,
12
- SCREEN_HEIGHT,
13
- SCREEN_WIDTH,
14
12
  VIDEO_TRANSITION_THRESHOLD,
15
13
  } from "./consts";
16
14
  import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
@@ -20,6 +18,7 @@ import { PROGRESS_BAR_HEIGHT } from "@applicaster/zapp-react-native-ui-component
20
18
  import { useSafeAreaInsets } from "react-native-safe-area-context";
21
19
  import { GestureAnimatedScrollView } from "./GestureAnimatedScrollView";
22
20
  import { PlayerDetailsWrapperHeightContext } from "./context";
21
+ import { getWindowHeight, getWindowWidth } from "./utils";
23
22
 
24
23
  const orientationStyles = StyleSheet.create({
25
24
  landscape: { flexDirection: "row" },
@@ -46,8 +45,6 @@ type AnimatedModalProps = {
46
45
  };
47
46
  };
48
47
 
49
- const height = SCREEN_HEIGHT;
50
-
51
48
  export function VideoPlayerModal({
52
49
  aspectRatio = DEFAULT_IMAGE_RATIO_VIDEO,
53
50
  content,
@@ -62,7 +59,11 @@ export function VideoPlayerModal({
62
59
  const enabled = !!modal && !fullscreen;
63
60
  const isTablet = useIsTablet();
64
61
 
65
- let width = SCREEN_WIDTH;
62
+ const WINDOW_WIDTH = useMemo(() => getWindowWidth(), []); // remember initial width, ignore rotation changes
63
+ const height = useMemo(() => getWindowHeight(), []); // remember initial height, ignore rotation changes
64
+ const MODAL_COLLAPSE_START = height * MODAL_COLLAPSE_RATIO;
65
+
66
+ let width = WINDOW_WIDTH;
66
67
 
67
68
  if (isTablet && !isTabletPortrait) {
68
69
  width = style?.tabletLandscapeWidth || width;
@@ -83,6 +84,8 @@ export function VideoPlayerModal({
83
84
  expand,
84
85
  } = useVideoModalState(modal ? translateYRef : dummyRef);
85
86
 
87
+ const heightAboveMinimised = height - collapsedHeight;
88
+
86
89
  const isTabletLandscape = !isTV() && isTablet && !isTabletPortrait;
87
90
 
88
91
  const styles = useStyles({ height: collapsedHeight });
@@ -96,7 +99,7 @@ export function VideoPlayerModal({
96
99
  // Interpolated opacities for smooth cross-fade
97
100
  const collapsedOpacity = !isFullScreenOrPIP
98
101
  ? translateY.interpolate({
99
- inputRange: [MODAL_COLLAPSE_START, height - collapsedHeight],
102
+ inputRange: [MODAL_COLLAPSE_START, heightAboveMinimised],
100
103
  outputRange: [0, 1],
101
104
  extrapolate: "clamp",
102
105
  })
@@ -104,7 +107,7 @@ export function VideoPlayerModal({
104
107
 
105
108
  const expandedOpacity = !isFullScreenOrPIP
106
109
  ? translateY.interpolate({
107
- inputRange: [0, height - collapsedHeight],
110
+ inputRange: [0, heightAboveMinimised],
108
111
  outputRange: [1, 0],
109
112
  extrapolate: "clamp",
110
113
  })
@@ -112,10 +115,7 @@ export function VideoPlayerModal({
112
115
 
113
116
  const xPosition = !isFullScreenOrPIP
114
117
  ? translateY.interpolate({
115
- inputRange: [
116
- height * VIDEO_TRANSITION_THRESHOLD,
117
- height - collapsedHeight,
118
- ],
118
+ inputRange: [height * VIDEO_TRANSITION_THRESHOLD, MODAL_COLLAPSE_START],
119
119
  outputRange: [0, -(width - width * SCALE_FACTOR) / 2],
120
120
  extrapolate: "clamp",
121
121
  })
@@ -125,10 +125,7 @@ export function VideoPlayerModal({
125
125
 
126
126
  const yPosition = !isFullScreenOrPIP
127
127
  ? translateY.interpolate({
128
- inputRange: [
129
- height * VIDEO_TRANSITION_THRESHOLD,
130
- height - collapsedHeight,
131
- ],
128
+ inputRange: [height * VIDEO_TRANSITION_THRESHOLD, MODAL_COLLAPSE_START],
132
129
  outputRange: [
133
130
  0,
134
131
  isTablet && isTabletLandscape
@@ -136,8 +133,8 @@ export function VideoPlayerModal({
136
133
  : -(
137
134
  PROGRESS_BAR_HEIGHT +
138
135
  insets.top / 2 +
139
- (SCREEN_WIDTH / aspectRatio -
140
- (SCREEN_WIDTH / aspectRatio) * SCALE_FACTOR) /
136
+ (WINDOW_WIDTH / aspectRatio -
137
+ (WINDOW_WIDTH / aspectRatio) * SCALE_FACTOR) /
141
138
  2
142
139
  ),
143
140
  ],
@@ -147,10 +144,7 @@ export function VideoPlayerModal({
147
144
 
148
145
  const scalePosition = !isFullScreenOrPIP
149
146
  ? translateY.interpolate({
150
- inputRange: [
151
- height * VIDEO_TRANSITION_THRESHOLD,
152
- height - collapsedHeight,
153
- ],
147
+ inputRange: [height * VIDEO_TRANSITION_THRESHOLD, MODAL_COLLAPSE_START],
154
148
  outputRange: [1, SCALE_FACTOR],
155
149
  extrapolate: "clamp",
156
150
  })
@@ -210,7 +204,10 @@ export function VideoPlayerModal({
210
204
  >
211
205
  <Animated.View
212
206
  pointerEvents={!collapsed ? "auto" : "none"}
213
- style={[styles.modalContent, directionStyles(isTabletLandscape)]}
207
+ style={[
208
+ pip ? styles.modalContentPiP : styles.modalContent,
209
+ directionStyles(isTabletLandscape),
210
+ ]}
214
211
  >
215
212
  <Animated.View
216
213
  style={[