@applicaster/quick-brick-player 13.0.0-rc.10 → 13.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 +27 -13
- package/src/CellPlayer/Assets/AssetsComponent.tsx +17 -3
- package/src/Player/AudioLayer/AudioDescription.tsx +5 -7
- package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +136 -181
- 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} +7 -25
- 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 +31 -19
- 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-rc.
|
|
3
|
+
"version": "13.0.0-rc.100",
|
|
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": "
|
|
41
|
-
"@applicaster/zapp-react-native-tvos-app": "13.0.0-rc.
|
|
42
|
-
"@applicaster/zapp-react-native-ui-components": "13.0.0-rc.
|
|
43
|
-
"@applicaster/zapp-react-native-utils": "13.0.0-rc.
|
|
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-rc.100",
|
|
41
|
+
"@applicaster/zapp-react-native-ui-components": "13.0.0-rc.100",
|
|
42
|
+
"@applicaster/zapp-react-native-utils": "13.0.0-rc.100",
|
|
45
43
|
"query-string": "7.1.3",
|
|
46
44
|
"react-native-web-linear-gradient": "1.1.2",
|
|
47
45
|
"shaka-player": "4.3.5",
|
|
@@ -51,12 +49,28 @@
|
|
|
51
49
|
"videojs-contrib-quality-levels": "3.0.0"
|
|
52
50
|
},
|
|
53
51
|
"peerDependencies": {
|
|
52
|
+
"@applicaster/applicaster-types": "*",
|
|
53
|
+
"@applicaster/blur": "*",
|
|
54
54
|
"@applicaster/quick-brick-xray": "*",
|
|
55
55
|
"@applicaster/react-native-color-thief": "*",
|
|
56
|
-
"@
|
|
57
|
-
"@
|
|
58
|
-
"react": "
|
|
59
|
-
"react-native": "
|
|
60
|
-
"
|
|
56
|
+
"@applicaster/zapp-pipes-v2-client": "*",
|
|
57
|
+
"@applicaster/zapp-react-dom-ui-components": "*",
|
|
58
|
+
"@applicaster/zapp-react-native-redux": "*",
|
|
59
|
+
"@applicaster/zapp-react-native-tvos-ui-components": "*",
|
|
60
|
+
"@babel/runtime": "*",
|
|
61
|
+
"@react-native-community/netinfo": "*",
|
|
62
|
+
"immer": "*",
|
|
63
|
+
"react": "*",
|
|
64
|
+
"react-native": "*",
|
|
65
|
+
"react-native-fs": "*",
|
|
66
|
+
"react-native-gesture-handler": "*",
|
|
67
|
+
"react-native-linear-gradient": "*",
|
|
68
|
+
"react-native-safe-area-context": "*",
|
|
69
|
+
"react-native-svg": "*",
|
|
70
|
+
"react-native-web": "*",
|
|
71
|
+
"react-native-webview": "*",
|
|
72
|
+
"uglify-js": "*",
|
|
73
|
+
"validate-color": "*",
|
|
74
|
+
"zustand": "*"
|
|
61
75
|
}
|
|
62
76
|
}
|
|
@@ -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";
|
|
@@ -17,22 +22,34 @@ import { PlayerDetails } from "@applicaster/zapp-react-native-ui-components/Comp
|
|
|
17
22
|
import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
18
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";
|
|
42
|
+
import {
|
|
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";
|
|
36
53
|
|
|
37
54
|
const { log_debug } = createLogger({
|
|
38
55
|
subsystem: "UIComponent",
|
|
@@ -50,6 +67,7 @@ type Configuration = {
|
|
|
50
67
|
[key: string]: any;
|
|
51
68
|
magic_background?: boolean;
|
|
52
69
|
audio_player_background_image_query?: string;
|
|
70
|
+
audio_player_background_image_default_color?: string;
|
|
53
71
|
tablet_landscape_sidebar_width?: string;
|
|
54
72
|
tablet_landscape_player_container_background_color?: string;
|
|
55
73
|
};
|
|
@@ -71,54 +89,26 @@ type Props = {
|
|
|
71
89
|
isTabletPortrait?: boolean;
|
|
72
90
|
};
|
|
73
91
|
|
|
74
|
-
|
|
75
|
-
(height / 9) * 16;
|
|
76
|
-
|
|
77
|
-
const Styles = {
|
|
78
|
-
mainContainer: (style: ViewStyle) => {
|
|
79
|
-
const combinedStyle: ViewStyle = {
|
|
80
|
-
flex: 1,
|
|
81
|
-
};
|
|
92
|
+
const { width: SCREEN_WIDTH } = ScreenDimensions.get("screen");
|
|
82
93
|
|
|
83
|
-
|
|
84
|
-
...combinedStyle,
|
|
85
|
-
...style,
|
|
86
|
-
};
|
|
87
|
-
},
|
|
94
|
+
const Styles = StyleSheet.create({
|
|
88
95
|
playerContainer: {
|
|
89
96
|
flex: 1,
|
|
90
97
|
},
|
|
91
98
|
paddingHorizontal: { paddingTop: 0, paddingHorizontal: PADDING_HORIZONTAL },
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
},
|
|
97
|
-
controlsTablet: {
|
|
98
|
-
height: 96,
|
|
99
|
-
marginHorizontal: 24,
|
|
99
|
+
playerDetails: {
|
|
100
|
+
marginTop: 60,
|
|
101
|
+
alignSelf: "center",
|
|
102
|
+
width: SCREEN_WIDTH - PADDING_HORIZONTAL * 2,
|
|
100
103
|
},
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
linearGradient: {
|
|
104
|
-
flex: 1,
|
|
105
|
-
},
|
|
106
|
-
};
|
|
104
|
+
});
|
|
107
105
|
|
|
108
|
-
const
|
|
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
|
-
];
|
|
106
|
+
const edges: Edge[] = ["right", "left", "top"];
|
|
118
107
|
|
|
119
108
|
export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
120
109
|
const player = usePlayer();
|
|
121
110
|
const playerState = usePlayerState("audio-player-wrapper");
|
|
111
|
+
const theme = useTheme();
|
|
122
112
|
|
|
123
113
|
const {
|
|
124
114
|
docked,
|
|
@@ -131,40 +121,39 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
131
121
|
isTabletPortrait,
|
|
132
122
|
} = props;
|
|
133
123
|
|
|
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
|
-
|
|
142
124
|
const isTablet = useIsTablet();
|
|
143
125
|
|
|
144
126
|
const isTabletLandscape = !isTV() && isTablet && !isTabletPortrait;
|
|
145
127
|
|
|
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
128
|
const { title, summary } = useTitleSummaryOverlay(
|
|
151
|
-
feedUrl,
|
|
152
129
|
entry,
|
|
153
130
|
playerState?.isLive,
|
|
154
131
|
configuration?.audio_live_player_update_interval
|
|
155
132
|
);
|
|
156
133
|
|
|
157
|
-
const [
|
|
158
|
-
|
|
159
|
-
value: 1.0,
|
|
160
|
-
});
|
|
134
|
+
const [sleepTimerManager, setSleepTimerManager] =
|
|
135
|
+
useState<SleepTimerController | null>(null);
|
|
161
136
|
|
|
162
|
-
const [
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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]);
|
|
166
150
|
|
|
167
|
-
const [
|
|
151
|
+
const [playbackSpeed, setPlaybackSpeed] =
|
|
152
|
+
useState<PlaybackSpeed>(DEFAULT_SPEED);
|
|
153
|
+
|
|
154
|
+
const [sleepTimerState, setSleepTimerState] = useState<SleepTimerState>(
|
|
155
|
+
new SleepTimerState(SLEEP_TIMER_OFF, 0)
|
|
156
|
+
);
|
|
168
157
|
|
|
169
158
|
const [defaultDimensions, setDefaultDimensions] =
|
|
170
159
|
React.useState<Dimensions>(undefined);
|
|
@@ -174,8 +163,6 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
174
163
|
height: 0,
|
|
175
164
|
});
|
|
176
165
|
|
|
177
|
-
const navigator = useNavigation();
|
|
178
|
-
|
|
179
166
|
const { maximiseVideoModal } = useNavigation();
|
|
180
167
|
|
|
181
168
|
const onLayout = (event: any) => {
|
|
@@ -189,82 +176,27 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
189
176
|
if (!defaultDimensions) setDefaultDimensions({ width, height });
|
|
190
177
|
};
|
|
191
178
|
|
|
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
|
-
}
|
|
219
|
-
};
|
|
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
179
|
useEffect(() => {
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (playerState.isReadyToPlay && speed) {
|
|
249
|
-
setPlaybackSpeed(speed);
|
|
250
|
-
player?.setPlaybackRate(speed.value);
|
|
251
|
-
}
|
|
252
|
-
};
|
|
180
|
+
const sleepSubscription =
|
|
181
|
+
sleepTimerManager?.sleepTimerState$.subscribe(setSleepTimerState);
|
|
253
182
|
|
|
254
|
-
const
|
|
255
|
-
|
|
183
|
+
const speedSubscription =
|
|
184
|
+
speedManager?.playbackSpeed$.subscribe(setPlaybackSpeed);
|
|
256
185
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const sleepTimerDate = await handleSleep(sleep.value, futureSleepDate);
|
|
261
|
-
setSleepTimerDate(sleepTimerDate);
|
|
262
|
-
}
|
|
186
|
+
return () => {
|
|
187
|
+
sleepSubscription?.unsubscribe();
|
|
188
|
+
speedSubscription?.unsubscribe();
|
|
263
189
|
};
|
|
190
|
+
}, [sleepTimerManager, speedManager]);
|
|
191
|
+
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
if (!playerState.isReadyToPlay) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
264
196
|
|
|
265
|
-
loadSpeed()
|
|
266
|
-
loadSleep()
|
|
267
|
-
}, [playerState.isReadyToPlay]);
|
|
197
|
+
void speedManager?.loadSpeed();
|
|
198
|
+
void sleepTimerManager?.loadSleep();
|
|
199
|
+
}, [playerState.isReadyToPlay, speedManager, sleepTimerManager]);
|
|
268
200
|
|
|
269
201
|
const getValue = (key: string, stylesObj: {}) =>
|
|
270
202
|
R.propOr(null, key, stylesObj);
|
|
@@ -292,7 +224,7 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
292
224
|
|
|
293
225
|
const onPlaybackButtonPress = React.useCallback(() => {
|
|
294
226
|
if (playerState.isBuffering) return;
|
|
295
|
-
playerState.isPaused ? player?.play() : player
|
|
227
|
+
playerState.isPaused ? player?.play() : player?.pause();
|
|
296
228
|
}, [playerState, player]);
|
|
297
229
|
|
|
298
230
|
const seekDuration = toNumberWithDefault(10, value("seek_duration"));
|
|
@@ -307,57 +239,77 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
307
239
|
|
|
308
240
|
const onSpeedSelect = React.useCallback(
|
|
309
241
|
async (item) => {
|
|
310
|
-
setPlaybackSpeed(item);
|
|
311
|
-
player.setPlaybackRate(item.value);
|
|
312
|
-
await localStorage.setItem("speed", item, NAMESPACE);
|
|
242
|
+
await speedManager?.setPlaybackSpeed(item);
|
|
313
243
|
},
|
|
314
|
-
[
|
|
244
|
+
[speedManager]
|
|
315
245
|
);
|
|
316
246
|
|
|
317
247
|
const onSleepSelect = React.useCallback(
|
|
318
248
|
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
|
-
);
|
|
249
|
+
await sleepTimerManager?.setSleepTimer(item);
|
|
328
250
|
},
|
|
329
|
-
[
|
|
251
|
+
[sleepTimerManager]
|
|
330
252
|
);
|
|
331
253
|
|
|
332
254
|
const onSpeedButtonPress = React.useCallback(() => {
|
|
333
|
-
|
|
255
|
+
openBottomSheetModal({
|
|
334
256
|
modalBottomSheetContentProps: {
|
|
335
|
-
items:
|
|
257
|
+
items: speedOptions,
|
|
336
258
|
current_selection: playbackSpeed,
|
|
337
259
|
onPress: onSpeedSelect,
|
|
338
260
|
title: configuration["playback_speed_title"],
|
|
261
|
+
itemProps: getBottomSheetModalItemProps,
|
|
262
|
+
iconProps: getBottomSheetModalIconProps,
|
|
263
|
+
labelProps: getBottomSheetModalLabelProps,
|
|
264
|
+
getSelectedItemIcon: getBottomSheetModalSelectedItemIcon,
|
|
265
|
+
getDefaultItemIcon: getBottomSheetModalDefaultItemIcon,
|
|
266
|
+
iconPlacement: "right",
|
|
339
267
|
},
|
|
340
268
|
options: {
|
|
341
|
-
supportedOrientations: [
|
|
269
|
+
supportedOrientations: [
|
|
270
|
+
"portrait",
|
|
271
|
+
"landscape",
|
|
272
|
+
"portrait-upside-down",
|
|
273
|
+
],
|
|
342
274
|
},
|
|
343
275
|
});
|
|
344
|
-
}, [playbackSpeed,
|
|
276
|
+
}, [playbackSpeed, onSpeedSelect, configuration["playback_speed_title"]]);
|
|
345
277
|
|
|
346
278
|
const onSleepButtonPress = React.useCallback(() => {
|
|
347
|
-
|
|
279
|
+
openBottomSheetModal({
|
|
348
280
|
modalBottomSheetContentProps: {
|
|
349
|
-
items:
|
|
350
|
-
|
|
351
|
-
].concat(sleeps),
|
|
352
|
-
current_selection: sleepTimer,
|
|
281
|
+
items: getSleepTimerOptions(configuration),
|
|
282
|
+
current_selection: sleepTimerState.timer,
|
|
353
283
|
onPress: onSleepSelect,
|
|
354
284
|
title: configuration["playback_sleep_title"],
|
|
285
|
+
itemProps: getBottomSheetModalItemProps,
|
|
286
|
+
iconProps: getBottomSheetModalIconProps,
|
|
287
|
+
labelProps: getBottomSheetModalLabelProps,
|
|
288
|
+
getSelectedItemIcon: getBottomSheetModalSelectedItemIcon,
|
|
289
|
+
iconPlacement: "right",
|
|
355
290
|
},
|
|
356
291
|
options: {
|
|
357
|
-
supportedOrientations: [
|
|
292
|
+
supportedOrientations: [
|
|
293
|
+
"portrait",
|
|
294
|
+
"landscape",
|
|
295
|
+
"portrait-upside-down",
|
|
296
|
+
],
|
|
358
297
|
},
|
|
359
298
|
});
|
|
360
|
-
}, [
|
|
299
|
+
}, [
|
|
300
|
+
sleepTimerState.timer,
|
|
301
|
+
configuration["playback_sleep_title"],
|
|
302
|
+
configuration["playback_sleep_off_label"],
|
|
303
|
+
onSleepSelect,
|
|
304
|
+
]);
|
|
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]);
|
|
361
313
|
|
|
362
314
|
const onCloseVideoModal = React.useCallback(() => {
|
|
363
315
|
playerManager.close();
|
|
@@ -378,13 +330,9 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
378
330
|
player?.seekTo(0);
|
|
379
331
|
}, [player]);
|
|
380
332
|
|
|
381
|
-
const edges = React.useMemo<Edge[]>(() => ["right", "left", "top"], [docked]);
|
|
382
|
-
|
|
383
|
-
const ContainerView = SafeAreaView;
|
|
384
|
-
|
|
385
333
|
const MainContent = React.useMemo(() => {
|
|
386
334
|
return (
|
|
387
|
-
<
|
|
335
|
+
<SafeAreaView style={[style, Styles.playerContainer]} edges={edges}>
|
|
388
336
|
<AnimationComponent
|
|
389
337
|
animationType={ComponentAnimationType.moveUpComponent}
|
|
390
338
|
additionalData={{ saveArea: true }}
|
|
@@ -406,9 +354,11 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
406
354
|
onSleepButtonPress={onSleepButtonPress}
|
|
407
355
|
playbackSpeedName={formatSpeed(playbackSpeed.value)}
|
|
408
356
|
playbackSleepName={
|
|
409
|
-
|
|
357
|
+
sleepTimerState.timer.value === 0
|
|
358
|
+
? "sleep_timer"
|
|
359
|
+
: "sleep_timer_active"
|
|
410
360
|
}
|
|
411
|
-
sleepTimerDate={
|
|
361
|
+
sleepTimerDate={sleepTimerState.timerDate}
|
|
412
362
|
cancelSleepTimer={cancelSleepTimer}
|
|
413
363
|
onSkipLive={onSkipLive}
|
|
414
364
|
onCloseVideoModal={onCloseVideoModal}
|
|
@@ -426,18 +376,18 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
426
376
|
animationType={ComponentAnimationType.componentFade}
|
|
427
377
|
>
|
|
428
378
|
<PlayerDetails
|
|
379
|
+
isAudioPlayer
|
|
429
380
|
configuration={configuration}
|
|
430
381
|
style={Styles.playerDetails}
|
|
431
382
|
entry={entry}
|
|
432
383
|
isTabletLandscape={isTabletLandscape}
|
|
433
|
-
isAudioPlayer={true}
|
|
434
384
|
/>
|
|
435
385
|
</AnimationComponent>
|
|
436
386
|
</View>
|
|
437
387
|
</View>
|
|
438
388
|
</AnimatedScrollModal>
|
|
439
389
|
</AnimationComponent>
|
|
440
|
-
</
|
|
390
|
+
</SafeAreaView>
|
|
441
391
|
);
|
|
442
392
|
}, [
|
|
443
393
|
docked,
|
|
@@ -458,26 +408,31 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
458
408
|
playNextData,
|
|
459
409
|
onSkipStart,
|
|
460
410
|
onSkipLive,
|
|
461
|
-
|
|
462
|
-
|
|
411
|
+
sleepTimerState.timer.value,
|
|
412
|
+
sleepTimerState.timerDate,
|
|
463
413
|
]);
|
|
464
414
|
|
|
465
415
|
if (configuration.magic_background) {
|
|
466
|
-
const baseUrl = entry
|
|
416
|
+
const baseUrl = imageSrcFromMediaItem(entry, null);
|
|
467
417
|
|
|
468
418
|
const queryParamName =
|
|
469
419
|
configuration.audio_player_background_image_query || "width";
|
|
470
420
|
|
|
421
|
+
const defaultBackgroundColor =
|
|
422
|
+
configuration.audio_player_background_image_default_color ||
|
|
423
|
+
theme?.app_background_color;
|
|
424
|
+
|
|
471
425
|
const queryParamValue = 15;
|
|
472
426
|
|
|
473
|
-
const url =
|
|
427
|
+
const url =
|
|
428
|
+
baseUrl && addQueryParam(baseUrl, queryParamName, queryParamValue);
|
|
474
429
|
|
|
475
430
|
return (
|
|
476
431
|
<MagicBG
|
|
477
432
|
image={url}
|
|
478
433
|
fallbackUrl={baseUrl}
|
|
479
434
|
colors={entry?.extensions?.backgroundColors}
|
|
480
|
-
defaultColor=
|
|
435
|
+
defaultColor={defaultBackgroundColor}
|
|
481
436
|
>
|
|
482
437
|
{MainContent}
|
|
483
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
|
+
};
|