@applicaster/quick-brick-player 13.0.0-alpha.3003916059 → 13.0.0-alpha.3013095924
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 +5 -5
- package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +81 -4
- package/src/Player/AudioLayer/Layout/Controls/index.tsx +49 -15
- package/src/Player/AudioLayer/Layout/Controls/utils.ts +2 -2
- package/src/Player/AudioLayer/Layout/DockedControls/index.tsx +7 -24
- package/src/Player/AudioLayer/Layout/MobileLayout.tsx +8 -0
- package/src/Player/AudioLayer/Layout/PlayerImage/AnimatedImage.tsx +32 -36
- package/src/Player/AudioLayer/Layout/PlayerImage/index.tsx +5 -9
- package/src/Player/AudioLayer/Layout/PlayerImage/styles.ts +1 -5
- package/src/Player/AudioLayer/Layout/TabletLandscapeLayout.tsx +8 -0
- package/src/Player/AudioLayer/Layout/TabletPortraitLayout.tsx +8 -0
- package/src/Player/AudioLayer/SleepTimerManager.ts +11 -22
- package/src/Player/Const/sleepTimerOptions.ts +5 -7
- package/src/Player/Utils/index.tsx +0 -6
- package/src/Player/index.tsx +3 -52
- package/src/Player/AudioLayer/Layout/PlayerImage/hooks/index.ts +0 -1
- package/src/Player/AudioLayer/Layout/PlayerImage/hooks/useChangePlayerState.ts +0 -99
- package/src/Player/AudioLayer/components/SleepTimerModalHeader/SleepTimerModalHeader.tsx +0 -66
- package/src/Player/AudioLayer/components/SleepTimerModalHeader/index.ts +0 -1
- package/src/Player/AudioLayer/useSleepTimerModal.ts +0 -109
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/quick-brick-player",
|
|
3
|
-
"version": "13.0.0-alpha.
|
|
3
|
+
"version": "13.0.0-alpha.3013095924",
|
|
4
4
|
"description": "Quick Brick Player",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
},
|
|
36
36
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@applicaster/quick-brick-mobile-transport-controls": "13.0.0-
|
|
38
|
+
"@applicaster/quick-brick-mobile-transport-controls": "13.0.0-alpha.3013095924",
|
|
39
39
|
"@applicaster/quick-brick-tv-transport-controls": "13.0.0-rc.56",
|
|
40
|
-
"@applicaster/zapp-react-native-tvos-app": "13.0.0-alpha.
|
|
41
|
-
"@applicaster/zapp-react-native-ui-components": "13.0.0-alpha.
|
|
42
|
-
"@applicaster/zapp-react-native-utils": "13.0.0-alpha.
|
|
40
|
+
"@applicaster/zapp-react-native-tvos-app": "13.0.0-alpha.3013095924",
|
|
41
|
+
"@applicaster/zapp-react-native-ui-components": "13.0.0-alpha.3013095924",
|
|
42
|
+
"@applicaster/zapp-react-native-utils": "13.0.0-alpha.3013095924",
|
|
43
43
|
"query-string": "7.1.3",
|
|
44
44
|
"react-native-web-linear-gradient": "1.1.2",
|
|
45
45
|
"shaka-player": "4.3.5",
|
|
@@ -37,13 +37,18 @@ import {
|
|
|
37
37
|
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
38
38
|
import { PADDING_HORIZONTAL } from "./Layout/const";
|
|
39
39
|
import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
40
|
-
|
|
40
|
+
import {
|
|
41
|
+
SLEEP_TIMER_OFF,
|
|
42
|
+
SleepTimerController,
|
|
43
|
+
SleepTimerState,
|
|
44
|
+
} from "./SleepTimerManager";
|
|
41
45
|
import { DEFAULT_SPEED, PlaybackSpeed, SpeedController } from "./SpeedManager";
|
|
42
46
|
import {
|
|
43
47
|
dismissModal as dismissBottomSheetModal,
|
|
44
48
|
openBottomSheetModal,
|
|
45
49
|
useModalStoreState,
|
|
46
50
|
} from "@applicaster/zapp-react-native-utils/modalState";
|
|
51
|
+
import { getSleepTimerOptions } from "../Const/sleepTimerOptions";
|
|
47
52
|
import { speedOptions } from "../Const/speedOptions";
|
|
48
53
|
|
|
49
54
|
const { log_debug } = createLogger({
|
|
@@ -123,14 +128,19 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
123
128
|
configuration?.audio_live_player_update_interval
|
|
124
129
|
);
|
|
125
130
|
|
|
131
|
+
const [sleepTimerManager, setSleepTimerManager] =
|
|
132
|
+
useState<SleepTimerController | null>(null);
|
|
133
|
+
|
|
126
134
|
const [speedManager, setSpeedManager] = useState<SpeedController | null>(
|
|
127
135
|
null
|
|
128
136
|
);
|
|
129
137
|
|
|
130
138
|
useEffect(() => {
|
|
131
139
|
if (player) {
|
|
140
|
+
setSleepTimerManager(new SleepTimerController(player));
|
|
132
141
|
setSpeedManager(new SpeedController(player));
|
|
133
142
|
} else {
|
|
143
|
+
setSleepTimerManager(null);
|
|
134
144
|
setSpeedManager(null);
|
|
135
145
|
}
|
|
136
146
|
}, [player]);
|
|
@@ -151,11 +161,22 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
151
161
|
});
|
|
152
162
|
|
|
153
163
|
return () => unsubscribe?.();
|
|
154
|
-
}, [player, modalBottomSheetState.visible]);
|
|
164
|
+
}, [player, modalBottomSheetState.visible, sleepTimerManager]);
|
|
165
|
+
|
|
166
|
+
React.useEffect(() => {
|
|
167
|
+
if (playerState?.isLive) {
|
|
168
|
+
// Resetting a sleep timer when audio content is live (Timer not supported on live video)
|
|
169
|
+
sleepTimerManager.setSleepTimer(SLEEP_TIMER_OFF).catch();
|
|
170
|
+
}
|
|
171
|
+
}, [playerState?.isLive]);
|
|
155
172
|
|
|
156
173
|
const [playbackSpeed, setPlaybackSpeed] =
|
|
157
174
|
useState<PlaybackSpeed>(DEFAULT_SPEED);
|
|
158
175
|
|
|
176
|
+
const [sleepTimerState, setSleepTimerState] = useState<SleepTimerState>(
|
|
177
|
+
new SleepTimerState(SLEEP_TIMER_OFF, 0)
|
|
178
|
+
);
|
|
179
|
+
|
|
159
180
|
const [defaultDimensions, setDefaultDimensions] =
|
|
160
181
|
React.useState<Dimensions>(undefined);
|
|
161
182
|
|
|
@@ -178,13 +199,17 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
178
199
|
};
|
|
179
200
|
|
|
180
201
|
useEffect(() => {
|
|
202
|
+
const sleepSubscription =
|
|
203
|
+
sleepTimerManager?.sleepTimerState$.subscribe(setSleepTimerState);
|
|
204
|
+
|
|
181
205
|
const speedSubscription =
|
|
182
206
|
speedManager?.playbackSpeed$.subscribe(setPlaybackSpeed);
|
|
183
207
|
|
|
184
208
|
return () => {
|
|
209
|
+
sleepSubscription?.unsubscribe();
|
|
185
210
|
speedSubscription?.unsubscribe();
|
|
186
211
|
};
|
|
187
|
-
}, [speedManager]);
|
|
212
|
+
}, [sleepTimerManager, speedManager]);
|
|
188
213
|
|
|
189
214
|
useEffect(() => {
|
|
190
215
|
if (!playerState.isReadyToPlay) {
|
|
@@ -192,7 +217,8 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
192
217
|
}
|
|
193
218
|
|
|
194
219
|
void speedManager?.loadSpeed();
|
|
195
|
-
|
|
220
|
+
void sleepTimerManager?.loadSleep();
|
|
221
|
+
}, [playerState.isReadyToPlay, speedManager, sleepTimerManager]);
|
|
196
222
|
|
|
197
223
|
const getValue = (key: string, stylesObj: {}) =>
|
|
198
224
|
R.propOr(null, key, stylesObj);
|
|
@@ -240,6 +266,13 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
240
266
|
[speedManager]
|
|
241
267
|
);
|
|
242
268
|
|
|
269
|
+
const onSleepSelect = React.useCallback(
|
|
270
|
+
async (item) => {
|
|
271
|
+
await sleepTimerManager?.setSleepTimer(item);
|
|
272
|
+
},
|
|
273
|
+
[sleepTimerManager]
|
|
274
|
+
);
|
|
275
|
+
|
|
243
276
|
const onSpeedButtonPress = React.useCallback(() => {
|
|
244
277
|
openBottomSheetModal({
|
|
245
278
|
modalBottomSheetContentProps: {
|
|
@@ -264,6 +297,42 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
264
297
|
});
|
|
265
298
|
}, [playbackSpeed, onSpeedSelect, configuration["playback_speed_title"]]);
|
|
266
299
|
|
|
300
|
+
const onSleepButtonPress = React.useCallback(() => {
|
|
301
|
+
openBottomSheetModal({
|
|
302
|
+
modalBottomSheetContentProps: {
|
|
303
|
+
items: getSleepTimerOptions(configuration),
|
|
304
|
+
current_selection: sleepTimerState.timer,
|
|
305
|
+
onPress: onSleepSelect,
|
|
306
|
+
title: configuration["playback_sleep_title"],
|
|
307
|
+
itemProps: getBottomSheetModalItemProps,
|
|
308
|
+
iconProps: getBottomSheetModalIconProps,
|
|
309
|
+
labelProps: getBottomSheetModalLabelProps,
|
|
310
|
+
getSelectedItemIcon: getBottomSheetModalSelectedItemIcon,
|
|
311
|
+
iconPlacement: "right",
|
|
312
|
+
},
|
|
313
|
+
options: {
|
|
314
|
+
supportedOrientations: [
|
|
315
|
+
"portrait",
|
|
316
|
+
"landscape",
|
|
317
|
+
"portrait-upside-down",
|
|
318
|
+
],
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
}, [
|
|
322
|
+
sleepTimerState.timer,
|
|
323
|
+
configuration["playback_sleep_title"],
|
|
324
|
+
configuration["playback_sleep_off_label"],
|
|
325
|
+
onSleepSelect,
|
|
326
|
+
]);
|
|
327
|
+
|
|
328
|
+
// This method does not actually cancel the sleep timer
|
|
329
|
+
// (and should not to avoid race condition),
|
|
330
|
+
// it just performs the cleanup when timer expires
|
|
331
|
+
// TODO: rename it in code above
|
|
332
|
+
const cancelSleepTimer = React.useCallback(() => {
|
|
333
|
+
sleepTimerManager?.onTimerExpired();
|
|
334
|
+
}, [sleepTimerManager]);
|
|
335
|
+
|
|
267
336
|
const onCloseVideoModal = React.useCallback(() => {
|
|
268
337
|
playerManager.close();
|
|
269
338
|
}, [playerManager]);
|
|
@@ -303,7 +372,15 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
303
372
|
onPlayerSeek={onPlayerSeek}
|
|
304
373
|
onSkipStart={onSkipStart}
|
|
305
374
|
onSpeedButtonPress={onSpeedButtonPress}
|
|
375
|
+
onSleepButtonPress={onSleepButtonPress}
|
|
306
376
|
playbackSpeedName={formatSpeed(playbackSpeed.value)}
|
|
377
|
+
playbackSleepName={
|
|
378
|
+
sleepTimerState.timer.value === 0
|
|
379
|
+
? "sleep_timer"
|
|
380
|
+
: "sleep_timer_active"
|
|
381
|
+
}
|
|
382
|
+
sleepTimerDate={sleepTimerState.timerDate}
|
|
383
|
+
cancelSleepTimer={cancelSleepTimer}
|
|
307
384
|
onSkipLive={onSkipLive}
|
|
308
385
|
onCloseVideoModal={onCloseVideoModal}
|
|
309
386
|
maximiseVideoModal={maximiseVideoModal}
|
|
@@ -2,10 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import { StyleSheet } from "react-native";
|
|
3
3
|
|
|
4
4
|
import { dayjs } from "@applicaster/zapp-react-native-utils/dateUtils";
|
|
5
|
-
import {
|
|
6
|
-
useIsTablet,
|
|
7
|
-
useZStore,
|
|
8
|
-
} from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
5
|
+
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
9
6
|
import {
|
|
10
7
|
Controls as MobileTransportControls,
|
|
11
8
|
ProgressBar,
|
|
@@ -30,7 +27,6 @@ import {
|
|
|
30
27
|
sleepButton,
|
|
31
28
|
speedButton,
|
|
32
29
|
} from "./utils";
|
|
33
|
-
import { useSleepTimerModal } from "../../useSleepTimerModal";
|
|
34
30
|
|
|
35
31
|
const styles = StyleSheet.create({
|
|
36
32
|
flex: {
|
|
@@ -68,8 +64,11 @@ type Props = {
|
|
|
68
64
|
value: GetValue;
|
|
69
65
|
title: string | number;
|
|
70
66
|
summary: string | number;
|
|
67
|
+
sleepTimerDate: number;
|
|
68
|
+
cancelSleepTimer: Callback;
|
|
71
69
|
|
|
72
70
|
playbackSpeedName: string;
|
|
71
|
+
playbackSleepName: string;
|
|
73
72
|
|
|
74
73
|
onPlaybackButtonPress: Callback;
|
|
75
74
|
onRewindButtonPress: Callback;
|
|
@@ -78,6 +77,7 @@ type Props = {
|
|
|
78
77
|
onSkipStart: Callback;
|
|
79
78
|
onSkipLive: Callback;
|
|
80
79
|
onSpeedButtonPress: Callback;
|
|
80
|
+
onSleepButtonPress: Callback;
|
|
81
81
|
|
|
82
82
|
maxWidth?: number;
|
|
83
83
|
|
|
@@ -92,8 +92,11 @@ export const Controls = (props: Props) => {
|
|
|
92
92
|
value,
|
|
93
93
|
title,
|
|
94
94
|
summary,
|
|
95
|
+
sleepTimerDate,
|
|
96
|
+
cancelSleepTimer,
|
|
95
97
|
|
|
96
98
|
playbackSpeedName,
|
|
99
|
+
playbackSleepName,
|
|
97
100
|
|
|
98
101
|
onPlaybackButtonPress,
|
|
99
102
|
onRewindButtonPress,
|
|
@@ -102,21 +105,12 @@ export const Controls = (props: Props) => {
|
|
|
102
105
|
onSkipStart,
|
|
103
106
|
onSkipLive,
|
|
104
107
|
onSpeedButtonPress,
|
|
108
|
+
onSleepButtonPress,
|
|
105
109
|
maxWidth = 320,
|
|
106
110
|
|
|
107
111
|
docked,
|
|
108
112
|
} = props;
|
|
109
113
|
|
|
110
|
-
const configuration = useZStore("playerConfiguration");
|
|
111
|
-
|
|
112
|
-
const { sleepTimerState, onSleepButtonPress } = useSleepTimerModal(
|
|
113
|
-
playerManager.getActivePlayer(),
|
|
114
|
-
configuration
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
const playbackSleepName =
|
|
118
|
-
sleepTimerState.timer.value === 0 ? "sleep_timer" : "sleep_timer_active";
|
|
119
|
-
|
|
120
114
|
const isTablet = useIsTablet();
|
|
121
115
|
const controller = playerManager.getActivePlayer();
|
|
122
116
|
let showFullControls = !controller?.isAd();
|
|
@@ -130,6 +124,45 @@ export const Controls = (props: Props) => {
|
|
|
130
124
|
showFullControls = !!(controller?.hasSeekableDuration?.() && seekEnabled);
|
|
131
125
|
}
|
|
132
126
|
|
|
127
|
+
const [timeLeft, setTimeLeft] = React.useState(
|
|
128
|
+
sleepTimerDate !== 0 ? sleepTimerDate - Date.now() : 0
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const showCounter = React.useMemo(() => sleepTimerDate > 0, [sleepTimerDate]);
|
|
132
|
+
|
|
133
|
+
const countDownOptions = React.useMemo(() => {
|
|
134
|
+
return {
|
|
135
|
+
showCounter: showCounter && timeLeft >= 0,
|
|
136
|
+
timeLeft,
|
|
137
|
+
};
|
|
138
|
+
}, [showCounter, timeLeft]);
|
|
139
|
+
|
|
140
|
+
React.useEffect(() => {
|
|
141
|
+
if (showCounter && sleepTimerDate !== 0) {
|
|
142
|
+
setTimeLeft(sleepTimerDate - Date.now());
|
|
143
|
+
|
|
144
|
+
const intervalId = setInterval(() => {
|
|
145
|
+
setTimeLeft(() => {
|
|
146
|
+
const newTimeLeft = sleepTimerDate - Date.now();
|
|
147
|
+
|
|
148
|
+
if (newTimeLeft <= 0) {
|
|
149
|
+
clearInterval(intervalId);
|
|
150
|
+
|
|
151
|
+
cancelSleepTimer();
|
|
152
|
+
|
|
153
|
+
return 0;
|
|
154
|
+
} else {
|
|
155
|
+
return newTimeLeft;
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}, 1000);
|
|
159
|
+
|
|
160
|
+
return () => clearInterval(intervalId);
|
|
161
|
+
} else {
|
|
162
|
+
setTimeLeft(0);
|
|
163
|
+
}
|
|
164
|
+
}, [sleepTimerDate, showCounter, cancelSleepTimer]);
|
|
165
|
+
|
|
133
166
|
const startTime = entry.extensions?.live?.start_time;
|
|
134
167
|
const endTime = entry.extensions?.live?.end_time;
|
|
135
168
|
|
|
@@ -262,6 +295,7 @@ export const Controls = (props: Props) => {
|
|
|
262
295
|
isDateBasedLiveContent={isDateBasedLiveContent}
|
|
263
296
|
onSkipLive={onSkipLive}
|
|
264
297
|
progressBarStyles={styles.progressBar}
|
|
298
|
+
countDownOptions={countDownOptions}
|
|
265
299
|
thumbStyles={styles.progressBarThumbStyles}
|
|
266
300
|
thumbStylesPressed={styles.progressBarThumbStyles_pressed}
|
|
267
301
|
trackStyles={styles.progressBarTrackStyles}
|
|
@@ -99,7 +99,7 @@ export const skipToLiveButton = (props: ButtonsProps): ControlsButton =>
|
|
|
99
99
|
skipButtonByName({ ...props, name: "skip_live" });
|
|
100
100
|
|
|
101
101
|
export const speedButton = (props: ButtonsProps): ControlsButton =>
|
|
102
|
-
speedButtonByName(props);
|
|
102
|
+
speedButtonByName({ ...props });
|
|
103
103
|
|
|
104
104
|
export const sleepButton = (props: ButtonsProps): ControlsButton =>
|
|
105
|
-
sleepButtonByName(props);
|
|
105
|
+
sleepButtonByName({ ...props });
|
|
@@ -24,10 +24,6 @@ import {
|
|
|
24
24
|
useModalAnimationContext,
|
|
25
25
|
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
26
26
|
import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
27
|
-
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
28
|
-
import { getTabBarHeight } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/getTabBarHeight";
|
|
29
|
-
|
|
30
|
-
const bottomTabBarHeight = getTabBarHeight();
|
|
31
27
|
|
|
32
28
|
type Props = {
|
|
33
29
|
entry: ZappEntry;
|
|
@@ -92,6 +88,10 @@ const styles = StyleSheet.create({
|
|
|
92
88
|
},
|
|
93
89
|
});
|
|
94
90
|
|
|
91
|
+
const backgroundColorStyle = (value: GetValue): ViewStyle => ({
|
|
92
|
+
backgroundColor: value("docked_player_background_color"),
|
|
93
|
+
});
|
|
94
|
+
|
|
95
95
|
const playerDimensionsStyle = (
|
|
96
96
|
minimisedHeight: number,
|
|
97
97
|
addPadding: boolean
|
|
@@ -208,10 +208,6 @@ export const DockedControls = (props: Props) => {
|
|
|
208
208
|
[]
|
|
209
209
|
);
|
|
210
210
|
|
|
211
|
-
const dockedBackgroundColor = value("docked_player_background_color");
|
|
212
|
-
|
|
213
|
-
const insets = useSafeAreaInsets();
|
|
214
|
-
|
|
215
211
|
if (!layoutState.docked && !startComponentsAnimation) return null;
|
|
216
212
|
|
|
217
213
|
return (
|
|
@@ -223,9 +219,7 @@ export const DockedControls = (props: Props) => {
|
|
|
223
219
|
isActiveGesture || layoutState.docked,
|
|
224
220
|
progressBarHeight
|
|
225
221
|
),
|
|
226
|
-
{
|
|
227
|
-
height: minimisedHeight + bottomTabBarHeight + insets.bottom,
|
|
228
|
-
},
|
|
222
|
+
{ height: minimisedHeight },
|
|
229
223
|
]}
|
|
230
224
|
>
|
|
231
225
|
{!playerState.isLive &&
|
|
@@ -267,12 +261,7 @@ export const DockedControls = (props: Props) => {
|
|
|
267
261
|
)}
|
|
268
262
|
/>
|
|
269
263
|
)}
|
|
270
|
-
<View
|
|
271
|
-
style={[
|
|
272
|
-
styles.contentInfo,
|
|
273
|
-
{ backgroundColor: dockedBackgroundColor },
|
|
274
|
-
]}
|
|
275
|
-
>
|
|
264
|
+
<View style={[styles.contentInfo, backgroundColorStyle(value)]}>
|
|
276
265
|
<ContentInfo
|
|
277
266
|
content={{ ...entry, title, summary }}
|
|
278
267
|
value={value}
|
|
@@ -286,7 +275,7 @@ export const DockedControls = (props: Props) => {
|
|
|
286
275
|
isTablet
|
|
287
276
|
? styles.controlsContainerForTablet
|
|
288
277
|
: styles.controlsContainerForMobile,
|
|
289
|
-
|
|
278
|
+
backgroundColorStyle(value),
|
|
290
279
|
]}
|
|
291
280
|
>
|
|
292
281
|
<Controls
|
|
@@ -308,12 +297,6 @@ export const DockedControls = (props: Props) => {
|
|
|
308
297
|
/>
|
|
309
298
|
</View>
|
|
310
299
|
</View>
|
|
311
|
-
<View
|
|
312
|
-
style={{
|
|
313
|
-
height: insets.bottom + bottomTabBarHeight,
|
|
314
|
-
backgroundColor: dockedBackgroundColor,
|
|
315
|
-
}}
|
|
316
|
-
/>
|
|
317
300
|
</AnimationComponent>
|
|
318
301
|
);
|
|
319
302
|
};
|
|
@@ -77,11 +77,15 @@ export function MobileLayout({
|
|
|
77
77
|
onSkipStart,
|
|
78
78
|
onSkipLive,
|
|
79
79
|
onSpeedButtonPress,
|
|
80
|
+
onSleepButtonPress,
|
|
80
81
|
onCloseVideoModal,
|
|
81
82
|
playbackSpeedName,
|
|
83
|
+
playbackSleepName,
|
|
82
84
|
configuration,
|
|
83
85
|
dimensions,
|
|
84
86
|
playNextData,
|
|
87
|
+
cancelSleepTimer,
|
|
88
|
+
sleepTimerDate,
|
|
85
89
|
title,
|
|
86
90
|
summary,
|
|
87
91
|
}: Props) {
|
|
@@ -192,7 +196,10 @@ export function MobileLayout({
|
|
|
192
196
|
value={value}
|
|
193
197
|
title={title}
|
|
194
198
|
summary={summary}
|
|
199
|
+
sleepTimerDate={sleepTimerDate}
|
|
200
|
+
cancelSleepTimer={cancelSleepTimer}
|
|
195
201
|
playbackSpeedName={playbackSpeedName}
|
|
202
|
+
playbackSleepName={playbackSleepName}
|
|
196
203
|
onPlaybackButtonPress={onPlaybackButtonPress}
|
|
197
204
|
onRewindButtonPress={onRewindButtonPress}
|
|
198
205
|
onForwardButtonPress={onForwardButtonPress}
|
|
@@ -200,6 +207,7 @@ export function MobileLayout({
|
|
|
200
207
|
onSkipStart={onSkipStart}
|
|
201
208
|
onSkipLive={onSkipLive}
|
|
202
209
|
onSpeedButtonPress={onSpeedButtonPress}
|
|
210
|
+
onSleepButtonPress={onSleepButtonPress}
|
|
203
211
|
docked={docked}
|
|
204
212
|
/>
|
|
205
213
|
</View>
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import React, { PropsWithChildren } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
Image,
|
|
4
|
+
Animated,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
ViewStyle,
|
|
7
|
+
ImageStyle,
|
|
8
|
+
} from "react-native";
|
|
3
9
|
|
|
4
10
|
import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
|
|
5
11
|
import {
|
|
@@ -8,9 +14,6 @@ import {
|
|
|
8
14
|
DURATION_TO_MINIMIZE,
|
|
9
15
|
DURATION_TO_MAXIMIZE,
|
|
10
16
|
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
11
|
-
import { PlayerAnimationStateT } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation/ModalAnimationContext";
|
|
12
|
-
|
|
13
|
-
import { useChangePlayerState } from "./hooks";
|
|
14
17
|
|
|
15
18
|
const styles = StyleSheet.create({
|
|
16
19
|
container: {
|
|
@@ -23,11 +26,16 @@ const styles = StyleSheet.create({
|
|
|
23
26
|
type Props = PropsWithChildren<{
|
|
24
27
|
entry: ZappEntry;
|
|
25
28
|
style?: ImageStyle | ImageStyle[];
|
|
29
|
+
wrapperViewStyle?: ViewStyle;
|
|
26
30
|
imageWidth: number;
|
|
27
|
-
docked: boolean;
|
|
28
31
|
}>;
|
|
29
32
|
|
|
30
|
-
export const AnimatedImage = ({
|
|
33
|
+
export const AnimatedImage = ({
|
|
34
|
+
entry,
|
|
35
|
+
style,
|
|
36
|
+
wrapperViewStyle,
|
|
37
|
+
imageWidth,
|
|
38
|
+
}: Props) => {
|
|
31
39
|
const heightAnim = React.useRef(new Animated.Value(imageWidth)).current;
|
|
32
40
|
|
|
33
41
|
const source = React.useMemo(
|
|
@@ -35,40 +43,27 @@ export const AnimatedImage = ({ entry, style, imageWidth, docked }: Props) => {
|
|
|
35
43
|
[entry]
|
|
36
44
|
);
|
|
37
45
|
|
|
38
|
-
const { minimisedHeight } = useModalAnimationContext();
|
|
39
|
-
|
|
40
|
-
const changePlayerState$ = useChangePlayerState({ docked });
|
|
46
|
+
const { playerAnimationState, minimisedHeight } = useModalAnimationContext();
|
|
41
47
|
|
|
42
48
|
React.useEffect(() => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
useNativeDriver: false, // width/height can't use native driver
|
|
50
|
-
}).start();
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (nextPlayerState === PlayerAnimationStateEnum.maximize) {
|
|
54
|
-
Animated.timing(heightAnim, {
|
|
55
|
-
toValue: imageWidth,
|
|
56
|
-
duration: DURATION_TO_MAXIMIZE,
|
|
57
|
-
useNativeDriver: false, // width/height can't use native driver
|
|
58
|
-
}).start();
|
|
59
|
-
}
|
|
49
|
+
if (playerAnimationState === PlayerAnimationStateEnum.minimize) {
|
|
50
|
+
Animated.timing(heightAnim, {
|
|
51
|
+
toValue: minimisedHeight,
|
|
52
|
+
duration: DURATION_TO_MINIMIZE,
|
|
53
|
+
useNativeDriver: false, // width/height can't use native driver
|
|
54
|
+
}).start();
|
|
60
55
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
heightAnim.setValue(minimisedHeight);
|
|
64
|
-
}
|
|
65
|
-
},
|
|
66
|
-
});
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
67
58
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
59
|
+
if (playerAnimationState === PlayerAnimationStateEnum.maximize) {
|
|
60
|
+
Animated.timing(heightAnim, {
|
|
61
|
+
toValue: imageWidth,
|
|
62
|
+
duration: DURATION_TO_MAXIMIZE,
|
|
63
|
+
useNativeDriver: false, // width/height can't use native driver
|
|
64
|
+
}).start();
|
|
65
|
+
}
|
|
66
|
+
}, [playerAnimationState, imageWidth]);
|
|
72
67
|
|
|
73
68
|
if (!source) return null;
|
|
74
69
|
|
|
@@ -76,6 +71,7 @@ export const AnimatedImage = ({ entry, style, imageWidth, docked }: Props) => {
|
|
|
76
71
|
<Animated.View
|
|
77
72
|
style={[
|
|
78
73
|
styles.container,
|
|
74
|
+
wrapperViewStyle,
|
|
79
75
|
{
|
|
80
76
|
width: heightAnim,
|
|
81
77
|
},
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { LayoutChangeEvent, View } from "react-native";
|
|
3
2
|
|
|
4
3
|
import {
|
|
5
4
|
AnimationComponent,
|
|
6
5
|
ComponentAnimationType,
|
|
7
6
|
useModalAnimationContext,
|
|
8
7
|
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
9
|
-
import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
10
|
-
import { toBooleanWithDefaultTrue } from "@applicaster/zapp-react-native-utils/booleanUtils";
|
|
11
8
|
|
|
12
9
|
import { AnimatedImage } from "./AnimatedImage";
|
|
13
10
|
import { FlexImage } from "./FlexImage";
|
|
@@ -15,6 +12,7 @@ import { FlexImage } from "./FlexImage";
|
|
|
15
12
|
import { styles } from "./styles";
|
|
16
13
|
import { calculateBorderRadius } from "../../utils";
|
|
17
14
|
import { PlayNextData } from "../types";
|
|
15
|
+
import { LayoutChangeEvent, View } from "react-native";
|
|
18
16
|
|
|
19
17
|
type Props = {
|
|
20
18
|
entry: ZappEntry;
|
|
@@ -31,7 +29,7 @@ type Props = {
|
|
|
31
29
|
docked: boolean;
|
|
32
30
|
imageWidth?: Option<number>;
|
|
33
31
|
verticalOffset: number;
|
|
34
|
-
onLayoutImage
|
|
32
|
+
onLayoutImage: (event: LayoutChangeEvent) => void;
|
|
35
33
|
};
|
|
36
34
|
|
|
37
35
|
export const PlayerImage = (props: Props) => {
|
|
@@ -42,7 +40,7 @@ export const PlayerImage = (props: Props) => {
|
|
|
42
40
|
verticalOffset,
|
|
43
41
|
imageWidth,
|
|
44
42
|
configuration,
|
|
45
|
-
onLayoutImage
|
|
43
|
+
onLayoutImage,
|
|
46
44
|
} = props;
|
|
47
45
|
|
|
48
46
|
const {
|
|
@@ -52,9 +50,8 @@ export const PlayerImage = (props: Props) => {
|
|
|
52
50
|
minimisedHeight,
|
|
53
51
|
} = useModalAnimationContext();
|
|
54
52
|
|
|
55
|
-
const isShadowEnabled =
|
|
56
|
-
configuration?.audio_player_artwork_shadow_enabled
|
|
57
|
-
);
|
|
53
|
+
const isShadowEnabled =
|
|
54
|
+
configuration?.audio_player_artwork_shadow_enabled ?? true;
|
|
58
55
|
|
|
59
56
|
const shouldShowShadow = !docked && isShadowEnabled;
|
|
60
57
|
|
|
@@ -102,7 +99,6 @@ export const PlayerImage = (props: Props) => {
|
|
|
102
99
|
entry={entry}
|
|
103
100
|
style={[styles.backgroundImageContainer, { borderRadius }]}
|
|
104
101
|
imageWidth={imageWidth}
|
|
105
|
-
docked={docked}
|
|
106
102
|
/>
|
|
107
103
|
)}
|
|
108
104
|
</View>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/* eslint-disable react-native/no-color-literals */
|
|
2
2
|
import { StyleSheet } from "react-native";
|
|
3
|
-
import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
4
3
|
|
|
5
4
|
const SHADOW_COLOR = "rgba(0, 0, 0, 1)";
|
|
6
5
|
|
|
@@ -26,10 +25,7 @@ export const styles = StyleSheet.create({
|
|
|
26
25
|
borderColor: "transparent",
|
|
27
26
|
shadowColor: SHADOW_COLOR,
|
|
28
27
|
shadowOffset: { width: 0, height: 24 },
|
|
29
|
-
shadowOpacity:
|
|
30
|
-
ios: 0.35,
|
|
31
|
-
default: 0.2,
|
|
32
|
-
}),
|
|
28
|
+
shadowOpacity: 0.2,
|
|
33
29
|
shadowRadius: 32,
|
|
34
30
|
elevation: 12,
|
|
35
31
|
},
|
|
@@ -98,11 +98,15 @@ export function TabletLandscapeLayout({
|
|
|
98
98
|
onSkipStart,
|
|
99
99
|
onSkipLive,
|
|
100
100
|
onSpeedButtonPress,
|
|
101
|
+
onSleepButtonPress,
|
|
101
102
|
onCloseVideoModal,
|
|
102
103
|
playbackSpeedName,
|
|
104
|
+
playbackSleepName,
|
|
103
105
|
configuration,
|
|
104
106
|
dimensions,
|
|
105
107
|
playNextData,
|
|
108
|
+
cancelSleepTimer,
|
|
109
|
+
sleepTimerDate,
|
|
106
110
|
title,
|
|
107
111
|
summary,
|
|
108
112
|
}: Props) {
|
|
@@ -180,7 +184,10 @@ export function TabletLandscapeLayout({
|
|
|
180
184
|
value={value}
|
|
181
185
|
title={title}
|
|
182
186
|
summary={summary}
|
|
187
|
+
sleepTimerDate={sleepTimerDate}
|
|
188
|
+
cancelSleepTimer={cancelSleepTimer}
|
|
183
189
|
playbackSpeedName={playbackSpeedName}
|
|
190
|
+
playbackSleepName={playbackSleepName}
|
|
184
191
|
onPlaybackButtonPress={onPlaybackButtonPress}
|
|
185
192
|
onRewindButtonPress={onRewindButtonPress}
|
|
186
193
|
onForwardButtonPress={onForwardButtonPress}
|
|
@@ -188,6 +195,7 @@ export function TabletLandscapeLayout({
|
|
|
188
195
|
onSkipStart={onSkipStart}
|
|
189
196
|
onSkipLive={onSkipLive}
|
|
190
197
|
onSpeedButtonPress={onSpeedButtonPress}
|
|
198
|
+
onSleepButtonPress={onSleepButtonPress}
|
|
191
199
|
docked={docked}
|
|
192
200
|
/>
|
|
193
201
|
</View>
|
|
@@ -82,11 +82,15 @@ export function TabletPortraitLayout({
|
|
|
82
82
|
onSkipStart,
|
|
83
83
|
onSkipLive,
|
|
84
84
|
onSpeedButtonPress,
|
|
85
|
+
onSleepButtonPress,
|
|
85
86
|
onCloseVideoModal,
|
|
86
87
|
playbackSpeedName,
|
|
88
|
+
playbackSleepName,
|
|
87
89
|
configuration,
|
|
88
90
|
dimensions,
|
|
89
91
|
playNextData,
|
|
92
|
+
cancelSleepTimer,
|
|
93
|
+
sleepTimerDate,
|
|
90
94
|
title,
|
|
91
95
|
summary,
|
|
92
96
|
}: Props) {
|
|
@@ -183,7 +187,10 @@ export function TabletPortraitLayout({
|
|
|
183
187
|
value={value}
|
|
184
188
|
title={title}
|
|
185
189
|
summary={summary}
|
|
190
|
+
sleepTimerDate={sleepTimerDate}
|
|
191
|
+
cancelSleepTimer={cancelSleepTimer}
|
|
186
192
|
playbackSpeedName={playbackSpeedName}
|
|
193
|
+
playbackSleepName={playbackSleepName}
|
|
187
194
|
onPlaybackButtonPress={onPlaybackButtonPress}
|
|
188
195
|
onRewindButtonPress={onRewindButtonPress}
|
|
189
196
|
onForwardButtonPress={onForwardButtonPress}
|
|
@@ -191,6 +198,7 @@ export function TabletPortraitLayout({
|
|
|
191
198
|
onSkipStart={onSkipStart}
|
|
192
199
|
onSkipLive={onSkipLive}
|
|
193
200
|
onSpeedButtonPress={onSpeedButtonPress}
|
|
201
|
+
onSleepButtonPress={onSleepButtonPress}
|
|
194
202
|
docked={docked}
|
|
195
203
|
/>
|
|
196
204
|
</View>
|
|
@@ -30,7 +30,6 @@ export class SleepTimerState {
|
|
|
30
30
|
const SLEEP_PREFERENCE_KEY = "sleep";
|
|
31
31
|
|
|
32
32
|
export class SleepTimerController {
|
|
33
|
-
// TODO cancel timer on reach time
|
|
34
33
|
private sleepTimerStateSubject = new BehaviorSubject<SleepTimerState>(
|
|
35
34
|
new SleepTimerState(SLEEP_TIMER_OFF, 0)
|
|
36
35
|
);
|
|
@@ -56,23 +55,18 @@ export class SleepTimerController {
|
|
|
56
55
|
}
|
|
57
56
|
}
|
|
58
57
|
|
|
59
|
-
async setSleepTimer(item: SleepTimer
|
|
58
|
+
async setSleepTimer(item: SleepTimer) {
|
|
60
59
|
// User has selected Off state
|
|
61
60
|
if (item.value === 0) {
|
|
62
61
|
log_debug("Cancelling sleep timer");
|
|
63
|
-
|
|
64
|
-
// Removing scheduled pause on timer expiration
|
|
65
62
|
this.player.cancelSleepTimer?.();
|
|
66
63
|
|
|
67
64
|
return this.onTimerExpired();
|
|
68
65
|
}
|
|
69
66
|
|
|
70
|
-
|
|
71
|
-
prevTimerSleepDate || Date.now() + item.value * 60000;
|
|
67
|
+
log_debug(`Starting sleep timer in ${item.value} minutes`);
|
|
72
68
|
|
|
73
|
-
|
|
74
|
-
`Starting sleep timer in ${(futureSleepDate - Date.now()) / 1000}s`
|
|
75
|
-
);
|
|
69
|
+
const futureSleepDate = Date.now() + item.value * 60000;
|
|
76
70
|
|
|
77
71
|
if (await this.scheduleSleep(futureSleepDate)) {
|
|
78
72
|
await localStorage.setItem(
|
|
@@ -88,26 +82,17 @@ export class SleepTimerController {
|
|
|
88
82
|
}
|
|
89
83
|
|
|
90
84
|
async onTimerExpired() {
|
|
91
|
-
|
|
92
|
-
log_error("Failed to remove sleep timer from local storage", {
|
|
93
|
-
error: error.message,
|
|
94
|
-
});
|
|
95
|
-
});
|
|
85
|
+
await localStorage.removeItem(SLEEP_PREFERENCE_KEY, NAMESPACE);
|
|
96
86
|
|
|
97
|
-
// Update UI state
|
|
98
87
|
this.sleepTimerStateSubject.next(new SleepTimerState(SLEEP_TIMER_OFF, 0));
|
|
99
88
|
}
|
|
100
89
|
|
|
101
|
-
async removeSleepTimerFromLocalStorage() {
|
|
102
|
-
await localStorage.removeItem(SLEEP_PREFERENCE_KEY, NAMESPACE);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
90
|
async loadSleep() {
|
|
106
91
|
const sleep = await localStorage.getItem(SLEEP_PREFERENCE_KEY, NAMESPACE);
|
|
107
92
|
|
|
108
93
|
if (!sleep) return;
|
|
109
94
|
|
|
110
|
-
const { futureSleepDate, ...
|
|
95
|
+
const { futureSleepDate, ...rest } = sleep;
|
|
111
96
|
|
|
112
97
|
if (!futureSleepDate || hasDatePassed(futureSleepDate)) {
|
|
113
98
|
log_debug("Sleep date has already passed");
|
|
@@ -116,9 +101,13 @@ export class SleepTimerController {
|
|
|
116
101
|
return;
|
|
117
102
|
}
|
|
118
103
|
|
|
119
|
-
|
|
104
|
+
if (await this.scheduleSleep(futureSleepDate)) {
|
|
105
|
+
log_debug(`Sleep state restored, will go to sleep at ${futureSleepDate}`);
|
|
120
106
|
|
|
121
|
-
|
|
107
|
+
this.sleepTimerStateSubject.next(
|
|
108
|
+
new SleepTimerState(rest as SleepTimer, futureSleepDate)
|
|
109
|
+
);
|
|
110
|
+
}
|
|
122
111
|
}
|
|
123
112
|
|
|
124
113
|
// Will be called if user closed the player explicitly.
|
|
@@ -28,14 +28,12 @@ const createTimerOptions = (value) => {
|
|
|
28
28
|
};
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
export const getSleepTimerOptions = (
|
|
31
|
+
export const getSleepTimerOptions = (configuration) => {
|
|
32
32
|
return [
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
: null,
|
|
33
|
+
{
|
|
34
|
+
label: configuration["playback_sleep_off_label"],
|
|
35
|
+
value: 0,
|
|
36
|
+
},
|
|
39
37
|
...[5, 10, 15, 30, 45, 60, 120].map(createTimerOptions),
|
|
40
38
|
];
|
|
41
39
|
};
|
|
@@ -116,9 +116,3 @@ export const getBottomSheetModalSelectedItemIcon = (theme) =>
|
|
|
116
116
|
|
|
117
117
|
export const getBottomSheetModalDefaultItemIcon = (theme) =>
|
|
118
118
|
theme["audio_modal_bottom_sheet_item_default_icon"] || null;
|
|
119
|
-
|
|
120
|
-
export const selectSleepTitle = (configuration) =>
|
|
121
|
-
configuration.playback_sleep_title;
|
|
122
|
-
|
|
123
|
-
export const selectSleepOffLabel = (configuration) =>
|
|
124
|
-
configuration.playback_sleep_off_label;
|
package/src/Player/index.tsx
CHANGED
|
@@ -50,7 +50,6 @@ const styles = StyleSheet.create({
|
|
|
50
50
|
position: "absolute",
|
|
51
51
|
zIndex: 1,
|
|
52
52
|
},
|
|
53
|
-
audioPlayerSize: { width: "100%", height: "100%" },
|
|
54
53
|
playerSize: { width: "100%", height: "100%" },
|
|
55
54
|
});
|
|
56
55
|
|
|
@@ -59,10 +58,6 @@ const logger = createLogger({
|
|
|
59
58
|
subsystem: Subsystem,
|
|
60
59
|
});
|
|
61
60
|
|
|
62
|
-
const isTvOS = isTvOSPlatform();
|
|
63
|
-
|
|
64
|
-
const AUDIO_PLAYER_NATIVE_ID = "audio-player-view";
|
|
65
|
-
|
|
66
61
|
export default class VideoPlayer extends React.Component<
|
|
67
62
|
QuickBrickPlayer.PlayerProps,
|
|
68
63
|
QuickBrickPlayer.NativePlayerState
|
|
@@ -98,8 +93,6 @@ export default class VideoPlayer extends React.Component<
|
|
|
98
93
|
// Player state
|
|
99
94
|
currentTime: 0,
|
|
100
95
|
duration: 0,
|
|
101
|
-
|
|
102
|
-
nativePlayerIsReady: false,
|
|
103
96
|
};
|
|
104
97
|
|
|
105
98
|
private _tracker: PlayerAnalyticsTrackerI;
|
|
@@ -495,9 +488,6 @@ export default class VideoPlayer extends React.Component<
|
|
|
495
488
|
|
|
496
489
|
onVideoFullscreenPlayerDidPresent = () => {
|
|
497
490
|
this.props.listener?.onVideoFullscreenPlayerDidPresent?.();
|
|
498
|
-
|
|
499
|
-
// we have to wait ready of native player view, before showing audio_player
|
|
500
|
-
this.setState({ nativePlayerIsReady: true });
|
|
501
491
|
};
|
|
502
492
|
|
|
503
493
|
onVideoFullscreenPlayerWillDismiss = () => {
|
|
@@ -684,7 +674,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
684
674
|
};
|
|
685
675
|
|
|
686
676
|
childrenWithPlayNext = () => {
|
|
687
|
-
if (!
|
|
677
|
+
if (!isTvOSPlatform() || !this.getPlayNextData()) {
|
|
688
678
|
return this.props.children;
|
|
689
679
|
}
|
|
690
680
|
|
|
@@ -709,35 +699,6 @@ export default class VideoPlayer extends React.Component<
|
|
|
709
699
|
];
|
|
710
700
|
};
|
|
711
701
|
|
|
712
|
-
childrenWithAudioPlayer = () => {
|
|
713
|
-
if (!isTvOS) {
|
|
714
|
-
return this.props.children;
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
const { nativePlayerIsReady } = this.state;
|
|
718
|
-
|
|
719
|
-
// we have to wait ready status of native player view, before showing audio_player
|
|
720
|
-
if (!nativePlayerIsReady) {
|
|
721
|
-
return this.props.children;
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
// tvos only
|
|
725
|
-
const { entry } = this.props;
|
|
726
|
-
|
|
727
|
-
const children = R.pathOr([], "children", this.props);
|
|
728
|
-
|
|
729
|
-
return [
|
|
730
|
-
...children,
|
|
731
|
-
<View nativeID={AUDIO_PLAYER_NATIVE_ID} key={AUDIO_PLAYER_NATIVE_ID}>
|
|
732
|
-
<AudioPlayer
|
|
733
|
-
style={styles.audioPlayerSize}
|
|
734
|
-
audio_item={entry}
|
|
735
|
-
plugin_configuration={this.getPluginConfiguration()}
|
|
736
|
-
/>
|
|
737
|
-
</View>,
|
|
738
|
-
];
|
|
739
|
-
};
|
|
740
|
-
|
|
741
702
|
get supportNativeCast() {
|
|
742
703
|
return this.props.controller.supportNativeCast();
|
|
743
704
|
}
|
|
@@ -762,8 +723,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
762
723
|
|
|
763
724
|
const isAudioItem =
|
|
764
725
|
entry?.content?.type?.includes?.("audio") ||
|
|
765
|
-
entry?.type?.value?.includes?.("audio")
|
|
766
|
-
entry?.content?.src?.includes?.("type=audio");
|
|
726
|
+
entry?.type?.value?.includes?.("audio");
|
|
767
727
|
|
|
768
728
|
const getStyle = (dimensions) => {
|
|
769
729
|
const generalStyles = isAudioItem ? styles.audio : styles.video;
|
|
@@ -1147,14 +1107,6 @@ export default class VideoPlayer extends React.Component<
|
|
|
1147
1107
|
const renderTVPlayer = () => {
|
|
1148
1108
|
return (
|
|
1149
1109
|
<View style={this.takeParentStyle} testID={"player-view"}>
|
|
1150
|
-
{!isTvOS && isAudioItem ? (
|
|
1151
|
-
<AudioPlayer
|
|
1152
|
-
style={getStyle(styles.audioPlayerSize)}
|
|
1153
|
-
audio_item={entry}
|
|
1154
|
-
plugin_configuration={this.getPluginConfiguration()}
|
|
1155
|
-
/>
|
|
1156
|
-
) : null}
|
|
1157
|
-
|
|
1158
1110
|
<this.props.PlayerComponent
|
|
1159
1111
|
ref={this._assignRoot}
|
|
1160
1112
|
{...nativeEvents}
|
|
@@ -1164,12 +1116,11 @@ export default class VideoPlayer extends React.Component<
|
|
|
1164
1116
|
pluginConfiguration={this.getPluginConfiguration()}
|
|
1165
1117
|
controller={this.props.controller}
|
|
1166
1118
|
>
|
|
1167
|
-
{this.childrenWithAudioPlayer()}
|
|
1168
1119
|
{this.childrenWithPlayNext()}
|
|
1169
1120
|
</this.props.PlayerComponent>
|
|
1170
1121
|
|
|
1171
1122
|
{renderTransportControls(this.takeParentStyle)}
|
|
1172
|
-
{!
|
|
1123
|
+
{!isTvOSPlatform() && (
|
|
1173
1124
|
<PlayNextOverlay
|
|
1174
1125
|
item={entry}
|
|
1175
1126
|
playerIsDocked={docked}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { useChangePlayerState } from "./useChangePlayerState";
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { Subject, Observable } from "rxjs";
|
|
3
|
-
|
|
4
|
-
import { distinctUntilChanged } from "rxjs/operators";
|
|
5
|
-
import { isNil } from "@applicaster/zapp-react-native-utils/utils";
|
|
6
|
-
import { useRefWithInitialValue } from "@applicaster/zapp-react-native-utils/reactHooks/state/useRefWithInitialValue";
|
|
7
|
-
import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";
|
|
8
|
-
import {
|
|
9
|
-
useModalAnimationContext,
|
|
10
|
-
PlayerAnimationStateEnum,
|
|
11
|
-
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
12
|
-
import { PlayerAnimationStateT } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation/ModalAnimationContext";
|
|
13
|
-
|
|
14
|
-
const builder = () => {
|
|
15
|
-
const changePlayerState$ = new Subject<PlayerAnimationStateT>();
|
|
16
|
-
|
|
17
|
-
const emitMaximized = (): void => {
|
|
18
|
-
changePlayerState$.next(PlayerAnimationStateEnum.maximize);
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const emitMinimized = (): void => {
|
|
22
|
-
changePlayerState$.next(PlayerAnimationStateEnum.minimize);
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const emitAppearInDocked = (): void => {
|
|
26
|
-
// if audio_player was presented in docked mode
|
|
27
|
-
changePlayerState$.next(PlayerAnimationStateEnum.appear_as_docked);
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
emitMaximized,
|
|
32
|
-
emitMinimized,
|
|
33
|
-
emitAppearInDocked,
|
|
34
|
-
changePlayerState$: changePlayerState$.pipe(distinctUntilChanged()),
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export const useChangePlayerState = ({
|
|
39
|
-
docked,
|
|
40
|
-
}): Observable<PlayerAnimationStateT> => {
|
|
41
|
-
const changePlayerStateRef =
|
|
42
|
-
useRefWithInitialValue<ReturnType<typeof builder>>(builder);
|
|
43
|
-
|
|
44
|
-
const { playerAnimationState } = useModalAnimationContext();
|
|
45
|
-
|
|
46
|
-
const previousPlayerAnimationState = usePrevious(playerAnimationState);
|
|
47
|
-
|
|
48
|
-
React.useEffect(() => {
|
|
49
|
-
if (
|
|
50
|
-
!docked &&
|
|
51
|
-
isNil(previousPlayerAnimationState) &&
|
|
52
|
-
playerAnimationState === PlayerAnimationStateEnum.minimize
|
|
53
|
-
) {
|
|
54
|
-
changePlayerStateRef.current.emitMinimized();
|
|
55
|
-
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
!docked &&
|
|
61
|
-
isNil(previousPlayerAnimationState) &&
|
|
62
|
-
playerAnimationState === PlayerAnimationStateEnum.maximize
|
|
63
|
-
) {
|
|
64
|
-
changePlayerStateRef.current.emitMaximized();
|
|
65
|
-
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (
|
|
70
|
-
!docked &&
|
|
71
|
-
previousPlayerAnimationState === PlayerAnimationStateEnum.drag_scroll &&
|
|
72
|
-
playerAnimationState === PlayerAnimationStateEnum.maximize
|
|
73
|
-
) {
|
|
74
|
-
changePlayerStateRef.current.emitMaximized();
|
|
75
|
-
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
!docked &&
|
|
81
|
-
previousPlayerAnimationState === PlayerAnimationStateEnum.drag_scroll &&
|
|
82
|
-
playerAnimationState === PlayerAnimationStateEnum.minimize
|
|
83
|
-
) {
|
|
84
|
-
changePlayerStateRef.current.emitMinimized();
|
|
85
|
-
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (
|
|
90
|
-
docked &&
|
|
91
|
-
isNil(previousPlayerAnimationState) &&
|
|
92
|
-
isNil(playerAnimationState)
|
|
93
|
-
) {
|
|
94
|
-
changePlayerStateRef.current.emitAppearInDocked();
|
|
95
|
-
}
|
|
96
|
-
}, [previousPlayerAnimationState, playerAnimationState, docked]);
|
|
97
|
-
|
|
98
|
-
return changePlayerStateRef.current.changePlayerState$;
|
|
99
|
-
};
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { ModalHeader } from "@applicaster/zapp-react-native-ui-components/Components/ModalComponent/Header";
|
|
3
|
-
import { isNil } from "lodash";
|
|
4
|
-
|
|
5
|
-
const formatTime = (milliseconds: number) => {
|
|
6
|
-
const totalSeconds = Math.floor(milliseconds / 1000);
|
|
7
|
-
const hours = Math.floor(totalSeconds / 3600);
|
|
8
|
-
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
9
|
-
const seconds = totalSeconds % 60;
|
|
10
|
-
|
|
11
|
-
if (milliseconds < 3600000) {
|
|
12
|
-
return `${minutes.toString().padStart(2, "0")}:${seconds
|
|
13
|
-
.toString()
|
|
14
|
-
.padStart(2, "0")}`;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return `${hours.toString().padStart(2, "0")}:${minutes
|
|
18
|
-
.toString()
|
|
19
|
-
.padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export const SleepTimerModalHeader = ({
|
|
23
|
-
summary,
|
|
24
|
-
...props
|
|
25
|
-
}: {
|
|
26
|
-
summary?: string;
|
|
27
|
-
}) => {
|
|
28
|
-
const sleepTimerDate = +summary;
|
|
29
|
-
|
|
30
|
-
const [timeLeft, setTimeLeft] = React.useState(
|
|
31
|
-
sleepTimerDate !== 0 ? sleepTimerDate - Date.now() : 0
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
const showCounter = React.useMemo(() => sleepTimerDate > 0, [sleepTimerDate]);
|
|
35
|
-
|
|
36
|
-
React.useLayoutEffect(() => {
|
|
37
|
-
if (showCounter && sleepTimerDate !== 0) {
|
|
38
|
-
setTimeLeft(sleepTimerDate - Date.now());
|
|
39
|
-
|
|
40
|
-
const intervalId = setInterval(() => {
|
|
41
|
-
setTimeLeft(() => {
|
|
42
|
-
const newTimeLeft = sleepTimerDate - Date.now();
|
|
43
|
-
|
|
44
|
-
if (newTimeLeft <= 0) {
|
|
45
|
-
clearInterval(intervalId);
|
|
46
|
-
|
|
47
|
-
return 0;
|
|
48
|
-
} else {
|
|
49
|
-
return newTimeLeft;
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
}, 1000);
|
|
53
|
-
|
|
54
|
-
return () => clearInterval(intervalId);
|
|
55
|
-
} else {
|
|
56
|
-
setTimeLeft(0);
|
|
57
|
-
}
|
|
58
|
-
}, [sleepTimerDate, showCounter]);
|
|
59
|
-
|
|
60
|
-
return (
|
|
61
|
-
<ModalHeader
|
|
62
|
-
summary={!isNil(summary) ? formatTime(timeLeft) : summary}
|
|
63
|
-
{...(props as any)}
|
|
64
|
-
/>
|
|
65
|
-
);
|
|
66
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./SleepTimerModalHeader";
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { usePlayerState } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerState";
|
|
2
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
3
|
-
import {
|
|
4
|
-
SLEEP_TIMER_OFF,
|
|
5
|
-
SleepTimerController,
|
|
6
|
-
SleepTimerState,
|
|
7
|
-
} from "./SleepTimerManager";
|
|
8
|
-
|
|
9
|
-
import { openBottomSheetModal } from "@applicaster/zapp-react-native-utils/modalState";
|
|
10
|
-
|
|
11
|
-
import { SleepTimerModalHeader } from "./components/SleepTimerModalHeader";
|
|
12
|
-
import { getSleepTimerOptions } from "../Const/sleepTimerOptions";
|
|
13
|
-
import {
|
|
14
|
-
getBottomSheetModalLabelProps,
|
|
15
|
-
getBottomSheetModalIconProps,
|
|
16
|
-
getBottomSheetModalItemProps,
|
|
17
|
-
getBottomSheetModalSelectedItemIcon,
|
|
18
|
-
selectSleepTitle,
|
|
19
|
-
selectSleepOffLabel,
|
|
20
|
-
} from "../Utils";
|
|
21
|
-
|
|
22
|
-
export const useSleepTimerModal = (player, configuration) => {
|
|
23
|
-
const playerState = usePlayerState("audio-player-sleep-timer-modal");
|
|
24
|
-
|
|
25
|
-
const title = configuration(selectSleepTitle);
|
|
26
|
-
const offLabel = configuration(selectSleepOffLabel);
|
|
27
|
-
|
|
28
|
-
const [sleepTimerManager, setSleepTimerManager] =
|
|
29
|
-
useState<SleepTimerController | null>(null);
|
|
30
|
-
|
|
31
|
-
const [sleepTimerState, setSleepTimerState] = useState<SleepTimerState>(
|
|
32
|
-
new SleepTimerState(SLEEP_TIMER_OFF, 0)
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
if (player) {
|
|
37
|
-
setSleepTimerManager(new SleepTimerController(player));
|
|
38
|
-
} else {
|
|
39
|
-
setSleepTimerManager(null);
|
|
40
|
-
}
|
|
41
|
-
}, [player]);
|
|
42
|
-
|
|
43
|
-
useEffect(() => {
|
|
44
|
-
if (playerState?.isLive) {
|
|
45
|
-
// Resetting a sleep timer when audio content is live (Timer not supported on live video)
|
|
46
|
-
sleepTimerManager.setSleepTimer(SLEEP_TIMER_OFF).catch();
|
|
47
|
-
}
|
|
48
|
-
}, [playerState?.isLive]);
|
|
49
|
-
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
if (!playerState.isReadyToPlay) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
void sleepTimerManager?.loadSleep();
|
|
56
|
-
}, [playerState.isReadyToPlay, sleepTimerManager]);
|
|
57
|
-
|
|
58
|
-
useEffect(() => {
|
|
59
|
-
const sleepSubscription =
|
|
60
|
-
sleepTimerManager?.sleepTimerState$.subscribe(setSleepTimerState);
|
|
61
|
-
|
|
62
|
-
return () => {
|
|
63
|
-
sleepSubscription?.unsubscribe();
|
|
64
|
-
};
|
|
65
|
-
}, [sleepTimerManager]);
|
|
66
|
-
|
|
67
|
-
const onSleepSelect = useCallback(
|
|
68
|
-
async (item) => {
|
|
69
|
-
await sleepTimerManager?.setSleepTimer(item);
|
|
70
|
-
},
|
|
71
|
-
[sleepTimerManager]
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
const onSleepButtonPress = useCallback(() => {
|
|
75
|
-
const timerDate = sleepTimerState.timerDate;
|
|
76
|
-
|
|
77
|
-
const timerRemaining =
|
|
78
|
-
timerDate !== 0 && +timerDate > Date.now() ? timerDate : null;
|
|
79
|
-
|
|
80
|
-
openBottomSheetModal({
|
|
81
|
-
modalBottomSheetContentProps: {
|
|
82
|
-
headerComponent: SleepTimerModalHeader,
|
|
83
|
-
items: getSleepTimerOptions(offLabel, sleepTimerState.timer.value),
|
|
84
|
-
onPress: onSleepSelect,
|
|
85
|
-
title,
|
|
86
|
-
summary: timerRemaining,
|
|
87
|
-
itemProps: getBottomSheetModalItemProps,
|
|
88
|
-
iconProps: getBottomSheetModalIconProps,
|
|
89
|
-
labelProps: getBottomSheetModalLabelProps,
|
|
90
|
-
getSelectedItemIcon: getBottomSheetModalSelectedItemIcon,
|
|
91
|
-
},
|
|
92
|
-
options: {
|
|
93
|
-
supportedOrientations: [
|
|
94
|
-
"portrait",
|
|
95
|
-
"landscape",
|
|
96
|
-
"portrait-upside-down",
|
|
97
|
-
],
|
|
98
|
-
},
|
|
99
|
-
});
|
|
100
|
-
}, [sleepTimerState.timerDate, sleepTimerState.timer, title, onSleepSelect]);
|
|
101
|
-
|
|
102
|
-
return useMemo(
|
|
103
|
-
() => ({
|
|
104
|
-
onSleepButtonPress,
|
|
105
|
-
sleepTimerState,
|
|
106
|
-
}),
|
|
107
|
-
[onSleepButtonPress, sleepTimerState]
|
|
108
|
-
);
|
|
109
|
-
};
|