@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/quick-brick-player",
3
- "version": "13.0.0-alpha.3003916059",
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-rc.124",
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.3003916059",
41
- "@applicaster/zapp-react-native-ui-components": "13.0.0-alpha.3003916059",
42
- "@applicaster/zapp-react-native-utils": "13.0.0-alpha.3003916059",
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
- }, [playerState.isReadyToPlay, speedManager]);
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
- { backgroundColor: dockedBackgroundColor },
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 { Image, Animated, StyleSheet, ImageStyle } from "react-native";
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 = ({ entry, style, imageWidth, docked }: Props) => {
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
- const subscription = changePlayerState$.subscribe({
44
- next: (nextPlayerState: PlayerAnimationStateT) => {
45
- if (nextPlayerState === PlayerAnimationStateEnum.minimize) {
46
- Animated.timing(heightAnim, {
47
- toValue: minimisedHeight,
48
- duration: DURATION_TO_MINIMIZE,
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
- if (nextPlayerState === PlayerAnimationStateEnum.appear_as_docked) {
62
- // minimize image immediatly
63
- heightAnim.setValue(minimisedHeight);
64
- }
65
- },
66
- });
56
+ return;
57
+ }
67
58
 
68
- return () => {
69
- subscription.unsubscribe();
70
- };
71
- }, [changePlayerState$, minimisedHeight, imageWidth]);
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?: (event: LayoutChangeEvent) => void;
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 = noop,
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 = toBooleanWithDefaultTrue(
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: platformSelect({
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, prevTimerSleepDate?: number) {
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
- const futureSleepDate =
71
- prevTimerSleepDate || Date.now() + item.value * 60000;
67
+ log_debug(`Starting sleep timer in ${item.value} minutes`);
72
68
 
73
- log_debug(
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
- this.removeSleepTimerFromLocalStorage().catch((error) => {
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, ...item } = sleep;
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
- log_debug("Sleep state restored");
104
+ if (await this.scheduleSleep(futureSleepDate)) {
105
+ log_debug(`Sleep state restored, will go to sleep at ${futureSleepDate}`);
120
106
 
121
- this.setSleepTimer(item as SleepTimer, futureSleepDate);
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 = (offLabel, sleepTimerValue) => {
31
+ export const getSleepTimerOptions = (configuration) => {
32
32
  return [
33
- sleepTimerValue
34
- ? {
35
- label: offLabel,
36
- value: 0,
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;
@@ -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 (!isTvOS || !this.getPlayNextData()) {
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
- {!isTvOS && (
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
- };