@applicaster/quick-brick-player 13.0.0-alpha.2807014304 → 13.0.0-alpha.2957047383
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 -6
- package/src/Player/AudioLayer/AudioDescription.tsx +4 -6
- package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +49 -80
- package/src/Player/AudioLayer/Layout/Controls/VerticalGutter.tsx +27 -0
- package/src/Player/AudioLayer/Layout/Controls/index.tsx +305 -0
- package/src/Player/AudioLayer/Layout/{defaultLayoutsConts.ts → Controls/utils.ts} +2 -12
- package/src/Player/AudioLayer/Layout/{DockedLayout.tsx → DockedControls/index.tsx} +28 -39
- package/src/Player/AudioLayer/Layout/MobileLayout.tsx +212 -0
- package/src/Player/AudioLayer/Layout/PlayerImage/AnimatedImage.tsx +83 -0
- package/src/Player/AudioLayer/Layout/PlayerImage/FlexImage.tsx +32 -0
- package/src/Player/AudioLayer/Layout/PlayerImage/index.tsx +95 -0
- package/src/Player/AudioLayer/Layout/PlayerImage/styles.ts +17 -0
- package/src/Player/AudioLayer/Layout/TabletLandscapeLayout.tsx +214 -0
- package/src/Player/AudioLayer/Layout/TabletPortraitLayout.tsx +224 -0
- package/src/Player/AudioLayer/Layout/TransportInfo/index.tsx +102 -0
- package/src/Player/AudioLayer/Layout/VerticalGutter/index.tsx +8 -0
- package/src/Player/AudioLayer/Layout/const.ts +20 -0
- package/src/Player/AudioLayer/Layout/debug.ts +6 -0
- package/src/Player/AudioLayer/Layout/hooks/index.ts +3 -0
- package/src/Player/AudioLayer/Layout/hooks/useImageSize.ts +100 -0
- package/src/Player/AudioLayer/Layout/hooks/useTransportInfoHeight.ts +54 -0
- package/src/Player/AudioLayer/Layout/index.tsx +21 -48
- package/src/Player/AudioLayer/Layout/types.ts +7 -0
- package/src/Player/AudioLayer/utils.ts +6 -3
- package/src/Player/Const/sleepTimerOptions.ts +10 -0
- package/src/Player/Const/speedOptions.ts +4 -0
- package/src/Player/Utils/Utils.ts +0 -5
- package/src/Player/Utils/index.tsx +117 -0
- package/src/Player/index.tsx +4 -1
- package/src/Player/AudioLayer/Layout/DefaultLayout.tsx +0 -495
- package/src/Player/Helpers/VideoResizeMode.ts +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/quick-brick-player",
|
|
3
|
-
"version": "13.0.0-alpha.
|
|
3
|
+
"version": "13.0.0-alpha.2957047383",
|
|
4
4
|
"description": "Quick Brick Player",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -35,12 +35,11 @@
|
|
|
35
35
|
},
|
|
36
36
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@applicaster/quick-brick-mobile-transport-controls": "13.0.0-rc.
|
|
38
|
+
"@applicaster/quick-brick-mobile-transport-controls": "13.0.0-rc.95",
|
|
39
39
|
"@applicaster/quick-brick-tv-transport-controls": "13.0.0-rc.56",
|
|
40
|
-
"@applicaster/zapp-react-native-tvos-app": "13.0.0-alpha.
|
|
41
|
-
"@applicaster/zapp-react-native-ui-components": "13.0.0-alpha.
|
|
42
|
-
"@applicaster/zapp-react-native-utils": "13.0.0-alpha.
|
|
43
|
-
"keymirror": "^0.1.1",
|
|
40
|
+
"@applicaster/zapp-react-native-tvos-app": "13.0.0-alpha.2957047383",
|
|
41
|
+
"@applicaster/zapp-react-native-ui-components": "13.0.0-alpha.2957047383",
|
|
42
|
+
"@applicaster/zapp-react-native-utils": "13.0.0-alpha.2957047383",
|
|
44
43
|
"query-string": "7.1.3",
|
|
45
44
|
"react-native-web-linear-gradient": "1.1.2",
|
|
46
45
|
"shaka-player": "4.3.5",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { Text, TextStyle
|
|
2
|
+
import { Text, TextStyle } from "react-native";
|
|
3
3
|
import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
4
4
|
|
|
5
5
|
type AudioDescriptionProps = {
|
|
@@ -13,8 +13,6 @@ type AudioDescriptionProps = {
|
|
|
13
13
|
value: GetValue;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
const containerStyles = { paddingTop: 32, paddingBottom: 12 };
|
|
17
|
-
|
|
18
16
|
const titleStyles = (value: GetValue, prefix: string): TextStyle => ({
|
|
19
17
|
textAlign: "left",
|
|
20
18
|
color: value(`${prefix}title_color`),
|
|
@@ -83,13 +81,13 @@ export const AudioDescription = (props: AudioDescriptionProps) => {
|
|
|
83
81
|
const prefix = layoutState.docked ? "docked_player_" : "";
|
|
84
82
|
|
|
85
83
|
return (
|
|
86
|
-
|
|
84
|
+
<>
|
|
87
85
|
<Text style={titleStyles(value, prefix)} numberOfLines={1}>
|
|
88
86
|
{title}
|
|
89
87
|
</Text>
|
|
90
|
-
<Text style={summaryStyles(value, prefix)} numberOfLines={
|
|
88
|
+
<Text style={summaryStyles(value, prefix)} numberOfLines={1}>
|
|
91
89
|
{summary}
|
|
92
90
|
</Text>
|
|
93
|
-
|
|
91
|
+
</>
|
|
94
92
|
);
|
|
95
93
|
};
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
StyleSheet,
|
|
4
|
+
ViewStyle,
|
|
5
|
+
View,
|
|
6
|
+
Dimensions as ScreenDimensions,
|
|
7
|
+
} from "react-native";
|
|
3
8
|
|
|
4
9
|
import { SafeAreaView, Edge } from "react-native-safe-area-context";
|
|
5
10
|
import { usePlayerState } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerState";
|
|
@@ -18,22 +23,22 @@ import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
|
18
23
|
import { useEffect, useState } from "react";
|
|
19
24
|
import { MagicBG } from "./MagicBG";
|
|
20
25
|
import { formatSpeed } from "./utils";
|
|
21
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
addQueryParam,
|
|
28
|
+
getBottomSheetModalDefaultItemIcon,
|
|
29
|
+
getBottomSheetModalIconProps,
|
|
30
|
+
getBottomSheetModalItemProps,
|
|
31
|
+
getBottomSheetModalLabelProps,
|
|
32
|
+
getBottomSheetModalSelectedItemIcon,
|
|
33
|
+
} from "../Utils";
|
|
22
34
|
import { useTitleSummaryOverlay } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerTitleSummaryOverlay";
|
|
23
35
|
import {
|
|
24
36
|
AnimationComponent,
|
|
25
37
|
ComponentAnimationType,
|
|
26
38
|
AnimatedScrollModal,
|
|
27
39
|
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
28
|
-
import { PADDING_HORIZONTAL } from "./Layout/
|
|
40
|
+
import { PADDING_HORIZONTAL } from "./Layout/const";
|
|
29
41
|
import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
30
|
-
import {
|
|
31
|
-
speed_0_8,
|
|
32
|
-
speed_1,
|
|
33
|
-
speed_1_2,
|
|
34
|
-
speed_1_5,
|
|
35
|
-
speed_2,
|
|
36
|
-
} from "@applicaster/quick-brick-mobile-transport-controls";
|
|
37
42
|
import {
|
|
38
43
|
SleepTimerController,
|
|
39
44
|
SleepTimerState,
|
|
@@ -42,6 +47,9 @@ import {
|
|
|
42
47
|
import { SpeedController, PlaybackSpeed, DEFAULT_SPEED } from "./SpeedManager";
|
|
43
48
|
import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
|
|
44
49
|
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
50
|
+
import { openBottomSheetModal } from "@applicaster/zapp-react-native-utils/modalState";
|
|
51
|
+
import { sleepTimerOptions } from "../Const/sleepTimerOptions";
|
|
52
|
+
import { speedOptions } from "../Const/speedOptions";
|
|
45
53
|
|
|
46
54
|
const { log_debug } = createLogger({
|
|
47
55
|
subsystem: "UIComponent",
|
|
@@ -83,51 +91,17 @@ type Props = {
|
|
|
83
91
|
|
|
84
92
|
const { width: SCREEN_WIDTH } = ScreenDimensions.get("screen");
|
|
85
93
|
|
|
86
|
-
const Styles = {
|
|
87
|
-
mainContainer: (style: ViewStyle) => {
|
|
88
|
-
const combinedStyle: ViewStyle = {
|
|
89
|
-
flex: 1,
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
...combinedStyle,
|
|
94
|
-
...style,
|
|
95
|
-
};
|
|
96
|
-
},
|
|
94
|
+
const Styles = StyleSheet.create({
|
|
97
95
|
playerContainer: {
|
|
98
96
|
flex: 1,
|
|
99
97
|
},
|
|
100
98
|
paddingHorizontal: { paddingTop: 0, paddingHorizontal: PADDING_HORIZONTAL },
|
|
101
|
-
|
|
102
|
-
controlsMobile: {
|
|
103
|
-
height: 50,
|
|
104
|
-
marginHorizontal: 24,
|
|
105
|
-
},
|
|
106
|
-
controlsTablet: {
|
|
107
|
-
height: 96,
|
|
108
|
-
marginHorizontal: 24,
|
|
109
|
-
},
|
|
110
99
|
playerDetails: {
|
|
111
100
|
marginTop: 60,
|
|
112
101
|
alignSelf: "center",
|
|
113
102
|
width: SCREEN_WIDTH - PADDING_HORIZONTAL * 2,
|
|
114
103
|
},
|
|
115
|
-
|
|
116
|
-
linearGradient: {
|
|
117
|
-
flex: 1,
|
|
118
|
-
},
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
const sleeps = [
|
|
122
|
-
// { label: "End of episode", value: "episode" },
|
|
123
|
-
{ label: "5 minutes", value: 5 },
|
|
124
|
-
{ label: "10 minutes", value: 10 },
|
|
125
|
-
{ label: "15 minutes", value: 15 },
|
|
126
|
-
{ label: "30 minutes", value: 30 },
|
|
127
|
-
{ label: "45 minutes", value: 45 },
|
|
128
|
-
{ label: "1 hour", value: 60 },
|
|
129
|
-
{ label: "2 hours", value: 120 },
|
|
130
|
-
];
|
|
104
|
+
});
|
|
131
105
|
|
|
132
106
|
const edges: Edge[] = ["right", "left", "top"];
|
|
133
107
|
|
|
@@ -147,24 +121,11 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
147
121
|
isTabletPortrait,
|
|
148
122
|
} = props;
|
|
149
123
|
|
|
150
|
-
const speeds = [
|
|
151
|
-
{ asset: configuration["speed_0_8"] || speed_0_8, value: 0.8 },
|
|
152
|
-
{ asset: configuration["speed_1"] || speed_1, value: 1.0 },
|
|
153
|
-
{ asset: configuration["speed_1_2"] || speed_1_2, value: 1.2 },
|
|
154
|
-
{ asset: configuration["speed_1_5"] || speed_1_5, value: 1.5 },
|
|
155
|
-
{ asset: configuration["speed_2"] || speed_2, value: 2.0 },
|
|
156
|
-
];
|
|
157
|
-
|
|
158
124
|
const isTablet = useIsTablet();
|
|
159
125
|
|
|
160
126
|
const isTabletLandscape = !isTV() && isTablet && !isTabletPortrait;
|
|
161
127
|
|
|
162
|
-
// TODO: Have generic place for live url from configuration. Currently its hardcoded for client.
|
|
163
|
-
|
|
164
|
-
const feedUrl = getLiveUrl(entry?.extensions?.live?.channel_src ?? "");
|
|
165
|
-
|
|
166
128
|
const { title, summary } = useTitleSummaryOverlay(
|
|
167
|
-
feedUrl,
|
|
168
129
|
entry,
|
|
169
130
|
playerState?.isLive,
|
|
170
131
|
configuration?.audio_live_player_update_interval
|
|
@@ -202,8 +163,6 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
202
163
|
height: 0,
|
|
203
164
|
});
|
|
204
165
|
|
|
205
|
-
const navigator = useNavigation();
|
|
206
|
-
|
|
207
166
|
const { maximiseVideoModal } = useNavigation();
|
|
208
167
|
|
|
209
168
|
const onLayout = (event: any) => {
|
|
@@ -293,44 +252,56 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
293
252
|
);
|
|
294
253
|
|
|
295
254
|
const onSpeedButtonPress = React.useCallback(() => {
|
|
296
|
-
|
|
255
|
+
openBottomSheetModal({
|
|
297
256
|
modalBottomSheetContentProps: {
|
|
298
|
-
items:
|
|
257
|
+
items: speedOptions,
|
|
299
258
|
current_selection: playbackSpeed,
|
|
300
259
|
onPress: onSpeedSelect,
|
|
301
260
|
title: configuration["playback_speed_title"],
|
|
261
|
+
itemProps: getBottomSheetModalItemProps,
|
|
262
|
+
iconProps: getBottomSheetModalIconProps,
|
|
263
|
+
labelProps: getBottomSheetModalLabelProps,
|
|
264
|
+
getSelectedItemIcon: getBottomSheetModalSelectedItemIcon,
|
|
265
|
+
getDefaultItemIcon: getBottomSheetModalDefaultItemIcon,
|
|
266
|
+
iconPlacement: "right",
|
|
302
267
|
},
|
|
303
268
|
options: {
|
|
304
|
-
supportedOrientations: [
|
|
269
|
+
supportedOrientations: [
|
|
270
|
+
"portrait",
|
|
271
|
+
"landscape",
|
|
272
|
+
"portrait-upside-down",
|
|
273
|
+
],
|
|
305
274
|
},
|
|
306
275
|
});
|
|
307
|
-
}, [
|
|
308
|
-
playbackSpeed,
|
|
309
|
-
setPlaybackSpeed,
|
|
310
|
-
navigator,
|
|
311
|
-
onSpeedSelect,
|
|
312
|
-
configuration["playback_speed_title"],
|
|
313
|
-
]);
|
|
276
|
+
}, [playbackSpeed, onSpeedSelect, configuration["playback_speed_title"]]);
|
|
314
277
|
|
|
315
278
|
const onSleepButtonPress = React.useCallback(() => {
|
|
316
|
-
|
|
279
|
+
openBottomSheetModal({
|
|
317
280
|
modalBottomSheetContentProps: {
|
|
318
281
|
items: [
|
|
319
282
|
{
|
|
320
283
|
label: configuration["playback_sleep_off_label"],
|
|
321
284
|
value: 0,
|
|
322
285
|
},
|
|
323
|
-
].concat(
|
|
286
|
+
].concat(sleepTimerOptions),
|
|
324
287
|
current_selection: sleepTimerState.timer,
|
|
325
288
|
onPress: onSleepSelect,
|
|
326
289
|
title: configuration["playback_sleep_title"],
|
|
290
|
+
itemProps: getBottomSheetModalItemProps,
|
|
291
|
+
iconProps: getBottomSheetModalIconProps,
|
|
292
|
+
labelProps: getBottomSheetModalLabelProps,
|
|
293
|
+
getSelectedItemIcon: getBottomSheetModalSelectedItemIcon,
|
|
294
|
+
iconPlacement: "right",
|
|
327
295
|
},
|
|
328
296
|
options: {
|
|
329
|
-
supportedOrientations: [
|
|
297
|
+
supportedOrientations: [
|
|
298
|
+
"portrait",
|
|
299
|
+
"landscape",
|
|
300
|
+
"portrait-upside-down",
|
|
301
|
+
],
|
|
330
302
|
},
|
|
331
303
|
});
|
|
332
304
|
}, [
|
|
333
|
-
navigator,
|
|
334
305
|
sleepTimerState.timer,
|
|
335
306
|
configuration["playback_sleep_title"],
|
|
336
307
|
configuration["playback_sleep_off_label"],
|
|
@@ -364,11 +335,9 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
364
335
|
player?.seekTo(0);
|
|
365
336
|
}, [player]);
|
|
366
337
|
|
|
367
|
-
const ContainerView = SafeAreaView;
|
|
368
|
-
|
|
369
338
|
const MainContent = React.useMemo(() => {
|
|
370
339
|
return (
|
|
371
|
-
<
|
|
340
|
+
<SafeAreaView style={[style, Styles.playerContainer]} edges={edges}>
|
|
372
341
|
<AnimationComponent
|
|
373
342
|
animationType={ComponentAnimationType.moveUpComponent}
|
|
374
343
|
additionalData={{ saveArea: true }}
|
|
@@ -412,18 +381,18 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
412
381
|
animationType={ComponentAnimationType.componentFade}
|
|
413
382
|
>
|
|
414
383
|
<PlayerDetails
|
|
384
|
+
isAudioPlayer
|
|
415
385
|
configuration={configuration}
|
|
416
386
|
style={Styles.playerDetails}
|
|
417
387
|
entry={entry}
|
|
418
388
|
isTabletLandscape={isTabletLandscape}
|
|
419
|
-
isAudioPlayer={true}
|
|
420
389
|
/>
|
|
421
390
|
</AnimationComponent>
|
|
422
391
|
</View>
|
|
423
392
|
</View>
|
|
424
393
|
</AnimatedScrollModal>
|
|
425
394
|
</AnimationComponent>
|
|
426
|
-
</
|
|
395
|
+
</SafeAreaView>
|
|
427
396
|
);
|
|
428
397
|
}, [
|
|
429
398
|
docked,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { View, StyleSheet } from "react-native";
|
|
3
|
+
|
|
4
|
+
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
5
|
+
|
|
6
|
+
import { TABLET_VERTICAL_SEPARATOR_HEIGHT } from "../const";
|
|
7
|
+
|
|
8
|
+
import { withDebug } from "../debug";
|
|
9
|
+
|
|
10
|
+
const styles = StyleSheet.create({
|
|
11
|
+
flexingHeight: {
|
|
12
|
+
flex: 1,
|
|
13
|
+
|
|
14
|
+
...withDebug({ backgroundColor: "red" }),
|
|
15
|
+
},
|
|
16
|
+
fixedHeight: {
|
|
17
|
+
height: TABLET_VERTICAL_SEPARATOR_HEIGHT,
|
|
18
|
+
|
|
19
|
+
...withDebug({ backgroundColor: "red" }),
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const VerticalGutter = () => {
|
|
24
|
+
const isTablet = useIsTablet();
|
|
25
|
+
|
|
26
|
+
return <View style={isTablet ? styles.fixedHeight : styles.flexingHeight} />;
|
|
27
|
+
};
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { StyleSheet } from "react-native";
|
|
3
|
+
|
|
4
|
+
import { dayjs } from "@applicaster/zapp-react-native-utils/dateUtils";
|
|
5
|
+
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
6
|
+
import {
|
|
7
|
+
Controls as MobileTransportControls,
|
|
8
|
+
CountDown,
|
|
9
|
+
ProgressBar,
|
|
10
|
+
} from "@applicaster/quick-brick-mobile-transport-controls";
|
|
11
|
+
import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
AnimationComponent,
|
|
15
|
+
ComponentAnimationType,
|
|
16
|
+
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
17
|
+
|
|
18
|
+
import { AudioDescription } from "../../AudioDescription";
|
|
19
|
+
import { isValidDate } from "../../../Utils";
|
|
20
|
+
import { VerticalGutter } from "./VerticalGutter";
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
forwardButton,
|
|
24
|
+
playbackButton,
|
|
25
|
+
rewindButton,
|
|
26
|
+
skipToLiveButton,
|
|
27
|
+
skipToStartButton,
|
|
28
|
+
sleepButton,
|
|
29
|
+
speedButton,
|
|
30
|
+
} from "./utils";
|
|
31
|
+
|
|
32
|
+
const styles = StyleSheet.create({
|
|
33
|
+
flex: {
|
|
34
|
+
flex: 1,
|
|
35
|
+
},
|
|
36
|
+
nonFlex: {
|
|
37
|
+
flex: 0,
|
|
38
|
+
},
|
|
39
|
+
progressBar: {
|
|
40
|
+
flex: 0,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const controlsStyles = ({
|
|
45
|
+
isTablet,
|
|
46
|
+
isLiveShowFullControls,
|
|
47
|
+
}: {
|
|
48
|
+
isTablet: boolean;
|
|
49
|
+
isLiveShowFullControls: boolean;
|
|
50
|
+
}) => ({
|
|
51
|
+
height: isTablet ? undefined : 50,
|
|
52
|
+
marginHorizontal: isTablet ? undefined : 10,
|
|
53
|
+
justifyContent: isLiveShowFullControls ? "space-between" : "space-evenly",
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
type Props = {
|
|
57
|
+
entry: ZappEntry;
|
|
58
|
+
layoutState: QuickBrickPlayer.LayoutState;
|
|
59
|
+
playerState: QuickBrickPlayer.PlayerState;
|
|
60
|
+
value: GetValue;
|
|
61
|
+
title: string | number;
|
|
62
|
+
summary: string | number;
|
|
63
|
+
sleepTimerDate: number;
|
|
64
|
+
cancelSleepTimer: Callback;
|
|
65
|
+
|
|
66
|
+
playbackSpeedName: string;
|
|
67
|
+
playbackSleepName: string;
|
|
68
|
+
|
|
69
|
+
onPlaybackButtonPress: Callback;
|
|
70
|
+
onRewindButtonPress: Callback;
|
|
71
|
+
onForwardButtonPress: Callback;
|
|
72
|
+
onPlayerSeek: (targetTime: number) => void;
|
|
73
|
+
onSkipStart: Callback;
|
|
74
|
+
onSkipLive: Callback;
|
|
75
|
+
onSpeedButtonPress: Callback;
|
|
76
|
+
onSleepButtonPress: Callback;
|
|
77
|
+
|
|
78
|
+
docked: boolean;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const Controls = (props: Props) => {
|
|
82
|
+
const {
|
|
83
|
+
entry,
|
|
84
|
+
layoutState,
|
|
85
|
+
playerState,
|
|
86
|
+
value,
|
|
87
|
+
title,
|
|
88
|
+
summary,
|
|
89
|
+
sleepTimerDate,
|
|
90
|
+
cancelSleepTimer,
|
|
91
|
+
|
|
92
|
+
playbackSpeedName,
|
|
93
|
+
playbackSleepName,
|
|
94
|
+
|
|
95
|
+
onPlaybackButtonPress,
|
|
96
|
+
onRewindButtonPress,
|
|
97
|
+
onForwardButtonPress,
|
|
98
|
+
onPlayerSeek,
|
|
99
|
+
onSkipStart,
|
|
100
|
+
onSkipLive,
|
|
101
|
+
onSpeedButtonPress,
|
|
102
|
+
onSleepButtonPress,
|
|
103
|
+
|
|
104
|
+
docked,
|
|
105
|
+
} = props;
|
|
106
|
+
|
|
107
|
+
const isTablet = useIsTablet();
|
|
108
|
+
const controller = playerManager.getActivePlayer();
|
|
109
|
+
let showFullControls = !controller?.isAd();
|
|
110
|
+
const liveButton = Boolean(value("live_badge_enabled"));
|
|
111
|
+
|
|
112
|
+
const isLiveContent = playerState.isLive;
|
|
113
|
+
|
|
114
|
+
if (isLiveContent) {
|
|
115
|
+
const seekEnabled = Boolean(value("liveSeekingEnabled"));
|
|
116
|
+
|
|
117
|
+
showFullControls = !!(controller?.hasSeekableDuration?.() && seekEnabled);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const sleepTitle = value("playback_sleep_countdown_label");
|
|
121
|
+
|
|
122
|
+
const [timeLeft, setTimeLeft] = React.useState(
|
|
123
|
+
sleepTimerDate !== 0 ? sleepTimerDate - Date.now() : 0
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const showCounter = React.useMemo(() => sleepTimerDate > 0, [sleepTimerDate]);
|
|
127
|
+
|
|
128
|
+
React.useEffect(() => {
|
|
129
|
+
if (showCounter && sleepTimerDate !== 0) {
|
|
130
|
+
setTimeLeft(sleepTimerDate - Date.now());
|
|
131
|
+
|
|
132
|
+
const intervalId = setInterval(() => {
|
|
133
|
+
setTimeLeft(() => {
|
|
134
|
+
const newTimeLeft = sleepTimerDate - Date.now();
|
|
135
|
+
|
|
136
|
+
if (newTimeLeft <= 0) {
|
|
137
|
+
clearInterval(intervalId);
|
|
138
|
+
|
|
139
|
+
cancelSleepTimer();
|
|
140
|
+
|
|
141
|
+
return 0;
|
|
142
|
+
} else {
|
|
143
|
+
return newTimeLeft;
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}, 1000);
|
|
147
|
+
|
|
148
|
+
return () => clearInterval(intervalId);
|
|
149
|
+
} else {
|
|
150
|
+
setTimeLeft(0);
|
|
151
|
+
}
|
|
152
|
+
}, [sleepTimerDate, showCounter, cancelSleepTimer]);
|
|
153
|
+
|
|
154
|
+
const startTime = entry.extensions?.live?.start_time;
|
|
155
|
+
const endTime = entry.extensions?.live?.end_time;
|
|
156
|
+
|
|
157
|
+
const hasValidStartTime = isValidDate(startTime);
|
|
158
|
+
const hasValidEndTime = isValidDate(endTime);
|
|
159
|
+
|
|
160
|
+
const isDateBasedLiveContent =
|
|
161
|
+
isLiveContent && hasValidStartTime && hasValidEndTime;
|
|
162
|
+
|
|
163
|
+
const needsToShowProgressBar =
|
|
164
|
+
(isLiveContent &&
|
|
165
|
+
(Boolean(value("liveSeekingEnabled")) || isDateBasedLiveContent)) ||
|
|
166
|
+
!isLiveContent;
|
|
167
|
+
|
|
168
|
+
const startTimestamp = hasValidStartTime
|
|
169
|
+
? dayjs.utc(startTime!).valueOf()
|
|
170
|
+
: undefined;
|
|
171
|
+
|
|
172
|
+
const endTimestamp = hasValidEndTime
|
|
173
|
+
? dayjs.utc(endTime!).valueOf()
|
|
174
|
+
: undefined;
|
|
175
|
+
|
|
176
|
+
const isLiveShowFullControls = showFullControls && isLiveContent;
|
|
177
|
+
|
|
178
|
+
const controlButtons = React.useCallback((): Array<ControlsButton> => {
|
|
179
|
+
const play = playbackButton({
|
|
180
|
+
playerState,
|
|
181
|
+
liveButton,
|
|
182
|
+
isTablet,
|
|
183
|
+
isLiveShowFullControls,
|
|
184
|
+
onPress: onPlaybackButtonPress,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
if (!showFullControls) {
|
|
188
|
+
return [play];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const rewind = rewindButton({
|
|
192
|
+
isTablet,
|
|
193
|
+
onPress: controller?.hasSeekableDuration?.() ? onRewindButtonPress : null,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const forward = forwardButton({
|
|
197
|
+
isTablet,
|
|
198
|
+
onPress: controller?.hasSeekableDuration?.()
|
|
199
|
+
? onForwardButtonPress
|
|
200
|
+
: null,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const speed = speedButton({
|
|
204
|
+
isTablet,
|
|
205
|
+
onPress: onSpeedButtonPress,
|
|
206
|
+
name: playbackSpeedName,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const sleep = sleepButton({
|
|
210
|
+
isTablet,
|
|
211
|
+
onPress: onSleepButtonPress,
|
|
212
|
+
name: playbackSleepName,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
if (isLiveContent) {
|
|
216
|
+
const skipToStart = skipToStartButton({
|
|
217
|
+
isTablet,
|
|
218
|
+
onPress: onSkipStart,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
const skipToLive = skipToLiveButton({
|
|
222
|
+
isTablet,
|
|
223
|
+
onPress: onSkipLive,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
return [skipToStart, rewind, play, forward, skipToLive];
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return [speed, rewind, play, forward, sleep];
|
|
230
|
+
}, [
|
|
231
|
+
playerState,
|
|
232
|
+
liveButton,
|
|
233
|
+
isTablet,
|
|
234
|
+
onPlaybackButtonPress,
|
|
235
|
+
onRewindButtonPress,
|
|
236
|
+
onForwardButtonPress,
|
|
237
|
+
onSpeedButtonPress,
|
|
238
|
+
showFullControls,
|
|
239
|
+
onSkipStart,
|
|
240
|
+
onSkipLive,
|
|
241
|
+
isLiveContent,
|
|
242
|
+
playbackSpeedName,
|
|
243
|
+
playbackSleepName,
|
|
244
|
+
controller,
|
|
245
|
+
isLiveShowFullControls,
|
|
246
|
+
]);
|
|
247
|
+
|
|
248
|
+
if (docked) {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return (
|
|
253
|
+
<AnimationComponent
|
|
254
|
+
animationType={ComponentAnimationType.componentFade}
|
|
255
|
+
style={isTablet ? styles.nonFlex : styles.flex}
|
|
256
|
+
>
|
|
257
|
+
<VerticalGutter />
|
|
258
|
+
|
|
259
|
+
<AudioDescription
|
|
260
|
+
title={title}
|
|
261
|
+
summary={summary}
|
|
262
|
+
value={value}
|
|
263
|
+
layoutState={layoutState}
|
|
264
|
+
/>
|
|
265
|
+
|
|
266
|
+
<VerticalGutter />
|
|
267
|
+
|
|
268
|
+
{needsToShowProgressBar ? (
|
|
269
|
+
<>
|
|
270
|
+
<ProgressBar
|
|
271
|
+
name={"audio-default-layout"}
|
|
272
|
+
content={entry}
|
|
273
|
+
value={value}
|
|
274
|
+
visible
|
|
275
|
+
layoutState={layoutState}
|
|
276
|
+
onPlayerSeek={onPlayerSeek}
|
|
277
|
+
playerState={playerState}
|
|
278
|
+
disableFullscreen
|
|
279
|
+
sliderStyle={2}
|
|
280
|
+
startTimestamp={startTimestamp}
|
|
281
|
+
endTimestamp={endTimestamp}
|
|
282
|
+
isDateBasedLiveContent={isDateBasedLiveContent}
|
|
283
|
+
onSkipLive={onSkipLive}
|
|
284
|
+
progressBarStyles={styles.progressBar}
|
|
285
|
+
/>
|
|
286
|
+
|
|
287
|
+
{showCounter && timeLeft >= 0 ? (
|
|
288
|
+
<CountDown timeLeft={timeLeft} value={value} title={sleepTitle} />
|
|
289
|
+
) : null}
|
|
290
|
+
|
|
291
|
+
<VerticalGutter />
|
|
292
|
+
</>
|
|
293
|
+
) : null}
|
|
294
|
+
|
|
295
|
+
<MobileTransportControls
|
|
296
|
+
value={value}
|
|
297
|
+
visible={true}
|
|
298
|
+
keepAlive={null}
|
|
299
|
+
buttons={controlButtons}
|
|
300
|
+
style={controlsStyles({ isTablet, isLiveShowFullControls })}
|
|
301
|
+
playerState={playerState}
|
|
302
|
+
/>
|
|
303
|
+
</AnimationComponent>
|
|
304
|
+
);
|
|
305
|
+
};
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
type PlayerState = {
|
|
2
|
-
currentTime: number;
|
|
3
|
-
duration: number;
|
|
4
|
-
seekableDuration: number;
|
|
5
|
-
isLive: boolean;
|
|
6
|
-
isPaused: boolean;
|
|
7
|
-
isBuffering: boolean;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
1
|
const rewindButtonSize = (isTablet: boolean): keyof ButtonSizeStyles =>
|
|
11
2
|
isTablet ? "big" : "smallMargins";
|
|
12
3
|
|
|
@@ -19,8 +10,6 @@ const playbackButtonMargins = (
|
|
|
19
10
|
): number =>
|
|
20
11
|
isLiveShowFullControls ? (isTablet ? 65 : 10) : isTablet ? undefined : 40;
|
|
21
12
|
|
|
22
|
-
export const PADDING_HORIZONTAL = 15;
|
|
23
|
-
|
|
24
13
|
export const playbackButton = ({
|
|
25
14
|
playerState,
|
|
26
15
|
liveButton,
|
|
@@ -28,7 +17,7 @@ export const playbackButton = ({
|
|
|
28
17
|
isLiveShowFullControls,
|
|
29
18
|
onPress,
|
|
30
19
|
}: {
|
|
31
|
-
playerState: PlayerState;
|
|
20
|
+
playerState: QuickBrickPlayer.PlayerState;
|
|
32
21
|
liveButton: boolean;
|
|
33
22
|
isTablet: boolean;
|
|
34
23
|
isLiveShowFullControls: boolean;
|
|
@@ -57,6 +46,7 @@ type ButtonsProps = {
|
|
|
57
46
|
style?: any;
|
|
58
47
|
name?: string;
|
|
59
48
|
};
|
|
49
|
+
|
|
60
50
|
type ButtonsByNameProps = ButtonsProps & { name?: string };
|
|
61
51
|
|
|
62
52
|
const rewindButtonByName = ({
|