@applicaster/quick-brick-player 13.0.0-rc.12 → 13.0.0-rc.121

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.
Files changed (44) hide show
  1. package/package.json +27 -14
  2. package/src/CellPlayer/Assets/AssetsComponent.tsx +17 -3
  3. package/src/Player/AudioLayer/AudioDescription.tsx +107 -68
  4. package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +205 -281
  5. package/src/Player/AudioLayer/Layout/Controls/VerticalGutter.tsx +45 -0
  6. package/src/Player/AudioLayer/Layout/Controls/index.tsx +320 -0
  7. package/src/Player/AudioLayer/Layout/{defaultLayoutsConts.ts → Controls/utils.ts} +7 -25
  8. package/src/Player/AudioLayer/Layout/Debug/index.tsx +55 -0
  9. package/src/Player/AudioLayer/Layout/{DockedLayout.tsx → DockedControls/index.tsx} +77 -81
  10. package/src/Player/AudioLayer/Layout/MobileLayout.tsx +230 -0
  11. package/src/Player/AudioLayer/Layout/PlayerImage/AnimatedImage.tsx +70 -0
  12. package/src/Player/AudioLayer/Layout/PlayerImage/FlexImage.tsx +20 -0
  13. package/src/Player/AudioLayer/Layout/PlayerImage/index.tsx +108 -0
  14. package/src/Player/AudioLayer/Layout/PlayerImage/styles.ts +36 -0
  15. package/src/Player/AudioLayer/Layout/TabletLandscapeLayout.tsx +220 -0
  16. package/src/Player/AudioLayer/Layout/TabletPortraitLayout.tsx +223 -0
  17. package/src/Player/AudioLayer/Layout/TransportInfo/index.tsx +128 -0
  18. package/src/Player/AudioLayer/Layout/VerticalGutter/index.tsx +8 -0
  19. package/src/Player/AudioLayer/Layout/const.ts +26 -0
  20. package/src/Player/AudioLayer/Layout/debug.ts +6 -0
  21. package/src/Player/AudioLayer/Layout/hooks/index.ts +5 -0
  22. package/src/Player/AudioLayer/Layout/hooks/useMobileLayoutImageSize.ts +14 -0
  23. package/src/Player/AudioLayer/Layout/hooks/useTabletLayoutImageSize.ts +105 -0
  24. package/src/Player/AudioLayer/Layout/hooks/useTransportInfoHeight.ts +54 -0
  25. package/src/Player/AudioLayer/Layout/index.tsx +21 -48
  26. package/src/Player/AudioLayer/Layout/types.ts +7 -0
  27. package/src/Player/AudioLayer/Layout/utils.ts +7 -0
  28. package/src/Player/AudioLayer/SleepTimerManager.ts +119 -0
  29. package/src/Player/AudioLayer/SpeedManager.ts +52 -0
  30. package/src/Player/AudioLayer/utils.ts +27 -0
  31. package/src/Player/Const/sleepTimerOptions.ts +39 -0
  32. package/src/Player/Const/speedOptions.ts +4 -0
  33. package/src/Player/Utils/Utils.ts +0 -5
  34. package/src/Player/Utils/index.tsx +117 -0
  35. package/src/Player/hooks/useIsCasting.tsx +57 -0
  36. package/src/Player/index.tsx +39 -22
  37. package/src/QuickBrickPlayer.tsx +3 -0
  38. package/src/Player/AudioLayer/Layout/DefaultLayout.tsx +0 -452
  39. package/src/Player/AudioLayer/LinearGradient/LinearGradientProxy.ts +0 -3
  40. package/src/Player/AudioLayer/LinearGradient/LinearGradientProxy.web.ts +0 -3
  41. package/src/Player/AudioLayer/LinearGradient/index.ts +0 -1
  42. package/src/Player/AudioLayer/MagicBG/index.tsx +0 -155
  43. package/src/Player/Helpers/VideoResizeMode.ts +0 -7
  44. package/src/utils/utils.ts +0 -25
@@ -0,0 +1,8 @@
1
+ import * as React from "react";
2
+ import { View } from "react-native";
3
+
4
+ import { withDebug } from "../debug";
5
+
6
+ export const VerticalGutter = ({ height }: { height: number }) => (
7
+ <View style={{ height, ...withDebug({ backgroundColor: "red" }) }} />
8
+ );
@@ -0,0 +1,26 @@
1
+ export const PADDING_HORIZONTAL = 15;
2
+
3
+ export const TABLET_VERTICAL_SEPARATOR_HEIGHT = 32;
4
+
5
+ export const TABLET_VERTICAL_SEPARATOR_HEIGHT_S = 16;
6
+
7
+ export const TABLET_LANDSCAPE = {
8
+ MARGIN_VERTICAL: 80, // gap between safe-area edge and audio-player container
9
+ MARGIN_VERTICAL_S: 60, // gap between safe-area edge and audio-player container
10
+ MAX_HEIGHT: 750,
11
+ MIN_WIDTH: 320, // Minimum width for the audio player container
12
+ };
13
+
14
+ export const TABLET_PORTRAIT = {
15
+ MARGIN_HORIZONTAL: 180,
16
+ MAX_WIDTH: 550,
17
+ };
18
+
19
+ export const MOBILE = {
20
+ MARGIN_HORIZONTAL: 30,
21
+ MARGIN_BOTTOM: 48,
22
+ MARGIN_TOP: 10,
23
+ MIN_HEIGHT: 458,
24
+ };
25
+
26
+ export const NAVBAR_HEIGHT = 60;
@@ -0,0 +1,6 @@
1
+ // use this variable to activate borders on AudioPlayer. It will help you to fix layout if needed.
2
+ export const DEBUG = false;
3
+
4
+ export const withDebug = (style) => {
5
+ return DEBUG ? style : {};
6
+ };
@@ -0,0 +1,5 @@
1
+ export { useTabletLayoutImageSize } from "./useTabletLayoutImageSize";
2
+
3
+ export { useMobileLayoutImageSize } from "./useMobileLayoutImageSize";
4
+
5
+ export { useTransportInfoHeight } from "./useTransportInfoHeight";
@@ -0,0 +1,14 @@
1
+ import { useDimensions } from "@applicaster/zapp-react-native-utils/reactHooks/layout";
2
+ import { MOBILE } from "../const";
3
+
4
+ type Return = {
5
+ imageSize: Option<number>;
6
+ };
7
+
8
+ export const useMobileLayoutImageSize = (): Return => {
9
+ const { width } = useDimensions("screen");
10
+
11
+ return {
12
+ imageSize: width - 2 * MOBILE.MARGIN_HORIZONTAL,
13
+ };
14
+ };
@@ -0,0 +1,105 @@
1
+ import * as React from "react";
2
+ import { LayoutChangeEvent } from "react-native";
3
+
4
+ import { toNumber } from "@applicaster/zapp-react-native-utils/numberUtils";
5
+ import { Subject } from "rxjs";
6
+ import { first, debounceTime, filter } from "rxjs/operators";
7
+ import { useRefWithInitialValue } from "@applicaster/zapp-react-native-utils/reactHooks/state/useRefWithInitialValue";
8
+
9
+ const TIMEOUT = 200; // ms
10
+
11
+ type Layout = {
12
+ x: number;
13
+ y: number;
14
+ width: number;
15
+ height: number;
16
+ };
17
+
18
+ const builder = () => {
19
+ const layoutChange$ = new Subject<Layout>();
20
+
21
+ const emitLayoutChange = (layout: Layout): void => {
22
+ layoutChange$.next(layout);
23
+ };
24
+
25
+ return {
26
+ emitLayoutChange,
27
+ layoutChange$,
28
+ };
29
+ };
30
+
31
+ type Props = {
32
+ isPortraitMode: boolean;
33
+ };
34
+
35
+ type Return = {
36
+ imageSize: Option<number>;
37
+ offsetY: Option<number>;
38
+ onLayout: (event: LayoutChangeEvent) => void;
39
+ };
40
+
41
+ export const useTabletLayoutImageSize = ({ isPortraitMode }: Props): Return => {
42
+ const [imageSize, setImageSize] = React.useState(undefined);
43
+ const [offsetY, setOffsetY] = React.useState(undefined);
44
+
45
+ const layoutRef = useRefWithInitialValue<ReturnType<typeof builder>>(builder);
46
+
47
+ React.useEffect(() => {
48
+ const subscription = layoutRef.current.layoutChange$
49
+ // for landscape layout we need to wait the second layout change in order to get proper size of image
50
+ .pipe(debounceTime(TIMEOUT), first())
51
+ .subscribe({
52
+ next: (layout: Layout) => {
53
+ const size = isPortraitMode
54
+ ? Math.min(layout.height, layout.width)
55
+ : layout.height;
56
+
57
+ setImageSize(size);
58
+ },
59
+ });
60
+
61
+ return () => {
62
+ subscription.unsubscribe();
63
+ };
64
+ }, [isPortraitMode]);
65
+
66
+ React.useEffect(() => {
67
+ const subscription = layoutRef.current.layoutChange$
68
+ .pipe(
69
+ filter((layout: Layout) => layout.y > 0),
70
+ debounceTime(TIMEOUT),
71
+ first()
72
+ )
73
+ .subscribe({
74
+ next: (layout: Layout) => {
75
+ setOffsetY(layout.y);
76
+ },
77
+ });
78
+
79
+ return () => {
80
+ subscription.unsubscribe();
81
+ };
82
+ }, []);
83
+
84
+ const handleLayout = React.useCallback((event: LayoutChangeEvent) => {
85
+ const height = toNumber(event?.nativeEvent?.layout?.height);
86
+ const width = toNumber(event?.nativeEvent?.layout?.width);
87
+ const y = toNumber(event?.nativeEvent?.layout?.y);
88
+ const x = toNumber(event?.nativeEvent?.layout?.x);
89
+
90
+ const layout: Layout = {
91
+ x,
92
+ y,
93
+ width,
94
+ height,
95
+ };
96
+
97
+ layoutRef.current.emitLayoutChange(layout);
98
+ }, []);
99
+
100
+ return {
101
+ imageSize,
102
+ offsetY,
103
+ onLayout: handleLayout,
104
+ };
105
+ };
@@ -0,0 +1,54 @@
1
+ import * as React from "react";
2
+ import { LayoutChangeEvent } from "react-native";
3
+ import { toNumber } from "@applicaster/zapp-react-native-utils/numberUtils";
4
+ import { Subject } from "rxjs";
5
+ import { first } from "rxjs/operators";
6
+ import { useRefWithInitialValue } from "@applicaster/zapp-react-native-utils/reactHooks/state/useRefWithInitialValue";
7
+
8
+ const builder = () => {
9
+ const layoutChange$ = new Subject<number>();
10
+
11
+ const emitLayoutChange = (height: number): void => {
12
+ layoutChange$.next(height);
13
+ };
14
+
15
+ return {
16
+ emitLayoutChange,
17
+ layoutChange$,
18
+ };
19
+ };
20
+
21
+ export const useTransportInfoHeight = () => {
22
+ const [height, setHeight] = React.useState(0);
23
+
24
+ const layoutRef = useRefWithInitialValue<ReturnType<typeof builder>>(builder);
25
+
26
+ React.useEffect(() => {
27
+ const subscription = layoutRef.current.layoutChange$
28
+ // skip first layout and take the second with proper size
29
+ // then ignore any layout update
30
+ .pipe(first())
31
+ .subscribe({
32
+ next: (size: number) => {
33
+ setHeight(size);
34
+ },
35
+ });
36
+
37
+ return () => {
38
+ subscription.unsubscribe();
39
+ };
40
+ }, []);
41
+
42
+ const handleLayout = React.useCallback((event: LayoutChangeEvent) => {
43
+ const height = toNumber(event?.nativeEvent?.layout?.height);
44
+
45
+ if (height) {
46
+ layoutRef.current.emitLayoutChange(height);
47
+ }
48
+ }, []);
49
+
50
+ return {
51
+ height,
52
+ onLayout: handleLayout,
53
+ };
54
+ };
@@ -1,52 +1,25 @@
1
- import React from "react";
2
- import DefaultLayout from "./DefaultLayout";
3
- import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
1
+ import * as React from "react";
4
2
 
5
- type Dimensions = {
6
- width: number;
7
- height: number;
8
- };
3
+ import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
4
+ import { useIsTabletLandscape } from "@applicaster/zapp-react-native-utils/reactHooks/device/useMemoizedIsTablet";
9
5
 
10
- type LayoutProps = {
11
- entry: ZappEntry;
12
- layoutState: {
13
- inline: boolean;
14
- docked: boolean;
15
- isModal: boolean;
16
- };
17
- playerState: {
18
- currentTime: number;
19
- duration: number;
20
- seekableDuration: number;
21
- isLive: boolean;
22
- isPaused: boolean;
23
- isBuffering: boolean;
24
- };
25
- value: GetValue;
26
- sleepTimerDate: number;
27
- playbackSpeedName: string;
28
- playbackSleepName: string;
29
- configuration: { [key: string]: any };
30
- onPlaybackButtonPress: () => void;
31
- onRewindButtonPress: () => void;
32
- onForwardButtonPress: () => void;
33
- onSpeedButtonPress: () => void;
34
- onSleepButtonPress: () => void;
35
- onCloseVideoModal: () => void;
36
- cancelSleepTimer: () => void;
37
- onPlayerSeek: (targetTime: number) => void;
38
- onSkipStart: () => void;
39
- onSkipLive: () => void;
40
- maximiseVideoModal: () => void;
41
- dimensions: Dimensions;
42
- defaultDimensions: Dimensions;
43
- playNextData: PlayNextData;
44
- title: string | number;
45
- summary: string | number;
46
- };
6
+ import { TabletPortraitLayout } from "./TabletPortraitLayout";
7
+ import { TabletLandscapeLayout } from "./TabletLandscapeLayout";
8
+ import { MobileLayout } from "./MobileLayout";
47
9
 
48
- export default function Layout(
49
- props: LayoutProps
50
- ): React.ReactElement<LayoutProps> {
51
- return <DefaultLayout {...props} />;
10
+ import { Props } from "./types";
11
+
12
+ export default function Layout(props: Props) {
13
+ const isTablet = useIsTablet();
14
+ const isTabletLandscape = useIsTabletLandscape();
15
+
16
+ if (!isTablet) {
17
+ return <MobileLayout {...props} />;
18
+ }
19
+
20
+ if (isTabletLandscape) {
21
+ return <TabletLandscapeLayout {...props} />;
22
+ }
23
+
24
+ return <TabletPortraitLayout {...props} />;
52
25
  }
@@ -0,0 +1,7 @@
1
+ import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
2
+
3
+ export type Props = QuickBrickPlayer.AudioPlayerProps & {
4
+ playNextData: PlayNextData;
5
+ };
6
+
7
+ export { PlayNextData };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Checks whether the screen height is matches with our threshold for small tablets.
3
+ * @param height screen height
4
+ * @returns boolean indicating if the height is less than 800 pixels
5
+ */
6
+ export const isSmallTabletHeight = (height: number): boolean =>
7
+ !(height >= 800);
@@ -0,0 +1,119 @@
1
+ import { BehaviorSubject } from "rxjs";
2
+ import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
3
+ import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
4
+ import { Player } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/player";
5
+ import { hasDatePassed, NAMESPACE } from "./utils";
6
+
7
+ const { log_debug, log_error } = createLogger({
8
+ subsystem: "Player",
9
+ category: "SleepTimerManager",
10
+ });
11
+
12
+ export type SleepTimer = {
13
+ label: string;
14
+ // interval in minutes for UI at the moment when timer was first started
15
+ value: number;
16
+ };
17
+
18
+ export const SLEEP_TIMER_OFF: SleepTimer = {
19
+ label: "playback_sleep_off_label",
20
+ value: 0,
21
+ };
22
+
23
+ export class SleepTimerState {
24
+ constructor(
25
+ public timer: SleepTimer,
26
+ public timerDate: number
27
+ ) {}
28
+ }
29
+
30
+ const SLEEP_PREFERENCE_KEY = "sleep";
31
+
32
+ export class SleepTimerController {
33
+ private sleepTimerStateSubject = new BehaviorSubject<SleepTimerState>(
34
+ new SleepTimerState(SLEEP_TIMER_OFF, 0)
35
+ );
36
+
37
+ constructor(private player: Player) {}
38
+
39
+ get sleepTimerState$() {
40
+ return this.sleepTimerStateSubject.asObservable();
41
+ }
42
+
43
+ private async scheduleSleep(sleepDate): Promise<boolean> {
44
+ log_debug(`Setting sleep interval to ${sleepDate}`);
45
+
46
+ try {
47
+ this.player.cancelSleepTimer?.();
48
+ this.player.startSleepTimer?.(sleepDate);
49
+
50
+ return true;
51
+ } catch (e) {
52
+ log_error("Failed to start sleep timer", { error: e.message });
53
+
54
+ return false;
55
+ }
56
+ }
57
+
58
+ async setSleepTimer(item: SleepTimer) {
59
+ // User has selected Off state
60
+ if (item.value === 0) {
61
+ log_debug("Cancelling sleep timer");
62
+ this.player.cancelSleepTimer?.();
63
+
64
+ return this.onTimerExpired();
65
+ }
66
+
67
+ log_debug(`Starting sleep timer in ${item.value} minutes`);
68
+
69
+ const futureSleepDate = Date.now() + item.value * 60000;
70
+
71
+ if (await this.scheduleSleep(futureSleepDate)) {
72
+ await localStorage.setItem(
73
+ SLEEP_PREFERENCE_KEY,
74
+ { ...item, futureSleepDate },
75
+ NAMESPACE
76
+ );
77
+
78
+ this.sleepTimerStateSubject.next(
79
+ new SleepTimerState(item, futureSleepDate)
80
+ );
81
+ }
82
+ }
83
+
84
+ async onTimerExpired() {
85
+ await localStorage.removeItem(SLEEP_PREFERENCE_KEY, NAMESPACE);
86
+
87
+ this.sleepTimerStateSubject.next(new SleepTimerState(SLEEP_TIMER_OFF, 0));
88
+ }
89
+
90
+ async loadSleep() {
91
+ const sleep = await localStorage.getItem(SLEEP_PREFERENCE_KEY, NAMESPACE);
92
+
93
+ if (!sleep) return;
94
+
95
+ const { futureSleepDate, ...rest } = sleep;
96
+
97
+ if (!futureSleepDate || hasDatePassed(futureSleepDate)) {
98
+ log_debug("Sleep date has already passed");
99
+ await localStorage.removeItem(SLEEP_PREFERENCE_KEY, NAMESPACE);
100
+
101
+ return;
102
+ }
103
+
104
+ if (await this.scheduleSleep(futureSleepDate)) {
105
+ log_debug(`Sleep state restored, will go to sleep at ${futureSleepDate}`);
106
+
107
+ this.sleepTimerStateSubject.next(
108
+ new SleepTimerState(rest as SleepTimer, futureSleepDate)
109
+ );
110
+ }
111
+ }
112
+
113
+ // Will be called if user closed the player explicitly.
114
+ // TODO: check if it will be called on Android when we need.
115
+ // Probably we will have to handle it on native instead.
116
+ public static async onPlayerDestroy() {
117
+ await localStorage.removeItem(SLEEP_PREFERENCE_KEY, NAMESPACE);
118
+ }
119
+ }
@@ -0,0 +1,52 @@
1
+ import { BehaviorSubject } from "rxjs";
2
+ import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
3
+ import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
4
+ import { Player } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/player";
5
+ import { NAMESPACE } from "./utils";
6
+
7
+ const { log_debug } = createLogger({
8
+ subsystem: "Player",
9
+ category: "PlaybackSpeedManager",
10
+ });
11
+
12
+ export type PlaybackSpeed = {
13
+ asset: string;
14
+ value: number;
15
+ };
16
+
17
+ const SPEED_PREFERENCE_KEY = "speed";
18
+
19
+ export const DEFAULT_SPEED: PlaybackSpeed = {
20
+ asset: "speed_1",
21
+ value: 1.0,
22
+ };
23
+
24
+ // todo: should not be applied to live streams
25
+ export class SpeedController {
26
+ private playbackSpeedSubject = new BehaviorSubject<PlaybackSpeed>(
27
+ DEFAULT_SPEED
28
+ );
29
+
30
+ constructor(private player: Player) {}
31
+
32
+ get playbackSpeed$() {
33
+ return this.playbackSpeedSubject.asObservable();
34
+ }
35
+
36
+ async loadSpeed() {
37
+ const speed = await localStorage.getItem(SPEED_PREFERENCE_KEY, NAMESPACE);
38
+
39
+ // checking for value in case we will pull empty or broken object
40
+ if (speed && speed.value) {
41
+ this.playbackSpeedSubject.next(speed);
42
+ this.player.setPlaybackRate?.(speed.value);
43
+ log_debug(`Speed loaded: ${speed}`);
44
+ }
45
+ }
46
+
47
+ async setPlaybackSpeed(speed: PlaybackSpeed) {
48
+ await localStorage.setItem(SPEED_PREFERENCE_KEY, speed, NAMESPACE);
49
+ this.playbackSpeedSubject.next(speed);
50
+ this.player.setPlaybackRate?.(speed.value);
51
+ }
52
+ }
@@ -1,5 +1,10 @@
1
1
  import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
2
+ import {
3
+ PlayerAnimationStateEnum,
4
+ PlayerAnimationStateT,
5
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation/ModalAnimationContext";
2
6
  import { parseJsonIfNeeded } from "@applicaster/zapp-react-native-utils/functionUtils";
7
+ import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
3
8
 
4
9
  export const NAMESPACE = "audio-player";
5
10
 
@@ -31,3 +36,25 @@ export const formatSpeed = (value: number) => {
31
36
  // Return the formatted string with "speed_" prefix
32
37
  return `speed_${formattedValue}`;
33
38
  };
39
+
40
+ export const calculateBorderRadius = (
41
+ docked: boolean,
42
+ startComponentsAnimation: boolean,
43
+ isActiveGesture: boolean,
44
+ playerAnimationState: PlayerAnimationStateT,
45
+ configuration: Record<string, any>
46
+ ): number => {
47
+ const shouldRemoveBorderRadius =
48
+ (startComponentsAnimation &&
49
+ isActiveGesture &&
50
+ playerAnimationState !== PlayerAnimationStateEnum.maximize) ||
51
+ docked;
52
+
53
+ if (shouldRemoveBorderRadius) {
54
+ return 0;
55
+ }
56
+
57
+ return toNumberWithDefaultZero(
58
+ configuration.audio_player_artwork_border_radius
59
+ );
60
+ };
@@ -0,0 +1,39 @@
1
+ import { toDayJSLocaleMap } from "@applicaster/zapp-react-native-utils/appUtils/localizationsHelper";
2
+ import { dayjs } from "@applicaster/zapp-react-native-utils/dateUtils";
3
+ import { getUILanguage } from "@applicaster/zapp-react-native-utils/localizationUtils";
4
+
5
+ const createTimerOptions = (value) => {
6
+ let label = "";
7
+
8
+ const str = dayjs
9
+ .duration(value, "minute")
10
+ .locale(toDayJSLocaleMap(getUILanguage()))
11
+ .humanize();
12
+
13
+ if (value === 60) {
14
+ /** Skipping prefix "an"/"un"/"una" for 1 hour */
15
+
16
+ if (str.includes(" ")) {
17
+ label = `1 ${str.split(" ")[1]}`; // Combine "1" with second part of the string
18
+ } else {
19
+ label = `1 ${str}`; // Use dayjs provided translation
20
+ }
21
+ } else {
22
+ label = str;
23
+ }
24
+
25
+ return {
26
+ label,
27
+ value,
28
+ };
29
+ };
30
+
31
+ export const getSleepTimerOptions = (configuration) => {
32
+ return [
33
+ {
34
+ label: configuration["playback_sleep_off_label"],
35
+ value: 0,
36
+ },
37
+ ...[5, 10, 15, 30, 45, 60, 120].map(createTimerOptions),
38
+ ];
39
+ };
@@ -0,0 +1,4 @@
1
+ export const speedOptions = [0.8, 1, 1.2, 1.5, 2].map((v) => ({
2
+ label: v + "x",
3
+ value: v,
4
+ }));
@@ -1,11 +1,6 @@
1
1
  import queryString from "query-string";
2
2
  import { dayjs } from "@applicaster/zapp-react-native-utils/dateUtils";
3
3
 
4
- export const getLiveUrl = (url: string) => {
5
- // Use the replace method with a regular expression to replace all instances of subClass with livePage
6
- return url.replace(new RegExp("subClass", "g"), "livePage");
7
- };
8
-
9
4
  export const addQueryParam = (
10
5
  baseUrl: string,
11
6
  paramName: string,