@applicaster/quick-brick-player 13.0.0-alpha.5718191297 → 13.0.0-alpha.5790181722
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 +8 -13
- package/src/CellPlayer/Assets/AssetsComponent.tsx +17 -3
- package/src/Player/AudioLayer/AudioDescription.tsx +4 -6
- package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +120 -198
- 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} +3 -13
- package/src/Player/AudioLayer/Layout/{DockedLayout.tsx → DockedControls/index.tsx} +44 -67
- 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/MagicBG/index.tsx +1 -1
- package/src/Player/AudioLayer/SleepTimerManager.ts +119 -0
- package/src/Player/AudioLayer/SpeedManager.ts +52 -0
- package/src/Player/AudioLayer/utils.ts +27 -0
- package/src/Player/Const/sleepTimerOptions.ts +10 -0
- package/src/Player/Const/speedOptions.ts +4 -0
- package/src/Player/Utils/Utils.ts +0 -5
- package/src/Player/Utils/index.tsx +117 -0
- package/src/Player/index.tsx +29 -17
- package/src/Player/AudioLayer/Layout/DefaultLayout.tsx +0 -452
- 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.5790181722",
|
|
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.95",
|
|
39
|
+
"@applicaster/quick-brick-tv-transport-controls": "13.0.0-rc.56",
|
|
40
|
+
"@applicaster/zapp-react-native-tvos-app": "13.0.0-alpha.5790181722",
|
|
41
|
+
"@applicaster/zapp-react-native-ui-components": "13.0.0-alpha.5790181722",
|
|
42
|
+
"@applicaster/zapp-react-native-utils": "13.0.0-alpha.5790181722",
|
|
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,12 +59,9 @@
|
|
|
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
|
-
"react": "
|
|
68
|
-
"react-
|
|
69
|
-
"react-native": "0.68.6",
|
|
63
|
+
"react": "*",
|
|
64
|
+
"react-native": "*",
|
|
70
65
|
"react-native-fs": "*",
|
|
71
66
|
"react-native-gesture-handler": "*",
|
|
72
67
|
"react-native-linear-gradient": "*",
|
|
@@ -55,6 +55,11 @@ const useMuteUnmuteListeners = (playerId, player, asset, setImageUri) =>
|
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
// initial state setup - show correct mute/unmute icon based on current state
|
|
59
|
+
if (player?.playerState?.isReadyToPlay) {
|
|
60
|
+
setImageUri(player.getIsMuted() ? asset.active : asset.inactive);
|
|
61
|
+
}
|
|
62
|
+
|
|
58
63
|
// active action = muted, inactive = unmuted
|
|
59
64
|
return player.addListener({
|
|
60
65
|
id: liveImageButtonsListenerKey,
|
|
@@ -73,10 +78,17 @@ const useMuteUnmuteListeners = (playerId, player, asset, setImageUri) =>
|
|
|
73
78
|
setImageUri(player?.getIsMuted() ? asset.active : asset.inactive);
|
|
74
79
|
},
|
|
75
80
|
onPlayerClose: () => {
|
|
76
|
-
|
|
81
|
+
if (isCurrentPlayer(playerId)) {
|
|
82
|
+
setImageUri(null);
|
|
83
|
+
}
|
|
77
84
|
},
|
|
78
85
|
onVideoEnd: () => {
|
|
79
|
-
|
|
86
|
+
if (isCurrentPlayer(playerId)) {
|
|
87
|
+
setImageUri(null);
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
onPlayerResume: () => {
|
|
91
|
+
setImageUri(player?.getIsMuted() ? asset.active : asset.inactive);
|
|
80
92
|
},
|
|
81
93
|
},
|
|
82
94
|
});
|
|
@@ -93,7 +105,7 @@ const styles = StyleSheet.create({
|
|
|
93
105
|
},
|
|
94
106
|
});
|
|
95
107
|
|
|
96
|
-
|
|
108
|
+
function AssetImage(props: CellActionAssetComponentProps) {
|
|
97
109
|
const { cellUUID: playerId, asset } = props;
|
|
98
110
|
const [imageUri, setImageUri] = useState<Asset>(null);
|
|
99
111
|
|
|
@@ -108,3 +120,5 @@ export function AssetComponent(props: CellActionAssetComponentProps) {
|
|
|
108
120
|
|
|
109
121
|
return <View />;
|
|
110
122
|
}
|
|
123
|
+
|
|
124
|
+
export const AssetComponent = React.memo(AssetImage);
|
|
@@ -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";
|
|
@@ -15,31 +20,36 @@ import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
|
|
|
15
20
|
import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
|
|
16
21
|
import { PlayerDetails } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/PlayerDetails";
|
|
17
22
|
import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
18
|
-
import {
|
|
23
|
+
import { useEffect, useState } from "react";
|
|
19
24
|
import { MagicBG } from "./MagicBG";
|
|
25
|
+
import { formatSpeed } from "./utils";
|
|
20
26
|
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
addQueryParam,
|
|
28
|
+
getBottomSheetModalDefaultItemIcon,
|
|
29
|
+
getBottomSheetModalIconProps,
|
|
30
|
+
getBottomSheetModalItemProps,
|
|
31
|
+
getBottomSheetModalLabelProps,
|
|
32
|
+
getBottomSheetModalSelectedItemIcon,
|
|
33
|
+
} from "../Utils";
|
|
28
34
|
import { useTitleSummaryOverlay } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerTitleSummaryOverlay";
|
|
29
35
|
import {
|
|
30
36
|
AnimationComponent,
|
|
31
37
|
ComponentAnimationType,
|
|
32
38
|
AnimatedScrollModal,
|
|
33
39
|
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
34
|
-
import { PADDING_HORIZONTAL } from "./Layout/
|
|
40
|
+
import { PADDING_HORIZONTAL } from "./Layout/const";
|
|
35
41
|
import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
36
42
|
import {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
} from "@applicaster/
|
|
43
|
+
SleepTimerController,
|
|
44
|
+
SleepTimerState,
|
|
45
|
+
SLEEP_TIMER_OFF,
|
|
46
|
+
} from "./SleepTimerManager";
|
|
47
|
+
import { SpeedController, PlaybackSpeed, DEFAULT_SPEED } from "./SpeedManager";
|
|
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 { sleepTimerOptions } from "../Const/sleepTimerOptions";
|
|
52
|
+
import { speedOptions } from "../Const/speedOptions";
|
|
43
53
|
|
|
44
54
|
const { log_debug } = createLogger({
|
|
45
55
|
subsystem: "UIComponent",
|
|
@@ -57,6 +67,7 @@ type Configuration = {
|
|
|
57
67
|
[key: string]: any;
|
|
58
68
|
magic_background?: boolean;
|
|
59
69
|
audio_player_background_image_query?: string;
|
|
70
|
+
audio_player_background_image_default_color?: string;
|
|
60
71
|
tablet_landscape_sidebar_width?: string;
|
|
61
72
|
tablet_landscape_player_container_background_color?: string;
|
|
62
73
|
};
|
|
@@ -78,54 +89,26 @@ type Props = {
|
|
|
78
89
|
isTabletPortrait?: boolean;
|
|
79
90
|
};
|
|
80
91
|
|
|
81
|
-
|
|
82
|
-
(height / 9) * 16;
|
|
83
|
-
|
|
84
|
-
const Styles = {
|
|
85
|
-
mainContainer: (style: ViewStyle) => {
|
|
86
|
-
const combinedStyle: ViewStyle = {
|
|
87
|
-
flex: 1,
|
|
88
|
-
};
|
|
92
|
+
const { width: SCREEN_WIDTH } = ScreenDimensions.get("screen");
|
|
89
93
|
|
|
90
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
},
|
|
104
|
-
controlsTablet: {
|
|
105
|
-
height: 96,
|
|
106
|
-
marginHorizontal: 24,
|
|
107
|
-
},
|
|
108
|
-
playerDetails: { marginTop: 60 },
|
|
109
|
-
|
|
110
|
-
linearGradient: {
|
|
111
|
-
flex: 1,
|
|
99
|
+
playerDetails: {
|
|
100
|
+
marginTop: 60,
|
|
101
|
+
alignSelf: "center",
|
|
102
|
+
width: SCREEN_WIDTH - PADDING_HORIZONTAL * 2,
|
|
112
103
|
},
|
|
113
|
-
};
|
|
104
|
+
});
|
|
114
105
|
|
|
115
|
-
const
|
|
116
|
-
// { label: "End of episode", value: "episode" },
|
|
117
|
-
{ label: "5 minutes", value: 5 },
|
|
118
|
-
{ label: "10 minutes", value: 10 },
|
|
119
|
-
{ label: "15 minutes", value: 15 },
|
|
120
|
-
{ label: "30 minutes", value: 30 },
|
|
121
|
-
{ label: "45 minutes", value: 45 },
|
|
122
|
-
{ label: "1 hour", value: 60 },
|
|
123
|
-
{ label: "2 hours", value: 120 },
|
|
124
|
-
];
|
|
106
|
+
const edges: Edge[] = ["right", "left", "top"];
|
|
125
107
|
|
|
126
108
|
export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
127
109
|
const player = usePlayer();
|
|
128
110
|
const playerState = usePlayerState("audio-player-wrapper");
|
|
111
|
+
const theme = useTheme();
|
|
129
112
|
|
|
130
113
|
const {
|
|
131
114
|
docked,
|
|
@@ -138,40 +121,39 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
138
121
|
isTabletPortrait,
|
|
139
122
|
} = props;
|
|
140
123
|
|
|
141
|
-
const speeds = [
|
|
142
|
-
{ asset: configuration["speed_0_8"] || speed_0_8, value: 0.8 },
|
|
143
|
-
{ asset: configuration["speed_1"] || speed_1, value: 1.0 },
|
|
144
|
-
{ asset: configuration["speed_1_2"] || speed_1_2, value: 1.2 },
|
|
145
|
-
{ asset: configuration["speed_1_5"] || speed_1_5, value: 1.5 },
|
|
146
|
-
{ asset: configuration["speed_2"] || speed_2, value: 2.0 },
|
|
147
|
-
];
|
|
148
|
-
|
|
149
124
|
const isTablet = useIsTablet();
|
|
150
125
|
|
|
151
126
|
const isTabletLandscape = !isTV() && isTablet && !isTabletPortrait;
|
|
152
127
|
|
|
153
|
-
// TODO: Have generic place for live url from configuration. Currently its hardcoded for client.
|
|
154
|
-
|
|
155
|
-
const feedUrl = getLiveUrl(entry?.extensions?.live?.channel_src ?? "");
|
|
156
|
-
|
|
157
128
|
const { title, summary } = useTitleSummaryOverlay(
|
|
158
|
-
feedUrl,
|
|
159
129
|
entry,
|
|
160
130
|
playerState?.isLive,
|
|
161
131
|
configuration?.audio_live_player_update_interval
|
|
162
132
|
);
|
|
163
133
|
|
|
164
|
-
const [
|
|
165
|
-
|
|
166
|
-
value: 1.0,
|
|
167
|
-
});
|
|
134
|
+
const [sleepTimerManager, setSleepTimerManager] =
|
|
135
|
+
useState<SleepTimerController | null>(null);
|
|
168
136
|
|
|
169
|
-
const [
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
137
|
+
const [speedManager, setSpeedManager] = useState<SpeedController | null>(
|
|
138
|
+
null
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
if (player) {
|
|
143
|
+
setSleepTimerManager(new SleepTimerController(player));
|
|
144
|
+
setSpeedManager(new SpeedController(player));
|
|
145
|
+
} else {
|
|
146
|
+
setSleepTimerManager(null);
|
|
147
|
+
setSpeedManager(null);
|
|
148
|
+
}
|
|
149
|
+
}, [player]);
|
|
150
|
+
|
|
151
|
+
const [playbackSpeed, setPlaybackSpeed] =
|
|
152
|
+
useState<PlaybackSpeed>(DEFAULT_SPEED);
|
|
173
153
|
|
|
174
|
-
const [
|
|
154
|
+
const [sleepTimerState, setSleepTimerState] = useState<SleepTimerState>(
|
|
155
|
+
new SleepTimerState(SLEEP_TIMER_OFF, 0)
|
|
156
|
+
);
|
|
175
157
|
|
|
176
158
|
const [defaultDimensions, setDefaultDimensions] =
|
|
177
159
|
React.useState<Dimensions>(undefined);
|
|
@@ -181,8 +163,6 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
181
163
|
height: 0,
|
|
182
164
|
});
|
|
183
165
|
|
|
184
|
-
const navigator = useNavigation();
|
|
185
|
-
|
|
186
166
|
const { maximiseVideoModal } = useNavigation();
|
|
187
167
|
|
|
188
168
|
const onLayout = (event: any) => {
|
|
@@ -196,90 +176,27 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
196
176
|
if (!defaultDimensions) setDefaultDimensions({ width, height });
|
|
197
177
|
};
|
|
198
178
|
|
|
199
|
-
const cancelSleepTimer = useCallback(async () => {
|
|
200
|
-
player?.cancelSleepTimer();
|
|
201
|
-
|
|
202
|
-
setSleepTimer({
|
|
203
|
-
label: "playback_sleep_off_label",
|
|
204
|
-
value: 0,
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
setSleepTimerDate(0);
|
|
208
|
-
|
|
209
|
-
await localStorage.removeItem("sleep", NAMESPACE);
|
|
210
|
-
}, [player, setSleepTimer, setSleepTimerDate]);
|
|
211
|
-
|
|
212
|
-
const startSleepTimer = useCallback(
|
|
213
|
-
(timerValue: number) => {
|
|
214
|
-
try {
|
|
215
|
-
const futureSleepDate = Date.now() + timerValue * 60000;
|
|
216
|
-
|
|
217
|
-
player?.cancelSleepTimer?.();
|
|
218
|
-
|
|
219
|
-
player?.startSleepTimer?.(futureSleepDate);
|
|
220
|
-
|
|
221
|
-
return futureSleepDate;
|
|
222
|
-
} catch (e) {
|
|
223
|
-
return 0;
|
|
224
|
-
}
|
|
225
|
-
},
|
|
226
|
-
[player]
|
|
227
|
-
);
|
|
228
|
-
|
|
229
|
-
const handleSleep = useCallback(
|
|
230
|
-
async (timerValue: number, currentFutureSleepDate = null) => {
|
|
231
|
-
if (currentFutureSleepDate) {
|
|
232
|
-
if (hasDatePassed(currentFutureSleepDate)) {
|
|
233
|
-
await cancelSleepTimer();
|
|
234
|
-
|
|
235
|
-
return 0;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
return currentFutureSleepDate;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (timerValue === 0) {
|
|
242
|
-
await cancelSleepTimer();
|
|
243
|
-
|
|
244
|
-
return timerValue;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
return startSleepTimer(timerValue);
|
|
248
|
-
},
|
|
249
|
-
[startSleepTimer, cancelSleepTimer]
|
|
250
|
-
);
|
|
251
|
-
|
|
252
179
|
useEffect(() => {
|
|
253
|
-
const
|
|
254
|
-
|
|
180
|
+
const sleepSubscription =
|
|
181
|
+
sleepTimerManager?.sleepTimerState$.subscribe(setSleepTimerState);
|
|
255
182
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
player?.setPlaybackRate(speed.value);
|
|
259
|
-
}
|
|
260
|
-
};
|
|
183
|
+
const speedSubscription =
|
|
184
|
+
speedManager?.playbackSpeed$.subscribe(setPlaybackSpeed);
|
|
261
185
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if (playerState.isReadyToPlay && sleep) {
|
|
266
|
-
const { futureSleepDate, ...rest } = sleep;
|
|
267
|
-
setSleepTimer(rest);
|
|
268
|
-
const sleepTimerDate = await handleSleep(sleep.value, futureSleepDate);
|
|
269
|
-
setSleepTimerDate(sleepTimerDate);
|
|
270
|
-
}
|
|
186
|
+
return () => {
|
|
187
|
+
sleepSubscription?.unsubscribe();
|
|
188
|
+
speedSubscription?.unsubscribe();
|
|
271
189
|
};
|
|
190
|
+
}, [sleepTimerManager, speedManager]);
|
|
272
191
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
player,
|
|
282
|
-
]);
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
if (!playerState.isReadyToPlay) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
void speedManager?.loadSpeed();
|
|
198
|
+
void sleepTimerManager?.loadSleep();
|
|
199
|
+
}, [playerState.isReadyToPlay, speedManager, sleepTimerManager]);
|
|
283
200
|
|
|
284
201
|
const getValue = (key: string, stylesObj: {}) =>
|
|
285
202
|
R.propOr(null, key, stylesObj);
|
|
@@ -322,73 +239,75 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
322
239
|
|
|
323
240
|
const onSpeedSelect = React.useCallback(
|
|
324
241
|
async (item) => {
|
|
325
|
-
setPlaybackSpeed(item);
|
|
326
|
-
player?.setPlaybackRate(item.value);
|
|
327
|
-
await localStorage.setItem("speed", item, NAMESPACE);
|
|
242
|
+
await speedManager?.setPlaybackSpeed(item);
|
|
328
243
|
},
|
|
329
|
-
[
|
|
244
|
+
[speedManager]
|
|
330
245
|
);
|
|
331
246
|
|
|
332
247
|
const onSleepSelect = React.useCallback(
|
|
333
248
|
async (item) => {
|
|
334
|
-
setSleepTimer(item);
|
|
335
|
-
const futureSleepDate = await handleSleep(item.value);
|
|
336
|
-
setSleepTimerDate(futureSleepDate);
|
|
337
|
-
|
|
338
|
-
await localStorage.setItem(
|
|
339
|
-
"sleep",
|
|
340
|
-
{ ...item, futureSleepDate },
|
|
341
|
-
NAMESPACE
|
|
342
|
-
);
|
|
249
|
+
await sleepTimerManager?.setSleepTimer(item);
|
|
343
250
|
},
|
|
344
|
-
[
|
|
251
|
+
[sleepTimerManager]
|
|
345
252
|
);
|
|
346
253
|
|
|
347
254
|
const onSpeedButtonPress = React.useCallback(() => {
|
|
348
|
-
|
|
255
|
+
openBottomSheetModal({
|
|
349
256
|
modalBottomSheetContentProps: {
|
|
350
|
-
items:
|
|
257
|
+
items: speedOptions,
|
|
351
258
|
current_selection: playbackSpeed,
|
|
352
259
|
onPress: onSpeedSelect,
|
|
353
260
|
title: configuration["playback_speed_title"],
|
|
261
|
+
itemProps: getBottomSheetModalItemProps,
|
|
262
|
+
iconProps: getBottomSheetModalIconProps,
|
|
263
|
+
labelProps: getBottomSheetModalLabelProps,
|
|
264
|
+
getSelectedItemIcon: getBottomSheetModalSelectedItemIcon,
|
|
265
|
+
getDefaultItemIcon: getBottomSheetModalDefaultItemIcon,
|
|
266
|
+
iconPlacement: "right",
|
|
354
267
|
},
|
|
355
268
|
options: {
|
|
356
269
|
supportedOrientations: ["portrait"],
|
|
357
270
|
},
|
|
358
271
|
});
|
|
359
|
-
}, [
|
|
360
|
-
playbackSpeed,
|
|
361
|
-
setPlaybackSpeed,
|
|
362
|
-
navigator,
|
|
363
|
-
onSpeedSelect,
|
|
364
|
-
configuration["playback_speed_title"],
|
|
365
|
-
]);
|
|
272
|
+
}, [playbackSpeed, onSpeedSelect, configuration["playback_speed_title"]]);
|
|
366
273
|
|
|
367
274
|
const onSleepButtonPress = React.useCallback(() => {
|
|
368
|
-
|
|
275
|
+
openBottomSheetModal({
|
|
369
276
|
modalBottomSheetContentProps: {
|
|
370
277
|
items: [
|
|
371
278
|
{
|
|
372
279
|
label: configuration["playback_sleep_off_label"],
|
|
373
280
|
value: 0,
|
|
374
281
|
},
|
|
375
|
-
].concat(
|
|
376
|
-
current_selection:
|
|
282
|
+
].concat(sleepTimerOptions),
|
|
283
|
+
current_selection: sleepTimerState.timer,
|
|
377
284
|
onPress: onSleepSelect,
|
|
378
285
|
title: configuration["playback_sleep_title"],
|
|
286
|
+
itemProps: getBottomSheetModalItemProps,
|
|
287
|
+
iconProps: getBottomSheetModalIconProps,
|
|
288
|
+
labelProps: getBottomSheetModalLabelProps,
|
|
289
|
+
getSelectedItemIcon: getBottomSheetModalSelectedItemIcon,
|
|
290
|
+
iconPlacement: "right",
|
|
379
291
|
},
|
|
380
292
|
options: {
|
|
381
293
|
supportedOrientations: ["portrait"],
|
|
382
294
|
},
|
|
383
295
|
});
|
|
384
296
|
}, [
|
|
385
|
-
|
|
386
|
-
sleepTimer,
|
|
297
|
+
sleepTimerState.timer,
|
|
387
298
|
configuration["playback_sleep_title"],
|
|
388
299
|
configuration["playback_sleep_off_label"],
|
|
389
300
|
onSleepSelect,
|
|
390
301
|
]);
|
|
391
302
|
|
|
303
|
+
// This method does not actually cancel the sleep timer
|
|
304
|
+
// (and should not to avoid race condition),
|
|
305
|
+
// it just performs the cleanup when timer expires
|
|
306
|
+
// TODO: rename it in code above
|
|
307
|
+
const cancelSleepTimer = React.useCallback(() => {
|
|
308
|
+
sleepTimerManager?.onTimerExpired();
|
|
309
|
+
}, [sleepTimerManager]);
|
|
310
|
+
|
|
392
311
|
const onCloseVideoModal = React.useCallback(() => {
|
|
393
312
|
playerManager.close();
|
|
394
313
|
}, [playerManager]);
|
|
@@ -408,13 +327,9 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
408
327
|
player?.seekTo(0);
|
|
409
328
|
}, [player]);
|
|
410
329
|
|
|
411
|
-
const edges = React.useMemo<Edge[]>(() => ["right", "left", "top"], [docked]);
|
|
412
|
-
|
|
413
|
-
const ContainerView = SafeAreaView;
|
|
414
|
-
|
|
415
330
|
const MainContent = React.useMemo(() => {
|
|
416
331
|
return (
|
|
417
|
-
<
|
|
332
|
+
<SafeAreaView style={[style, Styles.playerContainer]} edges={edges}>
|
|
418
333
|
<AnimationComponent
|
|
419
334
|
animationType={ComponentAnimationType.moveUpComponent}
|
|
420
335
|
additionalData={{ saveArea: true }}
|
|
@@ -436,9 +351,11 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
436
351
|
onSleepButtonPress={onSleepButtonPress}
|
|
437
352
|
playbackSpeedName={formatSpeed(playbackSpeed.value)}
|
|
438
353
|
playbackSleepName={
|
|
439
|
-
|
|
354
|
+
sleepTimerState.timer.value === 0
|
|
355
|
+
? "sleep_timer"
|
|
356
|
+
: "sleep_timer_active"
|
|
440
357
|
}
|
|
441
|
-
sleepTimerDate={
|
|
358
|
+
sleepTimerDate={sleepTimerState.timerDate}
|
|
442
359
|
cancelSleepTimer={cancelSleepTimer}
|
|
443
360
|
onSkipLive={onSkipLive}
|
|
444
361
|
onCloseVideoModal={onCloseVideoModal}
|
|
@@ -456,18 +373,18 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
456
373
|
animationType={ComponentAnimationType.componentFade}
|
|
457
374
|
>
|
|
458
375
|
<PlayerDetails
|
|
376
|
+
isAudioPlayer
|
|
459
377
|
configuration={configuration}
|
|
460
378
|
style={Styles.playerDetails}
|
|
461
379
|
entry={entry}
|
|
462
380
|
isTabletLandscape={isTabletLandscape}
|
|
463
|
-
isAudioPlayer={true}
|
|
464
381
|
/>
|
|
465
382
|
</AnimationComponent>
|
|
466
383
|
</View>
|
|
467
384
|
</View>
|
|
468
385
|
</AnimatedScrollModal>
|
|
469
386
|
</AnimationComponent>
|
|
470
|
-
</
|
|
387
|
+
</SafeAreaView>
|
|
471
388
|
);
|
|
472
389
|
}, [
|
|
473
390
|
docked,
|
|
@@ -488,26 +405,31 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
488
405
|
playNextData,
|
|
489
406
|
onSkipStart,
|
|
490
407
|
onSkipLive,
|
|
491
|
-
|
|
492
|
-
|
|
408
|
+
sleepTimerState.timer.value,
|
|
409
|
+
sleepTimerState.timerDate,
|
|
493
410
|
]);
|
|
494
411
|
|
|
495
412
|
if (configuration.magic_background) {
|
|
496
|
-
const baseUrl = entry
|
|
413
|
+
const baseUrl = imageSrcFromMediaItem(entry, null);
|
|
497
414
|
|
|
498
415
|
const queryParamName =
|
|
499
416
|
configuration.audio_player_background_image_query || "width";
|
|
500
417
|
|
|
418
|
+
const defaultBackgroundColor =
|
|
419
|
+
configuration.audio_player_background_image_default_color ||
|
|
420
|
+
theme?.app_background_color;
|
|
421
|
+
|
|
501
422
|
const queryParamValue = 15;
|
|
502
423
|
|
|
503
|
-
const url =
|
|
424
|
+
const url =
|
|
425
|
+
baseUrl && addQueryParam(baseUrl, queryParamName, queryParamValue);
|
|
504
426
|
|
|
505
427
|
return (
|
|
506
428
|
<MagicBG
|
|
507
429
|
image={url}
|
|
508
430
|
fallbackUrl={baseUrl}
|
|
509
431
|
colors={entry?.extensions?.backgroundColors}
|
|
510
|
-
defaultColor=
|
|
432
|
+
defaultColor={defaultBackgroundColor}
|
|
511
433
|
>
|
|
512
434
|
{MainContent}
|
|
513
435
|
</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
|
+
};
|