@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.
- package/package.json +5 -5
- package/src/Player/AudioLayer/AudioDescription.tsx +4 -6
- package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +36 -66
- 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 +101 -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/AudioLayer/Layout/DefaultLayout.tsx +0 -495
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/quick-brick-player",
|
|
3
|
-
"version": "13.0.0-rc.
|
|
3
|
+
"version": "13.0.0-rc.95",
|
|
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": "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-rc.
|
|
41
|
-
"@applicaster/zapp-react-native-ui-components": "13.0.0-rc.
|
|
42
|
-
"@applicaster/zapp-react-native-utils": "13.0.0-rc.
|
|
40
|
+
"@applicaster/zapp-react-native-tvos-app": "13.0.0-rc.95",
|
|
41
|
+
"@applicaster/zapp-react-native-ui-components": "13.0.0-rc.95",
|
|
42
|
+
"@applicaster/zapp-react-native-utils": "13.0.0-rc.95",
|
|
43
43
|
"query-string": "7.1.3",
|
|
44
44
|
"react-native-web-linear-gradient": "1.1.2",
|
|
45
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,16 +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
|
-
|
|
31
42
|
import {
|
|
32
43
|
SleepTimerController,
|
|
33
44
|
SleepTimerState,
|
|
@@ -37,6 +48,8 @@ import { SpeedController, PlaybackSpeed, DEFAULT_SPEED } from "./SpeedManager";
|
|
|
37
48
|
import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
|
|
38
49
|
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
39
50
|
import { openBottomSheetModal } from "@applicaster/zapp-react-native-utils/modalState";
|
|
51
|
+
import { sleepTimerOptions } from "../Const/sleepTimerOptions";
|
|
52
|
+
import { speedOptions } from "../Const/speedOptions";
|
|
40
53
|
|
|
41
54
|
const { log_debug } = createLogger({
|
|
42
55
|
subsystem: "UIComponent",
|
|
@@ -78,59 +91,17 @@ type Props = {
|
|
|
78
91
|
|
|
79
92
|
const { width: SCREEN_WIDTH } = ScreenDimensions.get("screen");
|
|
80
93
|
|
|
81
|
-
const Styles = {
|
|
82
|
-
mainContainer: (style: ViewStyle) => {
|
|
83
|
-
const combinedStyle: ViewStyle = {
|
|
84
|
-
flex: 1,
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
...combinedStyle,
|
|
89
|
-
...style,
|
|
90
|
-
};
|
|
91
|
-
},
|
|
94
|
+
const Styles = StyleSheet.create({
|
|
92
95
|
playerContainer: {
|
|
93
96
|
flex: 1,
|
|
94
97
|
},
|
|
95
98
|
paddingHorizontal: { paddingTop: 0, paddingHorizontal: PADDING_HORIZONTAL },
|
|
96
|
-
|
|
97
|
-
controlsMobile: {
|
|
98
|
-
height: 50,
|
|
99
|
-
marginHorizontal: 24,
|
|
100
|
-
},
|
|
101
|
-
controlsTablet: {
|
|
102
|
-
height: 96,
|
|
103
|
-
marginHorizontal: 24,
|
|
104
|
-
},
|
|
105
99
|
playerDetails: {
|
|
106
100
|
marginTop: 60,
|
|
107
101
|
alignSelf: "center",
|
|
108
102
|
width: SCREEN_WIDTH - PADDING_HORIZONTAL * 2,
|
|
109
103
|
},
|
|
110
|
-
|
|
111
|
-
linearGradient: {
|
|
112
|
-
flex: 1,
|
|
113
|
-
},
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
const sleeps = [
|
|
117
|
-
// { label: "End of episode", value: "episode" },
|
|
118
|
-
{ label: "5 minutes", value: 5 },
|
|
119
|
-
{ label: "10 minutes", value: 10 },
|
|
120
|
-
{ label: "15 minutes", value: 15 },
|
|
121
|
-
{ label: "30 minutes", value: 30 },
|
|
122
|
-
{ label: "45 minutes", value: 45 },
|
|
123
|
-
{ label: "1 hour", value: 60 },
|
|
124
|
-
{ label: "2 hours", value: 120 },
|
|
125
|
-
];
|
|
126
|
-
|
|
127
|
-
const speeds = [
|
|
128
|
-
{ label: "0.8x", value: 0.8 },
|
|
129
|
-
{ label: "1x", value: 1.0 },
|
|
130
|
-
{ label: "1.2x", value: 1.2 },
|
|
131
|
-
{ label: "1.5x", value: 1.5 },
|
|
132
|
-
{ label: "2x", value: 2.0 },
|
|
133
|
-
];
|
|
104
|
+
});
|
|
134
105
|
|
|
135
106
|
const edges: Edge[] = ["right", "left", "top"];
|
|
136
107
|
|
|
@@ -154,12 +125,7 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
154
125
|
|
|
155
126
|
const isTabletLandscape = !isTV() && isTablet && !isTabletPortrait;
|
|
156
127
|
|
|
157
|
-
// TODO: Have generic place for live url from configuration. Currently its hardcoded for client.
|
|
158
|
-
|
|
159
|
-
const feedUrl = getLiveUrl(entry?.extensions?.live?.channel_src ?? "");
|
|
160
|
-
|
|
161
128
|
const { title, summary } = useTitleSummaryOverlay(
|
|
162
|
-
feedUrl,
|
|
163
129
|
entry,
|
|
164
130
|
playerState?.isLive,
|
|
165
131
|
configuration?.audio_live_player_update_interval
|
|
@@ -288,21 +254,22 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
288
254
|
const onSpeedButtonPress = React.useCallback(() => {
|
|
289
255
|
openBottomSheetModal({
|
|
290
256
|
modalBottomSheetContentProps: {
|
|
291
|
-
items:
|
|
257
|
+
items: speedOptions,
|
|
292
258
|
current_selection: playbackSpeed,
|
|
293
259
|
onPress: onSpeedSelect,
|
|
294
260
|
title: configuration["playback_speed_title"],
|
|
261
|
+
itemProps: getBottomSheetModalItemProps,
|
|
262
|
+
iconProps: getBottomSheetModalIconProps,
|
|
263
|
+
labelProps: getBottomSheetModalLabelProps,
|
|
264
|
+
getSelectedItemIcon: getBottomSheetModalSelectedItemIcon,
|
|
265
|
+
getDefaultItemIcon: getBottomSheetModalDefaultItemIcon,
|
|
266
|
+
iconPlacement: "right",
|
|
295
267
|
},
|
|
296
268
|
options: {
|
|
297
269
|
supportedOrientations: ["portrait"],
|
|
298
270
|
},
|
|
299
271
|
});
|
|
300
|
-
}, [
|
|
301
|
-
playbackSpeed,
|
|
302
|
-
setPlaybackSpeed,
|
|
303
|
-
onSpeedSelect,
|
|
304
|
-
configuration["playback_speed_title"],
|
|
305
|
-
]);
|
|
272
|
+
}, [playbackSpeed, onSpeedSelect, configuration["playback_speed_title"]]);
|
|
306
273
|
|
|
307
274
|
const onSleepButtonPress = React.useCallback(() => {
|
|
308
275
|
openBottomSheetModal({
|
|
@@ -312,10 +279,15 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
312
279
|
label: configuration["playback_sleep_off_label"],
|
|
313
280
|
value: 0,
|
|
314
281
|
},
|
|
315
|
-
].concat(
|
|
282
|
+
].concat(sleepTimerOptions),
|
|
316
283
|
current_selection: sleepTimerState.timer,
|
|
317
284
|
onPress: onSleepSelect,
|
|
318
285
|
title: configuration["playback_sleep_title"],
|
|
286
|
+
itemProps: getBottomSheetModalItemProps,
|
|
287
|
+
iconProps: getBottomSheetModalIconProps,
|
|
288
|
+
labelProps: getBottomSheetModalLabelProps,
|
|
289
|
+
getSelectedItemIcon: getBottomSheetModalSelectedItemIcon,
|
|
290
|
+
iconPlacement: "right",
|
|
319
291
|
},
|
|
320
292
|
options: {
|
|
321
293
|
supportedOrientations: ["portrait"],
|
|
@@ -355,11 +327,9 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
355
327
|
player?.seekTo(0);
|
|
356
328
|
}, [player]);
|
|
357
329
|
|
|
358
|
-
const ContainerView = SafeAreaView;
|
|
359
|
-
|
|
360
330
|
const MainContent = React.useMemo(() => {
|
|
361
331
|
return (
|
|
362
|
-
<
|
|
332
|
+
<SafeAreaView style={[style, Styles.playerContainer]} edges={edges}>
|
|
363
333
|
<AnimationComponent
|
|
364
334
|
animationType={ComponentAnimationType.moveUpComponent}
|
|
365
335
|
additionalData={{ saveArea: true }}
|
|
@@ -403,18 +373,18 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
403
373
|
animationType={ComponentAnimationType.componentFade}
|
|
404
374
|
>
|
|
405
375
|
<PlayerDetails
|
|
376
|
+
isAudioPlayer
|
|
406
377
|
configuration={configuration}
|
|
407
378
|
style={Styles.playerDetails}
|
|
408
379
|
entry={entry}
|
|
409
380
|
isTabletLandscape={isTabletLandscape}
|
|
410
|
-
isAudioPlayer={true}
|
|
411
381
|
/>
|
|
412
382
|
</AnimationComponent>
|
|
413
383
|
</View>
|
|
414
384
|
</View>
|
|
415
385
|
</AnimatedScrollModal>
|
|
416
386
|
</AnimationComponent>
|
|
417
|
-
</
|
|
387
|
+
</SafeAreaView>
|
|
418
388
|
);
|
|
419
389
|
}, [
|
|
420
390
|
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 = ({
|