@applicaster/quick-brick-player 13.0.0-rc.13 → 13.0.0-rc.130

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 (49) 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 +142 -295
  5. package/src/Player/AudioLayer/Layout/Controls/VerticalGutter.tsx +45 -0
  6. package/src/Player/AudioLayer/Layout/Controls/index.tsx +291 -0
  7. package/src/Player/AudioLayer/Layout/{defaultLayoutsConts.ts → Controls/utils.ts} +9 -27
  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 +222 -0
  11. package/src/Player/AudioLayer/Layout/PlayerImage/AnimatedImage.tsx +87 -0
  12. package/src/Player/AudioLayer/Layout/PlayerImage/FlexImage.tsx +20 -0
  13. package/src/Player/AudioLayer/Layout/PlayerImage/hooks/index.ts +1 -0
  14. package/src/Player/AudioLayer/Layout/PlayerImage/hooks/useChangePlayerState.ts +99 -0
  15. package/src/Player/AudioLayer/Layout/PlayerImage/index.tsx +112 -0
  16. package/src/Player/AudioLayer/Layout/PlayerImage/styles.ts +36 -0
  17. package/src/Player/AudioLayer/Layout/TabletLandscapeLayout.tsx +212 -0
  18. package/src/Player/AudioLayer/Layout/TabletPortraitLayout.tsx +215 -0
  19. package/src/Player/AudioLayer/Layout/TransportInfo/index.tsx +128 -0
  20. package/src/Player/AudioLayer/Layout/VerticalGutter/index.tsx +8 -0
  21. package/src/Player/AudioLayer/Layout/const.ts +26 -0
  22. package/src/Player/AudioLayer/Layout/debug.ts +6 -0
  23. package/src/Player/AudioLayer/Layout/hooks/index.ts +5 -0
  24. package/src/Player/AudioLayer/Layout/hooks/useMobileLayoutImageSize.ts +14 -0
  25. package/src/Player/AudioLayer/Layout/hooks/useTabletLayoutImageSize.ts +105 -0
  26. package/src/Player/AudioLayer/Layout/hooks/useTransportInfoHeight.ts +54 -0
  27. package/src/Player/AudioLayer/Layout/index.tsx +21 -48
  28. package/src/Player/AudioLayer/Layout/types.ts +7 -0
  29. package/src/Player/AudioLayer/Layout/utils.ts +7 -0
  30. package/src/Player/AudioLayer/SleepTimerManager.ts +130 -0
  31. package/src/Player/AudioLayer/SpeedManager.ts +52 -0
  32. package/src/Player/AudioLayer/components/SleepTimerModalHeader/SleepTimerModalHeader.tsx +66 -0
  33. package/src/Player/AudioLayer/components/SleepTimerModalHeader/index.ts +1 -0
  34. package/src/Player/AudioLayer/useSleepTimerModal.ts +109 -0
  35. package/src/Player/AudioLayer/utils.ts +27 -0
  36. package/src/Player/Const/sleepTimerOptions.ts +41 -0
  37. package/src/Player/Const/speedOptions.ts +4 -0
  38. package/src/Player/Utils/Utils.ts +0 -5
  39. package/src/Player/Utils/index.tsx +123 -0
  40. package/src/Player/hooks/useIsCasting.tsx +57 -0
  41. package/src/Player/index.tsx +106 -49
  42. package/src/QuickBrickPlayer.tsx +3 -0
  43. package/src/Player/AudioLayer/Layout/DefaultLayout.tsx +0 -452
  44. package/src/Player/AudioLayer/LinearGradient/LinearGradientProxy.ts +0 -3
  45. package/src/Player/AudioLayer/LinearGradient/LinearGradientProxy.web.ts +0 -3
  46. package/src/Player/AudioLayer/LinearGradient/index.ts +0 -1
  47. package/src/Player/AudioLayer/MagicBG/index.tsx +0 -155
  48. package/src/Player/Helpers/VideoResizeMode.ts +0 -7
  49. package/src/utils/utils.ts +0 -25
@@ -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,130 @@
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
+ // TODO cancel timer on reach time
34
+ private sleepTimerStateSubject = new BehaviorSubject<SleepTimerState>(
35
+ new SleepTimerState(SLEEP_TIMER_OFF, 0)
36
+ );
37
+
38
+ constructor(private player: Player) {}
39
+
40
+ get sleepTimerState$() {
41
+ return this.sleepTimerStateSubject.asObservable();
42
+ }
43
+
44
+ private async scheduleSleep(sleepDate): Promise<boolean> {
45
+ log_debug(`Setting sleep interval to ${sleepDate}`);
46
+
47
+ try {
48
+ this.player.cancelSleepTimer?.();
49
+ this.player.startSleepTimer?.(sleepDate);
50
+
51
+ return true;
52
+ } catch (e) {
53
+ log_error("Failed to start sleep timer", { error: e.message });
54
+
55
+ return false;
56
+ }
57
+ }
58
+
59
+ async setSleepTimer(item: SleepTimer, prevTimerSleepDate?: number) {
60
+ // User has selected Off state
61
+ if (item.value === 0) {
62
+ log_debug("Cancelling sleep timer");
63
+
64
+ // Removing scheduled pause on timer expiration
65
+ this.player.cancelSleepTimer?.();
66
+
67
+ return this.onTimerExpired();
68
+ }
69
+
70
+ const futureSleepDate =
71
+ prevTimerSleepDate || Date.now() + item.value * 60000;
72
+
73
+ log_debug(
74
+ `Starting sleep timer in ${(futureSleepDate - Date.now()) / 1000}s`
75
+ );
76
+
77
+ if (await this.scheduleSleep(futureSleepDate)) {
78
+ await localStorage.setItem(
79
+ SLEEP_PREFERENCE_KEY,
80
+ { ...item, futureSleepDate },
81
+ NAMESPACE
82
+ );
83
+
84
+ this.sleepTimerStateSubject.next(
85
+ new SleepTimerState(item, futureSleepDate)
86
+ );
87
+ }
88
+ }
89
+
90
+ async onTimerExpired() {
91
+ this.removeSleepTimerFromLocalStorage().catch((error) => {
92
+ log_error("Failed to remove sleep timer from local storage", {
93
+ error: error.message,
94
+ });
95
+ });
96
+
97
+ // Update UI state
98
+ this.sleepTimerStateSubject.next(new SleepTimerState(SLEEP_TIMER_OFF, 0));
99
+ }
100
+
101
+ async removeSleepTimerFromLocalStorage() {
102
+ await localStorage.removeItem(SLEEP_PREFERENCE_KEY, NAMESPACE);
103
+ }
104
+
105
+ async loadSleep() {
106
+ const sleep = await localStorage.getItem(SLEEP_PREFERENCE_KEY, NAMESPACE);
107
+
108
+ if (!sleep) return;
109
+
110
+ const { futureSleepDate, ...item } = sleep;
111
+
112
+ if (!futureSleepDate || hasDatePassed(futureSleepDate)) {
113
+ log_debug("Sleep date has already passed");
114
+ await localStorage.removeItem(SLEEP_PREFERENCE_KEY, NAMESPACE);
115
+
116
+ return;
117
+ }
118
+
119
+ log_debug("Sleep state restored");
120
+
121
+ this.setSleepTimer(item as SleepTimer, futureSleepDate);
122
+ }
123
+
124
+ // Will be called if user closed the player explicitly.
125
+ // TODO: check if it will be called on Android when we need.
126
+ // Probably we will have to handle it on native instead.
127
+ public static async onPlayerDestroy() {
128
+ await localStorage.removeItem(SLEEP_PREFERENCE_KEY, NAMESPACE);
129
+ }
130
+ }
@@ -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
+ }
@@ -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, timerRemaining),
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
+ };
@@ -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,41 @@
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 = (offLabel, sleepTimerValue) => {
32
+ return [
33
+ sleepTimerValue
34
+ ? {
35
+ label: offLabel,
36
+ value: 0,
37
+ }
38
+ : null,
39
+ ...[5, 10, 15, 30, 45, 60, 120].map(createTimerOptions),
40
+ ];
41
+ };
@@ -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
+ }));