@applicaster/quick-brick-player 13.0.0-alpha.2245033574 → 13.0.0-alpha.2308533304
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 +5 -7
- package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +130 -205
- 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} +44 -67
- 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/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 +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 +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.2308533304",
|
|
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-alpha.
|
|
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-alpha.2308533304",
|
|
39
|
+
"@applicaster/quick-brick-tv-transport-controls": "13.0.0-rc.56",
|
|
40
|
+
"@applicaster/zapp-react-native-tvos-app": "13.0.0-alpha.2308533304",
|
|
41
|
+
"@applicaster/zapp-react-native-ui-components": "13.0.0-alpha.2308533304",
|
|
42
|
+
"@applicaster/zapp-react-native-utils": "13.0.0-alpha.2308533304",
|
|
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
|
-
|
|
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";
|
|
@@ -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 { getSleepTimerOptions } 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,78 @@ 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
|
-
supportedOrientations: [
|
|
269
|
+
supportedOrientations: [
|
|
270
|
+
"portrait",
|
|
271
|
+
"landscape",
|
|
272
|
+
"portrait-upside-down",
|
|
273
|
+
],
|
|
357
274
|
},
|
|
358
275
|
});
|
|
359
|
-
}, [
|
|
360
|
-
playbackSpeed,
|
|
361
|
-
setPlaybackSpeed,
|
|
362
|
-
navigator,
|
|
363
|
-
onSpeedSelect,
|
|
364
|
-
configuration["playback_speed_title"],
|
|
365
|
-
]);
|
|
276
|
+
}, [playbackSpeed, onSpeedSelect, configuration["playback_speed_title"]]);
|
|
366
277
|
|
|
367
278
|
const onSleepButtonPress = React.useCallback(() => {
|
|
368
|
-
|
|
279
|
+
openBottomSheetModal({
|
|
369
280
|
modalBottomSheetContentProps: {
|
|
370
|
-
items:
|
|
371
|
-
|
|
372
|
-
label: configuration["playback_sleep_off_label"],
|
|
373
|
-
value: 0,
|
|
374
|
-
},
|
|
375
|
-
].concat(sleeps),
|
|
376
|
-
current_selection: sleepTimer,
|
|
281
|
+
items: getSleepTimerOptions(configuration),
|
|
282
|
+
current_selection: sleepTimerState.timer,
|
|
377
283
|
onPress: onSleepSelect,
|
|
378
284
|
title: configuration["playback_sleep_title"],
|
|
285
|
+
itemProps: getBottomSheetModalItemProps,
|
|
286
|
+
iconProps: getBottomSheetModalIconProps,
|
|
287
|
+
labelProps: getBottomSheetModalLabelProps,
|
|
288
|
+
getSelectedItemIcon: getBottomSheetModalSelectedItemIcon,
|
|
289
|
+
iconPlacement: "right",
|
|
379
290
|
},
|
|
380
291
|
options: {
|
|
381
|
-
supportedOrientations: [
|
|
292
|
+
supportedOrientations: [
|
|
293
|
+
"portrait",
|
|
294
|
+
"landscape",
|
|
295
|
+
"portrait-upside-down",
|
|
296
|
+
],
|
|
382
297
|
},
|
|
383
298
|
});
|
|
384
299
|
}, [
|
|
385
|
-
|
|
386
|
-
sleepTimer,
|
|
300
|
+
sleepTimerState.timer,
|
|
387
301
|
configuration["playback_sleep_title"],
|
|
388
302
|
configuration["playback_sleep_off_label"],
|
|
389
303
|
onSleepSelect,
|
|
390
304
|
]);
|
|
391
305
|
|
|
306
|
+
// This method does not actually cancel the sleep timer
|
|
307
|
+
// (and should not to avoid race condition),
|
|
308
|
+
// it just performs the cleanup when timer expires
|
|
309
|
+
// TODO: rename it in code above
|
|
310
|
+
const cancelSleepTimer = React.useCallback(() => {
|
|
311
|
+
sleepTimerManager?.onTimerExpired();
|
|
312
|
+
}, [sleepTimerManager]);
|
|
313
|
+
|
|
392
314
|
const onCloseVideoModal = React.useCallback(() => {
|
|
393
315
|
playerManager.close();
|
|
394
316
|
}, [playerManager]);
|
|
@@ -408,13 +330,9 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
408
330
|
player?.seekTo(0);
|
|
409
331
|
}, [player]);
|
|
410
332
|
|
|
411
|
-
const edges = React.useMemo<Edge[]>(() => ["right", "left", "top"], [docked]);
|
|
412
|
-
|
|
413
|
-
const ContainerView = SafeAreaView;
|
|
414
|
-
|
|
415
333
|
const MainContent = React.useMemo(() => {
|
|
416
334
|
return (
|
|
417
|
-
<
|
|
335
|
+
<SafeAreaView style={[style, Styles.playerContainer]} edges={edges}>
|
|
418
336
|
<AnimationComponent
|
|
419
337
|
animationType={ComponentAnimationType.moveUpComponent}
|
|
420
338
|
additionalData={{ saveArea: true }}
|
|
@@ -436,9 +354,11 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
436
354
|
onSleepButtonPress={onSleepButtonPress}
|
|
437
355
|
playbackSpeedName={formatSpeed(playbackSpeed.value)}
|
|
438
356
|
playbackSleepName={
|
|
439
|
-
|
|
357
|
+
sleepTimerState.timer.value === 0
|
|
358
|
+
? "sleep_timer"
|
|
359
|
+
: "sleep_timer_active"
|
|
440
360
|
}
|
|
441
|
-
sleepTimerDate={
|
|
361
|
+
sleepTimerDate={sleepTimerState.timerDate}
|
|
442
362
|
cancelSleepTimer={cancelSleepTimer}
|
|
443
363
|
onSkipLive={onSkipLive}
|
|
444
364
|
onCloseVideoModal={onCloseVideoModal}
|
|
@@ -456,18 +376,18 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
456
376
|
animationType={ComponentAnimationType.componentFade}
|
|
457
377
|
>
|
|
458
378
|
<PlayerDetails
|
|
379
|
+
isAudioPlayer
|
|
459
380
|
configuration={configuration}
|
|
460
381
|
style={Styles.playerDetails}
|
|
461
382
|
entry={entry}
|
|
462
383
|
isTabletLandscape={isTabletLandscape}
|
|
463
|
-
isAudioPlayer={true}
|
|
464
384
|
/>
|
|
465
385
|
</AnimationComponent>
|
|
466
386
|
</View>
|
|
467
387
|
</View>
|
|
468
388
|
</AnimatedScrollModal>
|
|
469
389
|
</AnimationComponent>
|
|
470
|
-
</
|
|
390
|
+
</SafeAreaView>
|
|
471
391
|
);
|
|
472
392
|
}, [
|
|
473
393
|
docked,
|
|
@@ -488,26 +408,31 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
488
408
|
playNextData,
|
|
489
409
|
onSkipStart,
|
|
490
410
|
onSkipLive,
|
|
491
|
-
|
|
492
|
-
|
|
411
|
+
sleepTimerState.timer.value,
|
|
412
|
+
sleepTimerState.timerDate,
|
|
493
413
|
]);
|
|
494
414
|
|
|
495
415
|
if (configuration.magic_background) {
|
|
496
|
-
const baseUrl = entry
|
|
416
|
+
const baseUrl = imageSrcFromMediaItem(entry, null);
|
|
497
417
|
|
|
498
418
|
const queryParamName =
|
|
499
419
|
configuration.audio_player_background_image_query || "width";
|
|
500
420
|
|
|
421
|
+
const defaultBackgroundColor =
|
|
422
|
+
configuration.audio_player_background_image_default_color ||
|
|
423
|
+
theme?.app_background_color;
|
|
424
|
+
|
|
501
425
|
const queryParamValue = 15;
|
|
502
426
|
|
|
503
|
-
const url =
|
|
427
|
+
const url =
|
|
428
|
+
baseUrl && addQueryParam(baseUrl, queryParamName, queryParamValue);
|
|
504
429
|
|
|
505
430
|
return (
|
|
506
431
|
<MagicBG
|
|
507
432
|
image={url}
|
|
508
433
|
fallbackUrl={baseUrl}
|
|
509
434
|
colors={entry?.extensions?.backgroundColors}
|
|
510
|
-
defaultColor=
|
|
435
|
+
defaultColor={defaultBackgroundColor}
|
|
511
436
|
>
|
|
512
437
|
{MainContent}
|
|
513
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
|
+
};
|