@applicaster/quick-brick-player 13.0.0-alpha.3962361544 → 13.0.0-alpha.4012243505
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 +6 -11
- package/src/Player/AudioLayer/AudioDescription.tsx +5 -7
- package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +60 -88
- 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} +30 -53
- 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/MagicBG/index.tsx +1 -1
- package/src/Player/AudioLayer/utils.ts +27 -0
- 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 +13 -7
- package/src/Player/AudioLayer/Layout/DefaultLayout.tsx +0 -453
- package/src/Player/Helpers/VideoResizeMode.ts +0 -7
package/package.json
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/quick-brick-player",
|
|
3
|
-
"version": "13.0.0-alpha.
|
|
3
|
+
"version": "13.0.0-alpha.4012243505",
|
|
4
4
|
"description": "Quick Brick Player",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"test": "jest -w=2",
|
|
9
8
|
"build": "echo 'skipping ts build for core packages'"
|
|
10
9
|
},
|
|
11
10
|
"files": [
|
|
@@ -36,12 +35,11 @@
|
|
|
36
35
|
},
|
|
37
36
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
38
37
|
"dependencies": {
|
|
39
|
-
"@applicaster/quick-brick-mobile-transport-controls": "13.0.0-rc.
|
|
40
|
-
"@applicaster/quick-brick-tv-transport-controls": "13.0.0-rc.
|
|
41
|
-
"@applicaster/zapp-react-native-tvos-app": "13.0.0-alpha.
|
|
42
|
-
"@applicaster/zapp-react-native-ui-components": "13.0.0-alpha.
|
|
43
|
-
"@applicaster/zapp-react-native-utils": "13.0.0-alpha.
|
|
44
|
-
"keymirror": "^0.1.1",
|
|
38
|
+
"@applicaster/quick-brick-mobile-transport-controls": "13.0.0-rc.99",
|
|
39
|
+
"@applicaster/quick-brick-tv-transport-controls": "13.0.0-rc.56",
|
|
40
|
+
"@applicaster/zapp-react-native-tvos-app": "13.0.0-alpha.4012243505",
|
|
41
|
+
"@applicaster/zapp-react-native-ui-components": "13.0.0-alpha.4012243505",
|
|
42
|
+
"@applicaster/zapp-react-native-utils": "13.0.0-alpha.4012243505",
|
|
45
43
|
"query-string": "7.1.3",
|
|
46
44
|
"react-native-web-linear-gradient": "1.1.2",
|
|
47
45
|
"shaka-player": "4.3.5",
|
|
@@ -61,11 +59,8 @@
|
|
|
61
59
|
"@applicaster/zapp-react-native-tvos-ui-components": "*",
|
|
62
60
|
"@babel/runtime": "*",
|
|
63
61
|
"@react-native-community/netinfo": "*",
|
|
64
|
-
"@types/node": "*",
|
|
65
|
-
"@types/react-native": "0.69.6",
|
|
66
62
|
"immer": "*",
|
|
67
63
|
"react": "*",
|
|
68
|
-
"react-hot-loader": "*",
|
|
69
64
|
"react-native": "*",
|
|
70
65
|
"react-native-fs": "*",
|
|
71
66
|
"react-native-gesture-handler": "*",
|
|
@@ -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,
|
|
@@ -41,6 +46,10 @@ import {
|
|
|
41
46
|
} from "./SleepTimerManager";
|
|
42
47
|
import { SpeedController, PlaybackSpeed, DEFAULT_SPEED } from "./SpeedManager";
|
|
43
48
|
import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
|
|
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";
|
|
44
53
|
|
|
45
54
|
const { log_debug } = createLogger({
|
|
46
55
|
subsystem: "UIComponent",
|
|
@@ -58,6 +67,7 @@ type Configuration = {
|
|
|
58
67
|
[key: string]: any;
|
|
59
68
|
magic_background?: boolean;
|
|
60
69
|
audio_player_background_image_query?: string;
|
|
70
|
+
audio_player_background_image_default_color?: string;
|
|
61
71
|
tablet_landscape_sidebar_width?: string;
|
|
62
72
|
tablet_landscape_player_container_background_color?: string;
|
|
63
73
|
};
|
|
@@ -81,55 +91,24 @@ type Props = {
|
|
|
81
91
|
|
|
82
92
|
const { width: SCREEN_WIDTH } = ScreenDimensions.get("screen");
|
|
83
93
|
|
|
84
|
-
const Styles = {
|
|
85
|
-
mainContainer: (style: ViewStyle) => {
|
|
86
|
-
const combinedStyle: ViewStyle = {
|
|
87
|
-
flex: 1,
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
...combinedStyle,
|
|
92
|
-
...style,
|
|
93
|
-
};
|
|
94
|
-
},
|
|
94
|
+
const Styles = StyleSheet.create({
|
|
95
95
|
playerContainer: {
|
|
96
96
|
flex: 1,
|
|
97
97
|
},
|
|
98
98
|
paddingHorizontal: { paddingTop: 0, paddingHorizontal: PADDING_HORIZONTAL },
|
|
99
|
-
|
|
100
|
-
controlsMobile: {
|
|
101
|
-
height: 50,
|
|
102
|
-
marginHorizontal: 24,
|
|
103
|
-
},
|
|
104
|
-
controlsTablet: {
|
|
105
|
-
height: 96,
|
|
106
|
-
marginHorizontal: 24,
|
|
107
|
-
},
|
|
108
99
|
playerDetails: {
|
|
109
100
|
marginTop: 60,
|
|
110
101
|
alignSelf: "center",
|
|
111
102
|
width: SCREEN_WIDTH - PADDING_HORIZONTAL * 2,
|
|
112
103
|
},
|
|
104
|
+
});
|
|
113
105
|
|
|
114
|
-
|
|
115
|
-
flex: 1,
|
|
116
|
-
},
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
const sleeps = [
|
|
120
|
-
// { label: "End of episode", value: "episode" },
|
|
121
|
-
{ label: "5 minutes", value: 5 },
|
|
122
|
-
{ label: "10 minutes", value: 10 },
|
|
123
|
-
{ label: "15 minutes", value: 15 },
|
|
124
|
-
{ label: "30 minutes", value: 30 },
|
|
125
|
-
{ label: "45 minutes", value: 45 },
|
|
126
|
-
{ label: "1 hour", value: 60 },
|
|
127
|
-
{ label: "2 hours", value: 120 },
|
|
128
|
-
];
|
|
106
|
+
const edges: Edge[] = ["right", "left", "top"];
|
|
129
107
|
|
|
130
108
|
export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
131
109
|
const player = usePlayer();
|
|
132
110
|
const playerState = usePlayerState("audio-player-wrapper");
|
|
111
|
+
const theme = useTheme();
|
|
133
112
|
|
|
134
113
|
const {
|
|
135
114
|
docked,
|
|
@@ -142,24 +121,11 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
142
121
|
isTabletPortrait,
|
|
143
122
|
} = props;
|
|
144
123
|
|
|
145
|
-
const speeds = [
|
|
146
|
-
{ asset: configuration["speed_0_8"] || speed_0_8, value: 0.8 },
|
|
147
|
-
{ asset: configuration["speed_1"] || speed_1, value: 1.0 },
|
|
148
|
-
{ asset: configuration["speed_1_2"] || speed_1_2, value: 1.2 },
|
|
149
|
-
{ asset: configuration["speed_1_5"] || speed_1_5, value: 1.5 },
|
|
150
|
-
{ asset: configuration["speed_2"] || speed_2, value: 2.0 },
|
|
151
|
-
];
|
|
152
|
-
|
|
153
124
|
const isTablet = useIsTablet();
|
|
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
|
|
@@ -197,8 +163,6 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
197
163
|
height: 0,
|
|
198
164
|
});
|
|
199
165
|
|
|
200
|
-
const navigator = useNavigation();
|
|
201
|
-
|
|
202
166
|
const { maximiseVideoModal } = useNavigation();
|
|
203
167
|
|
|
204
168
|
const onLayout = (event: any) => {
|
|
@@ -288,44 +252,51 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
288
252
|
);
|
|
289
253
|
|
|
290
254
|
const onSpeedButtonPress = React.useCallback(() => {
|
|
291
|
-
|
|
255
|
+
openBottomSheetModal({
|
|
292
256
|
modalBottomSheetContentProps: {
|
|
293
|
-
items:
|
|
257
|
+
items: speedOptions,
|
|
294
258
|
current_selection: playbackSpeed,
|
|
295
259
|
onPress: onSpeedSelect,
|
|
296
260
|
title: configuration["playback_speed_title"],
|
|
261
|
+
itemProps: getBottomSheetModalItemProps,
|
|
262
|
+
iconProps: getBottomSheetModalIconProps,
|
|
263
|
+
labelProps: getBottomSheetModalLabelProps,
|
|
264
|
+
getSelectedItemIcon: getBottomSheetModalSelectedItemIcon,
|
|
265
|
+
getDefaultItemIcon: getBottomSheetModalDefaultItemIcon,
|
|
266
|
+
iconPlacement: "right",
|
|
297
267
|
},
|
|
298
268
|
options: {
|
|
299
|
-
supportedOrientations: [
|
|
269
|
+
supportedOrientations: [
|
|
270
|
+
"portrait",
|
|
271
|
+
"landscape",
|
|
272
|
+
"portrait-upside-down",
|
|
273
|
+
],
|
|
300
274
|
},
|
|
301
275
|
});
|
|
302
|
-
}, [
|
|
303
|
-
playbackSpeed,
|
|
304
|
-
setPlaybackSpeed,
|
|
305
|
-
navigator,
|
|
306
|
-
onSpeedSelect,
|
|
307
|
-
configuration["playback_speed_title"],
|
|
308
|
-
]);
|
|
276
|
+
}, [playbackSpeed, onSpeedSelect, configuration["playback_speed_title"]]);
|
|
309
277
|
|
|
310
278
|
const onSleepButtonPress = React.useCallback(() => {
|
|
311
|
-
|
|
279
|
+
openBottomSheetModal({
|
|
312
280
|
modalBottomSheetContentProps: {
|
|
313
|
-
items:
|
|
314
|
-
{
|
|
315
|
-
label: configuration["playback_sleep_off_label"],
|
|
316
|
-
value: 0,
|
|
317
|
-
},
|
|
318
|
-
].concat(sleeps),
|
|
281
|
+
items: getSleepTimerOptions(configuration),
|
|
319
282
|
current_selection: sleepTimerState.timer,
|
|
320
283
|
onPress: onSleepSelect,
|
|
321
284
|
title: configuration["playback_sleep_title"],
|
|
285
|
+
itemProps: getBottomSheetModalItemProps,
|
|
286
|
+
iconProps: getBottomSheetModalIconProps,
|
|
287
|
+
labelProps: getBottomSheetModalLabelProps,
|
|
288
|
+
getSelectedItemIcon: getBottomSheetModalSelectedItemIcon,
|
|
289
|
+
iconPlacement: "right",
|
|
322
290
|
},
|
|
323
291
|
options: {
|
|
324
|
-
supportedOrientations: [
|
|
292
|
+
supportedOrientations: [
|
|
293
|
+
"portrait",
|
|
294
|
+
"landscape",
|
|
295
|
+
"portrait-upside-down",
|
|
296
|
+
],
|
|
325
297
|
},
|
|
326
298
|
});
|
|
327
299
|
}, [
|
|
328
|
-
navigator,
|
|
329
300
|
sleepTimerState.timer,
|
|
330
301
|
configuration["playback_sleep_title"],
|
|
331
302
|
configuration["playback_sleep_off_label"],
|
|
@@ -359,13 +330,9 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
359
330
|
player?.seekTo(0);
|
|
360
331
|
}, [player]);
|
|
361
332
|
|
|
362
|
-
const edges = React.useMemo<Edge[]>(() => ["right", "left", "top"], [docked]);
|
|
363
|
-
|
|
364
|
-
const ContainerView = SafeAreaView;
|
|
365
|
-
|
|
366
333
|
const MainContent = React.useMemo(() => {
|
|
367
334
|
return (
|
|
368
|
-
<
|
|
335
|
+
<SafeAreaView style={[style, Styles.playerContainer]} edges={edges}>
|
|
369
336
|
<AnimationComponent
|
|
370
337
|
animationType={ComponentAnimationType.moveUpComponent}
|
|
371
338
|
additionalData={{ saveArea: true }}
|
|
@@ -409,18 +376,18 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
409
376
|
animationType={ComponentAnimationType.componentFade}
|
|
410
377
|
>
|
|
411
378
|
<PlayerDetails
|
|
379
|
+
isAudioPlayer
|
|
412
380
|
configuration={configuration}
|
|
413
381
|
style={Styles.playerDetails}
|
|
414
382
|
entry={entry}
|
|
415
383
|
isTabletLandscape={isTabletLandscape}
|
|
416
|
-
isAudioPlayer={true}
|
|
417
384
|
/>
|
|
418
385
|
</AnimationComponent>
|
|
419
386
|
</View>
|
|
420
387
|
</View>
|
|
421
388
|
</AnimatedScrollModal>
|
|
422
389
|
</AnimationComponent>
|
|
423
|
-
</
|
|
390
|
+
</SafeAreaView>
|
|
424
391
|
);
|
|
425
392
|
}, [
|
|
426
393
|
docked,
|
|
@@ -451,16 +418,21 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
451
418
|
const queryParamName =
|
|
452
419
|
configuration.audio_player_background_image_query || "width";
|
|
453
420
|
|
|
421
|
+
const defaultBackgroundColor =
|
|
422
|
+
configuration.audio_player_background_image_default_color ||
|
|
423
|
+
theme?.app_background_color;
|
|
424
|
+
|
|
454
425
|
const queryParamValue = 15;
|
|
455
426
|
|
|
456
|
-
const url =
|
|
427
|
+
const url =
|
|
428
|
+
baseUrl && addQueryParam(baseUrl, queryParamName, queryParamValue);
|
|
457
429
|
|
|
458
430
|
return (
|
|
459
431
|
<MagicBG
|
|
460
432
|
image={url}
|
|
461
433
|
fallbackUrl={baseUrl}
|
|
462
434
|
colors={entry?.extensions?.backgroundColors}
|
|
463
|
-
defaultColor=
|
|
435
|
+
defaultColor={defaultBackgroundColor}
|
|
464
436
|
>
|
|
465
437
|
{MainContent}
|
|
466
438
|
</MagicBG>
|
|
@@ -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
|
+
};
|