@applicaster/quick-brick-player 12.0.0-rc.8 → 12.0.0-rc.81

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.
@@ -1,26 +1,40 @@
1
1
  import * as React from "react";
2
2
  import * as R from "ramda";
3
3
  import { View } from "react-native";
4
+ import { dayjs } from "@applicaster/zapp-react-native-utils/dateUtils";
4
5
 
5
6
  import {
6
7
  ProgressBar,
7
8
  Controls,
8
9
  TransportInfo,
10
+ CountDown,
9
11
  } from "@applicaster/quick-brick-mobile-transport-controls";
10
12
 
11
13
  import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
12
14
 
13
- import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks/device/useIsTablet";
15
+ import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
14
16
  import {
15
17
  forwardButton,
16
18
  playbackButton,
17
19
  rewindButton,
18
20
  skipToStartButton,
19
21
  skipToLiveButton,
22
+ PADDING_HORIZONTAL,
23
+ speedButton,
24
+ sleepButton,
20
25
  } from "./defaultLayoutsConts";
21
26
  import { AudioDescription } from "../AudioDescription";
22
27
  import { PlayerImageBackground } from "@applicaster/zapp-react-native-ui-components/Components/PlayerImageBackground";
23
28
  import { useLanguageData } from "@applicaster/zapp-react-native-utils/reactHooks/player";
29
+ import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
30
+ import { useEffect, useMemo, useState } from "react";
31
+ import {
32
+ useModalAnimationContext,
33
+ AnimationComponent,
34
+ ComponentAnimationType,
35
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
36
+ import DockedLayout from "./DockedLayout";
37
+ import { isValidDate } from "../../Utils";
24
38
 
25
39
  type Dimensions = {
26
40
  width: number;
@@ -42,54 +56,76 @@ type LayoutProps = {
42
56
  isPaused: boolean;
43
57
  isBuffering: boolean;
44
58
  };
59
+ sleepTimerDate: number;
45
60
  value: GetValue;
61
+ playbackSpeedName: string;
62
+ playbackSleepName: string;
46
63
  configuration: { [key: string]: any };
47
64
  onPlaybackButtonPress: () => void;
48
65
  onRewindButtonPress: () => void;
49
66
  onForwardButtonPress: () => void;
67
+ onSpeedButtonPress: () => void;
68
+ onSleepButtonPress: () => void;
69
+ cancelSleepTimer: () => void;
50
70
  onPlayerSeek: (targetTime: number) => void;
51
71
  onSkipLive: () => void;
52
72
  onSkipStart: () => void;
73
+ onCloseVideoModal: () => void;
74
+ maximiseVideoModal: () => void;
53
75
  dimensions: Dimensions;
76
+ playNextData: PlayNextData;
77
+ title: string | number;
78
+ summary: string | number;
79
+ defaultDimensions: Dimensions;
54
80
  };
55
81
 
56
82
  const Styles = {
57
83
  container: { flex: 1 },
58
- tabletContainer: (isPortraitMode) => ({
84
+ tabletContainer: (isPortraitMode: boolean) => ({
59
85
  flex: 1,
60
86
  alignItems: "center",
61
87
  justifyContent: isPortraitMode ? "center" : "flex-start",
62
88
  }),
63
- tabletLayout: {
64
- width: 463,
89
+ playerImageWrapper: {
90
+ width: "100%",
65
91
  maxHeight: 750,
66
92
  marginTop: 80,
93
+ },
94
+ tabletLayout: {
95
+ width: 463,
67
96
  marginBottom: 80,
68
97
  },
69
98
  controls: (isTablet: boolean, isLiveShowFullControls: boolean) => ({
70
99
  height: isTablet ? 64 : 50,
71
- marginHorizontal: isLiveShowFullControls ? 10 : 24,
72
- justifyContent: isLiveShowFullControls ? "space-between" : "center",
100
+ marginHorizontal: 10,
101
+ justifyContent: isLiveShowFullControls ? "space-between" : "space-evenly",
73
102
  }),
74
103
  backgroundImageContainer: (dimensions: Dimensions, isTablet: boolean) => {
104
+ const mobileValue = dimensions.width
105
+ ? dimensions.width - PADDING_HORIZONTAL * 2
106
+ : 330;
107
+
75
108
  return {
76
- width: isTablet ? 463 : dimensions.width,
77
- height: isTablet ? 463 : dimensions.width,
109
+ width: isTablet ? 463 : mobileValue,
110
+ height: isTablet ? 463 : mobileValue,
78
111
  };
79
112
  },
80
- backgroundImageContainerImageStyle: (isTablet) => ({
81
- borderRadius: isTablet ? 24 : 10,
113
+ backgroundImageContainerImageStyle: (
114
+ isTablet: boolean,
115
+ docked: boolean,
116
+ startComponentsAnimation: boolean,
117
+ isActiveGesture: boolean
118
+ ) => ({
119
+ borderRadius:
120
+ (startComponentsAnimation && isActiveGesture) || docked
121
+ ? 0
122
+ : isTablet
123
+ ? 24
124
+ : 10,
82
125
  }),
83
- playerContainer: (docked: boolean, style) => {
84
- const combinedStyle = {
85
- flex: 1,
86
- };
87
-
88
- if (docked) {
89
- combinedStyle["height"] = style.height;
90
- }
91
126
 
92
- return combinedStyle;
127
+ paddingHorizontal: {
128
+ paddingHorizontal: PADDING_HORIZONTAL,
93
129
  },
94
130
 
95
131
  controlsMobile: {
@@ -103,6 +139,12 @@ const Styles = {
103
139
  separator: {
104
140
  height: 20,
105
141
  },
142
+ flexDirectionRow: {
143
+ flexDirection: "row",
144
+ },
145
+ alignItemsCenter: {
146
+ alignItems: "center",
147
+ },
106
148
  };
107
149
 
108
150
  export default function DefaultLayout(props: LayoutProps) {
@@ -117,25 +159,64 @@ export default function DefaultLayout(props: LayoutProps) {
117
159
  onPlayerSeek,
118
160
  onSkipStart,
119
161
  onSkipLive,
162
+ onSpeedButtonPress,
163
+ onSleepButtonPress,
164
+ playbackSpeedName,
165
+ playbackSleepName,
120
166
  configuration,
121
167
  dimensions,
168
+ cancelSleepTimer,
169
+ sleepTimerDate,
170
+ title,
171
+ summary,
172
+ defaultDimensions,
122
173
  } = props;
123
174
 
124
175
  const isTablet = useIsTablet();
125
176
  const { width, height } = dimensions;
126
177
  const isPortraitMode = width < height;
127
178
  const isLiveContent = playerState.isLive;
179
+ const docked = layoutState.docked;
128
180
 
129
181
  const languageData = useLanguageData("audio-player");
130
182
 
183
+ const showCounter = useMemo(() => sleepTimerDate > 0, [sleepTimerDate]);
184
+
131
185
  const controller = playerManager.getActivePlayer();
132
186
  const liveButton = Boolean(value("live_badge_enabled"));
187
+ const sleepTitle = value("playback_sleep_countdown_label");
133
188
 
134
- let showFullControls =
135
- controller?.hasContentDuration?.() && !controller?.isAd();
189
+ const [timeLeft, setTimeLeft] = useState(
190
+ sleepTimerDate !== 0 ? sleepTimerDate - Date.now() : 0
191
+ );
192
+
193
+ let showFullControls = !controller?.isAd();
194
+
195
+ const startTime = entry.extensions?.live?.start_time;
196
+ const endTime = entry.extensions?.live?.end_time;
197
+
198
+ const hasValidStartTime = isValidDate(startTime);
199
+ const hasValidEndTime = isValidDate(endTime);
200
+
201
+ const isDateBasedLiveContent =
202
+ isLiveContent && hasValidStartTime && hasValidEndTime;
203
+
204
+ const needsToShowProgressBar =
205
+ (isLiveContent &&
206
+ (Boolean(value("liveSeekingEnabled")) || isDateBasedLiveContent)) ||
207
+ !isLiveContent;
208
+
209
+ const startTimestamp = hasValidStartTime
210
+ ? dayjs.utc(startTime!).valueOf()
211
+ : undefined;
212
+
213
+ const endTimestamp = hasValidEndTime
214
+ ? dayjs.utc(endTime!).valueOf()
215
+ : undefined;
136
216
 
137
217
  if (isLiveContent) {
138
218
  const seekEnabled = Boolean(value("liveSeekingEnabled"));
219
+
139
220
  showFullControls = !!(controller?.hasSeekableDuration?.() && seekEnabled);
140
221
  }
141
222
 
@@ -163,8 +244,29 @@ export default function DefaultLayout(props: LayoutProps) {
163
244
  return [play];
164
245
  }
165
246
 
166
- const rewind = rewindButton({ isTablet, onPress: onRewindButtonPress });
167
- const forward = forwardButton({ isTablet, onPress: onForwardButtonPress });
247
+ const rewind = rewindButton({
248
+ isTablet,
249
+ onPress: controller?.hasSeekableDuration?.() ? onRewindButtonPress : null,
250
+ });
251
+
252
+ const forward = forwardButton({
253
+ isTablet,
254
+ onPress: controller?.hasSeekableDuration?.()
255
+ ? onForwardButtonPress
256
+ : null,
257
+ });
258
+
259
+ const speed = speedButton({
260
+ isTablet,
261
+ onPress: onSpeedButtonPress,
262
+ name: playbackSpeedName,
263
+ });
264
+
265
+ const sleep = sleepButton({
266
+ isTablet,
267
+ onPress: onSleepButtonPress,
268
+ name: playbackSleepName,
269
+ });
168
270
 
169
271
  if (isLiveContent) {
170
272
  const skipToStart = skipToStartButton({
@@ -180,7 +282,7 @@ export default function DefaultLayout(props: LayoutProps) {
180
282
  return [skipToStart, rewind, play, forward, skipToLive];
181
283
  }
182
284
 
183
- return [rewind, play, forward];
285
+ return [speed, rewind, play, forward, sleep];
184
286
  }, [
185
287
  playerState,
186
288
  liveButton,
@@ -188,18 +290,53 @@ export default function DefaultLayout(props: LayoutProps) {
188
290
  onPlaybackButtonPress,
189
291
  onRewindButtonPress,
190
292
  onForwardButtonPress,
293
+ onSpeedButtonPress,
191
294
  showFullControls,
192
295
  onSkipStart,
193
296
  onSkipLive,
194
297
  isLiveContent,
298
+ playbackSpeedName,
299
+ playbackSleepName,
300
+ controller,
195
301
  ]);
196
302
 
303
+ useEffect(() => {
304
+ if (showCounter && sleepTimerDate !== 0) {
305
+ setTimeLeft(sleepTimerDate - Date.now());
306
+
307
+ const intervalId = setInterval(() => {
308
+ setTimeLeft(() => {
309
+ const newTimeLeft = sleepTimerDate - Date.now();
310
+
311
+ if (newTimeLeft <= 0) {
312
+ clearInterval(intervalId);
313
+
314
+ cancelSleepTimer();
315
+
316
+ return 0;
317
+ } else {
318
+ return newTimeLeft;
319
+ }
320
+ });
321
+ }, 1000);
322
+
323
+ return () => clearInterval(intervalId);
324
+ } else {
325
+ setTimeLeft(0);
326
+ }
327
+ }, [sleepTimerDate, showCounter, cancelSleepTimer]);
328
+
197
329
  const Separator = () => <View style={Styles.separator} />;
198
330
 
199
- const liveSeekingEnabled = value("liveSeekingEnabled");
331
+ const defaultImageDimensions = React.useMemo(
332
+ () =>
333
+ defaultDimensions &&
334
+ Styles.backgroundImageContainer(defaultDimensions, isTablet),
335
+ [defaultDimensions, isTablet]
336
+ );
200
337
 
201
- const needsToShowProgressBar =
202
- (isLiveContent && liveSeekingEnabled) || !isLiveContent;
338
+ const { isActiveGesture, startComponentsAnimation } =
339
+ useModalAnimationContext();
203
340
 
204
341
  return (
205
342
  <View
@@ -207,56 +344,108 @@ export default function DefaultLayout(props: LayoutProps) {
207
344
  isTablet ? Styles.tabletContainer(isPortraitMode) : Styles.container
208
345
  }
209
346
  >
210
- <TransportInfo
211
- content={entry}
212
- value={value}
213
- visible={true}
214
- languageData={languageData}
215
- layoutState={layoutState}
216
- actionButtons={actionButtons}
217
- />
218
- <View
219
- style={
220
- isTablet ? Styles.tabletContainer(isPortraitMode) : Styles.container
221
- }
347
+ <AnimationComponent animationType={ComponentAnimationType.componentFade}>
348
+ <TransportInfo
349
+ content={entry}
350
+ value={value}
351
+ visible={true}
352
+ languageData={languageData}
353
+ layoutState={layoutState}
354
+ actionButtons={actionButtons}
355
+ />
356
+ </AnimationComponent>
357
+ <AnimationComponent
358
+ animationType={ComponentAnimationType.moveUpComponent}
359
+ style={isTablet ? Styles.playerImageWrapper : undefined}
222
360
  >
223
- <View style={isTablet ? Styles.tabletLayout : null}>
361
+ <AnimationComponent
362
+ animationType={ComponentAnimationType.moveComponentHorizontal}
363
+ additionalData={{
364
+ disableAnimatedComponent: !isTablet,
365
+ moveValue: (dimensions.width - 463) / 2,
366
+ extraAnimation: !isTablet
367
+ ? {
368
+ [ComponentAnimationType.audioPlayerPaddingHorizontal]: {
369
+ style: Styles.paddingHorizontal,
370
+ },
371
+ }
372
+ : null,
373
+ }}
374
+ style={isTablet ? Styles.alignItemsCenter : undefined}
375
+ >
224
376
  <PlayerImageBackground
225
- configuration={configuration}
226
377
  entry={entry}
227
378
  style={Styles.backgroundImageContainer(dimensions, isTablet)}
228
- imageStyle={Styles.backgroundImageContainerImageStyle(isTablet)}
229
- />
230
- <AudioDescription
231
- content={entry}
232
- value={value}
233
- layoutState={layoutState}
379
+ imageStyle={Styles.backgroundImageContainerImageStyle(
380
+ isTablet,
381
+ docked,
382
+ startComponentsAnimation,
383
+ isActiveGesture
384
+ )}
385
+ defaultImageDimensions={defaultImageDimensions}
234
386
  />
235
- <Separator />
236
- {needsToShowProgressBar && (
237
- <View>
238
- <ProgressBar
239
- content={entry}
240
- value={value}
241
- visible={true}
242
- layoutState={layoutState}
243
- onPlayerSeek={onPlayerSeek}
244
- playerState={playerState}
245
- disableFullscreen={true}
246
- sliderStyle={2}
247
- />
248
- <Separator />
249
- </View>
250
- )}
251
- <Controls
252
- value={value}
253
- visible={true}
254
- keepAlive={null}
255
- buttons={controlButtons}
256
- style={Styles.controls(isTablet, isLiveContent && showFullControls)}
257
- playerState={playerState}
258
- />
259
- </View>
387
+ </AnimationComponent>
388
+
389
+ <DockedLayout {...props} />
390
+ </AnimationComponent>
391
+ <View
392
+ style={[
393
+ Styles.paddingHorizontal,
394
+ isTablet ? Styles.tabletLayout : undefined,
395
+ ]}
396
+ >
397
+ {!docked && (
398
+ <AnimationComponent
399
+ animationType={ComponentAnimationType.componentFade}
400
+ >
401
+ <AudioDescription
402
+ title={title}
403
+ summary={summary}
404
+ value={value}
405
+ layoutState={layoutState}
406
+ />
407
+ <Separator />
408
+ {needsToShowProgressBar && (
409
+ <View>
410
+ <ProgressBar
411
+ content={entry}
412
+ value={value}
413
+ visible={true}
414
+ layoutState={layoutState}
415
+ onPlayerSeek={onPlayerSeek}
416
+ playerState={playerState}
417
+ disableFullscreen={true}
418
+ sliderStyle={2}
419
+ startTimestamp={startTimestamp}
420
+ endTimestamp={endTimestamp}
421
+ isDateBasedLiveContent={isDateBasedLiveContent}
422
+ onSkipLive={onSkipLive}
423
+ />
424
+
425
+ {showCounter && timeLeft >= 0 && (
426
+ <CountDown
427
+ timeLeft={timeLeft}
428
+ value={value}
429
+ title={sleepTitle}
430
+ />
431
+ )}
432
+
433
+ <Separator />
434
+ </View>
435
+ )}
436
+ <Controls
437
+ value={value}
438
+ visible={true}
439
+ keepAlive={null}
440
+ buttons={controlButtons}
441
+ style={Styles.controls(
442
+ isTablet,
443
+ isLiveContent && showFullControls
444
+ )}
445
+ playerState={playerState}
446
+ />
447
+ </AnimationComponent>
448
+ )}
260
449
  </View>
261
450
  </View>
262
451
  );
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import * as React from "react";
2
2
  import {
3
3
  StyleSheet,
4
4
  TouchableWithoutFeedback,
@@ -6,7 +6,7 @@ import {
6
6
  ViewStyle,
7
7
  } from "react-native";
8
8
  import * as R from "ramda";
9
- import { useDimensions } from "@applicaster/zapp-react-native-utils/reactHooks";
9
+ import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
10
10
 
11
11
  import {
12
12
  ContentInfo,
@@ -15,8 +15,13 @@ import {
15
15
  Button,
16
16
  } from "@applicaster/quick-brick-mobile-transport-controls";
17
17
 
18
- import { PlayerImageBackground } from "@applicaster/zapp-react-native-ui-components/Components/PlayerImageBackground";
19
18
  import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
19
+ import {
20
+ useModalAnimationContext,
21
+ AnimationComponent,
22
+ ComponentAnimationType,
23
+ PlayerAnimationStateEnum,
24
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
20
25
 
21
26
  type Dimensions = {
22
27
  width: number;
@@ -48,6 +53,8 @@ type LayoutProps = {
48
53
  maximiseVideoModal: () => void;
49
54
  dimensions: Dimensions;
50
55
  playNextData: PlayNextData;
56
+ title: string | number;
57
+ summary: string | number;
51
58
  };
52
59
 
53
60
  const styles = StyleSheet.create({
@@ -102,14 +109,26 @@ const backgroundColorStyle = (value: GetValue): ViewStyle => ({
102
109
  backgroundColor: value("docked_player_background_color"),
103
110
  });
104
111
 
105
- const playerDimensionsStyle = ({
106
- height,
107
- }: PlayerDimensionsProp): ViewStyle => ({
108
- paddingLeft: height || 100,
112
+ const playerDimensionsStyle = (
113
+ minimisedHeight: number,
114
+ addPadding: boolean
115
+ ): ViewStyle => ({
116
+ paddingLeft: addPadding ? minimisedHeight || 100 : 0,
117
+ });
118
+
119
+ const animatedContainerStyle = (docked: boolean): ViewStyle => ({
120
+ top: docked ? 2 : null,
121
+ position: "absolute",
122
+ width: "100%",
123
+ zIndex: docked ? 1 : -1,
109
124
  });
110
125
 
111
- const backgroundImageStyle = (dimensions: Dimensions): ViewStyle => ({
112
- height: dimensions.height,
126
+ const backgroundImageStyle = (
127
+ dimensions: Dimensions,
128
+ minimisedHeight: number,
129
+ isActiveGesture: boolean
130
+ ): ViewStyle => ({
131
+ height: isActiveGesture ? minimisedHeight : dimensions.height,
113
132
  aspectRatio: 1,
114
133
  position: "absolute",
115
134
  });
@@ -191,23 +210,42 @@ export default function DockedLayout(props: LayoutProps) {
191
210
  dimensions,
192
211
  entry,
193
212
  onCloseVideoModal,
194
- maximiseVideoModal,
195
213
  onPlayerSeek,
196
214
  playerState,
197
215
  configuration,
198
216
  playNextData,
217
+ title,
218
+ summary,
199
219
  } = props;
200
220
 
201
- const {
202
- deviceInfo: { isTablet },
203
- } = useDimensions("screen", { deviceInfo: true });
221
+ const isTablet = useIsTablet();
204
222
 
205
223
  const visible = true;
206
224
  const liveButton = Boolean(value("live_badge_enabled"));
207
225
 
226
+ const {
227
+ isActiveGesture,
228
+ setPlayerAnimationState,
229
+ startComponentsAnimation,
230
+ minimisedHeight,
231
+ } = useModalAnimationContext();
232
+
233
+ const maximazeVideoModal = React.useCallback(
234
+ () => setPlayerAnimationState(PlayerAnimationStateEnum.maximaze),
235
+ []
236
+ );
237
+
238
+ if (!layoutState.docked && !startComponentsAnimation) return null;
239
+
208
240
  return (
209
- <View style={styles.container}>
210
- {!playerState.isLive && (
241
+ <AnimationComponent
242
+ animationType={ComponentAnimationType.componentAppears}
243
+ style={[
244
+ styles.container,
245
+ animatedContainerStyle(isActiveGesture || layoutState.docked),
246
+ ]}
247
+ >
248
+ {!playerState.isLive && layoutState.docked && (
211
249
  <ProgressBar
212
250
  content={entry}
213
251
  value={value}
@@ -215,18 +253,27 @@ export default function DockedLayout(props: LayoutProps) {
215
253
  layoutState={layoutState}
216
254
  onPlayerSeek={onPlayerSeek}
217
255
  playerState={playerState}
256
+ docked
218
257
  />
219
258
  )}
220
259
 
221
260
  <View style={styles.content}>
222
- <PlayerImageBackground
223
- configuration={configuration}
224
- entry={entry}
225
- style={backgroundImageStyle(dimensions)}
261
+ <View
262
+ style={backgroundImageStyle(
263
+ dimensions,
264
+ minimisedHeight,
265
+ isActiveGesture
266
+ )}
226
267
  />
227
- <TouchableWithoutFeedback onPress={maximiseVideoModal}>
268
+ <TouchableWithoutFeedback onPress={maximazeVideoModal}>
228
269
  <View
229
- style={[styles.touchableArea, playerDimensionsStyle(dimensions)]}
270
+ style={[
271
+ styles.touchableArea,
272
+ playerDimensionsStyle(
273
+ minimisedHeight,
274
+ isActiveGesture || layoutState.docked
275
+ ),
276
+ ]}
230
277
  >
231
278
  <View
232
279
  style={[
@@ -234,7 +281,11 @@ export default function DockedLayout(props: LayoutProps) {
234
281
  !configuration.magic_background && backgroundColorStyle(value),
235
282
  ]}
236
283
  >
237
- <ContentInfo content={entry} value={value} docked />
284
+ <ContentInfo
285
+ content={{ ...entry, title, summary }}
286
+ value={value}
287
+ docked
288
+ />
238
289
  </View>
239
290
  </View>
240
291
  </TouchableWithoutFeedback>
@@ -266,6 +317,6 @@ export default function DockedLayout(props: LayoutProps) {
266
317
  />
267
318
  </View>
268
319
  </View>
269
- </View>
320
+ </AnimationComponent>
270
321
  );
271
322
  }
@@ -19,6 +19,8 @@ const playbackButtonMargins = (
19
19
  ): number =>
20
20
  isLiveShowFullControls ? (isTablet ? 65 : 10) : isTablet ? 95 : 40;
21
21
 
22
+ export const PADDING_HORIZONTAL = 15;
23
+
22
24
  export const playbackButton = ({
23
25
  playerState,
24
26
  liveButton,
@@ -53,8 +55,9 @@ type ButtonsProps = {
53
55
  onPress: () => void;
54
56
  isTablet: boolean;
55
57
  style?: any;
58
+ name?: string;
56
59
  };
57
- type ButtonsByNameProps = ButtonsProps & { name: string };
60
+ type ButtonsByNameProps = ButtonsProps & { name?: string };
58
61
 
59
62
  const rewindButtonByName = ({
60
63
  name,
@@ -79,6 +82,28 @@ const skipButtonByName = ({
79
82
  style,
80
83
  });
81
84
 
85
+ const speedButtonByName = ({
86
+ name = "speed_1",
87
+ onPress,
88
+ style,
89
+ }: ButtonsByNameProps): ControlsButton => ({
90
+ size: "small",
91
+ icon: name,
92
+ onPress,
93
+ style,
94
+ });
95
+
96
+ const sleepButtonByName = ({
97
+ name = "sleep_timer",
98
+ onPress,
99
+ style,
100
+ }: ButtonsByNameProps): ControlsButton => ({
101
+ size: "small",
102
+ icon: name,
103
+ onPress,
104
+ style,
105
+ });
106
+
82
107
  export const rewindButton = (props: ButtonsProps): ControlsButton =>
83
108
  rewindButtonByName({ ...props, name: "rewind" });
84
109
 
@@ -90,3 +115,9 @@ export const skipToStartButton = (props: ButtonsProps): ControlsButton =>
90
115
 
91
116
  export const skipToLiveButton = (props: ButtonsProps): ControlsButton =>
92
117
  skipButtonByName({ ...props, name: "skip_live" });
118
+
119
+ export const speedButton = (props: ButtonsProps): ControlsButton =>
120
+ speedButtonByName({ ...props });
121
+
122
+ export const sleepButton = (props: ButtonsProps): ControlsButton =>
123
+ sleepButtonByName({ ...props });