@applicaster/quick-brick-player 15.0.0-rc.5 → 15.0.0-rc.50

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 (32) hide show
  1. package/package.json +6 -6
  2. package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +20 -8
  3. package/src/Player/AudioLayer/Layout/DockedControls/index.tsx +78 -65
  4. package/src/Player/AudioLayer/Layout/MobileLayout.tsx +1 -6
  5. package/src/Player/AudioLayer/Layout/PlayerImage/index.tsx +27 -25
  6. package/src/Player/AudioLayer/Layout/PlayerImage/styles.ts +6 -1
  7. package/src/Player/AudioLayer/Layout/TabletLandscapeLayout.tsx +5 -8
  8. package/src/Player/AudioLayer/Layout/TabletPortraitLayout.tsx +1 -6
  9. package/src/Player/AudioLayer/utils.ts +1 -27
  10. package/src/Player/PlayNextOverlay/index.tsx +2 -2
  11. package/src/Player/PlayerModal/PlayerModal.tsx +16 -15
  12. package/src/Player/PlayerModal/VideoPlayerModal.tsx +138 -120
  13. package/src/Player/PlayerModal/consts/index.ts +2 -19
  14. package/src/Player/PlayerModal/hooks/index.ts +115 -139
  15. package/src/Player/PlayerModal/styles.ts +5 -0
  16. package/src/Player/PlayerModal/utils/index.ts +111 -0
  17. package/src/Player/PlayerView/types.ts +1 -2
  18. package/src/Player/Utils/index.tsx +9 -9
  19. package/src/Player/hooks/progressStates/__tests__/utils.test.ts +23 -0
  20. package/src/Player/hooks/progressStates/useLiveProgressState.tsx +78 -0
  21. package/src/Player/hooks/progressStates/useProgressState.tsx +30 -0
  22. package/src/Player/hooks/progressStates/useVodProgressState.tsx +115 -0
  23. package/src/Player/hooks/progressStates/utils.ts +33 -0
  24. package/src/Player/index.tsx +559 -357
  25. package/src/utils/dimensions.ts +29 -0
  26. package/src/utils/index.ts +12 -0
  27. package/src/utils/logger.ts +6 -0
  28. package/src/utils/playerHelpers.ts +11 -0
  29. package/src/utils/playerStyles.ts +50 -0
  30. package/src/Player/AudioLayer/Layout/PlayerImage/AnimatedImage.tsx +0 -82
  31. package/src/Player/AudioLayer/Layout/PlayerImage/hooks/index.ts +0 -1
  32. package/src/Player/AudioLayer/Layout/PlayerImage/hooks/useChangePlayerState.ts +0 -99
@@ -1,34 +1,36 @@
1
- import React, { ReactElement, useRef } from "react";
1
+ import React, { ReactElement, useMemo, useRef } from "react";
2
2
 
3
- import { Animated, Pressable, StyleSheet, ViewStyle } from "react-native";
3
+ import { Animated, Pressable } from "react-native";
4
4
  import { PanGestureHandler } from "react-native-gesture-handler";
5
5
  import { useStyles } from "./styles";
6
6
  import { useConfiguration } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/utils";
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";
17
- import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
18
- import { useModalAnimationContext } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation/useModalAnimationContext";
19
15
  import { PROGRESS_BAR_HEIGHT } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation/utils";
20
- import { useSafeAreaInsets } from "react-native-safe-area-context";
16
+ import {
17
+ SafeAreaView,
18
+ useSafeAreaFrame,
19
+ useSafeAreaInsets,
20
+ } from "react-native-safe-area-context";
21
21
  import { GestureAnimatedScrollView } from "./GestureAnimatedScrollView";
22
22
  import { PlayerDetailsWrapperHeightContext } from "./context";
23
+ import {
24
+ getInsetsOffset,
25
+ getWindowHeight,
26
+ getWindowWidth,
27
+ directionStyles,
28
+ getScaledPos,
29
+ getExtraContentPadding,
30
+ } from "./utils";
31
+ import { addOrientationChangeListener } from "@applicaster/zapp-react-native-utils/appUtils/orientationHelper";
23
32
 
24
- const orientationStyles = StyleSheet.create({
25
- landscape: { flexDirection: "row" },
26
- portrait: { flexDirection: "column" },
27
- });
28
-
29
- const directionStyles = (isLandscape: boolean): ViewStyle => {
30
- return isLandscape ? orientationStyles.landscape : orientationStyles.portrait;
31
- };
33
+ const AnimatedSafeAreaView = Animated.createAnimatedComponent(SafeAreaView);
32
34
 
33
35
  type AnimatedModalProps = {
34
36
  pip?: boolean;
@@ -46,8 +48,6 @@ type AnimatedModalProps = {
46
48
  };
47
49
  };
48
50
 
49
- const height = SCREEN_HEIGHT;
50
-
51
51
  export function VideoPlayerModal({
52
52
  aspectRatio = DEFAULT_IMAGE_RATIO_VIDEO,
53
53
  content,
@@ -62,28 +62,33 @@ export function VideoPlayerModal({
62
62
  const enabled = !!modal && !fullscreen;
63
63
  const isTablet = useIsTablet();
64
64
 
65
- let width = SCREEN_WIDTH;
65
+ // remember initial width, ignore rotation changes
66
+ const width = useMemo(
67
+ () =>
68
+ isTablet && !isTabletPortrait
69
+ ? style.tabletLandscapeWidth
70
+ : getWindowWidth(isTablet, !isTabletPortrait),
71
+ // eslint-disable-next-line react-hooks/exhaustive-deps
72
+ []
73
+ );
74
+
75
+ const height = useMemo(
76
+ () => getWindowHeight(isTablet, !isTabletPortrait),
77
+ []
78
+ ); // remember initial height, ignore rotation changes
66
79
 
67
- if (isTablet && !isTabletPortrait) {
68
- width = style?.tabletLandscapeWidth || width;
69
- }
80
+ const MODAL_COLLAPSE_START = height * MODAL_COLLAPSE_RATIO;
70
81
 
71
82
  const { minimised_height: minimisedHeight } = useConfiguration();
72
83
  const scrollViewRef = useRef<typeof Animated.ScrollView | null>(null);
73
84
 
74
- const translateYRef = useModalAnimationContext().yTranslate;
75
- // used when modal is false, should not be used in animation
76
- const dummyRef = useRef(new Animated.Value(0));
85
+ const { translateY, collapsed, gestureHandlerProps, expand, offset } =
86
+ useVideoModalState();
77
87
 
78
- const {
79
- translateY,
80
- collapsed,
81
- collapsedHeight,
82
- gestureHandlerProps,
83
- expand,
84
- } = useVideoModalState(modal ? translateYRef : dummyRef);
88
+ const frame = useSafeAreaFrame();
89
+ const collapsedHeight = frame.height - offset.current;
85
90
 
86
- const isTabletLandscape = !isTV() && isTablet && !isTabletPortrait;
91
+ const heightAboveMinimised = height - collapsedHeight;
87
92
 
88
93
  const styles = useStyles({ height: collapsedHeight });
89
94
 
@@ -91,66 +96,73 @@ export function VideoPlayerModal({
91
96
 
92
97
  const insets = useSafeAreaInsets();
93
98
 
94
- const isFullScreenOrPIP = fullscreen || pip;
99
+ const [isFullScreenMode, setFullScreenMode] = React.useState(
100
+ fullscreen || pip
101
+ );
102
+
103
+ React.useEffect(() => {
104
+ const listener = addOrientationChangeListener(({ toOrientation }) => {
105
+ /* ignored in collapsed mode non-PiP player */
106
+ if (collapsed && !pip) return;
107
+ setFullScreenMode(!(toOrientation === 1));
108
+ });
109
+
110
+ return () => {
111
+ listener.remove();
112
+ };
113
+ }, [collapsed, pip]);
95
114
 
96
- // Interpolated opacities for smooth cross-fade
115
+ const isFullScreenOrPIP = isFullScreenMode;
116
+
117
+ const vidHeight = width / aspectRatio;
118
+
119
+ // animated opacity for smooth cross-fade of collapsed content
97
120
  const collapsedOpacity = !isFullScreenOrPIP
98
121
  ? translateY.interpolate({
99
- inputRange: [MODAL_COLLAPSE_START, height - collapsedHeight],
122
+ inputRange: [MODAL_COLLAPSE_START, heightAboveMinimised],
100
123
  outputRange: [0, 1],
101
124
  extrapolate: "clamp",
102
125
  })
103
126
  : new Animated.Value(0);
104
127
 
128
+ // animated opacity for extra content
105
129
  const expandedOpacity = !isFullScreenOrPIP
106
130
  ? translateY.interpolate({
107
- inputRange: [0, height - collapsedHeight],
131
+ inputRange: [0, heightAboveMinimised],
108
132
  outputRange: [1, 0],
109
133
  extrapolate: "clamp",
110
134
  })
111
135
  : new Animated.Value(1);
112
136
 
137
+ // animated x position for video content
113
138
  const xPosition = !isFullScreenOrPIP
114
139
  ? translateY.interpolate({
115
- inputRange: [
116
- height * VIDEO_TRANSITION_THRESHOLD,
117
- height - collapsedHeight,
118
- ],
140
+ inputRange: [height * VIDEO_TRANSITION_THRESHOLD, MODAL_COLLAPSE_START],
119
141
  outputRange: [0, -(width - width * SCALE_FACTOR) / 2],
120
142
  extrapolate: "clamp",
121
143
  })
122
144
  : new Animated.Value(0);
123
145
 
124
- const vidHeight = width / aspectRatio;
125
-
146
+ // animated y position for video content
126
147
  const yPosition = !isFullScreenOrPIP
127
148
  ? translateY.interpolate({
128
- inputRange: [
129
- height * VIDEO_TRANSITION_THRESHOLD,
130
- height - collapsedHeight,
131
- ],
149
+ inputRange: [height * VIDEO_TRANSITION_THRESHOLD, MODAL_COLLAPSE_START],
132
150
  outputRange: [
133
151
  0,
134
- isTablet && isTabletLandscape
135
- ? -((-PROGRESS_BAR_HEIGHT + height - vidHeight * SCALE_FACTOR) / 2)
136
- : -(
137
- PROGRESS_BAR_HEIGHT +
138
- insets.top / 2 +
139
- (SCREEN_WIDTH / aspectRatio -
140
- (SCREEN_WIDTH / aspectRatio) * SCALE_FACTOR) /
141
- 2
142
- ),
152
+ -(
153
+ PROGRESS_BAR_HEIGHT +
154
+ getInsetsOffset(insets, isTabletPortrait) +
155
+ getScaledPos(height, vidHeight, minimisedHeight, isTabletPortrait)
156
+ ),
143
157
  ],
144
158
  extrapolate: "clamp",
145
159
  })
146
160
  : new Animated.Value(0);
147
161
 
162
+ // animated position for video content
148
163
  const scalePosition = !isFullScreenOrPIP
149
164
  ? translateY.interpolate({
150
- inputRange: [
151
- height * VIDEO_TRANSITION_THRESHOLD,
152
- height - collapsedHeight,
153
- ],
165
+ inputRange: [height * VIDEO_TRANSITION_THRESHOLD, MODAL_COLLAPSE_START],
154
166
  outputRange: [1, SCALE_FACTOR],
155
167
  extrapolate: "clamp",
156
168
  })
@@ -158,7 +170,7 @@ export function VideoPlayerModal({
158
170
 
159
171
  const borderTopRadiusAnimated = !isFullScreenOrPIP
160
172
  ? translateY.interpolate({
161
- inputRange: [0, height - collapsedHeight],
173
+ inputRange: [0, heightAboveMinimised],
162
174
  outputRange: [MODAL_RADIUS, 0],
163
175
  extrapolate: "clamp",
164
176
  })
@@ -182,90 +194,96 @@ export function VideoPlayerModal({
182
194
  <PanGestureHandler
183
195
  enabled={enabled}
184
196
  waitFor={scrollViewRef}
197
+ activeOffsetY={[-25, 25]}
198
+ activeOffsetX={[-25, 25]}
185
199
  {...gestureHandlerProps}
186
200
  >
187
- <Animated.View
188
- pointerEvents={"auto"}
189
- style={[
190
- styles.modalWrapper,
191
- {
192
- borderTopLeftRadius: borderTopRadiusAnimated,
193
- borderTopRightRadius: borderTopRadiusAnimated,
194
- transform: [{ translateY: isFullScreenOrPIP ? 0 : translateY }],
195
- height: height,
196
- },
197
- ]}
198
- >
201
+ <AnimatedSafeAreaView pointerEvents="box-none" edges={["bottom"]}>
199
202
  <Animated.View
200
203
  pointerEvents={"auto"}
201
204
  style={[
202
- styles.modal,
203
- collapsed ? styles.collapsedModalBg : null,
205
+ styles.modalWrapper,
204
206
  {
205
207
  borderTopLeftRadius: borderTopRadiusAnimated,
206
208
  borderTopRightRadius: borderTopRadiusAnimated,
207
- height: height,
209
+ transform: [{ translateY: isFullScreenOrPIP ? 0 : translateY }],
210
+ height: frame.height,
208
211
  },
209
212
  ]}
210
213
  >
211
214
  <Animated.View
212
- pointerEvents={!collapsed ? "auto" : "none"}
213
- style={[styles.modalContent, directionStyles(isTabletLandscape)]}
215
+ pointerEvents={"auto"}
216
+ style={[
217
+ styles.modal,
218
+ collapsed ? styles.collapsedModalBg : null,
219
+ {
220
+ borderTopLeftRadius: borderTopRadiusAnimated,
221
+ borderTopRightRadius: borderTopRadiusAnimated,
222
+ },
223
+ ]}
214
224
  >
215
225
  <Animated.View
226
+ pointerEvents={!collapsed ? "auto" : "none"}
216
227
  style={[
217
- styles.expandedContentContainer,
218
- {
219
- transform: [
220
- { translateX: xPosition },
221
- { translateY: yPosition },
222
- { scale: scalePosition },
223
- ],
224
- },
228
+ pip ? styles.modalContentPiP : styles.modalContent,
229
+ directionStyles(isTabletPortrait),
225
230
  ]}
226
231
  >
227
- {content}
232
+ <Animated.View
233
+ style={[
234
+ styles.expandedContentContainer,
235
+ {
236
+ transform: [
237
+ { translateX: xPosition },
238
+ { translateY: yPosition },
239
+ { scale: scalePosition },
240
+ ],
241
+ },
242
+ ]}
243
+ >
244
+ {content}
245
+ </Animated.View>
246
+ {extraContent ? (
247
+ <GestureAnimatedScrollView
248
+ ref={scrollViewRef}
249
+ contentContainerStyle={getExtraContentPadding(insets)}
250
+ bounces={false}
251
+ overScrollMode="never"
252
+ onLayout={onScrollViewLayout}
253
+ style={[
254
+ styles.extraContentScrollView,
255
+ { opacity: expandedOpacity },
256
+ ]}
257
+ >
258
+ <PlayerDetailsWrapperHeightContext.Provider
259
+ value={playerDetailsWrapperHeight}
260
+ >
261
+ {extraContent}
262
+ </PlayerDetailsWrapperHeightContext.Provider>
263
+ </GestureAnimatedScrollView>
264
+ ) : null}
228
265
  </Animated.View>
229
- {extraContent ? (
230
- <GestureAnimatedScrollView
231
- ref={scrollViewRef}
232
- contentContainerStyle={{
233
- paddingBottom: insets.bottom,
234
- }}
235
- onLayout={onScrollViewLayout}
266
+
267
+ {!isFullScreenOrPIP ? (
268
+ <Animated.View
269
+ pointerEvents={collapsed ? "box-none" : "none"}
236
270
  style={[
237
- styles.extraContentScrollView,
238
- { opacity: expandedOpacity },
271
+ styles.collapsedBarContainer,
272
+ { opacity: collapsedOpacity },
239
273
  ]}
240
274
  >
241
- <PlayerDetailsWrapperHeightContext.Provider
242
- value={playerDetailsWrapperHeight}
275
+ <Pressable
276
+ testID="collapsedBarWrapper"
277
+ onPress={expand}
278
+ style={styles.collapsedBarWrapper}
243
279
  >
244
- {extraContent}
245
- </PlayerDetailsWrapperHeightContext.Provider>
246
- </GestureAnimatedScrollView>
280
+ {collapsedContent}
281
+ </Pressable>
282
+ </Animated.View>
247
283
  ) : null}
248
284
  </Animated.View>
249
-
250
- {!isFullScreenOrPIP ? (
251
- <Animated.View
252
- pointerEvents={collapsed ? "box-none" : "none"}
253
- style={[
254
- styles.collapsedBarContainer,
255
- { opacity: collapsedOpacity },
256
- ]}
257
- >
258
- <Pressable
259
- testID="collapsedBarWrapper"
260
- onPress={expand}
261
- style={styles.collapsedBarWrapper}
262
- >
263
- {collapsedContent}
264
- </Pressable>
265
- </Animated.View>
266
- ) : null}
267
285
  </Animated.View>
268
- </Animated.View>
286
+ </AnimatedSafeAreaView>
269
287
  </PanGestureHandler>
270
288
  );
271
289
  }
@@ -1,26 +1,7 @@
1
- import { isAndroidTablet } from "@applicaster/zapp-react-native-utils/reactHooks/layout/isTablet";
2
- import {
3
- isAndroidPlatform,
4
- isAndroidVersionAtLeast,
5
- } from "@applicaster/zapp-react-native-utils/reactUtils";
6
- import { Dimensions, StatusBar } from "react-native";
7
-
8
1
  export const DOCKED_PROGRESSBAR_HEIGHT = 3; // Height of the docked progress bar plugins/mobile-transport-controls/src/ProgressBar/layouts/DockedLayout.tsx
9
2
 
10
3
  export const VIDEO_TRANSITION_THRESHOLD = 0.5; // Threshold for video transition
11
4
 
12
- const windowDimensions = Dimensions.get("window");
13
-
14
- const isOldAndroidDevice =
15
- isAndroidPlatform() && !isAndroidVersionAtLeast(34) && !isAndroidTablet();
16
-
17
- export const SCREEN_WIDTH = windowDimensions.width;
18
-
19
- export const SCREEN_HEIGHT =
20
- windowDimensions.height + (isOldAndroidDevice ? StatusBar.currentHeight : 0);
21
-
22
- export const MODAL_COLLAPSE_START = SCREEN_HEIGHT * 0.7; // 70% of screen height
23
-
24
5
  export const DRAG_TO_COLLAPSE = 0.02; // 2% to collapse
25
6
 
26
7
  export const DRAG_TO_EXPAND = 0.02; // 2% to expand
@@ -32,3 +13,5 @@ export const DEFAULT_IMAGE_RATIO_AUDIO = 1; // Default aspect ratio for audio
32
13
  export const ANIMATION_DURATION = 250; // Default animation duration in milliseconds
33
14
 
34
15
  export const MODAL_RADIUS = 16; // Default border radius for modal
16
+
17
+ export const MODAL_COLLAPSE_RATIO = 0.7; // 70% of the screen height