@applicaster/quick-brick-player 13.0.0-alpha.7586259907 → 13.0.0-alpha.7620873141
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 +5 -7
- package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +49 -85
- package/src/Player/AudioLayer/Layout/Controls/VerticalGutter.tsx +27 -0
- package/src/Player/AudioLayer/Layout/Controls/index.tsx +316 -0
- package/src/Player/AudioLayer/Layout/{defaultLayoutsConts.ts → Controls/utils.ts} +5 -23
- package/src/Player/AudioLayer/Layout/Debug/index.tsx +55 -0
- package/src/Player/AudioLayer/Layout/{DockedLayout.tsx → DockedControls/index.tsx} +28 -39
- package/src/Player/AudioLayer/Layout/MobileLayout.tsx +231 -0
- package/src/Player/AudioLayer/Layout/PlayerImage/AnimatedImage.tsx +83 -0
- package/src/Player/AudioLayer/Layout/PlayerImage/FlexImage.tsx +25 -0
- package/src/Player/AudioLayer/Layout/PlayerImage/index.tsx +101 -0
- package/src/Player/AudioLayer/Layout/PlayerImage/styles.ts +26 -0
- package/src/Player/AudioLayer/Layout/TabletLandscapeLayout.tsx +216 -0
- package/src/Player/AudioLayer/Layout/TabletPortraitLayout.tsx +224 -0
- package/src/Player/AudioLayer/Layout/TransportInfo/index.tsx +114 -0
- package/src/Player/AudioLayer/Layout/VerticalGutter/index.tsx +8 -0
- package/src/Player/AudioLayer/Layout/const.ts +22 -0
- package/src/Player/AudioLayer/Layout/debug.ts +6 -0
- package/src/Player/AudioLayer/Layout/hooks/index.ts +5 -0
- package/src/Player/AudioLayer/Layout/hooks/useMobileLayoutImageSize.ts +14 -0
- package/src/Player/AudioLayer/Layout/hooks/useTabletLayoutImageSize.ts +105 -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 +39 -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.7620873141",
|
|
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.99",
|
|
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.7620873141",
|
|
41
|
+
"@applicaster/zapp-react-native-ui-components": "13.0.0-alpha.7620873141",
|
|
42
|
+
"@applicaster/zapp-react-native-utils": "13.0.0-alpha.7620873141",
|
|
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
|
-
|
|
87
|
-
<Text style={titleStyles(value, prefix)} numberOfLines={
|
|
84
|
+
<>
|
|
85
|
+
<Text style={titleStyles(value, prefix)} numberOfLines={2}>
|
|
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 { getSleepTimerOptions } 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,51 @@ 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
|
-
items:
|
|
319
|
-
{
|
|
320
|
-
label: configuration["playback_sleep_off_label"],
|
|
321
|
-
value: 0,
|
|
322
|
-
},
|
|
323
|
-
].concat(sleeps),
|
|
281
|
+
items: getSleepTimerOptions(configuration),
|
|
324
282
|
current_selection: sleepTimerState.timer,
|
|
325
283
|
onPress: onSleepSelect,
|
|
326
284
|
title: configuration["playback_sleep_title"],
|
|
285
|
+
itemProps: getBottomSheetModalItemProps,
|
|
286
|
+
iconProps: getBottomSheetModalIconProps,
|
|
287
|
+
labelProps: getBottomSheetModalLabelProps,
|
|
288
|
+
getSelectedItemIcon: getBottomSheetModalSelectedItemIcon,
|
|
289
|
+
iconPlacement: "right",
|
|
327
290
|
},
|
|
328
291
|
options: {
|
|
329
|
-
supportedOrientations: [
|
|
292
|
+
supportedOrientations: [
|
|
293
|
+
"portrait",
|
|
294
|
+
"landscape",
|
|
295
|
+
"portrait-upside-down",
|
|
296
|
+
],
|
|
330
297
|
},
|
|
331
298
|
});
|
|
332
299
|
}, [
|
|
333
|
-
navigator,
|
|
334
300
|
sleepTimerState.timer,
|
|
335
301
|
configuration["playback_sleep_title"],
|
|
336
302
|
configuration["playback_sleep_off_label"],
|
|
@@ -364,11 +330,9 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
364
330
|
player?.seekTo(0);
|
|
365
331
|
}, [player]);
|
|
366
332
|
|
|
367
|
-
const ContainerView = SafeAreaView;
|
|
368
|
-
|
|
369
333
|
const MainContent = React.useMemo(() => {
|
|
370
334
|
return (
|
|
371
|
-
<
|
|
335
|
+
<SafeAreaView style={[style, Styles.playerContainer]} edges={edges}>
|
|
372
336
|
<AnimationComponent
|
|
373
337
|
animationType={ComponentAnimationType.moveUpComponent}
|
|
374
338
|
additionalData={{ saveArea: true }}
|
|
@@ -412,18 +376,18 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
412
376
|
animationType={ComponentAnimationType.componentFade}
|
|
413
377
|
>
|
|
414
378
|
<PlayerDetails
|
|
379
|
+
isAudioPlayer
|
|
415
380
|
configuration={configuration}
|
|
416
381
|
style={Styles.playerDetails}
|
|
417
382
|
entry={entry}
|
|
418
383
|
isTabletLandscape={isTabletLandscape}
|
|
419
|
-
isAudioPlayer={true}
|
|
420
384
|
/>
|
|
421
385
|
</AnimationComponent>
|
|
422
386
|
</View>
|
|
423
387
|
</View>
|
|
424
388
|
</AnimatedScrollModal>
|
|
425
389
|
</AnimationComponent>
|
|
426
|
-
</
|
|
390
|
+
</SafeAreaView>
|
|
427
391
|
);
|
|
428
392
|
}, [
|
|
429
393
|
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,316 @@
|
|
|
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
|
+
ProgressBar,
|
|
9
|
+
} from "@applicaster/quick-brick-mobile-transport-controls";
|
|
10
|
+
import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
AnimationComponent,
|
|
14
|
+
ComponentAnimationType,
|
|
15
|
+
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
16
|
+
|
|
17
|
+
import { AudioDescription } from "../../AudioDescription";
|
|
18
|
+
import { isValidDate } from "../../../Utils";
|
|
19
|
+
import { VerticalGutter } from "./VerticalGutter";
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
forwardButton,
|
|
23
|
+
playbackButton,
|
|
24
|
+
rewindButton,
|
|
25
|
+
skipToLiveButton,
|
|
26
|
+
skipToStartButton,
|
|
27
|
+
sleepButton,
|
|
28
|
+
speedButton,
|
|
29
|
+
} from "./utils";
|
|
30
|
+
|
|
31
|
+
const styles = StyleSheet.create({
|
|
32
|
+
flex: {
|
|
33
|
+
flex: 1,
|
|
34
|
+
},
|
|
35
|
+
nonFlex: {
|
|
36
|
+
flex: 0,
|
|
37
|
+
},
|
|
38
|
+
progressBar: {
|
|
39
|
+
flex: 0,
|
|
40
|
+
},
|
|
41
|
+
progressBarTrackStyles: {
|
|
42
|
+
borderRadius: 3,
|
|
43
|
+
height: 6,
|
|
44
|
+
},
|
|
45
|
+
progressBarThumbStyles: {
|
|
46
|
+
width: 14,
|
|
47
|
+
height: 14,
|
|
48
|
+
borderRadius: 7,
|
|
49
|
+
},
|
|
50
|
+
progressBarThumbStyles_pressed: {
|
|
51
|
+
width: 20,
|
|
52
|
+
height: 20,
|
|
53
|
+
borderRadius: 10,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const getControlJustifyContent = (countOfButtons: number) =>
|
|
58
|
+
countOfButtons <= 1 ? "center" : "space-between";
|
|
59
|
+
|
|
60
|
+
type Props = {
|
|
61
|
+
entry: ZappEntry;
|
|
62
|
+
layoutState: QuickBrickPlayer.LayoutState;
|
|
63
|
+
playerState: QuickBrickPlayer.PlayerState;
|
|
64
|
+
value: GetValue;
|
|
65
|
+
title: string | number;
|
|
66
|
+
summary: string | number;
|
|
67
|
+
sleepTimerDate: number;
|
|
68
|
+
cancelSleepTimer: Callback;
|
|
69
|
+
|
|
70
|
+
playbackSpeedName: string;
|
|
71
|
+
playbackSleepName: string;
|
|
72
|
+
|
|
73
|
+
onPlaybackButtonPress: Callback;
|
|
74
|
+
onRewindButtonPress: Callback;
|
|
75
|
+
onForwardButtonPress: Callback;
|
|
76
|
+
onPlayerSeek: (targetTime: number) => void;
|
|
77
|
+
onSkipStart: Callback;
|
|
78
|
+
onSkipLive: Callback;
|
|
79
|
+
onSpeedButtonPress: Callback;
|
|
80
|
+
onSleepButtonPress: Callback;
|
|
81
|
+
|
|
82
|
+
docked: boolean;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const Controls = (props: Props) => {
|
|
86
|
+
const {
|
|
87
|
+
entry,
|
|
88
|
+
layoutState,
|
|
89
|
+
playerState,
|
|
90
|
+
value,
|
|
91
|
+
title,
|
|
92
|
+
summary,
|
|
93
|
+
sleepTimerDate,
|
|
94
|
+
cancelSleepTimer,
|
|
95
|
+
|
|
96
|
+
playbackSpeedName,
|
|
97
|
+
playbackSleepName,
|
|
98
|
+
|
|
99
|
+
onPlaybackButtonPress,
|
|
100
|
+
onRewindButtonPress,
|
|
101
|
+
onForwardButtonPress,
|
|
102
|
+
onPlayerSeek,
|
|
103
|
+
onSkipStart,
|
|
104
|
+
onSkipLive,
|
|
105
|
+
onSpeedButtonPress,
|
|
106
|
+
onSleepButtonPress,
|
|
107
|
+
|
|
108
|
+
docked,
|
|
109
|
+
} = props;
|
|
110
|
+
|
|
111
|
+
const isTablet = useIsTablet();
|
|
112
|
+
const controller = playerManager.getActivePlayer();
|
|
113
|
+
let showFullControls = !controller?.isAd();
|
|
114
|
+
const liveButton = Boolean(value("live_badge_enabled"));
|
|
115
|
+
|
|
116
|
+
const isLiveContent = playerState.isLive;
|
|
117
|
+
|
|
118
|
+
if (isLiveContent) {
|
|
119
|
+
const seekEnabled = Boolean(value("liveSeekingEnabled"));
|
|
120
|
+
|
|
121
|
+
showFullControls = !!(controller?.hasSeekableDuration?.() && seekEnabled);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const [timeLeft, setTimeLeft] = React.useState(
|
|
125
|
+
sleepTimerDate !== 0 ? sleepTimerDate - Date.now() : 0
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const showCounter = React.useMemo(() => sleepTimerDate > 0, [sleepTimerDate]);
|
|
129
|
+
|
|
130
|
+
const countDownOptions = React.useMemo(() => {
|
|
131
|
+
return {
|
|
132
|
+
showCounter: showCounter && timeLeft >= 0,
|
|
133
|
+
timeLeft,
|
|
134
|
+
};
|
|
135
|
+
}, [showCounter, timeLeft]);
|
|
136
|
+
|
|
137
|
+
React.useEffect(() => {
|
|
138
|
+
if (showCounter && sleepTimerDate !== 0) {
|
|
139
|
+
setTimeLeft(sleepTimerDate - Date.now());
|
|
140
|
+
|
|
141
|
+
const intervalId = setInterval(() => {
|
|
142
|
+
setTimeLeft(() => {
|
|
143
|
+
const newTimeLeft = sleepTimerDate - Date.now();
|
|
144
|
+
|
|
145
|
+
if (newTimeLeft <= 0) {
|
|
146
|
+
clearInterval(intervalId);
|
|
147
|
+
|
|
148
|
+
cancelSleepTimer();
|
|
149
|
+
|
|
150
|
+
return 0;
|
|
151
|
+
} else {
|
|
152
|
+
return newTimeLeft;
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}, 1000);
|
|
156
|
+
|
|
157
|
+
return () => clearInterval(intervalId);
|
|
158
|
+
} else {
|
|
159
|
+
setTimeLeft(0);
|
|
160
|
+
}
|
|
161
|
+
}, [sleepTimerDate, showCounter, cancelSleepTimer]);
|
|
162
|
+
|
|
163
|
+
const startTime = entry.extensions?.live?.start_time;
|
|
164
|
+
const endTime = entry.extensions?.live?.end_time;
|
|
165
|
+
|
|
166
|
+
const hasValidStartTime = isValidDate(startTime);
|
|
167
|
+
const hasValidEndTime = isValidDate(endTime);
|
|
168
|
+
|
|
169
|
+
const isDateBasedLiveContent =
|
|
170
|
+
isLiveContent && hasValidStartTime && hasValidEndTime;
|
|
171
|
+
|
|
172
|
+
const needsToShowProgressBar =
|
|
173
|
+
(isLiveContent &&
|
|
174
|
+
(Boolean(value("liveSeekingEnabled")) || isDateBasedLiveContent)) ||
|
|
175
|
+
!isLiveContent;
|
|
176
|
+
|
|
177
|
+
const startTimestamp = hasValidStartTime
|
|
178
|
+
? dayjs.utc(startTime!).valueOf()
|
|
179
|
+
: undefined;
|
|
180
|
+
|
|
181
|
+
const endTimestamp = hasValidEndTime
|
|
182
|
+
? dayjs.utc(endTime!).valueOf()
|
|
183
|
+
: undefined;
|
|
184
|
+
|
|
185
|
+
const isLiveShowFullControls = showFullControls && isLiveContent;
|
|
186
|
+
|
|
187
|
+
const controlButtons = React.useCallback((): Array<ControlsButton> => {
|
|
188
|
+
const play = playbackButton({
|
|
189
|
+
playerState,
|
|
190
|
+
liveButton,
|
|
191
|
+
isTablet,
|
|
192
|
+
isLiveShowFullControls,
|
|
193
|
+
onPress: onPlaybackButtonPress,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
if (!showFullControls) {
|
|
197
|
+
return [play];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const rewind = rewindButton({
|
|
201
|
+
isTablet,
|
|
202
|
+
onPress: controller?.hasSeekableDuration?.() ? onRewindButtonPress : null,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const forward = forwardButton({
|
|
206
|
+
isTablet,
|
|
207
|
+
onPress: controller?.hasSeekableDuration?.()
|
|
208
|
+
? onForwardButtonPress
|
|
209
|
+
: null,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const speed = speedButton({
|
|
213
|
+
isTablet,
|
|
214
|
+
onPress: onSpeedButtonPress,
|
|
215
|
+
name: playbackSpeedName,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const sleep = sleepButton({
|
|
219
|
+
isTablet,
|
|
220
|
+
onPress: onSleepButtonPress,
|
|
221
|
+
name: playbackSleepName,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
if (isLiveContent) {
|
|
225
|
+
const skipToStart = skipToStartButton({
|
|
226
|
+
isTablet,
|
|
227
|
+
onPress: onSkipStart,
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const skipToLive = skipToLiveButton({
|
|
231
|
+
isTablet,
|
|
232
|
+
onPress: onSkipLive,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
return [skipToStart, rewind, play, forward, skipToLive];
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return [speed, rewind, play, forward, sleep];
|
|
239
|
+
}, [
|
|
240
|
+
playerState,
|
|
241
|
+
liveButton,
|
|
242
|
+
isTablet,
|
|
243
|
+
onPlaybackButtonPress,
|
|
244
|
+
onRewindButtonPress,
|
|
245
|
+
onForwardButtonPress,
|
|
246
|
+
onSpeedButtonPress,
|
|
247
|
+
showFullControls,
|
|
248
|
+
onSkipStart,
|
|
249
|
+
onSkipLive,
|
|
250
|
+
isLiveContent,
|
|
251
|
+
playbackSpeedName,
|
|
252
|
+
playbackSleepName,
|
|
253
|
+
controller,
|
|
254
|
+
isLiveShowFullControls,
|
|
255
|
+
]);
|
|
256
|
+
|
|
257
|
+
if (docked) {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return (
|
|
262
|
+
<AnimationComponent
|
|
263
|
+
animationType={ComponentAnimationType.componentFade}
|
|
264
|
+
style={isTablet ? styles.nonFlex : styles.flex}
|
|
265
|
+
>
|
|
266
|
+
<VerticalGutter />
|
|
267
|
+
|
|
268
|
+
<AudioDescription
|
|
269
|
+
title={title}
|
|
270
|
+
summary={summary}
|
|
271
|
+
value={value}
|
|
272
|
+
layoutState={layoutState}
|
|
273
|
+
/>
|
|
274
|
+
|
|
275
|
+
<VerticalGutter />
|
|
276
|
+
|
|
277
|
+
{needsToShowProgressBar ? (
|
|
278
|
+
<>
|
|
279
|
+
<ProgressBar
|
|
280
|
+
name={"audio-default-layout"}
|
|
281
|
+
content={entry}
|
|
282
|
+
value={value}
|
|
283
|
+
visible
|
|
284
|
+
layoutState={layoutState}
|
|
285
|
+
onPlayerSeek={onPlayerSeek}
|
|
286
|
+
playerState={playerState}
|
|
287
|
+
disableFullscreen
|
|
288
|
+
sliderStyle={2}
|
|
289
|
+
startTimestamp={startTimestamp}
|
|
290
|
+
endTimestamp={endTimestamp}
|
|
291
|
+
isDateBasedLiveContent={isDateBasedLiveContent}
|
|
292
|
+
onSkipLive={onSkipLive}
|
|
293
|
+
progressBarStyles={styles.progressBar}
|
|
294
|
+
countDownOptions={countDownOptions}
|
|
295
|
+
thumbStyles={styles.progressBarThumbStyles}
|
|
296
|
+
thumbStylesPressed={styles.progressBarThumbStyles_pressed}
|
|
297
|
+
trackStyles={styles.progressBarTrackStyles}
|
|
298
|
+
/>
|
|
299
|
+
|
|
300
|
+
<VerticalGutter />
|
|
301
|
+
</>
|
|
302
|
+
) : null}
|
|
303
|
+
|
|
304
|
+
<MobileTransportControls
|
|
305
|
+
value={value}
|
|
306
|
+
visible={true}
|
|
307
|
+
keepAlive={null}
|
|
308
|
+
buttons={controlButtons}
|
|
309
|
+
style={{
|
|
310
|
+
justifyContent: getControlJustifyContent(controlButtons().length),
|
|
311
|
+
}}
|
|
312
|
+
playerState={playerState}
|
|
313
|
+
/>
|
|
314
|
+
</AnimationComponent>
|
|
315
|
+
);
|
|
316
|
+
};
|
|
@@ -1,25 +1,7 @@
|
|
|
1
|
-
type PlayerState = {
|
|
2
|
-
currentTime: number;
|
|
3
|
-
duration: number;
|
|
4
|
-
seekableDuration: number;
|
|
5
|
-
isLive: boolean;
|
|
6
|
-
isPaused: boolean;
|
|
7
|
-
isBuffering: boolean;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
const rewindButtonSize = (isTablet: boolean): keyof ButtonSizeStyles =>
|
|
11
|
-
isTablet ? "big" : "smallMargins";
|
|
12
|
-
|
|
13
|
-
const playbackButtonSize = (isTablet: boolean): keyof ButtonSizeStyles =>
|
|
14
|
-
isTablet ? "bigExtended" : "mediumExtended";
|
|
15
|
-
|
|
16
1
|
const playbackButtonMargins = (
|
|
17
2
|
isTablet: boolean,
|
|
18
3
|
isLiveShowFullControls: boolean
|
|
19
|
-
): number =>
|
|
20
|
-
isLiveShowFullControls ? (isTablet ? 65 : 10) : isTablet ? undefined : 40;
|
|
21
|
-
|
|
22
|
-
export const PADDING_HORIZONTAL = 15;
|
|
4
|
+
): number => (isLiveShowFullControls ? (isTablet ? 65 : 10) : undefined);
|
|
23
5
|
|
|
24
6
|
export const playbackButton = ({
|
|
25
7
|
playerState,
|
|
@@ -28,7 +10,7 @@ export const playbackButton = ({
|
|
|
28
10
|
isLiveShowFullControls,
|
|
29
11
|
onPress,
|
|
30
12
|
}: {
|
|
31
|
-
playerState: PlayerState;
|
|
13
|
+
playerState: QuickBrickPlayer.PlayerState;
|
|
32
14
|
liveButton: boolean;
|
|
33
15
|
isTablet: boolean;
|
|
34
16
|
isLiveShowFullControls: boolean;
|
|
@@ -41,7 +23,7 @@ export const playbackButton = ({
|
|
|
41
23
|
: "pause";
|
|
42
24
|
|
|
43
25
|
return {
|
|
44
|
-
size:
|
|
26
|
+
size: "bigExtended",
|
|
45
27
|
icon: playerState.isBuffering ? "buffering" : icon,
|
|
46
28
|
preferredFocus: true,
|
|
47
29
|
onPress,
|
|
@@ -57,15 +39,15 @@ type ButtonsProps = {
|
|
|
57
39
|
style?: any;
|
|
58
40
|
name?: string;
|
|
59
41
|
};
|
|
42
|
+
|
|
60
43
|
type ButtonsByNameProps = ButtonsProps & { name?: string };
|
|
61
44
|
|
|
62
45
|
const rewindButtonByName = ({
|
|
63
46
|
name,
|
|
64
47
|
onPress,
|
|
65
|
-
isTablet,
|
|
66
48
|
style,
|
|
67
49
|
}: ButtonsByNameProps): ControlsButton => ({
|
|
68
|
-
size:
|
|
50
|
+
size: "big",
|
|
69
51
|
icon: name,
|
|
70
52
|
onPress,
|
|
71
53
|
style,
|