@applicaster/quick-brick-player 15.0.0-rc.4 → 15.0.0-rc.41
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 +6 -6
- package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +20 -8
- package/src/Player/AudioLayer/Layout/DockedControls/index.tsx +71 -63
- package/src/Player/AudioLayer/Layout/MobileLayout.tsx +1 -6
- package/src/Player/AudioLayer/Layout/PlayerImage/index.tsx +27 -25
- package/src/Player/AudioLayer/Layout/PlayerImage/styles.ts +6 -1
- package/src/Player/AudioLayer/Layout/TabletLandscapeLayout.tsx +5 -8
- package/src/Player/AudioLayer/Layout/TabletPortraitLayout.tsx +1 -6
- package/src/Player/AudioLayer/utils.ts +1 -27
- package/src/Player/PlayerModal/PlayerModal.tsx +16 -15
- package/src/Player/PlayerModal/VideoPlayerModal.tsx +138 -120
- package/src/Player/PlayerModal/consts/index.ts +2 -19
- package/src/Player/PlayerModal/hooks/index.ts +117 -141
- package/src/Player/PlayerModal/styles.ts +5 -0
- package/src/Player/PlayerModal/utils/index.ts +111 -0
- package/src/Player/PlayerView/types.ts +1 -2
- package/src/Player/hooks/progressStates/__tests__/utils.test.ts +23 -0
- package/src/Player/hooks/progressStates/useLiveProgressState.tsx +78 -0
- package/src/Player/hooks/progressStates/useProgressState.tsx +30 -0
- package/src/Player/hooks/progressStates/useVodProgressState.tsx +115 -0
- package/src/Player/hooks/progressStates/utils.ts +33 -0
- package/src/Player/index.tsx +559 -357
- package/src/utils/dimensions.ts +29 -0
- package/src/utils/index.ts +12 -0
- package/src/utils/logger.ts +6 -0
- package/src/utils/playerHelpers.ts +11 -0
- package/src/utils/playerStyles.ts +50 -0
- package/src/Player/AudioLayer/Layout/PlayerImage/AnimatedImage.tsx +0 -82
- package/src/Player/AudioLayer/Layout/PlayerImage/hooks/index.ts +0 -1
- 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
|
|
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
|
-
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
75
|
-
|
|
76
|
-
const dummyRef = useRef(new Animated.Value(0));
|
|
85
|
+
const { translateY, collapsed, gestureHandlerProps, expand, offset } =
|
|
86
|
+
useVideoModalState();
|
|
77
87
|
|
|
78
|
-
const
|
|
79
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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,
|
|
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
|
-
<
|
|
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.
|
|
203
|
-
collapsed ? styles.collapsedModalBg : null,
|
|
205
|
+
styles.modalWrapper,
|
|
204
206
|
{
|
|
205
207
|
borderTopLeftRadius: borderTopRadiusAnimated,
|
|
206
208
|
borderTopRightRadius: borderTopRadiusAnimated,
|
|
207
|
-
|
|
209
|
+
transform: [{ translateY: isFullScreenOrPIP ? 0 : translateY }],
|
|
210
|
+
height: frame.height,
|
|
208
211
|
},
|
|
209
212
|
]}
|
|
210
213
|
>
|
|
211
214
|
<Animated.View
|
|
212
|
-
pointerEvents={
|
|
213
|
-
style={[
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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.
|
|
238
|
-
{ opacity:
|
|
271
|
+
styles.collapsedBarContainer,
|
|
272
|
+
{ opacity: collapsedOpacity },
|
|
239
273
|
]}
|
|
240
274
|
>
|
|
241
|
-
<
|
|
242
|
-
|
|
275
|
+
<Pressable
|
|
276
|
+
testID="collapsedBarWrapper"
|
|
277
|
+
onPress={expand}
|
|
278
|
+
style={styles.collapsedBarWrapper}
|
|
243
279
|
>
|
|
244
|
-
{
|
|
245
|
-
</
|
|
246
|
-
</
|
|
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
|
-
</
|
|
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
|