@applicaster/quick-brick-player 13.0.0-rc.94 → 13.0.0-rc.95

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 (29) hide show
  1. package/package.json +5 -5
  2. package/src/Player/AudioLayer/AudioDescription.tsx +4 -6
  3. package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +36 -66
  4. package/src/Player/AudioLayer/Layout/Controls/VerticalGutter.tsx +27 -0
  5. package/src/Player/AudioLayer/Layout/Controls/index.tsx +305 -0
  6. package/src/Player/AudioLayer/Layout/{defaultLayoutsConts.ts → Controls/utils.ts} +2 -12
  7. package/src/Player/AudioLayer/Layout/{DockedLayout.tsx → DockedControls/index.tsx} +28 -39
  8. package/src/Player/AudioLayer/Layout/MobileLayout.tsx +212 -0
  9. package/src/Player/AudioLayer/Layout/PlayerImage/AnimatedImage.tsx +83 -0
  10. package/src/Player/AudioLayer/Layout/PlayerImage/FlexImage.tsx +32 -0
  11. package/src/Player/AudioLayer/Layout/PlayerImage/index.tsx +95 -0
  12. package/src/Player/AudioLayer/Layout/PlayerImage/styles.ts +17 -0
  13. package/src/Player/AudioLayer/Layout/TabletLandscapeLayout.tsx +214 -0
  14. package/src/Player/AudioLayer/Layout/TabletPortraitLayout.tsx +224 -0
  15. package/src/Player/AudioLayer/Layout/TransportInfo/index.tsx +102 -0
  16. package/src/Player/AudioLayer/Layout/VerticalGutter/index.tsx +8 -0
  17. package/src/Player/AudioLayer/Layout/const.ts +20 -0
  18. package/src/Player/AudioLayer/Layout/debug.ts +6 -0
  19. package/src/Player/AudioLayer/Layout/hooks/index.ts +3 -0
  20. package/src/Player/AudioLayer/Layout/hooks/useImageSize.ts +101 -0
  21. package/src/Player/AudioLayer/Layout/hooks/useTransportInfoHeight.ts +54 -0
  22. package/src/Player/AudioLayer/Layout/index.tsx +21 -48
  23. package/src/Player/AudioLayer/Layout/types.ts +7 -0
  24. package/src/Player/AudioLayer/utils.ts +6 -3
  25. package/src/Player/Const/sleepTimerOptions.ts +10 -0
  26. package/src/Player/Const/speedOptions.ts +4 -0
  27. package/src/Player/Utils/Utils.ts +0 -5
  28. package/src/Player/Utils/index.tsx +117 -0
  29. package/src/Player/AudioLayer/Layout/DefaultLayout.tsx +0 -495
@@ -16,43 +16,24 @@ import {
16
16
  } from "@applicaster/quick-brick-mobile-transport-controls";
17
17
 
18
18
  import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
19
+
19
20
  import {
20
21
  AnimationComponent,
21
22
  ComponentAnimationType,
22
- defaultAspectRatioWidth,
23
23
  PlayerAnimationStateEnum,
24
24
  useModalAnimationContext,
25
25
  } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
26
+ import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
26
27
 
27
- type Dimensions = {
28
- width: number;
29
- height: number;
30
- };
31
-
32
- type LayoutProps = {
28
+ type Props = {
33
29
  entry: ZappEntry;
34
- layoutState: {
35
- inline: boolean;
36
- docked: boolean;
37
- isModal: boolean;
38
- };
39
- playerState: {
40
- currentTime: number;
41
- duration: number;
42
- seekableDuration: number;
43
- isLive: boolean;
44
- isPaused: boolean;
45
- isBuffering: boolean;
46
- };
30
+ layoutState: QuickBrickPlayer.LayoutState;
31
+ playerState: QuickBrickPlayer.PlayerState;
47
32
  value: GetValue;
48
- configuration: { [key: string]: any };
49
- onPlaybackButtonPress: () => void;
50
- onRewindButtonPress: () => void;
51
- onForwardButtonPress: () => void;
52
- onPlayerSeek: (targetTime: number) => void;
33
+ configuration: QuickBrickPlayer.AudioPlayerProps["configuration"];
34
+ dimensions: QuickBrickPlayer.AudioPlayerProps["dimensions"];
53
35
  onCloseVideoModal: () => void;
54
- maximiseVideoModal: () => void;
55
- dimensions: Dimensions;
36
+ onPlayerSeek: (targetTime: number) => void;
56
37
  playNextData: PlayNextData;
57
38
  title: string | number;
58
39
  summary: string | number;
@@ -114,17 +95,21 @@ const playerDimensionsStyle = (
114
95
  minimisedHeight: number,
115
96
  addPadding: boolean
116
97
  ): ViewStyle => ({
117
- paddingLeft: addPadding ? defaultAspectRatioWidth(minimisedHeight) || 100 : 0,
98
+ paddingLeft: addPadding ? toNumberWithDefault(100, minimisedHeight) : 0,
118
99
  });
119
100
 
120
- const animatedContainerStyle = (docked: boolean): ViewStyle => ({
101
+ const animatedContainerStyle = (
102
+ docked: boolean,
103
+ progressBarHeight: number
104
+ ): ViewStyle => ({
105
+ top: progressBarHeight,
121
106
  position: "absolute",
122
107
  width: "100%",
123
108
  zIndex: docked ? 1 : -1,
124
109
  });
125
110
 
126
111
  const backgroundImageStyle = (
127
- dimensions: Dimensions,
112
+ dimensions: QuickBrickPlayer.AudioPlayerProps["dimensions"],
128
113
  minimisedHeight: number,
129
114
  isActiveGesture: boolean
130
115
  ): ViewStyle => ({
@@ -190,16 +175,16 @@ const controlButtons = (
190
175
  ];
191
176
  };
192
177
 
193
- export default function DockedLayout(props: LayoutProps) {
178
+ export const DockedControls = (props: Props) => {
194
179
  const {
180
+ entry,
195
181
  layoutState,
182
+ playerState,
196
183
  value,
184
+ configuration,
197
185
  dimensions,
198
- entry,
199
186
  onCloseVideoModal,
200
187
  onPlayerSeek,
201
- playerState,
202
- configuration,
203
188
  playNextData,
204
189
  title,
205
190
  summary,
@@ -207,7 +192,6 @@ export default function DockedLayout(props: LayoutProps) {
207
192
 
208
193
  const isTablet = useIsTablet();
209
194
 
210
- const visible = true;
211
195
  const liveButton = Boolean(value("live_badge_enabled"));
212
196
 
213
197
  const {
@@ -215,6 +199,7 @@ export default function DockedLayout(props: LayoutProps) {
215
199
  setPlayerAnimationState,
216
200
  startComponentsAnimation,
217
201
  minimisedHeight,
202
+ progressBarHeight,
218
203
  } = useModalAnimationContext();
219
204
 
220
205
  const maximazeVideoModal = React.useCallback(
@@ -229,7 +214,11 @@ export default function DockedLayout(props: LayoutProps) {
229
214
  animationType={ComponentAnimationType.componentAppears}
230
215
  style={[
231
216
  styles.container,
232
- animatedContainerStyle(isActiveGesture || layoutState.docked),
217
+ animatedContainerStyle(
218
+ isActiveGesture || layoutState.docked,
219
+ progressBarHeight
220
+ ),
221
+ { height: minimisedHeight },
233
222
  ]}
234
223
  >
235
224
  {!playerState.isLive &&
@@ -238,7 +227,7 @@ export default function DockedLayout(props: LayoutProps) {
238
227
  name={"audio-docked-layout"}
239
228
  content={entry}
240
229
  value={value}
241
- visible={visible}
230
+ visible
242
231
  layoutState={layoutState}
243
232
  onPlayerSeek={onPlayerSeek}
244
233
  playerState={playerState}
@@ -289,7 +278,7 @@ export default function DockedLayout(props: LayoutProps) {
289
278
  >
290
279
  <Controls
291
280
  value={value}
292
- visible={visible}
281
+ visible
293
282
  buttons={R.partial(controlButtons, [
294
283
  playerState.isLive && liveButton,
295
284
  ])}
@@ -308,4 +297,4 @@ export default function DockedLayout(props: LayoutProps) {
308
297
  </View>
309
298
  </AnimationComponent>
310
299
  );
311
- }
300
+ };
@@ -0,0 +1,212 @@
1
+ import * as React from "react";
2
+ import { View, ViewStyle, StyleSheet } from "react-native";
3
+ import {
4
+ useSafeAreaInsets,
5
+ useSafeAreaFrame,
6
+ } from "react-native-safe-area-context";
7
+ import { useModalAnimationContext } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
8
+
9
+ import { MOBILE } from "./const";
10
+ import { TransportInfo } from "./TransportInfo";
11
+ import { PlayerImage } from "./PlayerImage";
12
+ import { Controls } from "./Controls";
13
+
14
+ import { useImageSize, useTransportInfoHeight } from "./hooks";
15
+ import { DockedControls } from "./DockedControls";
16
+ import { withDebug } from "./debug";
17
+
18
+ import { Props } from "./types";
19
+
20
+ const styles = StyleSheet.create({
21
+ container: {
22
+ flex: 1,
23
+ },
24
+
25
+ flex: {
26
+ flex: 1,
27
+ },
28
+
29
+ audioPlayerContainer: {
30
+ flex: 1,
31
+ justifyContent: "flex-start",
32
+
33
+ minHeight: MOBILE.MIN_HEIGHT,
34
+
35
+ marginLeft: MOBILE.MARGIN_HORIZONTAL,
36
+ marginRight: MOBILE.MARGIN_HORIZONTAL,
37
+
38
+ marginTop: MOBILE.MARGIN_TOP,
39
+
40
+ ...withDebug({
41
+ borderWidth: 2,
42
+ borderColor: "blue",
43
+ }),
44
+ },
45
+ });
46
+
47
+ const mobileStyles = ({
48
+ screenHeight,
49
+ topInset,
50
+ bottomInset,
51
+ imageSize,
52
+ }): ViewStyle => ({
53
+ height: screenHeight - topInset - bottomInset,
54
+
55
+ opacity: imageSize ? 1 : 0, // hide content until imageSize is calculated
56
+
57
+ ...withDebug({
58
+ borderWidth: 2,
59
+ borderColor: "magenta",
60
+ }),
61
+ });
62
+
63
+ export function MobileLayout({
64
+ entry,
65
+ layoutState,
66
+ playerState,
67
+ value,
68
+ onPlaybackButtonPress,
69
+ onRewindButtonPress,
70
+ onForwardButtonPress,
71
+ onPlayerSeek,
72
+ onSkipStart,
73
+ onSkipLive,
74
+ onSpeedButtonPress,
75
+ onSleepButtonPress,
76
+ onCloseVideoModal,
77
+ playbackSpeedName,
78
+ playbackSleepName,
79
+ configuration,
80
+ dimensions,
81
+ playNextData,
82
+ cancelSleepTimer,
83
+ sleepTimerDate,
84
+ title,
85
+ summary,
86
+ }: Props) {
87
+ const { docked } = layoutState;
88
+
89
+ const { bottom: bottomInset, top: topInset } = useSafeAreaInsets();
90
+ const { height: screenHeight } = useSafeAreaFrame();
91
+ const { progressBarHeight } = useModalAnimationContext();
92
+
93
+ const { imageSize, onLayout } = useImageSize({ isPortraitMode: true });
94
+
95
+ const { height: transportInfoHeight, onLayout: onTransportInfoLayout } =
96
+ useTransportInfoHeight();
97
+
98
+ return (
99
+ <View
100
+ style={[
101
+ styles.container,
102
+ mobileStyles({
103
+ screenHeight,
104
+ topInset,
105
+ bottomInset,
106
+ imageSize,
107
+ }),
108
+ ]}
109
+ >
110
+ {/* TransportInfo */}
111
+ <TransportInfo
112
+ entry={entry}
113
+ layoutState={layoutState}
114
+ value={value}
115
+ configuration={configuration}
116
+ onLayout={onTransportInfoLayout}
117
+ />
118
+
119
+ {/* TransportInfo */}
120
+
121
+ <View
122
+ style={[
123
+ styles.audioPlayerContainer,
124
+ {
125
+ width: imageSize,
126
+ },
127
+ ]}
128
+ >
129
+ {/* PlayerImage */}
130
+ <View
131
+ style={[
132
+ styles.flex,
133
+ {
134
+ maxHeight: imageSize,
135
+
136
+ ...withDebug({
137
+ borderWidth: 2,
138
+ borderColor: "yellow",
139
+ }),
140
+ },
141
+ ]}
142
+ onLayout={onLayout}
143
+ >
144
+ <PlayerImage
145
+ entry={entry}
146
+ layoutState={layoutState}
147
+ playerState={playerState}
148
+ value={value}
149
+ configuration={configuration}
150
+ onPlayerSeek={onPlayerSeek}
151
+ onCloseVideoModal={onCloseVideoModal}
152
+ dimensions={dimensions}
153
+ playNextData={playNextData}
154
+ title={title}
155
+ summary={summary}
156
+ docked={docked}
157
+ imageWidth={imageSize}
158
+ verticalOffset={
159
+ transportInfoHeight + MOBILE.MARGIN_TOP - progressBarHeight
160
+ }
161
+ />
162
+ </View>
163
+ {/* PlayerImage */}
164
+
165
+ <View
166
+ style={[
167
+ styles.flex,
168
+ {
169
+ marginBottom: MOBILE.MARGIN_BOTTOM,
170
+ },
171
+ ]}
172
+ >
173
+ <Controls
174
+ entry={entry}
175
+ layoutState={layoutState}
176
+ playerState={playerState}
177
+ value={value}
178
+ title={title}
179
+ summary={summary}
180
+ sleepTimerDate={sleepTimerDate}
181
+ cancelSleepTimer={cancelSleepTimer}
182
+ playbackSpeedName={playbackSpeedName}
183
+ playbackSleepName={playbackSleepName}
184
+ onPlaybackButtonPress={onPlaybackButtonPress}
185
+ onRewindButtonPress={onRewindButtonPress}
186
+ onForwardButtonPress={onForwardButtonPress}
187
+ onPlayerSeek={onPlayerSeek}
188
+ onSkipStart={onSkipStart}
189
+ onSkipLive={onSkipLive}
190
+ onSpeedButtonPress={onSpeedButtonPress}
191
+ onSleepButtonPress={onSleepButtonPress}
192
+ docked={docked}
193
+ />
194
+ </View>
195
+ </View>
196
+
197
+ <DockedControls
198
+ entry={entry}
199
+ layoutState={layoutState}
200
+ playerState={playerState}
201
+ value={value}
202
+ configuration={configuration}
203
+ dimensions={dimensions}
204
+ onCloseVideoModal={onCloseVideoModal}
205
+ onPlayerSeek={onPlayerSeek}
206
+ playNextData={playNextData}
207
+ title={title}
208
+ summary={summary}
209
+ />
210
+ </View>
211
+ );
212
+ }
@@ -0,0 +1,83 @@
1
+ import React, { PropsWithChildren } from "react";
2
+ import { ImageBackground, Animated, StyleSheet } from "react-native";
3
+
4
+ import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
5
+ import {
6
+ useModalAnimationContext,
7
+ PlayerAnimationStateEnum,
8
+ DURATION_TO_MINIMIZE,
9
+ DURATION_TO_MAXIMIZE,
10
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
11
+
12
+ const styles = StyleSheet.create({
13
+ container: {
14
+ // we have to specify flex:0 and dimensions to make animation work
15
+ flex: 0,
16
+ aspectRatio: 1,
17
+ },
18
+ });
19
+
20
+ type Props = PropsWithChildren<{
21
+ entry: ZappEntry;
22
+ style?: { [K: string]: any };
23
+ imageStyle?: { [K: string]: any };
24
+ imageWidth: number;
25
+ }>;
26
+
27
+ export const AnimatedImage = ({
28
+ entry,
29
+ style,
30
+ imageStyle,
31
+ imageWidth,
32
+ }: Props) => {
33
+ const heightAnim = React.useRef(new Animated.Value(imageWidth)).current;
34
+
35
+ const source = React.useMemo(
36
+ () => ({ uri: imageSrcFromMediaItem(entry, undefined) }),
37
+ [entry]
38
+ );
39
+
40
+ const { playerAnimationState, minimisedHeight } = useModalAnimationContext();
41
+
42
+ React.useEffect(() => {
43
+ if (playerAnimationState === PlayerAnimationStateEnum.minimize) {
44
+ Animated.timing(heightAnim, {
45
+ toValue: minimisedHeight,
46
+ duration: DURATION_TO_MINIMIZE,
47
+ useNativeDriver: false, // width/height can't use native driver
48
+ }).start();
49
+
50
+ return;
51
+ }
52
+
53
+ if (playerAnimationState === PlayerAnimationStateEnum.maximize) {
54
+ Animated.timing(heightAnim, {
55
+ toValue: imageWidth,
56
+ duration: DURATION_TO_MAXIMIZE,
57
+ useNativeDriver: false, // width/height can't use native driver
58
+ }).start();
59
+ }
60
+ }, [playerAnimationState, imageWidth]);
61
+
62
+ if (!source) return null;
63
+
64
+ return (
65
+ <Animated.View
66
+ style={[
67
+ styles.container,
68
+ {
69
+ width: heightAnim,
70
+ },
71
+ ]}
72
+ >
73
+ <ImageBackground
74
+ resizeMode="cover"
75
+ style={style}
76
+ imageStyle={imageStyle}
77
+ source={source}
78
+ >
79
+ {null}
80
+ </ImageBackground>
81
+ </Animated.View>
82
+ );
83
+ };
@@ -0,0 +1,32 @@
1
+ import * as React from "react";
2
+ import { ImageBackground } from "react-native";
3
+
4
+ import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
5
+
6
+ type Props = {
7
+ entry: ZappEntry;
8
+ style?: { [K: string]: any };
9
+ imageStyle?: { [K: string]: any };
10
+ };
11
+
12
+ const style = { flex: 1, aspectRatio: 1 };
13
+
14
+ export const FlexImage = ({ entry, imageStyle }: Props) => {
15
+ const source = React.useMemo(
16
+ () => ({ uri: imageSrcFromMediaItem(entry, undefined) }),
17
+ [entry]
18
+ );
19
+
20
+ if (!source) return null;
21
+
22
+ return (
23
+ <ImageBackground
24
+ resizeMode="cover"
25
+ style={style}
26
+ source={source}
27
+ imageStyle={imageStyle}
28
+ >
29
+ {null}
30
+ </ImageBackground>
31
+ );
32
+ };
@@ -0,0 +1,95 @@
1
+ import * as React from "react";
2
+
3
+ import {
4
+ AnimationComponent,
5
+ ComponentAnimationType,
6
+ useModalAnimationContext,
7
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
8
+
9
+ import { AnimatedImage } from "./AnimatedImage";
10
+ import { FlexImage } from "./FlexImage";
11
+
12
+ import { styles } from "./styles";
13
+ import { calculateBorderRadius } from "../../utils";
14
+ import { PlayNextData } from "../types";
15
+
16
+ type Props = {
17
+ entry: ZappEntry;
18
+ layoutState: QuickBrickPlayer.LayoutState;
19
+ playerState: QuickBrickPlayer.PlayerState;
20
+ value: GetValue;
21
+ configuration: QuickBrickPlayer.AudioPlayerProps["configuration"];
22
+ dimensions: QuickBrickPlayer.AudioPlayerProps["dimensions"];
23
+ onCloseVideoModal: Callback;
24
+ onPlayerSeek: (targetTime: number) => void;
25
+ playNextData: PlayNextData;
26
+ title: string | number;
27
+ summary: string | number;
28
+ docked: boolean;
29
+ imageWidth: Option<number>;
30
+ verticalOffset: number;
31
+ };
32
+
33
+ export const PlayerImage = (props: Props) => {
34
+ const {
35
+ entry,
36
+ dimensions,
37
+ docked,
38
+ imageWidth,
39
+ verticalOffset,
40
+ configuration,
41
+ } = props;
42
+
43
+ const {
44
+ isActiveGesture,
45
+ startComponentsAnimation,
46
+ playerAnimationState,
47
+ minimisedHeight,
48
+ } = useModalAnimationContext();
49
+
50
+ const borderRadius = calculateBorderRadius(
51
+ docked,
52
+ startComponentsAnimation,
53
+ isActiveGesture,
54
+ playerAnimationState,
55
+ configuration
56
+ );
57
+
58
+ return (
59
+ <AnimationComponent
60
+ animationType={ComponentAnimationType.moveUpComponent}
61
+ additionalData={{
62
+ marginTop: verticalOffset,
63
+ }}
64
+ style={styles.flex}
65
+ >
66
+ <AnimationComponent
67
+ animationType={ComponentAnimationType.moveComponentHorizontal}
68
+ additionalData={{
69
+ // we move image horiontally to this value due artwork is centered
70
+ moveValue: (dimensions.width - minimisedHeight) / 2,
71
+ }}
72
+ style={styles.alignItemsCenter}
73
+ >
74
+ {!imageWidth ? (
75
+ // we render image with flex until we get size of this image
76
+ <FlexImage
77
+ entry={entry}
78
+ style={styles.backgroundImageContainer}
79
+ imageStyle={{ borderRadius }}
80
+ />
81
+ ) : null}
82
+
83
+ {imageWidth ? (
84
+ // we have imageSize here and now we could animate resizing of this image
85
+ <AnimatedImage
86
+ entry={entry}
87
+ style={styles.backgroundImageContainer}
88
+ imageStyle={{ borderRadius }}
89
+ imageWidth={imageWidth}
90
+ />
91
+ ) : null}
92
+ </AnimationComponent>
93
+ </AnimationComponent>
94
+ );
95
+ };
@@ -0,0 +1,17 @@
1
+ import { StyleSheet } from "react-native";
2
+
3
+ export const styles = StyleSheet.create({
4
+ backgroundImageContainer: {
5
+ flex: 1,
6
+ aspectRatio: 1,
7
+ },
8
+
9
+ alignItemsCenter: {
10
+ flex: 1,
11
+ alignItems: "center",
12
+ },
13
+
14
+ flex: {
15
+ flex: 1,
16
+ },
17
+ });