@applicaster/quick-brick-player 12.0.0-rc.10 → 12.0.0-rc.100
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 +7 -6
- package/src/CellPlayer/Assets/AssetsComponent.tsx +61 -50
- package/src/CellPlayer/ContextProvider.tsx +50 -1
- package/src/Player/AudioLayer/AudioDescription.tsx +3 -3
- package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +277 -58
- package/src/Player/AudioLayer/Layout/DefaultLayout.tsx +260 -71
- package/src/Player/AudioLayer/Layout/DockedLayout.tsx +81 -27
- package/src/Player/AudioLayer/Layout/defaultLayoutsConts.ts +32 -1
- package/src/Player/AudioLayer/Layout/index.tsx +9 -7
- package/src/Player/AudioLayer/MagicBG/index.tsx +96 -27
- package/src/Player/AudioLayer/utils.ts +33 -0
- package/src/Player/Utils/Utils.ts +26 -16
- package/src/Player/Utils/index.tsx +0 -2
- package/src/Player/index.tsx +130 -72
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/quick-brick-player",
|
|
3
|
-
"version": "12.0.0-rc.
|
|
3
|
+
"version": "12.0.0-rc.100",
|
|
4
4
|
"description": "Quick Brick Player",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -36,12 +36,13 @@
|
|
|
36
36
|
},
|
|
37
37
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@applicaster/quick-brick-mobile-transport-controls": "12.0.0-rc.
|
|
40
|
-
"@applicaster/quick-brick-tv-transport-controls": "
|
|
41
|
-
"@applicaster/zapp-react-native-tvos-app": "12.0.0-rc.
|
|
42
|
-
"@applicaster/zapp-react-native-ui-components": "12.0.0-rc.
|
|
43
|
-
"@applicaster/zapp-react-native-utils": "12.0.0-rc.
|
|
39
|
+
"@applicaster/quick-brick-mobile-transport-controls": "12.0.0-rc.96",
|
|
40
|
+
"@applicaster/quick-brick-tv-transport-controls": "12.0.0-rc.100",
|
|
41
|
+
"@applicaster/zapp-react-native-tvos-app": "12.0.0-rc.100",
|
|
42
|
+
"@applicaster/zapp-react-native-ui-components": "12.0.0-rc.100",
|
|
43
|
+
"@applicaster/zapp-react-native-utils": "12.0.0-rc.100",
|
|
44
44
|
"keymirror": "^0.1.1",
|
|
45
|
+
"query-string": "7.1.3",
|
|
45
46
|
"react-native-web-linear-gradient": "1.1.2",
|
|
46
47
|
"shaka-player": "4.3.5",
|
|
47
48
|
"typeface-montserrat": "^0.0.54",
|
|
@@ -1,74 +1,85 @@
|
|
|
1
1
|
import React, { useEffect, useState } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { StyleSheet, View } from "react-native";
|
|
3
3
|
import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
|
|
4
4
|
import { MUTED_USAGE } from "../const";
|
|
5
5
|
import { LiveImageManager } from "@applicaster/zapp-react-native-ui-components/Components/VideoLive/LiveImageManager";
|
|
6
|
+
import { QBImage as Image } from "@applicaster/zapp-react-native-ui-components/Components/Image";
|
|
6
7
|
|
|
7
8
|
type Asset = string | undefined;
|
|
8
9
|
|
|
9
10
|
const isCurrentPlayer = (playerId) =>
|
|
10
11
|
LiveImageManager.instance.getCurrentlyPlaying()?.playerId === playerId;
|
|
11
12
|
|
|
13
|
+
const liveImageButtonsListenerKey = "live-image-buttons";
|
|
14
|
+
|
|
12
15
|
const usePlayPauseListeners = (playerId, player, asset, setImageUri) =>
|
|
13
16
|
useEffect(() => {
|
|
14
17
|
if (playerId && player) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
setImageUri(null);
|
|
19
|
+
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (player.playerState.isReadyToPlay) {
|
|
24
|
+
if (player.playerState.isPaused) {
|
|
25
|
+
setImageUri(asset.active);
|
|
26
|
+
} else {
|
|
27
|
+
setImageUri(asset.inactive);
|
|
21
28
|
}
|
|
29
|
+
}
|
|
22
30
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
},
|
|
29
|
-
onPlayerPause: () => {
|
|
30
|
-
setImageUri(asset.active);
|
|
31
|
-
},
|
|
32
|
-
onPlayerClose: () => {
|
|
33
|
-
setImageUri(null);
|
|
34
|
-
},
|
|
35
|
-
onLoad() {
|
|
36
|
-
setImageUri(asset.inactive);
|
|
37
|
-
},
|
|
31
|
+
return player.addListener({
|
|
32
|
+
id: liveImageButtonsListenerKey,
|
|
33
|
+
listener: {
|
|
34
|
+
onPlayerResume: () => {
|
|
35
|
+
setImageUri(asset.inactive);
|
|
38
36
|
},
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
onPlayerPause: () => {
|
|
38
|
+
setImageUri(asset.active);
|
|
39
|
+
},
|
|
40
|
+
onPlayerClose: () => {
|
|
41
|
+
setImageUri(null);
|
|
42
|
+
},
|
|
43
|
+
onLoad() {
|
|
44
|
+
setImageUri(asset.inactive);
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
});
|
|
41
48
|
}, [player, playerId, asset]);
|
|
42
49
|
|
|
43
50
|
const useMuteUnmuteListeners = (playerId, player, asset, setImageUri) =>
|
|
44
51
|
useEffect(() => {
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
listener: {
|
|
50
|
-
onPlayerMute: () => {
|
|
51
|
-
if (isCurrentPlayer(playerId)) {
|
|
52
|
-
setImageUri(asset.active);
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
onPlayerUnmute: () => {
|
|
56
|
-
if (isCurrentPlayer(playerId)) {
|
|
57
|
-
setImageUri(asset.inactive);
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
|
-
onLoad() {
|
|
61
|
-
setImageUri(player?.getIsMuted() ? asset.active : asset.inactive);
|
|
62
|
-
},
|
|
63
|
-
onPlayerClose: () => {
|
|
64
|
-
setImageUri(null);
|
|
65
|
-
},
|
|
66
|
-
onVideoEnd: () => {
|
|
67
|
-
setImageUri(null);
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
});
|
|
52
|
+
if (!player || !playerId) {
|
|
53
|
+
setImageUri(null);
|
|
54
|
+
|
|
55
|
+
return;
|
|
71
56
|
}
|
|
57
|
+
|
|
58
|
+
// active action = muted, inactive = unmuted
|
|
59
|
+
return player.addListener({
|
|
60
|
+
id: liveImageButtonsListenerKey,
|
|
61
|
+
listener: {
|
|
62
|
+
onPlayerMute: () => {
|
|
63
|
+
if (isCurrentPlayer(playerId)) {
|
|
64
|
+
setImageUri(asset.active);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
onPlayerUnmute: () => {
|
|
68
|
+
if (isCurrentPlayer(playerId)) {
|
|
69
|
+
setImageUri(asset.inactive);
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
onLoad() {
|
|
73
|
+
setImageUri(player?.getIsMuted() ? asset.active : asset.inactive);
|
|
74
|
+
},
|
|
75
|
+
onPlayerClose: () => {
|
|
76
|
+
setImageUri(null);
|
|
77
|
+
},
|
|
78
|
+
onVideoEnd: () => {
|
|
79
|
+
setImageUri(null);
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
});
|
|
72
83
|
}, [player, playerId, asset]);
|
|
73
84
|
|
|
74
85
|
const useListeners = MUTED_USAGE
|
|
@@ -21,7 +21,7 @@ const playActionCallback = (player) => {
|
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
const invokeAction = () => {
|
|
24
|
+
const invokeAction = (_item: ZappEntry, _object) => {
|
|
25
25
|
const actionCallback = MUTED_USAGE ? muteActionCallback : playActionCallback;
|
|
26
26
|
// We only allow one player to be present at a time
|
|
27
27
|
const player = LiveImageManager.instance.getCurrentlyPlaying()?.player;
|
|
@@ -29,6 +29,54 @@ const invokeAction = () => {
|
|
|
29
29
|
actionCallback(player);
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
// Not exported because currently the is no way
|
|
33
|
+
// to subscribe to the action availability change.
|
|
34
|
+
// We currently return a null state to disable the action.
|
|
35
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
36
|
+
const isActionAvailable = (item: ZappEntry) => {
|
|
37
|
+
return (
|
|
38
|
+
LiveImageManager.instance.getCurrentlyPlaying()?.getPlayer().entry.id ===
|
|
39
|
+
item.id
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const addListener = (
|
|
44
|
+
entryId: string,
|
|
45
|
+
listener: CellActionStateListenerFunction
|
|
46
|
+
) => {
|
|
47
|
+
const callback = (event: Record<string, any>) => {
|
|
48
|
+
if (event.entry?.id !== entryId) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const player = LiveImageManager.instance.getCurrentlyPlaying()?.getPlayer();
|
|
53
|
+
|
|
54
|
+
if (!player || player.entry?.id !== entryId) {
|
|
55
|
+
listener(null);
|
|
56
|
+
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const actionState = MUTED_USAGE ? player.getIsMuted() : player.isPaused();
|
|
61
|
+
|
|
62
|
+
listener({
|
|
63
|
+
state: actionState ? 1 : 0,
|
|
64
|
+
label: "",
|
|
65
|
+
asset: AssetComponent,
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return LiveImageManager.instance.addListener({
|
|
70
|
+
id: `cell-player-${entryId}`,
|
|
71
|
+
listener: {
|
|
72
|
+
onEnd: callback,
|
|
73
|
+
onStart: callback,
|
|
74
|
+
onError: callback,
|
|
75
|
+
onDestroy: callback,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
|
|
32
80
|
const emptyState = [];
|
|
33
81
|
|
|
34
82
|
const initialEntryState = () => ({
|
|
@@ -46,6 +94,7 @@ export function ContextProvider(Component: ReactComponent<any>) {
|
|
|
46
94
|
state: emptyState,
|
|
47
95
|
invokeAction,
|
|
48
96
|
initialEntryState,
|
|
97
|
+
addListener,
|
|
49
98
|
}),
|
|
50
99
|
[invokeAction, initialEntryState]
|
|
51
100
|
);
|
|
@@ -3,7 +3,8 @@ import { Text, TextStyle, View } from "react-native";
|
|
|
3
3
|
import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
4
4
|
|
|
5
5
|
type AudioDescriptionProps = {
|
|
6
|
-
|
|
6
|
+
title: string | number;
|
|
7
|
+
summary: string | number;
|
|
7
8
|
layoutState: {
|
|
8
9
|
inline: boolean;
|
|
9
10
|
docked: boolean;
|
|
@@ -78,8 +79,7 @@ const summaryStyles = (value: GetValue, prefix: string): TextStyle => ({
|
|
|
78
79
|
});
|
|
79
80
|
|
|
80
81
|
export const AudioDescription = (props: AudioDescriptionProps) => {
|
|
81
|
-
const {
|
|
82
|
-
const { title, summary } = content;
|
|
82
|
+
const { title, summary, layoutState, value } = props;
|
|
83
83
|
const prefix = layoutState.docked ? "docked_player_" : "";
|
|
84
84
|
|
|
85
85
|
return (
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { ViewStyle, View
|
|
2
|
+
import { ViewStyle, View } from "react-native";
|
|
3
3
|
|
|
4
4
|
import { SafeAreaView, Edge } from "react-native-safe-area-context";
|
|
5
5
|
import { usePlayerState } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerState";
|
|
@@ -15,7 +15,24 @@ import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
|
|
|
15
15
|
import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
|
|
16
16
|
import { PlayerDetails } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/PlayerDetails";
|
|
17
17
|
import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
18
|
+
import { useEffect, useState } from "react";
|
|
18
19
|
import { MagicBG } from "./MagicBG";
|
|
20
|
+
import {
|
|
21
|
+
formatSpeed,
|
|
22
|
+
hasDatePassed,
|
|
23
|
+
loadItemsFromStorage,
|
|
24
|
+
NAMESPACE,
|
|
25
|
+
} from "./utils";
|
|
26
|
+
import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
|
|
27
|
+
import { addQueryParam, getLiveUrl } from "../Utils";
|
|
28
|
+
import { useTitleSummaryOverlay } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerTitleSummaryOverlay";
|
|
29
|
+
import {
|
|
30
|
+
AnimationComponent,
|
|
31
|
+
ComponentAnimationType,
|
|
32
|
+
AnimatedScrollModal,
|
|
33
|
+
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
34
|
+
import { PADDING_HORIZONTAL } from "./Layout/defaultLayoutsConts";
|
|
35
|
+
import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
19
36
|
|
|
20
37
|
const { log_debug } = createLogger({
|
|
21
38
|
subsystem: "UIComponent",
|
|
@@ -32,6 +49,7 @@ type Dimensions = {
|
|
|
32
49
|
type Configuration = {
|
|
33
50
|
[key: string]: any;
|
|
34
51
|
magic_background?: boolean;
|
|
52
|
+
audio_player_background_image_query?: string;
|
|
35
53
|
tablet_landscape_sidebar_width?: string;
|
|
36
54
|
tablet_landscape_player_container_background_color?: string;
|
|
37
55
|
};
|
|
@@ -57,29 +75,20 @@ export const defaultAspectRatioWidth = (height: number): number =>
|
|
|
57
75
|
(height / 9) * 16;
|
|
58
76
|
|
|
59
77
|
const Styles = {
|
|
60
|
-
mainContainer: (
|
|
78
|
+
mainContainer: (style: ViewStyle) => {
|
|
61
79
|
const combinedStyle: ViewStyle = {
|
|
62
80
|
flex: 1,
|
|
63
81
|
};
|
|
64
82
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
style
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return { ...combinedStyle, ...style };
|
|
71
|
-
},
|
|
72
|
-
playerContainer: (docked: boolean, style: ViewStyle) => {
|
|
73
|
-
const combinedStyle: ViewStyle = {
|
|
74
|
-
flex: 1,
|
|
83
|
+
return {
|
|
84
|
+
...combinedStyle,
|
|
85
|
+
...style,
|
|
75
86
|
};
|
|
76
|
-
|
|
77
|
-
if (docked) {
|
|
78
|
-
combinedStyle.height = style.height;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return combinedStyle;
|
|
82
87
|
},
|
|
88
|
+
playerContainer: {
|
|
89
|
+
flex: 1,
|
|
90
|
+
},
|
|
91
|
+
paddingHorizontal: { paddingTop: 0, paddingHorizontal: PADDING_HORIZONTAL },
|
|
83
92
|
|
|
84
93
|
controlsMobile: {
|
|
85
94
|
height: 50,
|
|
@@ -96,6 +105,17 @@ const Styles = {
|
|
|
96
105
|
},
|
|
97
106
|
};
|
|
98
107
|
|
|
108
|
+
const sleeps = [
|
|
109
|
+
// { label: "End of episode", value: "episode" },
|
|
110
|
+
{ label: "5 minutes", value: 5 },
|
|
111
|
+
{ label: "10 minutes", value: 10 },
|
|
112
|
+
{ label: "15 minutes", value: 15 },
|
|
113
|
+
{ label: "30 minutes", value: 30 },
|
|
114
|
+
{ label: "45 minutes", value: 45 },
|
|
115
|
+
{ label: "1 hour", value: 60 },
|
|
116
|
+
{ label: "2 hours", value: 120 },
|
|
117
|
+
];
|
|
118
|
+
|
|
99
119
|
export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
100
120
|
const player = usePlayer();
|
|
101
121
|
const playerState = usePlayerState("audio-player-wrapper");
|
|
@@ -111,26 +131,141 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
111
131
|
isTabletPortrait,
|
|
112
132
|
} = props;
|
|
113
133
|
|
|
134
|
+
const speeds = [
|
|
135
|
+
{ asset: configuration["speed_0_8"], value: 0.8 },
|
|
136
|
+
{ asset: configuration["speed_1"], value: 1.0 },
|
|
137
|
+
{ asset: configuration["speed_1_2"], value: 1.2 },
|
|
138
|
+
{ asset: configuration["speed_1_5"], value: 1.5 },
|
|
139
|
+
{ asset: configuration["speed_2"], value: 2.0 },
|
|
140
|
+
];
|
|
141
|
+
|
|
114
142
|
const isTablet = useIsTablet();
|
|
115
143
|
|
|
116
144
|
const isTabletLandscape = !isTV() && isTablet && !isTabletPortrait;
|
|
117
145
|
|
|
146
|
+
// TODO: Have generic place for live url from configuration. Currently its hardcoded for client.
|
|
147
|
+
|
|
148
|
+
const feedUrl = getLiveUrl(entry?.extensions?.live?.channel_src ?? "");
|
|
149
|
+
|
|
150
|
+
const { title, summary } = useTitleSummaryOverlay(
|
|
151
|
+
feedUrl,
|
|
152
|
+
entry,
|
|
153
|
+
playerState?.isLive,
|
|
154
|
+
configuration?.audio_live_player_update_interval
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const [playbackSpeed, setPlaybackSpeed] = useState({
|
|
158
|
+
asset: configuration["speed_1"],
|
|
159
|
+
value: 1.0,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const [sleepTimer, setSleepTimer] = useState({
|
|
163
|
+
label: "playback_sleep_off_label",
|
|
164
|
+
value: 0,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const [sleepTimerDate, setSleepTimerDate] = useState(0);
|
|
168
|
+
|
|
169
|
+
const [defaultDimensions, setDefaultDimensions] =
|
|
170
|
+
React.useState<Dimensions>(undefined);
|
|
171
|
+
|
|
118
172
|
const [dimensions, setDimensions] = React.useState<Dimensions>({
|
|
119
173
|
width: 0,
|
|
120
174
|
height: 0,
|
|
121
175
|
});
|
|
122
176
|
|
|
177
|
+
const navigator = useNavigation();
|
|
178
|
+
|
|
123
179
|
const { maximiseVideoModal } = useNavigation();
|
|
124
180
|
|
|
125
181
|
const onLayout = (event: any) => {
|
|
126
182
|
const { width, height } = event.nativeEvent.layout;
|
|
127
183
|
|
|
128
184
|
setDimensions({
|
|
129
|
-
width
|
|
185
|
+
width,
|
|
130
186
|
height,
|
|
131
187
|
});
|
|
188
|
+
|
|
189
|
+
if (!defaultDimensions) setDefaultDimensions({ width, height });
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const cancelSleepTimer = async () => {
|
|
193
|
+
player?.cancelSleepTimer();
|
|
194
|
+
|
|
195
|
+
setSleepTimer({
|
|
196
|
+
label: "playback_sleep_off_label",
|
|
197
|
+
value: 0,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
setSleepTimerDate(0);
|
|
201
|
+
|
|
202
|
+
await localStorage.removeItem("sleep", NAMESPACE);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const startSleepTimer = (timerValue: number) => {
|
|
206
|
+
try {
|
|
207
|
+
const currentDate = new Date();
|
|
208
|
+
const futureDate = new Date(currentDate.getTime() + timerValue * 60000);
|
|
209
|
+
const futureSleepDate = futureDate.getTime();
|
|
210
|
+
|
|
211
|
+
player?.cancelSleepTimer();
|
|
212
|
+
|
|
213
|
+
player?.startSleepTimer(futureSleepDate);
|
|
214
|
+
|
|
215
|
+
return futureSleepDate;
|
|
216
|
+
} catch (e) {
|
|
217
|
+
return 0;
|
|
218
|
+
}
|
|
132
219
|
};
|
|
133
220
|
|
|
221
|
+
const handleSleep = async (
|
|
222
|
+
timerValue: number,
|
|
223
|
+
currentFutureSleepDate = null
|
|
224
|
+
) => {
|
|
225
|
+
if (currentFutureSleepDate) {
|
|
226
|
+
if (hasDatePassed(currentFutureSleepDate)) {
|
|
227
|
+
await cancelSleepTimer();
|
|
228
|
+
|
|
229
|
+
return 0;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return currentFutureSleepDate;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (timerValue === 0) {
|
|
236
|
+
await cancelSleepTimer();
|
|
237
|
+
|
|
238
|
+
return timerValue;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return startSleepTimer(timerValue);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
useEffect(() => {
|
|
245
|
+
const loadSpeed = async () => {
|
|
246
|
+
const speed = await loadItemsFromStorage("speed");
|
|
247
|
+
|
|
248
|
+
if (playerState.isReadyToPlay && speed) {
|
|
249
|
+
setPlaybackSpeed(speed);
|
|
250
|
+
player?.setPlaybackRate(speed.value);
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const loadSleep = async () => {
|
|
255
|
+
const sleep = await loadItemsFromStorage("sleep");
|
|
256
|
+
|
|
257
|
+
if (playerState.isReadyToPlay && sleep) {
|
|
258
|
+
const { futureSleepDate, ...rest } = sleep;
|
|
259
|
+
setSleepTimer(rest);
|
|
260
|
+
const sleepTimerDate = await handleSleep(sleep.value, futureSleepDate);
|
|
261
|
+
setSleepTimerDate(sleepTimerDate);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
loadSpeed().then();
|
|
266
|
+
loadSleep().then();
|
|
267
|
+
}, [playerState.isReadyToPlay]);
|
|
268
|
+
|
|
134
269
|
const getValue = (key: string, stylesObj: {}) =>
|
|
135
270
|
R.propOr(null, key, stylesObj);
|
|
136
271
|
|
|
@@ -160,7 +295,7 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
160
295
|
playerState.isPaused ? player?.play() : player.pause();
|
|
161
296
|
}, [playerState, player]);
|
|
162
297
|
|
|
163
|
-
const seekDuration =
|
|
298
|
+
const seekDuration = toNumberWithDefault(10, value("seek_duration"));
|
|
164
299
|
|
|
165
300
|
const onRewindButtonPress = React.useCallback(() => {
|
|
166
301
|
player?.rewind(seekDuration);
|
|
@@ -170,6 +305,60 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
170
305
|
player?.forward(seekDuration);
|
|
171
306
|
}, [seekDuration, player]);
|
|
172
307
|
|
|
308
|
+
const onSpeedSelect = React.useCallback(
|
|
309
|
+
async (item) => {
|
|
310
|
+
setPlaybackSpeed(item);
|
|
311
|
+
player.setPlaybackRate(item.value);
|
|
312
|
+
await localStorage.setItem("speed", item, NAMESPACE);
|
|
313
|
+
},
|
|
314
|
+
[player, setPlaybackSpeed]
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
const onSleepSelect = React.useCallback(
|
|
318
|
+
async (item) => {
|
|
319
|
+
setSleepTimer(item);
|
|
320
|
+
const futureSleepDate = await handleSleep(item.value);
|
|
321
|
+
setSleepTimerDate(futureSleepDate);
|
|
322
|
+
|
|
323
|
+
await localStorage.setItem(
|
|
324
|
+
"sleep",
|
|
325
|
+
{ ...item, futureSleepDate },
|
|
326
|
+
NAMESPACE
|
|
327
|
+
);
|
|
328
|
+
},
|
|
329
|
+
[player, setSleepTimer]
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
const onSpeedButtonPress = React.useCallback(() => {
|
|
333
|
+
navigator.openModalBottomSheet({
|
|
334
|
+
modalBottomSheetContentProps: {
|
|
335
|
+
items: speeds,
|
|
336
|
+
current_selection: playbackSpeed,
|
|
337
|
+
onPress: onSpeedSelect,
|
|
338
|
+
title: configuration["playback_speed_title"],
|
|
339
|
+
},
|
|
340
|
+
options: {
|
|
341
|
+
supportedOrientations: ["portrait"],
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
}, [playbackSpeed, setPlaybackSpeed]);
|
|
345
|
+
|
|
346
|
+
const onSleepButtonPress = React.useCallback(() => {
|
|
347
|
+
navigator.openModalBottomSheet({
|
|
348
|
+
modalBottomSheetContentProps: {
|
|
349
|
+
items: [
|
|
350
|
+
{ label: configuration["playback_sleep_off_label"], value: 0 },
|
|
351
|
+
].concat(sleeps),
|
|
352
|
+
current_selection: sleepTimer,
|
|
353
|
+
onPress: onSleepSelect,
|
|
354
|
+
title: configuration["playback_sleep_title"],
|
|
355
|
+
},
|
|
356
|
+
options: {
|
|
357
|
+
supportedOrientations: ["portrait"],
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
}, [sleepTimer, setSleepTimer, configuration]);
|
|
361
|
+
|
|
173
362
|
const onCloseVideoModal = React.useCallback(() => {
|
|
174
363
|
playerManager.close();
|
|
175
364
|
}, [playerManager]);
|
|
@@ -189,48 +378,65 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
189
378
|
player?.seekTo(0);
|
|
190
379
|
}, [player]);
|
|
191
380
|
|
|
192
|
-
const edges = React.useMemo<Edge[]>(
|
|
193
|
-
() => (docked ? [] : ["right", "left", "top"]),
|
|
194
|
-
[docked]
|
|
195
|
-
);
|
|
381
|
+
const edges = React.useMemo<Edge[]>(() => ["right", "left", "top"], [docked]);
|
|
196
382
|
|
|
197
|
-
const ContainerView =
|
|
383
|
+
const ContainerView = SafeAreaView;
|
|
198
384
|
|
|
199
385
|
const MainContent = React.useMemo(() => {
|
|
200
386
|
return (
|
|
201
|
-
<ContainerView style={Styles.mainContainer(
|
|
202
|
-
<
|
|
203
|
-
|
|
204
|
-
|
|
387
|
+
<ContainerView style={Styles.mainContainer(style)} edges={edges}>
|
|
388
|
+
<AnimationComponent
|
|
389
|
+
animationType={ComponentAnimationType.moveUpComponent}
|
|
390
|
+
additionalData={{ saveArea: true }}
|
|
391
|
+
style={Styles.playerContainer}
|
|
205
392
|
>
|
|
206
|
-
<
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
393
|
+
<AnimatedScrollModal>
|
|
394
|
+
<View style={Styles.playerContainer} onLayout={onLayout}>
|
|
395
|
+
<Layout
|
|
396
|
+
entry={entry}
|
|
397
|
+
playerState={playerState}
|
|
398
|
+
layoutState={layoutState}
|
|
399
|
+
value={value}
|
|
400
|
+
onPlaybackButtonPress={onPlaybackButtonPress}
|
|
401
|
+
onRewindButtonPress={onRewindButtonPress}
|
|
402
|
+
onForwardButtonPress={onForwardButtonPress}
|
|
403
|
+
onPlayerSeek={onPlayerSeek}
|
|
404
|
+
onSkipStart={onSkipStart}
|
|
405
|
+
onSpeedButtonPress={onSpeedButtonPress}
|
|
406
|
+
onSleepButtonPress={onSleepButtonPress}
|
|
407
|
+
playbackSpeedName={formatSpeed(playbackSpeed.value)}
|
|
408
|
+
playbackSleepName={
|
|
409
|
+
sleepTimer.value === 0 ? "sleep_timer" : "sleep_timer_active"
|
|
410
|
+
}
|
|
411
|
+
sleepTimerDate={sleepTimerDate}
|
|
412
|
+
cancelSleepTimer={cancelSleepTimer}
|
|
413
|
+
onSkipLive={onSkipLive}
|
|
414
|
+
onCloseVideoModal={onCloseVideoModal}
|
|
415
|
+
maximiseVideoModal={maximiseVideoModal}
|
|
416
|
+
configuration={configuration}
|
|
417
|
+
dimensions={dimensions}
|
|
418
|
+
playNextData={playNextData}
|
|
419
|
+
title={title}
|
|
420
|
+
summary={summary}
|
|
421
|
+
defaultDimensions={defaultDimensions}
|
|
422
|
+
/>
|
|
423
|
+
<View style={Styles.paddingHorizontal}>
|
|
424
|
+
{children}
|
|
425
|
+
<AnimationComponent
|
|
426
|
+
animationType={ComponentAnimationType.componentFade}
|
|
427
|
+
>
|
|
428
|
+
<PlayerDetails
|
|
429
|
+
configuration={configuration}
|
|
430
|
+
style={Styles.playerDetails}
|
|
431
|
+
entry={entry}
|
|
432
|
+
isTabletLandscape={isTabletLandscape}
|
|
433
|
+
isAudioPlayer={true}
|
|
434
|
+
/>
|
|
435
|
+
</AnimationComponent>
|
|
436
|
+
</View>
|
|
437
|
+
</View>
|
|
438
|
+
</AnimatedScrollModal>
|
|
439
|
+
</AnimationComponent>
|
|
234
440
|
</ContainerView>
|
|
235
441
|
);
|
|
236
442
|
}, [
|
|
@@ -252,12 +458,25 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
252
458
|
playNextData,
|
|
253
459
|
onSkipStart,
|
|
254
460
|
onSkipLive,
|
|
461
|
+
sleepTimer.value,
|
|
462
|
+
sleepTimerDate,
|
|
255
463
|
]);
|
|
256
464
|
|
|
257
465
|
if (configuration.magic_background) {
|
|
466
|
+
const baseUrl = entry?.extensions?.audio_player_background_image ?? "";
|
|
467
|
+
|
|
468
|
+
const queryParamName =
|
|
469
|
+
configuration.audio_player_background_image_query || "width";
|
|
470
|
+
|
|
471
|
+
const queryParamValue = 15;
|
|
472
|
+
|
|
473
|
+
const url = addQueryParam(baseUrl, queryParamName, queryParamValue);
|
|
474
|
+
|
|
258
475
|
return (
|
|
259
476
|
<MagicBG
|
|
260
|
-
image={
|
|
477
|
+
image={url}
|
|
478
|
+
fallbackUrl={baseUrl}
|
|
479
|
+
colors={entry?.extensions?.backgroundColors}
|
|
261
480
|
defaultColor="#000000"
|
|
262
481
|
>
|
|
263
482
|
{MainContent}
|