@applicaster/quick-brick-player 14.0.0-rc.82 → 14.0.0-rc.84
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 +5 -5
- package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +17 -1
- package/src/Player/AudioLayer/Layout/DockedControls/index.tsx +70 -56
- package/src/Player/AudioLayer/Layout/MobileLayout.tsx +1 -6
- package/src/Player/AudioLayer/Layout/PlayerImage/index.tsx +5 -23
- 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 +4 -5
- package/src/Player/PlayerModal/VideoPlayerModal.tsx +101 -100
- package/src/Player/PlayerModal/hooks/index.ts +15 -10
- package/src/Player/PlayerModal/styles.ts +5 -0
- package/src/Player/PlayerModal/utils/index.ts +73 -10
- package/src/Player/index.tsx +23 -22
- 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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/quick-brick-player",
|
|
3
|
-
"version": "14.0.0-rc.
|
|
3
|
+
"version": "14.0.0-rc.84",
|
|
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": "14.0.0-rc.
|
|
38
|
+
"@applicaster/quick-brick-mobile-transport-controls": "14.0.0-rc.83",
|
|
39
39
|
"@applicaster/quick-brick-tv-transport-controls": "14.0.0-rc.71",
|
|
40
|
-
"@applicaster/zapp-react-native-tvos-app": "14.0.0-rc.
|
|
41
|
-
"@applicaster/zapp-react-native-ui-components": "14.0.0-rc.
|
|
42
|
-
"@applicaster/zapp-react-native-utils": "14.0.0-rc.
|
|
40
|
+
"@applicaster/zapp-react-native-tvos-app": "14.0.0-rc.84",
|
|
41
|
+
"@applicaster/zapp-react-native-ui-components": "14.0.0-rc.84",
|
|
42
|
+
"@applicaster/zapp-react-native-utils": "14.0.0-rc.84",
|
|
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;
|
|
@@ -246,13 +247,28 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
246
247
|
player?.seekTo(0);
|
|
247
248
|
}, [player]);
|
|
248
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
|
+
|
|
249
265
|
return (
|
|
250
266
|
<SafeAreaView style={[style, Styles.playerContainer]} edges={edges}>
|
|
251
267
|
<View style={Styles.playerContainer} onLayout={onLayout}>
|
|
252
268
|
<Layout
|
|
253
269
|
entry={entry}
|
|
254
270
|
playerState={playerState}
|
|
255
|
-
layoutState={
|
|
271
|
+
layoutState={layoutStateMemo}
|
|
256
272
|
value={value}
|
|
257
273
|
onPlaybackButtonPress={onPlaybackButtonPress}
|
|
258
274
|
onRewindButtonPress={onRewindButtonPress}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { StyleSheet, View
|
|
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,6 +25,7 @@ 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
|
|
|
@@ -45,7 +44,6 @@ const styles = StyleSheet.create({
|
|
|
45
44
|
flex: 1,
|
|
46
45
|
flexDirection: "column",
|
|
47
46
|
alignItems: "stretch",
|
|
48
|
-
marginTop: PROGRESS_BAR_HEIGHT,
|
|
49
47
|
},
|
|
50
48
|
content: {
|
|
51
49
|
flex: 1,
|
|
@@ -88,14 +86,12 @@ const styles = StyleSheet.create({
|
|
|
88
86
|
buttons: {
|
|
89
87
|
marginVertical: 10,
|
|
90
88
|
},
|
|
89
|
+
liveMarginTop: { marginTop: 0 },
|
|
90
|
+
marginTop: { marginTop: PROGRESS_BAR_HEIGHT },
|
|
91
91
|
});
|
|
92
92
|
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
addPadding: boolean
|
|
96
|
-
): ViewStyle => ({
|
|
97
|
-
width: addPadding ? toNumberWithDefault(100, minimisedHeight) : 0,
|
|
98
|
-
});
|
|
93
|
+
const getValue = (key: string, stylesObj: Record<string, any>) =>
|
|
94
|
+
stylesObj?.[key] ?? null;
|
|
99
95
|
|
|
100
96
|
const controlButtons = (
|
|
101
97
|
live: boolean,
|
|
@@ -161,7 +157,6 @@ export const DockedControls = (props: Props) => {
|
|
|
161
157
|
onPlayerSeek = noop,
|
|
162
158
|
playNextData,
|
|
163
159
|
ImageComponent,
|
|
164
|
-
isActiveGesture = true,
|
|
165
160
|
aspectRatio,
|
|
166
161
|
} = props;
|
|
167
162
|
|
|
@@ -179,11 +174,6 @@ export const DockedControls = (props: Props) => {
|
|
|
179
174
|
const configuration = useZStore("playerConfiguration")();
|
|
180
175
|
const playerState = usePlayerState("shared-player-wrapper-docked");
|
|
181
176
|
|
|
182
|
-
const getValue = React.useCallback(
|
|
183
|
-
(key: string, stylesObj: {}) => R.propOr(null, key, stylesObj),
|
|
184
|
-
[]
|
|
185
|
-
);
|
|
186
|
-
|
|
187
177
|
const value = React.useCallback(
|
|
188
178
|
(key: string) => {
|
|
189
179
|
const retVal = getValue(key, configuration);
|
|
@@ -202,7 +192,7 @@ export const DockedControls = (props: Props) => {
|
|
|
202
192
|
|
|
203
193
|
return retVal;
|
|
204
194
|
},
|
|
205
|
-
[...Object.values(configuration), playerState.seekableDuration
|
|
195
|
+
[...Object.values(configuration), playerState.seekableDuration]
|
|
206
196
|
);
|
|
207
197
|
|
|
208
198
|
const { title, summary } = useTitleSummaryOverlay(
|
|
@@ -219,15 +209,65 @@ export const DockedControls = (props: Props) => {
|
|
|
219
209
|
|
|
220
210
|
const insets = useSafeAreaInsets();
|
|
221
211
|
|
|
212
|
+
const progressBarLayoutState = React.useMemo(
|
|
213
|
+
() => ({
|
|
214
|
+
inline: layoutState.inline,
|
|
215
|
+
docked: true,
|
|
216
|
+
isModal: layoutState.isModal,
|
|
217
|
+
pip: layoutState.pip,
|
|
218
|
+
}),
|
|
219
|
+
[layoutState.inline, layoutState.isModal, layoutState.pip]
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const _controlButtons = React.useMemo(
|
|
223
|
+
() => partial(controlButtons, playerState.isLive && liveButton),
|
|
224
|
+
[playerState.isLive, liveButton]
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
const containerStyles = React.useMemo(
|
|
228
|
+
() => ({
|
|
229
|
+
height: minimisedHeight,
|
|
230
|
+
maxHeight: minimisedHeight,
|
|
231
|
+
}),
|
|
232
|
+
[minimisedHeight]
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
const imageComponentWrapperStyles = React.useMemo(
|
|
236
|
+
() => ({
|
|
237
|
+
aspectRatio: aspectRatio,
|
|
238
|
+
height: minimisedHeight,
|
|
239
|
+
}),
|
|
240
|
+
[aspectRatio, minimisedHeight]
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const backgroundColorStyle = React.useMemo(
|
|
244
|
+
() => ({
|
|
245
|
+
backgroundColor: dockedBackgroundColor,
|
|
246
|
+
}),
|
|
247
|
+
[dockedBackgroundColor]
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
const bottomSpacerStyle = React.useMemo(() => {
|
|
251
|
+
const tabBarHeight = menuVisible ? bottomTabBarHeight : 0;
|
|
252
|
+
const safeAreaBottomInset = insets.bottom || 0;
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
height: safeAreaBottomInset + tabBarHeight,
|
|
256
|
+
};
|
|
257
|
+
}, [menuVisible, insets.bottom]);
|
|
258
|
+
|
|
259
|
+
const contentInfoContent = React.useMemo(
|
|
260
|
+
() => ({ ...entry, title, summary }),
|
|
261
|
+
[title, summary, entry]
|
|
262
|
+
);
|
|
263
|
+
|
|
222
264
|
return (
|
|
223
265
|
<>
|
|
224
266
|
<View
|
|
225
267
|
style={[
|
|
226
268
|
styles.container,
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
maxHeight: minimisedHeight,
|
|
230
|
-
},
|
|
269
|
+
containerStyles,
|
|
270
|
+
playerState.isLive ? styles.liveMarginTop : styles.marginTop,
|
|
231
271
|
]}
|
|
232
272
|
>
|
|
233
273
|
{!playerState.isLive ? (
|
|
@@ -236,42 +276,23 @@ export const DockedControls = (props: Props) => {
|
|
|
236
276
|
content={entry}
|
|
237
277
|
value={value}
|
|
238
278
|
visible
|
|
239
|
-
layoutState={
|
|
279
|
+
layoutState={progressBarLayoutState}
|
|
240
280
|
onPlayerSeek={onPlayerSeek}
|
|
241
281
|
playerState={playerState}
|
|
242
|
-
startComponentsAnimation={noop}
|
|
243
282
|
docked
|
|
244
283
|
/>
|
|
245
284
|
) : null}
|
|
246
285
|
|
|
247
286
|
<View style={styles.content}>
|
|
248
287
|
<View style={styles.touchableArea}>
|
|
249
|
-
{
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
>
|
|
253
|
-
{ImageComponent || null}
|
|
254
|
-
</View>
|
|
255
|
-
) : (
|
|
256
|
-
<View
|
|
257
|
-
style={playerDimensionsStyle(
|
|
258
|
-
minimisedHeight,
|
|
259
|
-
isActiveGesture || layoutState.docked
|
|
260
|
-
)}
|
|
261
|
-
/>
|
|
262
|
-
)}
|
|
288
|
+
<View style={imageComponentWrapperStyles}>
|
|
289
|
+
{ImageComponent || null}
|
|
290
|
+
</View>
|
|
263
291
|
<View
|
|
264
292
|
testID="content-info"
|
|
265
|
-
style={[
|
|
266
|
-
styles.contentInfo,
|
|
267
|
-
{ backgroundColor: dockedBackgroundColor },
|
|
268
|
-
]}
|
|
293
|
+
style={[styles.contentInfo, backgroundColorStyle]}
|
|
269
294
|
>
|
|
270
|
-
<ContentInfo
|
|
271
|
-
content={{ ...entry, title, summary }}
|
|
272
|
-
value={value}
|
|
273
|
-
docked
|
|
274
|
-
/>
|
|
295
|
+
<ContentInfo content={contentInfoContent} value={value} docked />
|
|
275
296
|
</View>
|
|
276
297
|
</View>
|
|
277
298
|
<View
|
|
@@ -279,15 +300,13 @@ export const DockedControls = (props: Props) => {
|
|
|
279
300
|
isTablet
|
|
280
301
|
? styles.controlsContainerForTablet
|
|
281
302
|
: styles.controlsContainerForMobile,
|
|
282
|
-
|
|
303
|
+
backgroundColorStyle,
|
|
283
304
|
]}
|
|
284
305
|
>
|
|
285
306
|
<Controls
|
|
286
307
|
value={value}
|
|
287
308
|
visible
|
|
288
|
-
buttons={
|
|
289
|
-
playerState.isLive && liveButton,
|
|
290
|
-
])}
|
|
309
|
+
buttons={_controlButtons}
|
|
291
310
|
style={styles.playPauseButton}
|
|
292
311
|
playNextData={playNextData}
|
|
293
312
|
playerState={playerState}
|
|
@@ -302,12 +321,7 @@ export const DockedControls = (props: Props) => {
|
|
|
302
321
|
</View>
|
|
303
322
|
</View>
|
|
304
323
|
</View>
|
|
305
|
-
<View
|
|
306
|
-
style={{
|
|
307
|
-
height: insets.bottom + (menuVisible ? bottomTabBarHeight : 0),
|
|
308
|
-
backgroundColor: dockedBackgroundColor,
|
|
309
|
-
}}
|
|
310
|
-
/>
|
|
324
|
+
<View style={[bottomSpacerStyle, backgroundColorStyle]} />
|
|
311
325
|
</>
|
|
312
326
|
);
|
|
313
327
|
};
|
|
@@ -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
|
|
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
|
-
{
|
|
47
|
-
<
|
|
48
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
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
|
-
};
|
|
@@ -29,10 +29,10 @@ export function AnimatedModal({
|
|
|
29
29
|
|
|
30
30
|
const styles = useStyles({ height: collapsedHeight, type: "audio" });
|
|
31
31
|
|
|
32
|
-
const
|
|
33
|
-
const heightAboveMinimised =
|
|
32
|
+
const height = getWindowHeight();
|
|
33
|
+
const heightAboveMinimised = height - collapsedHeight;
|
|
34
34
|
|
|
35
|
-
const MODAL_COLLAPSE_START =
|
|
35
|
+
const MODAL_COLLAPSE_START = height * MODAL_COLLAPSE_RATIO;
|
|
36
36
|
|
|
37
37
|
// Interpolated opacities for smooth cross-fade
|
|
38
38
|
const collapsedOpacity = translateY.interpolate({
|
|
@@ -63,7 +63,7 @@ export function AnimatedModal({
|
|
|
63
63
|
borderTopLeftRadius: borderTopRadiusAnimated,
|
|
64
64
|
borderTopRightRadius: borderTopRadiusAnimated,
|
|
65
65
|
transform: [{ translateY }],
|
|
66
|
-
height:
|
|
66
|
+
height: height,
|
|
67
67
|
},
|
|
68
68
|
]}
|
|
69
69
|
>
|
|
@@ -73,7 +73,6 @@ export function AnimatedModal({
|
|
|
73
73
|
{
|
|
74
74
|
borderTopLeftRadius: borderTopRadiusAnimated,
|
|
75
75
|
borderTopRightRadius: borderTopRadiusAnimated,
|
|
76
|
-
height: WINDOW_HEIGHT,
|
|
77
76
|
},
|
|
78
77
|
]}
|
|
79
78
|
>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
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";
|
|
@@ -12,22 +12,23 @@ import {
|
|
|
12
12
|
VIDEO_TRANSITION_THRESHOLD,
|
|
13
13
|
} from "./consts";
|
|
14
14
|
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
15
|
-
import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
16
15
|
import { useModalAnimationContext } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation/useModalAnimationContext";
|
|
17
16
|
import { PROGRESS_BAR_HEIGHT } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation/utils";
|
|
18
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
SafeAreaView,
|
|
19
|
+
useSafeAreaFrame,
|
|
20
|
+
useSafeAreaInsets,
|
|
21
|
+
} from "react-native-safe-area-context";
|
|
19
22
|
import { GestureAnimatedScrollView } from "./GestureAnimatedScrollView";
|
|
20
23
|
import { PlayerDetailsWrapperHeightContext } from "./context";
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return isLandscape ? orientationStyles.landscape : orientationStyles.portrait;
|
|
30
|
-
};
|
|
24
|
+
import {
|
|
25
|
+
getInsetsOffset,
|
|
26
|
+
getWindowHeight,
|
|
27
|
+
getWindowWidth,
|
|
28
|
+
directionStyles,
|
|
29
|
+
getScaledPos,
|
|
30
|
+
getExtraContentPadding,
|
|
31
|
+
} from "./utils";
|
|
31
32
|
|
|
32
33
|
type AnimatedModalProps = {
|
|
33
34
|
pip?: boolean;
|
|
@@ -59,16 +60,19 @@ export function VideoPlayerModal({
|
|
|
59
60
|
const enabled = !!modal && !fullscreen;
|
|
60
61
|
const isTablet = useIsTablet();
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
// remember initial width, ignore rotation changes
|
|
64
|
+
const width = useMemo(
|
|
65
|
+
() =>
|
|
66
|
+
isTablet && !isTabletPortrait
|
|
67
|
+
? style.tabletLandscapeWidth
|
|
68
|
+
: getWindowWidth(),
|
|
69
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
70
|
+
[]
|
|
71
|
+
);
|
|
72
|
+
|
|
63
73
|
const height = useMemo(() => getWindowHeight(), []); // remember initial height, ignore rotation changes
|
|
64
74
|
const MODAL_COLLAPSE_START = height * MODAL_COLLAPSE_RATIO;
|
|
65
75
|
|
|
66
|
-
let width = WINDOW_WIDTH;
|
|
67
|
-
|
|
68
|
-
if (isTablet && !isTabletPortrait) {
|
|
69
|
-
width = style?.tabletLandscapeWidth || width;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
76
|
const { minimised_height: minimisedHeight } = useConfiguration();
|
|
73
77
|
const scrollViewRef = useRef<typeof Animated.ScrollView | null>(null);
|
|
74
78
|
|
|
@@ -84,9 +88,9 @@ export function VideoPlayerModal({
|
|
|
84
88
|
expand,
|
|
85
89
|
} = useVideoModalState(modal ? translateYRef : dummyRef);
|
|
86
90
|
|
|
87
|
-
const
|
|
91
|
+
const frame = useSafeAreaFrame();
|
|
88
92
|
|
|
89
|
-
const
|
|
93
|
+
const heightAboveMinimised = height - collapsedHeight;
|
|
90
94
|
|
|
91
95
|
const styles = useStyles({ height: collapsedHeight });
|
|
92
96
|
|
|
@@ -113,9 +117,10 @@ export function VideoPlayerModal({
|
|
|
113
117
|
})
|
|
114
118
|
: new Animated.Value(1);
|
|
115
119
|
|
|
120
|
+
// animated position for video content during modal transition
|
|
116
121
|
const xPosition = !isFullScreenOrPIP
|
|
117
122
|
? translateY.interpolate({
|
|
118
|
-
inputRange: [height * VIDEO_TRANSITION_THRESHOLD,
|
|
123
|
+
inputRange: [height * VIDEO_TRANSITION_THRESHOLD, MODAL_COLLAPSE_START],
|
|
119
124
|
outputRange: [0, -(width - width * SCALE_FACTOR) / 2],
|
|
120
125
|
extrapolate: "clamp",
|
|
121
126
|
})
|
|
@@ -123,34 +128,26 @@ export function VideoPlayerModal({
|
|
|
123
128
|
|
|
124
129
|
const vidHeight = width / aspectRatio;
|
|
125
130
|
|
|
131
|
+
// animated position for video content during modal transition
|
|
126
132
|
const yPosition = !isFullScreenOrPIP
|
|
127
133
|
? translateY.interpolate({
|
|
128
|
-
inputRange: [
|
|
129
|
-
height * VIDEO_TRANSITION_THRESHOLD,
|
|
130
|
-
height - collapsedHeight,
|
|
131
|
-
],
|
|
134
|
+
inputRange: [height * VIDEO_TRANSITION_THRESHOLD, MODAL_COLLAPSE_START],
|
|
132
135
|
outputRange: [
|
|
133
136
|
0,
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
(WINDOW_WIDTH / aspectRatio -
|
|
140
|
-
(WINDOW_WIDTH / aspectRatio) * SCALE_FACTOR) /
|
|
141
|
-
2
|
|
142
|
-
),
|
|
137
|
+
-(
|
|
138
|
+
PROGRESS_BAR_HEIGHT +
|
|
139
|
+
getInsetsOffset(insets, isTabletPortrait) +
|
|
140
|
+
getScaledPos(height, vidHeight, minimisedHeight, isTabletPortrait)
|
|
141
|
+
),
|
|
143
142
|
],
|
|
144
143
|
extrapolate: "clamp",
|
|
145
144
|
})
|
|
146
145
|
: new Animated.Value(0);
|
|
147
146
|
|
|
147
|
+
// animated position for video content during modal transition
|
|
148
148
|
const scalePosition = !isFullScreenOrPIP
|
|
149
149
|
? translateY.interpolate({
|
|
150
|
-
inputRange: [
|
|
151
|
-
height * VIDEO_TRANSITION_THRESHOLD,
|
|
152
|
-
height - collapsedHeight,
|
|
153
|
-
],
|
|
150
|
+
inputRange: [height * VIDEO_TRANSITION_THRESHOLD, MODAL_COLLAPSE_START],
|
|
154
151
|
outputRange: [1, SCALE_FACTOR],
|
|
155
152
|
extrapolate: "clamp",
|
|
156
153
|
})
|
|
@@ -158,7 +155,7 @@ export function VideoPlayerModal({
|
|
|
158
155
|
|
|
159
156
|
const borderTopRadiusAnimated = !isFullScreenOrPIP
|
|
160
157
|
? translateY.interpolate({
|
|
161
|
-
inputRange: [0,
|
|
158
|
+
inputRange: [0, heightAboveMinimised],
|
|
162
159
|
outputRange: [MODAL_RADIUS, 0],
|
|
163
160
|
extrapolate: "clamp",
|
|
164
161
|
})
|
|
@@ -184,88 +181,92 @@ export function VideoPlayerModal({
|
|
|
184
181
|
waitFor={scrollViewRef}
|
|
185
182
|
{...gestureHandlerProps}
|
|
186
183
|
>
|
|
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
|
-
>
|
|
184
|
+
<SafeAreaView pointerEvents="box-none" edges={["bottom"]}>
|
|
199
185
|
<Animated.View
|
|
200
186
|
pointerEvents={"auto"}
|
|
201
187
|
style={[
|
|
202
|
-
styles.
|
|
203
|
-
collapsed ? styles.collapsedModalBg : null,
|
|
188
|
+
styles.modalWrapper,
|
|
204
189
|
{
|
|
205
190
|
borderTopLeftRadius: borderTopRadiusAnimated,
|
|
206
191
|
borderTopRightRadius: borderTopRadiusAnimated,
|
|
207
|
-
|
|
192
|
+
transform: [{ translateY: isFullScreenOrPIP ? 0 : translateY }],
|
|
193
|
+
height: frame.height,
|
|
208
194
|
},
|
|
209
195
|
]}
|
|
210
196
|
>
|
|
211
197
|
<Animated.View
|
|
212
|
-
pointerEvents={
|
|
213
|
-
style={[
|
|
198
|
+
pointerEvents={"auto"}
|
|
199
|
+
style={[
|
|
200
|
+
styles.modal,
|
|
201
|
+
collapsed ? styles.collapsedModalBg : null,
|
|
202
|
+
{
|
|
203
|
+
borderTopLeftRadius: borderTopRadiusAnimated,
|
|
204
|
+
borderTopRightRadius: borderTopRadiusAnimated,
|
|
205
|
+
},
|
|
206
|
+
]}
|
|
214
207
|
>
|
|
215
208
|
<Animated.View
|
|
209
|
+
pointerEvents={!collapsed ? "auto" : "none"}
|
|
216
210
|
style={[
|
|
217
|
-
styles.
|
|
218
|
-
|
|
219
|
-
transform: [
|
|
220
|
-
{ translateX: xPosition },
|
|
221
|
-
{ translateY: yPosition },
|
|
222
|
-
{ scale: scalePosition },
|
|
223
|
-
],
|
|
224
|
-
},
|
|
211
|
+
pip ? styles.modalContentPiP : styles.modalContent,
|
|
212
|
+
directionStyles(isTabletPortrait),
|
|
225
213
|
]}
|
|
226
214
|
>
|
|
227
|
-
|
|
215
|
+
<Animated.View
|
|
216
|
+
style={[
|
|
217
|
+
styles.expandedContentContainer,
|
|
218
|
+
{
|
|
219
|
+
transform: [
|
|
220
|
+
{ translateX: xPosition },
|
|
221
|
+
{ translateY: yPosition },
|
|
222
|
+
{ scale: scalePosition },
|
|
223
|
+
],
|
|
224
|
+
},
|
|
225
|
+
]}
|
|
226
|
+
>
|
|
227
|
+
{content}
|
|
228
|
+
</Animated.View>
|
|
229
|
+
{extraContent ? (
|
|
230
|
+
<GestureAnimatedScrollView
|
|
231
|
+
ref={scrollViewRef}
|
|
232
|
+
contentContainerStyle={getExtraContentPadding(insets)}
|
|
233
|
+
bounces={false}
|
|
234
|
+
overScrollMode="never"
|
|
235
|
+
onLayout={onScrollViewLayout}
|
|
236
|
+
style={[
|
|
237
|
+
styles.extraContentScrollView,
|
|
238
|
+
{ opacity: expandedOpacity },
|
|
239
|
+
]}
|
|
240
|
+
>
|
|
241
|
+
<PlayerDetailsWrapperHeightContext.Provider
|
|
242
|
+
value={playerDetailsWrapperHeight}
|
|
243
|
+
>
|
|
244
|
+
{extraContent}
|
|
245
|
+
</PlayerDetailsWrapperHeightContext.Provider>
|
|
246
|
+
</GestureAnimatedScrollView>
|
|
247
|
+
) : null}
|
|
228
248
|
</Animated.View>
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
paddingBottom: insets.bottom,
|
|
234
|
-
}}
|
|
235
|
-
onLayout={onScrollViewLayout}
|
|
249
|
+
|
|
250
|
+
{!isFullScreenOrPIP ? (
|
|
251
|
+
<Animated.View
|
|
252
|
+
pointerEvents={collapsed ? "box-none" : "none"}
|
|
236
253
|
style={[
|
|
237
|
-
styles.
|
|
238
|
-
{ opacity:
|
|
254
|
+
styles.collapsedBarContainer,
|
|
255
|
+
{ opacity: collapsedOpacity },
|
|
239
256
|
]}
|
|
240
257
|
>
|
|
241
|
-
<
|
|
242
|
-
|
|
258
|
+
<Pressable
|
|
259
|
+
testID="collapsedBarWrapper"
|
|
260
|
+
onPress={expand}
|
|
261
|
+
style={styles.collapsedBarWrapper}
|
|
243
262
|
>
|
|
244
|
-
{
|
|
245
|
-
</
|
|
246
|
-
</
|
|
263
|
+
{collapsedContent}
|
|
264
|
+
</Pressable>
|
|
265
|
+
</Animated.View>
|
|
247
266
|
) : null}
|
|
248
267
|
</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
268
|
</Animated.View>
|
|
268
|
-
</
|
|
269
|
+
</SafeAreaView>
|
|
269
270
|
</PanGestureHandler>
|
|
270
271
|
);
|
|
271
272
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
useSafeAreaFrame,
|
|
4
|
+
useSafeAreaInsets,
|
|
5
|
+
} from "react-native-safe-area-context";
|
|
3
6
|
import { State } from "react-native-gesture-handler";
|
|
4
7
|
import { Animated } from "react-native";
|
|
5
8
|
|
|
@@ -15,7 +18,7 @@ import {
|
|
|
15
18
|
} from "../consts";
|
|
16
19
|
import { PROGRESS_BAR_HEIGHT } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation/utils";
|
|
17
20
|
import { usePlugins } from "@applicaster/zapp-react-native-redux";
|
|
18
|
-
import {
|
|
21
|
+
import { isOldAndroidDevice } from "../utils";
|
|
19
22
|
|
|
20
23
|
const bottomTabBarHeight = getTabBarHeight();
|
|
21
24
|
|
|
@@ -24,8 +27,6 @@ export const useVideoModalState = (
|
|
|
24
27
|
) => {
|
|
25
28
|
const { minimised_height: minimisedHeight } = useConfiguration();
|
|
26
29
|
|
|
27
|
-
const WINDOW_HEIGHT = getWindowHeight();
|
|
28
|
-
|
|
29
30
|
const {
|
|
30
31
|
maximiseVideoModal,
|
|
31
32
|
minimiseVideoModal,
|
|
@@ -42,17 +43,16 @@ export const useVideoModalState = (
|
|
|
42
43
|
videoModalState.visible &&
|
|
43
44
|
videoModalState.mode === VideoModalMode.MINIMIZED;
|
|
44
45
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
const initialBottomOffset = useRef(bottom).current;
|
|
46
|
+
const frame = useSafeAreaFrame();
|
|
47
|
+
const insets = useSafeAreaInsets();
|
|
48
48
|
|
|
49
49
|
const collapsedHeight =
|
|
50
50
|
minimisedHeight +
|
|
51
|
-
initialBottomOffset +
|
|
52
51
|
(menuVisible ? bottomTabBarHeight : 0) +
|
|
52
|
+
(isOldAndroidDevice ? 0 : insets.bottom) + // insets.bottom is added to properly display docked modal
|
|
53
53
|
PROGRESS_BAR_HEIGHT;
|
|
54
54
|
|
|
55
|
-
const heightAboveMinimised =
|
|
55
|
+
const heightAboveMinimised = frame.height - collapsedHeight;
|
|
56
56
|
|
|
57
57
|
const translateY = translateYRef.current;
|
|
58
58
|
|
|
@@ -179,7 +179,12 @@ export const useVideoModalState = (
|
|
|
179
179
|
});
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
|
-
}, [
|
|
182
|
+
}, [
|
|
183
|
+
videoModalState.visible,
|
|
184
|
+
videoModalState.mode,
|
|
185
|
+
collapsedHeight,
|
|
186
|
+
heightAboveMinimised,
|
|
187
|
+
]);
|
|
183
188
|
|
|
184
189
|
return useMemo(
|
|
185
190
|
() => ({
|
|
@@ -63,6 +63,7 @@ export const useStyles = ({ height }: { height: number }) => {
|
|
|
63
63
|
backgroundColor,
|
|
64
64
|
elevation: 10,
|
|
65
65
|
zIndex: 100,
|
|
66
|
+
height: "100%",
|
|
66
67
|
overflow: "hidden",
|
|
67
68
|
},
|
|
68
69
|
modalWrapper: {
|
|
@@ -75,6 +76,10 @@ export const useStyles = ({ height }: { height: number }) => {
|
|
|
75
76
|
flex: 1,
|
|
76
77
|
alignItems: "center",
|
|
77
78
|
},
|
|
79
|
+
modalContentPiP: {
|
|
80
|
+
flex: 1,
|
|
81
|
+
alignItems: "flex-start",
|
|
82
|
+
},
|
|
78
83
|
expandedContentContainer: {
|
|
79
84
|
justifyContent: "flex-start",
|
|
80
85
|
backgroundColor: BG_COLOR_DEFAULT_TRANSPARENT,
|
|
@@ -1,25 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StyleSheet, Dimensions, ViewStyle } from "react-native";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
isAndroidPlatform,
|
|
5
5
|
isAndroidVersionAtLeast,
|
|
6
|
+
isTV,
|
|
6
7
|
} from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
7
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
isTablet,
|
|
10
|
+
useIsTablet as getIsTablet,
|
|
11
|
+
} from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
8
12
|
|
|
9
|
-
const
|
|
13
|
+
const SAFE_AREA_BREAKING_API_VERSION = 35;
|
|
10
14
|
|
|
11
|
-
const isOldAndroidDevice =
|
|
15
|
+
export const isOldAndroidDevice =
|
|
12
16
|
isAndroidPlatform() &&
|
|
13
|
-
!isAndroidVersionAtLeast(
|
|
14
|
-
!isAndroidTablet();
|
|
17
|
+
!isAndroidVersionAtLeast(SAFE_AREA_BREAKING_API_VERSION);
|
|
15
18
|
|
|
16
19
|
export const getWindowHeight = (): number => {
|
|
17
20
|
const windowDimensions = Dimensions.get("window");
|
|
18
21
|
|
|
19
|
-
return
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
return windowDimensions.height;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const getScreenHeight = (): number => {
|
|
26
|
+
const screenDimensions = Dimensions.get("screen");
|
|
27
|
+
|
|
28
|
+
return screenDimensions.height;
|
|
23
29
|
};
|
|
24
30
|
|
|
25
31
|
export const getWindowWidth = (): number => {
|
|
@@ -27,3 +33,60 @@ export const getWindowWidth = (): number => {
|
|
|
27
33
|
|
|
28
34
|
return windowDimensions.width;
|
|
29
35
|
};
|
|
36
|
+
|
|
37
|
+
const getIsTabletLandscape = (isTabletPortrait: boolean): boolean => {
|
|
38
|
+
return !isTV() && isTablet() && !isTabletPortrait;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const getInsetsOffset = (
|
|
42
|
+
insets: { top: number; bottom: number },
|
|
43
|
+
isTabletPortrait: boolean
|
|
44
|
+
): number => {
|
|
45
|
+
const isTabletLandscape = getIsTabletLandscape(isTabletPortrait);
|
|
46
|
+
|
|
47
|
+
return isTablet() && isTabletLandscape
|
|
48
|
+
? isOldAndroidDevice
|
|
49
|
+
? insets.top / 2
|
|
50
|
+
: 0
|
|
51
|
+
: insets.top / 2;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const orientationStyles = StyleSheet.create({
|
|
55
|
+
landscape: { flexDirection: "row" },
|
|
56
|
+
portrait: { flexDirection: "column" },
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export const directionStyles = (isTabletPortrait: boolean): ViewStyle => {
|
|
60
|
+
const isTabletLandscape = getIsTabletLandscape(isTabletPortrait);
|
|
61
|
+
|
|
62
|
+
return isTabletLandscape
|
|
63
|
+
? orientationStyles.landscape
|
|
64
|
+
: orientationStyles.portrait;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const getScaledPos = (
|
|
68
|
+
height,
|
|
69
|
+
vidHeight,
|
|
70
|
+
minimisedHeight,
|
|
71
|
+
isTabletPortrait
|
|
72
|
+
) => {
|
|
73
|
+
const isTabletLandscape = getIsTabletLandscape(isTabletPortrait);
|
|
74
|
+
|
|
75
|
+
const SCALE_FACTOR = minimisedHeight / vidHeight; // Scale factor for expanded content
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
((isTabletLandscape ? height : vidHeight) - vidHeight * SCALE_FACTOR) / 2
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const getExtraContentPadding = (insets: {
|
|
83
|
+
top: number;
|
|
84
|
+
bottom: number;
|
|
85
|
+
}): ViewStyle => {
|
|
86
|
+
const isTablet = getIsTablet(); // not a hook, just a utility function
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
paddingTop: isTablet ? insets.top : 0,
|
|
90
|
+
paddingBottom: insets.bottom,
|
|
91
|
+
};
|
|
92
|
+
};
|
package/src/Player/index.tsx
CHANGED
|
@@ -83,7 +83,6 @@ type VideoPlayerProps = QuickBrickPlayer.PlayerProps & {
|
|
|
83
83
|
updatePlayerState?: (state: string) => void;
|
|
84
84
|
setNextVideoPreloadThresholdPercentage?: (percentage: number) => void;
|
|
85
85
|
hideTransportControls?: boolean;
|
|
86
|
-
startComponentsAnimation?: boolean;
|
|
87
86
|
playableItem?: any;
|
|
88
87
|
playerPluginIdentifier?: string;
|
|
89
88
|
controller?: {
|
|
@@ -836,25 +835,25 @@ export default class VideoPlayer extends React.Component<
|
|
|
836
835
|
: styles.playerContainerBlack;
|
|
837
836
|
|
|
838
837
|
private getBackgroundImageStyle = (
|
|
839
|
-
dimensions:
|
|
838
|
+
dimensions: ViewStyle,
|
|
840
839
|
isAudioItem: boolean
|
|
841
|
-
): ViewStyle =>
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
840
|
+
): ViewStyle[] => [
|
|
841
|
+
isAudioItem ? styles.audio : { position: "absolute" },
|
|
842
|
+
dimensions,
|
|
843
|
+
];
|
|
845
844
|
|
|
846
845
|
/* Returns player view style based on platform and content type (audio/video) */
|
|
847
846
|
private getPlayerViewStyle = (
|
|
848
847
|
dimensions: ViewStyle,
|
|
849
848
|
isAudioItem: boolean
|
|
850
|
-
): ViewStyle => {
|
|
851
|
-
const generalStyles = isAudioItem ? styles.audio :
|
|
849
|
+
): ViewStyle | ViewStyle[] => {
|
|
850
|
+
const generalStyles = isAudioItem ? styles.audio : undefined;
|
|
852
851
|
|
|
853
852
|
if (isAndroidPlatform() && isAudioItem) {
|
|
854
853
|
return generalStyles;
|
|
855
854
|
}
|
|
856
855
|
|
|
857
|
-
return
|
|
856
|
+
return [generalStyles, dimensions];
|
|
858
857
|
};
|
|
859
858
|
|
|
860
859
|
private renderAudioPlayer = (
|
|
@@ -877,7 +876,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
877
876
|
shouldRender: boolean,
|
|
878
877
|
imageKey: string,
|
|
879
878
|
entry: ZappEntry,
|
|
880
|
-
style: ViewStyle
|
|
879
|
+
style: ViewStyle | ViewStyle[]
|
|
881
880
|
) => {
|
|
882
881
|
if (!shouldRender) return null;
|
|
883
882
|
|
|
@@ -888,7 +887,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
888
887
|
|
|
889
888
|
private renderNativePlayer = (
|
|
890
889
|
shouldRender: boolean,
|
|
891
|
-
style: ViewStyle,
|
|
890
|
+
style: ViewStyle | ViewStyle[],
|
|
892
891
|
nativeEvents: ReturnType<typeof this.getNativeEvents>,
|
|
893
892
|
nativeProps: ReturnType<typeof this.getNativeProps>,
|
|
894
893
|
pluginConfiguration: Record<string, any>
|
|
@@ -1047,10 +1046,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
1047
1046
|
: supportsNativeControls && !this.getPlayNextData();
|
|
1048
1047
|
|
|
1049
1048
|
const shouldUseNativeControls =
|
|
1050
|
-
controls &&
|
|
1051
|
-
!this.props.startComponentsAnimation &&
|
|
1052
|
-
!docked &&
|
|
1053
|
-
!castAction?.state?.connected;
|
|
1049
|
+
controls && !docked && !castAction?.state?.connected;
|
|
1054
1050
|
|
|
1055
1051
|
platformSpecificProps = {
|
|
1056
1052
|
...platformSpecificProps,
|
|
@@ -1134,7 +1130,8 @@ export default class VideoPlayer extends React.Component<
|
|
|
1134
1130
|
const needsToRender =
|
|
1135
1131
|
!hideTransportControls &&
|
|
1136
1132
|
!needToShowPlayNextOverlay &&
|
|
1137
|
-
this.props.mode !== "PIP"
|
|
1133
|
+
this.props.mode !== "PIP" &&
|
|
1134
|
+
!docked;
|
|
1138
1135
|
|
|
1139
1136
|
const supportsNativeControls =
|
|
1140
1137
|
!!this.props.controller?.supportsNativeControls?.();
|
|
@@ -1145,10 +1142,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
1145
1142
|
: supportsNativeControls && !this.getPlayNextData();
|
|
1146
1143
|
|
|
1147
1144
|
const shouldUseNativeControls =
|
|
1148
|
-
controls &&
|
|
1149
|
-
!this.props.startComponentsAnimation &&
|
|
1150
|
-
!docked &&
|
|
1151
|
-
!castAction?.state?.connected;
|
|
1145
|
+
controls && !docked && !castAction?.state?.connected;
|
|
1152
1146
|
|
|
1153
1147
|
if (shouldUseNativeControls) {
|
|
1154
1148
|
return null;
|
|
@@ -1271,13 +1265,13 @@ export default class VideoPlayer extends React.Component<
|
|
|
1271
1265
|
</View>
|
|
1272
1266
|
}
|
|
1273
1267
|
/>
|
|
1274
|
-
) : (
|
|
1268
|
+
) : isModal ? (
|
|
1275
1269
|
<VideoPlayerModal
|
|
1276
1270
|
isTabletPortrait={isTabletPortrait}
|
|
1277
1271
|
aspectRatio={aspectRatio}
|
|
1278
1272
|
pip={piped}
|
|
1279
1273
|
fullscreen={fullscreen}
|
|
1280
|
-
modal
|
|
1274
|
+
modal
|
|
1281
1275
|
style={{
|
|
1282
1276
|
tabletLandscapeWidth: getTabletWidth(
|
|
1283
1277
|
this.getPluginConfiguration().tablet_landscape_sidebar_width,
|
|
@@ -1315,6 +1309,13 @@ export default class VideoPlayer extends React.Component<
|
|
|
1315
1309
|
</View>
|
|
1316
1310
|
}
|
|
1317
1311
|
/>
|
|
1312
|
+
) : (
|
|
1313
|
+
<View style={style} testID={"player-view"}>
|
|
1314
|
+
<PlayerWrapper
|
|
1315
|
+
{...sharedWrapperViewProps}
|
|
1316
|
+
playerContent={this.renderPlayerContent}
|
|
1317
|
+
/>
|
|
1318
|
+
</View>
|
|
1318
1319
|
);
|
|
1319
1320
|
};
|
|
1320
1321
|
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import React, { PropsWithChildren } from "react";
|
|
2
|
-
import { Image, Animated, StyleSheet, ImageStyle } from "react-native";
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
useModalAnimationContext,
|
|
6
|
-
PlayerAnimationStateEnum,
|
|
7
|
-
DURATION_TO_MINIMIZE,
|
|
8
|
-
DURATION_TO_MAXIMIZE,
|
|
9
|
-
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
10
|
-
import { PlayerAnimationStateT } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation/ModalAnimationContext";
|
|
11
|
-
import { useBackgroundImage } from "@applicaster/zapp-react-native-utils/audioPlayerUtils";
|
|
12
|
-
|
|
13
|
-
import { useChangePlayerState } from "./hooks";
|
|
14
|
-
|
|
15
|
-
const styles = StyleSheet.create({
|
|
16
|
-
container: {
|
|
17
|
-
// we have to specify flex:0 and dimensions to make animation work
|
|
18
|
-
flex: 0,
|
|
19
|
-
aspectRatio: 1,
|
|
20
|
-
},
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
type Props = PropsWithChildren<{
|
|
24
|
-
entry: ZappEntry;
|
|
25
|
-
style?: ImageStyle | ImageStyle[];
|
|
26
|
-
imageWidth: number;
|
|
27
|
-
docked: boolean;
|
|
28
|
-
}>;
|
|
29
|
-
|
|
30
|
-
export const AnimatedImage = ({ entry, style, imageWidth, docked }: Props) => {
|
|
31
|
-
const heightAnim = React.useRef(new Animated.Value(imageWidth)).current;
|
|
32
|
-
|
|
33
|
-
const backgroundImageSource = useBackgroundImage(entry);
|
|
34
|
-
|
|
35
|
-
const { minimisedHeight } = useModalAnimationContext();
|
|
36
|
-
|
|
37
|
-
const changePlayerState$ = useChangePlayerState({ docked });
|
|
38
|
-
|
|
39
|
-
React.useEffect(() => {
|
|
40
|
-
const subscription = changePlayerState$.subscribe({
|
|
41
|
-
next: (nextPlayerState: PlayerAnimationStateT) => {
|
|
42
|
-
if (nextPlayerState === PlayerAnimationStateEnum.minimize) {
|
|
43
|
-
Animated.timing(heightAnim, {
|
|
44
|
-
toValue: minimisedHeight,
|
|
45
|
-
duration: DURATION_TO_MINIMIZE,
|
|
46
|
-
useNativeDriver: false, // width/height can't use native driver
|
|
47
|
-
}).start();
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (nextPlayerState === PlayerAnimationStateEnum.maximize) {
|
|
51
|
-
Animated.timing(heightAnim, {
|
|
52
|
-
toValue: imageWidth,
|
|
53
|
-
duration: DURATION_TO_MAXIMIZE,
|
|
54
|
-
useNativeDriver: false, // width/height can't use native driver
|
|
55
|
-
}).start();
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (nextPlayerState === PlayerAnimationStateEnum.appear_as_docked) {
|
|
59
|
-
// minimize image immediatly
|
|
60
|
-
heightAnim.setValue(minimisedHeight);
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
return () => {
|
|
66
|
-
subscription.unsubscribe();
|
|
67
|
-
};
|
|
68
|
-
}, [changePlayerState$, minimisedHeight, imageWidth]);
|
|
69
|
-
|
|
70
|
-
return (
|
|
71
|
-
<Animated.View
|
|
72
|
-
style={[
|
|
73
|
-
styles.container,
|
|
74
|
-
{
|
|
75
|
-
width: heightAnim,
|
|
76
|
-
},
|
|
77
|
-
]}
|
|
78
|
-
>
|
|
79
|
-
<Image resizeMode="cover" style={style} source={backgroundImageSource} />
|
|
80
|
-
</Animated.View>
|
|
81
|
-
);
|
|
82
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { useChangePlayerState } from "./useChangePlayerState";
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { Subject, Observable } from "rxjs";
|
|
3
|
-
|
|
4
|
-
import { distinctUntilChanged } from "rxjs/operators";
|
|
5
|
-
import { isNil } from "@applicaster/zapp-react-native-utils/utils";
|
|
6
|
-
import { useRefWithInitialValue } from "@applicaster/zapp-react-native-utils/reactHooks/state/useRefWithInitialValue";
|
|
7
|
-
import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";
|
|
8
|
-
import {
|
|
9
|
-
useModalAnimationContext,
|
|
10
|
-
PlayerAnimationStateEnum,
|
|
11
|
-
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
12
|
-
import { PlayerAnimationStateT } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation/ModalAnimationContext";
|
|
13
|
-
|
|
14
|
-
const builder = () => {
|
|
15
|
-
const changePlayerState$ = new Subject<PlayerAnimationStateT>();
|
|
16
|
-
|
|
17
|
-
const emitMaximized = (): void => {
|
|
18
|
-
changePlayerState$.next(PlayerAnimationStateEnum.maximize);
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const emitMinimized = (): void => {
|
|
22
|
-
changePlayerState$.next(PlayerAnimationStateEnum.minimize);
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const emitAppearInDocked = (): void => {
|
|
26
|
-
// if audio_player was presented in docked mode
|
|
27
|
-
changePlayerState$.next(PlayerAnimationStateEnum.appear_as_docked);
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
emitMaximized,
|
|
32
|
-
emitMinimized,
|
|
33
|
-
emitAppearInDocked,
|
|
34
|
-
changePlayerState$: changePlayerState$.pipe(distinctUntilChanged()),
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export const useChangePlayerState = ({
|
|
39
|
-
docked,
|
|
40
|
-
}): Observable<PlayerAnimationStateT> => {
|
|
41
|
-
const changePlayerStateRef =
|
|
42
|
-
useRefWithInitialValue<ReturnType<typeof builder>>(builder);
|
|
43
|
-
|
|
44
|
-
const { playerAnimationState } = useModalAnimationContext();
|
|
45
|
-
|
|
46
|
-
const previousPlayerAnimationState = usePrevious(playerAnimationState);
|
|
47
|
-
|
|
48
|
-
React.useEffect(() => {
|
|
49
|
-
if (
|
|
50
|
-
!docked &&
|
|
51
|
-
isNil(previousPlayerAnimationState) &&
|
|
52
|
-
playerAnimationState === PlayerAnimationStateEnum.minimize
|
|
53
|
-
) {
|
|
54
|
-
changePlayerStateRef.current.emitMinimized();
|
|
55
|
-
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
!docked &&
|
|
61
|
-
isNil(previousPlayerAnimationState) &&
|
|
62
|
-
playerAnimationState === PlayerAnimationStateEnum.maximize
|
|
63
|
-
) {
|
|
64
|
-
changePlayerStateRef.current.emitMaximized();
|
|
65
|
-
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (
|
|
70
|
-
!docked &&
|
|
71
|
-
previousPlayerAnimationState === PlayerAnimationStateEnum.drag_scroll &&
|
|
72
|
-
playerAnimationState === PlayerAnimationStateEnum.maximize
|
|
73
|
-
) {
|
|
74
|
-
changePlayerStateRef.current.emitMaximized();
|
|
75
|
-
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
!docked &&
|
|
81
|
-
previousPlayerAnimationState === PlayerAnimationStateEnum.drag_scroll &&
|
|
82
|
-
playerAnimationState === PlayerAnimationStateEnum.minimize
|
|
83
|
-
) {
|
|
84
|
-
changePlayerStateRef.current.emitMinimized();
|
|
85
|
-
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (
|
|
90
|
-
docked &&
|
|
91
|
-
isNil(previousPlayerAnimationState) &&
|
|
92
|
-
isNil(playerAnimationState)
|
|
93
|
-
) {
|
|
94
|
-
changePlayerStateRef.current.emitAppearInDocked();
|
|
95
|
-
}
|
|
96
|
-
}, [previousPlayerAnimationState, playerAnimationState, docked]);
|
|
97
|
-
|
|
98
|
-
return changePlayerStateRef.current.changePlayerState$;
|
|
99
|
-
};
|