@applicaster/quick-brick-player 13.0.0-rc.123 → 13.0.0-rc.124

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/quick-brick-player",
3
- "version": "13.0.0-rc.123",
3
+ "version": "13.0.0-rc.124",
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-rc.120",
38
+ "@applicaster/quick-brick-mobile-transport-controls": "13.0.0-rc.124",
39
39
  "@applicaster/quick-brick-tv-transport-controls": "13.0.0-rc.56",
40
- "@applicaster/zapp-react-native-tvos-app": "13.0.0-rc.123",
41
- "@applicaster/zapp-react-native-ui-components": "13.0.0-rc.123",
42
- "@applicaster/zapp-react-native-utils": "13.0.0-rc.123",
40
+ "@applicaster/zapp-react-native-tvos-app": "13.0.0-rc.124",
41
+ "@applicaster/zapp-react-native-ui-components": "13.0.0-rc.124",
42
+ "@applicaster/zapp-react-native-utils": "13.0.0-rc.124",
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,18 +37,13 @@ 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
- import {
41
- SLEEP_TIMER_OFF,
42
- SleepTimerController,
43
- SleepTimerState,
44
- } from "./SleepTimerManager";
40
+
45
41
  import { DEFAULT_SPEED, PlaybackSpeed, SpeedController } from "./SpeedManager";
46
42
  import {
47
43
  dismissModal as dismissBottomSheetModal,
48
44
  openBottomSheetModal,
49
45
  useModalStoreState,
50
46
  } from "@applicaster/zapp-react-native-utils/modalState";
51
- import { getSleepTimerOptions } from "../Const/sleepTimerOptions";
52
47
  import { speedOptions } from "../Const/speedOptions";
53
48
 
54
49
  const { log_debug } = createLogger({
@@ -128,19 +123,14 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
128
123
  configuration?.audio_live_player_update_interval
129
124
  );
130
125
 
131
- const [sleepTimerManager, setSleepTimerManager] =
132
- useState<SleepTimerController | null>(null);
133
-
134
126
  const [speedManager, setSpeedManager] = useState<SpeedController | null>(
135
127
  null
136
128
  );
137
129
 
138
130
  useEffect(() => {
139
131
  if (player) {
140
- setSleepTimerManager(new SleepTimerController(player));
141
132
  setSpeedManager(new SpeedController(player));
142
133
  } else {
143
- setSleepTimerManager(null);
144
134
  setSpeedManager(null);
145
135
  }
146
136
  }, [player]);
@@ -161,22 +151,11 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
161
151
  });
162
152
 
163
153
  return () => unsubscribe?.();
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]);
154
+ }, [player, modalBottomSheetState.visible]);
172
155
 
173
156
  const [playbackSpeed, setPlaybackSpeed] =
174
157
  useState<PlaybackSpeed>(DEFAULT_SPEED);
175
158
 
176
- const [sleepTimerState, setSleepTimerState] = useState<SleepTimerState>(
177
- new SleepTimerState(SLEEP_TIMER_OFF, 0)
178
- );
179
-
180
159
  const [defaultDimensions, setDefaultDimensions] =
181
160
  React.useState<Dimensions>(undefined);
182
161
 
@@ -199,17 +178,13 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
199
178
  };
200
179
 
201
180
  useEffect(() => {
202
- const sleepSubscription =
203
- sleepTimerManager?.sleepTimerState$.subscribe(setSleepTimerState);
204
-
205
181
  const speedSubscription =
206
182
  speedManager?.playbackSpeed$.subscribe(setPlaybackSpeed);
207
183
 
208
184
  return () => {
209
- sleepSubscription?.unsubscribe();
210
185
  speedSubscription?.unsubscribe();
211
186
  };
212
- }, [sleepTimerManager, speedManager]);
187
+ }, [speedManager]);
213
188
 
214
189
  useEffect(() => {
215
190
  if (!playerState.isReadyToPlay) {
@@ -217,8 +192,7 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
217
192
  }
218
193
 
219
194
  void speedManager?.loadSpeed();
220
- void sleepTimerManager?.loadSleep();
221
- }, [playerState.isReadyToPlay, speedManager, sleepTimerManager]);
195
+ }, [playerState.isReadyToPlay, speedManager]);
222
196
 
223
197
  const getValue = (key: string, stylesObj: {}) =>
224
198
  R.propOr(null, key, stylesObj);
@@ -266,13 +240,6 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
266
240
  [speedManager]
267
241
  );
268
242
 
269
- const onSleepSelect = React.useCallback(
270
- async (item) => {
271
- await sleepTimerManager?.setSleepTimer(item);
272
- },
273
- [sleepTimerManager]
274
- );
275
-
276
243
  const onSpeedButtonPress = React.useCallback(() => {
277
244
  openBottomSheetModal({
278
245
  modalBottomSheetContentProps: {
@@ -297,42 +264,6 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
297
264
  });
298
265
  }, [playbackSpeed, onSpeedSelect, configuration["playback_speed_title"]]);
299
266
 
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
-
336
267
  const onCloseVideoModal = React.useCallback(() => {
337
268
  playerManager.close();
338
269
  }, [playerManager]);
@@ -372,15 +303,7 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
372
303
  onPlayerSeek={onPlayerSeek}
373
304
  onSkipStart={onSkipStart}
374
305
  onSpeedButtonPress={onSpeedButtonPress}
375
- onSleepButtonPress={onSleepButtonPress}
376
306
  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}
384
307
  onSkipLive={onSkipLive}
385
308
  onCloseVideoModal={onCloseVideoModal}
386
309
  maximiseVideoModal={maximiseVideoModal}
@@ -2,7 +2,10 @@ 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 { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
5
+ import {
6
+ useIsTablet,
7
+ useZStore,
8
+ } from "@applicaster/zapp-react-native-utils/reactHooks";
6
9
  import {
7
10
  Controls as MobileTransportControls,
8
11
  ProgressBar,
@@ -27,6 +30,7 @@ import {
27
30
  sleepButton,
28
31
  speedButton,
29
32
  } from "./utils";
33
+ import { useSleepTimerModal } from "../../useSleepTimerModal";
30
34
 
31
35
  const styles = StyleSheet.create({
32
36
  flex: {
@@ -64,11 +68,8 @@ type Props = {
64
68
  value: GetValue;
65
69
  title: string | number;
66
70
  summary: string | number;
67
- sleepTimerDate: number;
68
- cancelSleepTimer: Callback;
69
71
 
70
72
  playbackSpeedName: string;
71
- playbackSleepName: string;
72
73
 
73
74
  onPlaybackButtonPress: Callback;
74
75
  onRewindButtonPress: Callback;
@@ -77,7 +78,6 @@ type Props = {
77
78
  onSkipStart: Callback;
78
79
  onSkipLive: Callback;
79
80
  onSpeedButtonPress: Callback;
80
- onSleepButtonPress: Callback;
81
81
 
82
82
  maxWidth?: number;
83
83
 
@@ -92,11 +92,8 @@ export const Controls = (props: Props) => {
92
92
  value,
93
93
  title,
94
94
  summary,
95
- sleepTimerDate,
96
- cancelSleepTimer,
97
95
 
98
96
  playbackSpeedName,
99
- playbackSleepName,
100
97
 
101
98
  onPlaybackButtonPress,
102
99
  onRewindButtonPress,
@@ -105,12 +102,21 @@ export const Controls = (props: Props) => {
105
102
  onSkipStart,
106
103
  onSkipLive,
107
104
  onSpeedButtonPress,
108
- onSleepButtonPress,
109
105
  maxWidth = 320,
110
106
 
111
107
  docked,
112
108
  } = props;
113
109
 
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
+
114
120
  const isTablet = useIsTablet();
115
121
  const controller = playerManager.getActivePlayer();
116
122
  let showFullControls = !controller?.isAd();
@@ -124,45 +130,6 @@ export const Controls = (props: Props) => {
124
130
  showFullControls = !!(controller?.hasSeekableDuration?.() && seekEnabled);
125
131
  }
126
132
 
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
-
166
133
  const startTime = entry.extensions?.live?.start_time;
167
134
  const endTime = entry.extensions?.live?.end_time;
168
135
 
@@ -295,7 +262,6 @@ export const Controls = (props: Props) => {
295
262
  isDateBasedLiveContent={isDateBasedLiveContent}
296
263
  onSkipLive={onSkipLive}
297
264
  progressBarStyles={styles.progressBar}
298
- countDownOptions={countDownOptions}
299
265
  thumbStyles={styles.progressBarThumbStyles}
300
266
  thumbStylesPressed={styles.progressBarThumbStyles_pressed}
301
267
  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);
@@ -77,15 +77,11 @@ export function MobileLayout({
77
77
  onSkipStart,
78
78
  onSkipLive,
79
79
  onSpeedButtonPress,
80
- onSleepButtonPress,
81
80
  onCloseVideoModal,
82
81
  playbackSpeedName,
83
- playbackSleepName,
84
82
  configuration,
85
83
  dimensions,
86
84
  playNextData,
87
- cancelSleepTimer,
88
- sleepTimerDate,
89
85
  title,
90
86
  summary,
91
87
  }: Props) {
@@ -196,10 +192,7 @@ export function MobileLayout({
196
192
  value={value}
197
193
  title={title}
198
194
  summary={summary}
199
- sleepTimerDate={sleepTimerDate}
200
- cancelSleepTimer={cancelSleepTimer}
201
195
  playbackSpeedName={playbackSpeedName}
202
- playbackSleepName={playbackSleepName}
203
196
  onPlaybackButtonPress={onPlaybackButtonPress}
204
197
  onRewindButtonPress={onRewindButtonPress}
205
198
  onForwardButtonPress={onForwardButtonPress}
@@ -207,7 +200,6 @@ export function MobileLayout({
207
200
  onSkipStart={onSkipStart}
208
201
  onSkipLive={onSkipLive}
209
202
  onSpeedButtonPress={onSpeedButtonPress}
210
- onSleepButtonPress={onSleepButtonPress}
211
203
  docked={docked}
212
204
  />
213
205
  </View>
@@ -98,15 +98,11 @@ export function TabletLandscapeLayout({
98
98
  onSkipStart,
99
99
  onSkipLive,
100
100
  onSpeedButtonPress,
101
- onSleepButtonPress,
102
101
  onCloseVideoModal,
103
102
  playbackSpeedName,
104
- playbackSleepName,
105
103
  configuration,
106
104
  dimensions,
107
105
  playNextData,
108
- cancelSleepTimer,
109
- sleepTimerDate,
110
106
  title,
111
107
  summary,
112
108
  }: Props) {
@@ -184,10 +180,7 @@ export function TabletLandscapeLayout({
184
180
  value={value}
185
181
  title={title}
186
182
  summary={summary}
187
- sleepTimerDate={sleepTimerDate}
188
- cancelSleepTimer={cancelSleepTimer}
189
183
  playbackSpeedName={playbackSpeedName}
190
- playbackSleepName={playbackSleepName}
191
184
  onPlaybackButtonPress={onPlaybackButtonPress}
192
185
  onRewindButtonPress={onRewindButtonPress}
193
186
  onForwardButtonPress={onForwardButtonPress}
@@ -195,7 +188,6 @@ export function TabletLandscapeLayout({
195
188
  onSkipStart={onSkipStart}
196
189
  onSkipLive={onSkipLive}
197
190
  onSpeedButtonPress={onSpeedButtonPress}
198
- onSleepButtonPress={onSleepButtonPress}
199
191
  docked={docked}
200
192
  />
201
193
  </View>
@@ -82,15 +82,11 @@ export function TabletPortraitLayout({
82
82
  onSkipStart,
83
83
  onSkipLive,
84
84
  onSpeedButtonPress,
85
- onSleepButtonPress,
86
85
  onCloseVideoModal,
87
86
  playbackSpeedName,
88
- playbackSleepName,
89
87
  configuration,
90
88
  dimensions,
91
89
  playNextData,
92
- cancelSleepTimer,
93
- sleepTimerDate,
94
90
  title,
95
91
  summary,
96
92
  }: Props) {
@@ -187,10 +183,7 @@ export function TabletPortraitLayout({
187
183
  value={value}
188
184
  title={title}
189
185
  summary={summary}
190
- sleepTimerDate={sleepTimerDate}
191
- cancelSleepTimer={cancelSleepTimer}
192
186
  playbackSpeedName={playbackSpeedName}
193
- playbackSleepName={playbackSleepName}
194
187
  onPlaybackButtonPress={onPlaybackButtonPress}
195
188
  onRewindButtonPress={onRewindButtonPress}
196
189
  onForwardButtonPress={onForwardButtonPress}
@@ -198,7 +191,6 @@ export function TabletPortraitLayout({
198
191
  onSkipStart={onSkipStart}
199
192
  onSkipLive={onSkipLive}
200
193
  onSpeedButtonPress={onSpeedButtonPress}
201
- onSleepButtonPress={onSleepButtonPress}
202
194
  docked={docked}
203
195
  />
204
196
  </View>
@@ -30,6 +30,7 @@ export class SleepTimerState {
30
30
  const SLEEP_PREFERENCE_KEY = "sleep";
31
31
 
32
32
  export class SleepTimerController {
33
+ // TODO cancel timer on reach time
33
34
  private sleepTimerStateSubject = new BehaviorSubject<SleepTimerState>(
34
35
  new SleepTimerState(SLEEP_TIMER_OFF, 0)
35
36
  );
@@ -55,18 +56,23 @@ export class SleepTimerController {
55
56
  }
56
57
  }
57
58
 
58
- async setSleepTimer(item: SleepTimer) {
59
+ async setSleepTimer(item: SleepTimer, prevTimerSleepDate?: number) {
59
60
  // User has selected Off state
60
61
  if (item.value === 0) {
61
62
  log_debug("Cancelling sleep timer");
63
+
64
+ // Removing scheduled pause on timer expiration
62
65
  this.player.cancelSleepTimer?.();
63
66
 
64
67
  return this.onTimerExpired();
65
68
  }
66
69
 
67
- log_debug(`Starting sleep timer in ${item.value} minutes`);
70
+ const futureSleepDate =
71
+ prevTimerSleepDate || Date.now() + item.value * 60000;
68
72
 
69
- const futureSleepDate = Date.now() + item.value * 60000;
73
+ log_debug(
74
+ `Starting sleep timer in ${(futureSleepDate - Date.now()) / 1000}s`
75
+ );
70
76
 
71
77
  if (await this.scheduleSleep(futureSleepDate)) {
72
78
  await localStorage.setItem(
@@ -82,17 +88,26 @@ export class SleepTimerController {
82
88
  }
83
89
 
84
90
  async onTimerExpired() {
85
- await localStorage.removeItem(SLEEP_PREFERENCE_KEY, NAMESPACE);
91
+ this.removeSleepTimerFromLocalStorage().catch((error) => {
92
+ log_error("Failed to remove sleep timer from local storage", {
93
+ error: error.message,
94
+ });
95
+ });
86
96
 
97
+ // Update UI state
87
98
  this.sleepTimerStateSubject.next(new SleepTimerState(SLEEP_TIMER_OFF, 0));
88
99
  }
89
100
 
101
+ async removeSleepTimerFromLocalStorage() {
102
+ await localStorage.removeItem(SLEEP_PREFERENCE_KEY, NAMESPACE);
103
+ }
104
+
90
105
  async loadSleep() {
91
106
  const sleep = await localStorage.getItem(SLEEP_PREFERENCE_KEY, NAMESPACE);
92
107
 
93
108
  if (!sleep) return;
94
109
 
95
- const { futureSleepDate, ...rest } = sleep;
110
+ const { futureSleepDate, ...item } = sleep;
96
111
 
97
112
  if (!futureSleepDate || hasDatePassed(futureSleepDate)) {
98
113
  log_debug("Sleep date has already passed");
@@ -101,13 +116,9 @@ export class SleepTimerController {
101
116
  return;
102
117
  }
103
118
 
104
- if (await this.scheduleSleep(futureSleepDate)) {
105
- log_debug(`Sleep state restored, will go to sleep at ${futureSleepDate}`);
119
+ log_debug("Sleep state restored");
106
120
 
107
- this.sleepTimerStateSubject.next(
108
- new SleepTimerState(rest as SleepTimer, futureSleepDate)
109
- );
110
- }
121
+ this.setSleepTimer(item as SleepTimer, futureSleepDate);
111
122
  }
112
123
 
113
124
  // Will be called if user closed the player explicitly.
@@ -0,0 +1,66 @@
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
+ };
@@ -0,0 +1 @@
1
+ export * from "./SleepTimerModalHeader";
@@ -0,0 +1,109 @@
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
+ };
@@ -28,12 +28,14 @@ const createTimerOptions = (value) => {
28
28
  };
29
29
  };
30
30
 
31
- export const getSleepTimerOptions = (configuration) => {
31
+ export const getSleepTimerOptions = (offLabel, sleepTimerValue) => {
32
32
  return [
33
- {
34
- label: configuration["playback_sleep_off_label"],
35
- value: 0,
36
- },
33
+ sleepTimerValue
34
+ ? {
35
+ label: offLabel,
36
+ value: 0,
37
+ }
38
+ : null,
37
39
  ...[5, 10, 15, 30, 45, 60, 120].map(createTimerOptions),
38
40
  ];
39
41
  };
@@ -116,3 +116,9 @@ 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;