@applicaster/quick-brick-player 13.0.0-rc.99 → 14.0.0-alpha.1235043154
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 +6 -8
- package/src/Player/AudioLayer/AudioDescription.tsx +109 -68
- package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +81 -189
- package/src/Player/AudioLayer/Layout/Controls/VerticalGutter.tsx +23 -5
- package/src/Player/AudioLayer/Layout/Controls/index.tsx +24 -49
- package/src/Player/AudioLayer/Layout/Controls/utils.ts +2 -2
- package/src/Player/AudioLayer/Layout/DockedControls/index.tsx +38 -19
- package/src/Player/AudioLayer/Layout/MobileLayout.tsx +4 -13
- package/src/Player/AudioLayer/Layout/PlayerImage/AnimatedImage.tsx +39 -40
- package/src/Player/AudioLayer/Layout/PlayerImage/FlexImage.tsx +6 -22
- package/src/Player/AudioLayer/Layout/PlayerImage/hooks/index.ts +1 -0
- package/src/Player/AudioLayer/Layout/PlayerImage/hooks/useChangePlayerState.ts +99 -0
- package/src/Player/AudioLayer/Layout/PlayerImage/index.tsx +37 -20
- package/src/Player/AudioLayer/Layout/PlayerImage/styles.ts +19 -0
- package/src/Player/AudioLayer/Layout/TabletLandscapeLayout.tsx +101 -105
- package/src/Player/AudioLayer/Layout/TabletPortraitLayout.tsx +2 -11
- package/src/Player/AudioLayer/Layout/TransportInfo/index.tsx +21 -13
- package/src/Player/AudioLayer/Layout/const.ts +4 -0
- package/src/Player/AudioLayer/Layout/hooks/useTransportInfoHeight.ts +1 -1
- package/src/Player/AudioLayer/Layout/utils.ts +7 -0
- package/src/Player/AudioLayer/SleepTimerManager.ts +22 -11
- package/src/Player/AudioLayer/components/SleepTimerModalHeader/SleepTimerModalHeader.tsx +66 -0
- package/src/Player/AudioLayer/components/SleepTimerModalHeader/index.ts +1 -0
- package/src/Player/AudioLayer/useSleepTimerModal.ts +109 -0
- package/src/Player/Const/sleepTimerOptions.ts +7 -5
- package/src/Player/PlayerView/GenericTVPlayerView.tsx +43 -0
- package/src/Player/PlayerView/TVPlayerView.tsx +26 -0
- package/src/Player/PlayerView/TvOSPlayerView.tsx +51 -0
- package/src/Player/PlayerView/types.ts +42 -0
- package/src/Player/Utils/index.tsx +6 -0
- package/src/Player/hooks/useIsCasting.tsx +57 -0
- package/src/Player/index.tsx +52 -61
- package/src/QuickBrickPlayer.tsx +3 -0
- package/src/Player/AudioLayer/LinearGradient/LinearGradientProxy.ts +0 -3
- package/src/Player/AudioLayer/LinearGradient/LinearGradientProxy.web.ts +0 -3
- package/src/Player/AudioLayer/LinearGradient/index.ts +0 -1
- package/src/Player/AudioLayer/MagicBG/index.tsx +0 -155
- package/src/utils/utils.ts +0 -25
|
@@ -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
|
+
};
|
|
@@ -28,12 +28,14 @@ const createTimerOptions = (value) => {
|
|
|
28
28
|
};
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
export const getSleepTimerOptions = (
|
|
31
|
+
export const getSleepTimerOptions = (offLabel, sleepTimerValue) => {
|
|
32
32
|
return [
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
sleepTimerValue
|
|
34
|
+
? {
|
|
35
|
+
label: offLabel,
|
|
36
|
+
value: 0,
|
|
37
|
+
}
|
|
38
|
+
: null,
|
|
37
39
|
...[5, 10, 15, 30, 45, 60, 120].map(createTimerOptions),
|
|
38
40
|
];
|
|
39
41
|
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { PlayNextOverlay } from "../PlayNextOverlay";
|
|
3
|
+
import { GenericTVPlayerViewProps } from "./types";
|
|
4
|
+
import { AudioPlayer } from "@applicaster/zapp-react-native-ui-components/Components/AudioPlayer";
|
|
5
|
+
|
|
6
|
+
export const GenericTVPlayerView: React.FC<GenericTVPlayerViewProps> = ({
|
|
7
|
+
children,
|
|
8
|
+
type,
|
|
9
|
+
PlayerComponent,
|
|
10
|
+
pluginConfiguration,
|
|
11
|
+
playerProps,
|
|
12
|
+
playNextProps,
|
|
13
|
+
audioPlayerProps,
|
|
14
|
+
transportControls,
|
|
15
|
+
entry,
|
|
16
|
+
inline,
|
|
17
|
+
}) => {
|
|
18
|
+
return (
|
|
19
|
+
<>
|
|
20
|
+
{type === "audio" && inline ? (
|
|
21
|
+
<AudioPlayer
|
|
22
|
+
audio_item={entry}
|
|
23
|
+
plugin_configuration={pluginConfiguration}
|
|
24
|
+
{...audioPlayerProps}
|
|
25
|
+
/>
|
|
26
|
+
) : null}
|
|
27
|
+
<PlayerComponent
|
|
28
|
+
pluginConfiguration={pluginConfiguration}
|
|
29
|
+
{...playerProps}
|
|
30
|
+
>
|
|
31
|
+
{children}
|
|
32
|
+
</PlayerComponent>
|
|
33
|
+
{transportControls}
|
|
34
|
+
{playNextProps.playNextData ? (
|
|
35
|
+
<PlayNextOverlay
|
|
36
|
+
item={entry}
|
|
37
|
+
pluginConfiguration={pluginConfiguration}
|
|
38
|
+
{...playNextProps}
|
|
39
|
+
/>
|
|
40
|
+
) : null}
|
|
41
|
+
</>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { isTvOSPlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
3
|
+
import { TvOSPlayerView } from "./TvOSPlayerView";
|
|
4
|
+
import { GenericTVPlayerView } from "./GenericTVPlayerView";
|
|
5
|
+
import {
|
|
6
|
+
GenericTVPlayerViewProps,
|
|
7
|
+
TvOSPlayerViewProps,
|
|
8
|
+
TVPlayerViewProps,
|
|
9
|
+
} from "./types";
|
|
10
|
+
|
|
11
|
+
const isTvOS = isTvOSPlatform();
|
|
12
|
+
|
|
13
|
+
const shouldRenderTvOSPlayerView = (
|
|
14
|
+
isTvOS: boolean,
|
|
15
|
+
inline: boolean
|
|
16
|
+
): boolean => {
|
|
17
|
+
return isTvOS && !inline;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const TVPlayerView: React.FC<TVPlayerViewProps> = (props) => {
|
|
21
|
+
return shouldRenderTvOSPlayerView(isTvOS, props.inline) ? (
|
|
22
|
+
<TvOSPlayerView {...(props as TvOSPlayerViewProps)} />
|
|
23
|
+
) : (
|
|
24
|
+
<GenericTVPlayerView {...(props as GenericTVPlayerViewProps)} />
|
|
25
|
+
);
|
|
26
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { PlayNextOverlay } from "../PlayNextOverlay";
|
|
4
|
+
import { TvOSPlayerViewProps } from "./types";
|
|
5
|
+
|
|
6
|
+
const AUDIO_PLAYER_NATIVE_ID = "audio-player-view";
|
|
7
|
+
|
|
8
|
+
export const TvOSPlayerView: React.FC<TvOSPlayerViewProps> = ({
|
|
9
|
+
children,
|
|
10
|
+
type,
|
|
11
|
+
PlayerComponent,
|
|
12
|
+
pluginConfiguration,
|
|
13
|
+
playerProps,
|
|
14
|
+
playNextProps,
|
|
15
|
+
nativePlayerIsReady,
|
|
16
|
+
transportControls,
|
|
17
|
+
entry,
|
|
18
|
+
inline,
|
|
19
|
+
}) => {
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
<PlayerComponent
|
|
23
|
+
pluginConfiguration={pluginConfiguration}
|
|
24
|
+
{...playerProps}
|
|
25
|
+
>
|
|
26
|
+
{!inline ? (
|
|
27
|
+
<>
|
|
28
|
+
{type === "audio" && nativePlayerIsReady ? (
|
|
29
|
+
<View
|
|
30
|
+
nativeID={AUDIO_PLAYER_NATIVE_ID}
|
|
31
|
+
key={AUDIO_PLAYER_NATIVE_ID}
|
|
32
|
+
>
|
|
33
|
+
{children}
|
|
34
|
+
</View>
|
|
35
|
+
) : (
|
|
36
|
+
children
|
|
37
|
+
)}
|
|
38
|
+
{playNextProps.playNextData ? (
|
|
39
|
+
<PlayNextOverlay
|
|
40
|
+
item={entry}
|
|
41
|
+
pluginConfiguration={pluginConfiguration}
|
|
42
|
+
{...playNextProps}
|
|
43
|
+
/>
|
|
44
|
+
) : null}
|
|
45
|
+
</>
|
|
46
|
+
) : null}
|
|
47
|
+
</PlayerComponent>
|
|
48
|
+
{transportControls}
|
|
49
|
+
</>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ReactNode, ComponentType } from "react";
|
|
2
|
+
import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
|
|
3
|
+
import { ViewStyle } from "react-native";
|
|
4
|
+
|
|
5
|
+
export interface PlayNextOverlayProps {
|
|
6
|
+
playerIsDocked: boolean;
|
|
7
|
+
playerId?: string;
|
|
8
|
+
playNextData: PlayNextData;
|
|
9
|
+
setNextVideoPreloadThresholdPercentage: (percentage: number) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface PlayerComponentProps {
|
|
13
|
+
ref: React.RefObject<any>;
|
|
14
|
+
listener: Record<string, any>;
|
|
15
|
+
configuration: Record<string, any>;
|
|
16
|
+
style: ViewStyle;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface AudioPlayerProps {
|
|
20
|
+
style: ViewStyle;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface PlayerViewBaseProps {
|
|
24
|
+
children?: ReactNode;
|
|
25
|
+
type: "audio" | "video";
|
|
26
|
+
entry: ZappEntry;
|
|
27
|
+
pluginConfiguration: Record<string, any>;
|
|
28
|
+
PlayerComponent: ComponentType<any>;
|
|
29
|
+
playerProps: PlayerComponentProps;
|
|
30
|
+
playNextProps: PlayNextOverlayProps;
|
|
31
|
+
audioPlayerProps: AudioPlayerProps;
|
|
32
|
+
transportControls?: ReactNode;
|
|
33
|
+
inline?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface TvOSPlayerViewProps extends PlayerViewBaseProps {
|
|
37
|
+
nativePlayerIsReady: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type GenericTVPlayerViewProps = PlayerViewBaseProps;
|
|
41
|
+
|
|
42
|
+
export type TVPlayerViewProps = TvOSPlayerViewProps | GenericTVPlayerViewProps;
|
|
@@ -116,3 +116,9 @@ export const getBottomSheetModalSelectedItemIcon = (theme) =>
|
|
|
116
116
|
|
|
117
117
|
export const getBottomSheetModalDefaultItemIcon = (theme) =>
|
|
118
118
|
theme["audio_modal_bottom_sheet_item_default_icon"] || null;
|
|
119
|
+
|
|
120
|
+
export const selectSleepTitle = (configuration) =>
|
|
121
|
+
configuration.playback_sleep_title;
|
|
122
|
+
|
|
123
|
+
export const selectSleepOffLabel = (configuration) =>
|
|
124
|
+
configuration.playback_sleep_off_label;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
3
|
+
import { useAppState } from "@applicaster/zapp-react-native-utils/reactHooks/app";
|
|
4
|
+
import { isApplePlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
5
|
+
|
|
6
|
+
const CHROMECAST_IDENTIFIER = "quick-brick-chromecast-action";
|
|
7
|
+
|
|
8
|
+
const fakeEntry: any = {
|
|
9
|
+
title: "Chromecast use is casting fake entry",
|
|
10
|
+
id: "quick-brick-use-is-casting-fake-entry",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Returns the cast action state connection status, does not update when app is in background on Apple platforms
|
|
15
|
+
* On other platforms, it continues to check the cast action state even when the app is in background
|
|
16
|
+
*/
|
|
17
|
+
export const useIsCasting = () => {
|
|
18
|
+
const actionContext = useActions(CHROMECAST_IDENTIFIER);
|
|
19
|
+
const appState = useAppState();
|
|
20
|
+
const isAppInForeground = appState === "active";
|
|
21
|
+
const shouldOnlyUpdateInForeground = isApplePlatform();
|
|
22
|
+
|
|
23
|
+
const [castActionState, setCastActionState] = React.useState(
|
|
24
|
+
!actionContext ? null : actionContext.initialEntryState(fakeEntry)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
React.useEffect(() => {
|
|
28
|
+
if (!shouldOnlyUpdateInForeground || isAppInForeground) {
|
|
29
|
+
if (actionContext && castActionState === null) {
|
|
30
|
+
setCastActionState(actionContext.initialEntryState(fakeEntry));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}, [
|
|
34
|
+
actionContext,
|
|
35
|
+
setCastActionState,
|
|
36
|
+
isAppInForeground,
|
|
37
|
+
shouldOnlyUpdateInForeground,
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
React.useEffect(() => {
|
|
41
|
+
if (typeof actionContext?.addListener === "function") {
|
|
42
|
+
return actionContext?.addListener(String(fakeEntry.id), (state) => {
|
|
43
|
+
// Only check foreground state on Apple platforms
|
|
44
|
+
if (!shouldOnlyUpdateInForeground || isAppInForeground) {
|
|
45
|
+
setCastActionState(state);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}, [
|
|
50
|
+
setCastActionState,
|
|
51
|
+
actionContext,
|
|
52
|
+
isAppInForeground,
|
|
53
|
+
shouldOnlyUpdateInForeground,
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
return !!castActionState?.connected;
|
|
57
|
+
};
|
package/src/Player/index.tsx
CHANGED
|
@@ -31,7 +31,6 @@ import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
|
|
|
31
31
|
import { getAllAccessibilityProps } from "@applicaster/zapp-react-native-utils/configurationUtils";
|
|
32
32
|
import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
|
|
33
33
|
import { getTargetScreenDataFromEntry } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useTargetScreenData";
|
|
34
|
-
import { isAppleTV } from "@applicaster/zapp-react-native-ui-components/Helpers/Platform";
|
|
35
34
|
import {
|
|
36
35
|
AnimationComponent,
|
|
37
36
|
ComponentAnimationType,
|
|
@@ -40,6 +39,12 @@ import { PlayerAnalyticsManager } from "@applicaster/zapp-react-native-utils/ana
|
|
|
40
39
|
import { PlayerAnalyticsTrackerI } from "@applicaster/zapp-react-native-utils/analyticsUtils/playerAnalyticsTracker";
|
|
41
40
|
|
|
42
41
|
import { RNPlayerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/nativePlayerManager";
|
|
42
|
+
import {
|
|
43
|
+
isAudioItem as isAudioItemUtil,
|
|
44
|
+
isInlineTV as isInlineTVUtil,
|
|
45
|
+
} from "@applicaster/zapp-react-native-utils/playerUtils";
|
|
46
|
+
|
|
47
|
+
import { TVPlayerView } from "./PlayerView/TVPlayerView";
|
|
43
48
|
|
|
44
49
|
const styles = StyleSheet.create({
|
|
45
50
|
container: { flex: 1 },
|
|
@@ -58,6 +63,8 @@ const logger = createLogger({
|
|
|
58
63
|
subsystem: Subsystem,
|
|
59
64
|
});
|
|
60
65
|
|
|
66
|
+
const isTvOS = isTvOSPlatform();
|
|
67
|
+
|
|
61
68
|
export default class VideoPlayer extends React.Component<
|
|
62
69
|
QuickBrickPlayer.PlayerProps,
|
|
63
70
|
QuickBrickPlayer.NativePlayerState
|
|
@@ -74,6 +81,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
74
81
|
state: QuickBrickPlayer.NativePlayerState = {
|
|
75
82
|
audioTrackId: null,
|
|
76
83
|
textTrackId: null,
|
|
84
|
+
isCasting: this.props.isCasting,
|
|
77
85
|
|
|
78
86
|
// TODO: Can be deleted, on controller logic
|
|
79
87
|
isAd: false,
|
|
@@ -92,6 +100,8 @@ export default class VideoPlayer extends React.Component<
|
|
|
92
100
|
// Player state
|
|
93
101
|
currentTime: 0,
|
|
94
102
|
duration: 0,
|
|
103
|
+
|
|
104
|
+
nativePlayerIsReady: false,
|
|
95
105
|
};
|
|
96
106
|
|
|
97
107
|
private _tracker: PlayerAnalyticsTrackerI;
|
|
@@ -487,6 +497,9 @@ export default class VideoPlayer extends React.Component<
|
|
|
487
497
|
|
|
488
498
|
onVideoFullscreenPlayerDidPresent = () => {
|
|
489
499
|
this.props.listener?.onVideoFullscreenPlayerDidPresent?.();
|
|
500
|
+
|
|
501
|
+
// we have to have a signal that player is ready, in order to show audio_player(for example)
|
|
502
|
+
this.setState({ nativePlayerIsReady: true });
|
|
490
503
|
};
|
|
491
504
|
|
|
492
505
|
onVideoFullscreenPlayerWillDismiss = () => {
|
|
@@ -672,31 +685,9 @@ export default class VideoPlayer extends React.Component<
|
|
|
672
685
|
}
|
|
673
686
|
};
|
|
674
687
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
// tvos only
|
|
681
|
-
const { docked, entry } = this.props;
|
|
682
|
-
|
|
683
|
-
const children = R.pathOr([], "children", this.props);
|
|
684
|
-
|
|
685
|
-
return [
|
|
686
|
-
...children,
|
|
687
|
-
<PlayNextOverlay
|
|
688
|
-
key="play-next-overlay"
|
|
689
|
-
item={entry}
|
|
690
|
-
playerIsDocked={docked}
|
|
691
|
-
pluginConfiguration={this.getPluginConfiguration()}
|
|
692
|
-
playerId={this.props.playerId}
|
|
693
|
-
playNextData={this.getPlayNextData()}
|
|
694
|
-
setNextVideoPreloadThresholdPercentage={
|
|
695
|
-
this.props.setNextVideoPreloadThresholdPercentage
|
|
696
|
-
}
|
|
697
|
-
/>,
|
|
698
|
-
];
|
|
699
|
-
};
|
|
688
|
+
get supportNativeCast() {
|
|
689
|
+
return this.props.controller.supportNativeCast();
|
|
690
|
+
}
|
|
700
691
|
|
|
701
692
|
render() {
|
|
702
693
|
const {
|
|
@@ -710,18 +701,13 @@ export default class VideoPlayer extends React.Component<
|
|
|
710
701
|
muted,
|
|
711
702
|
style,
|
|
712
703
|
castAction,
|
|
713
|
-
controller,
|
|
714
704
|
startComponentsAnimation,
|
|
715
705
|
} = this.props;
|
|
716
706
|
|
|
717
|
-
const supportNativeCast = controller.supportNativeCast();
|
|
718
|
-
|
|
719
707
|
const { audioTrackId, keyEvent, paused, seek, textTrackId, rate } =
|
|
720
708
|
this.state;
|
|
721
709
|
|
|
722
|
-
const isAudioItem =
|
|
723
|
-
entry?.content?.type?.includes?.("audio") ||
|
|
724
|
-
entry?.type?.value?.includes?.("audio");
|
|
710
|
+
const isAudioItem = isAudioItemUtil(entry);
|
|
725
711
|
|
|
726
712
|
const getStyle = (dimensions) => {
|
|
727
713
|
const generalStyles = isAudioItem ? styles.audio : styles.video;
|
|
@@ -765,7 +751,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
765
751
|
// TODO: Get screen data more reliable way useCurrentScreenData
|
|
766
752
|
const screenData = getTargetScreenDataFromEntry(this.props.playableItem);
|
|
767
753
|
|
|
768
|
-
const isInlineTV = screenData
|
|
754
|
+
const isInlineTV = isInlineTVUtil(screenData);
|
|
769
755
|
|
|
770
756
|
let platformSpecificProps: any = isApplePlatform()
|
|
771
757
|
? {
|
|
@@ -776,7 +762,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
776
762
|
const supportsNativeControls =
|
|
777
763
|
!!this.props.controller?.supportsNativeControls?.();
|
|
778
764
|
|
|
779
|
-
const controls =
|
|
765
|
+
const controls = isTvOS
|
|
780
766
|
? (supportsNativeControls && !isInlineTV) || !!this.getPlayNextData()
|
|
781
767
|
: supportsNativeControls && !this.getPlayNextData();
|
|
782
768
|
|
|
@@ -823,7 +809,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
823
809
|
),
|
|
824
810
|
};
|
|
825
811
|
|
|
826
|
-
if (!
|
|
812
|
+
if (!isTvOS) {
|
|
827
813
|
// TODO: Move to RN method and remove from props.
|
|
828
814
|
// Causes tvOS bug: native player passes rate on fast seek,
|
|
829
815
|
// RN returns it and it breaks playback.
|
|
@@ -995,11 +981,14 @@ export default class VideoPlayer extends React.Component<
|
|
|
995
981
|
const backgroundImageKey =
|
|
996
982
|
this.getPluginConfiguration()?.player_preview_image_key;
|
|
997
983
|
|
|
984
|
+
const shouldRenderNativePlayer =
|
|
985
|
+
!this.props.isCasting || this.supportNativeCast;
|
|
986
|
+
|
|
998
987
|
const renderMobilePlayer = () => {
|
|
999
988
|
return isFullScreenAudioPlayer && isAudioItem ? (
|
|
1000
989
|
// Ensure the View remains in the native hierarchy for proper rendering and interaction
|
|
1001
990
|
<View collapsable={false} style={containerStyle}>
|
|
1002
|
-
{
|
|
991
|
+
{shouldRenderNativePlayer ? (
|
|
1003
992
|
<this.props.PlayerComponent
|
|
1004
993
|
ref={this._assignRoot}
|
|
1005
994
|
{...nativeEvents}
|
|
@@ -1051,7 +1040,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
1051
1040
|
/>
|
|
1052
1041
|
) : null}
|
|
1053
1042
|
|
|
1054
|
-
{
|
|
1043
|
+
{shouldRenderNativePlayer ? (
|
|
1055
1044
|
<AnimationComponent
|
|
1056
1045
|
animationType={ComponentAnimationType.player}
|
|
1057
1046
|
additionalData={{
|
|
@@ -1073,7 +1062,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
1073
1062
|
pluginConfiguration={this.getPluginConfiguration()}
|
|
1074
1063
|
controller={this.props.controller}
|
|
1075
1064
|
>
|
|
1076
|
-
{this.
|
|
1065
|
+
{this.props.children}
|
|
1077
1066
|
</this.props.PlayerComponent>
|
|
1078
1067
|
</AnimationComponent>
|
|
1079
1068
|
) : null}
|
|
@@ -1102,31 +1091,33 @@ export default class VideoPlayer extends React.Component<
|
|
|
1102
1091
|
const renderTVPlayer = () => {
|
|
1103
1092
|
return (
|
|
1104
1093
|
<View style={this.takeParentStyle} testID={"player-view"}>
|
|
1105
|
-
<
|
|
1106
|
-
|
|
1107
|
-
{
|
|
1108
|
-
{
|
|
1109
|
-
|
|
1110
|
-
|
|
1094
|
+
<TVPlayerView
|
|
1095
|
+
type={isAudioItem ? "audio" : "video"}
|
|
1096
|
+
entry={entry}
|
|
1097
|
+
inline={isInlineTV}
|
|
1098
|
+
transportControls={renderTransportControls(this.takeParentStyle)}
|
|
1099
|
+
PlayerComponent={this.props.PlayerComponent}
|
|
1100
|
+
nativePlayerIsReady={this.state.nativePlayerIsReady}
|
|
1111
1101
|
pluginConfiguration={this.getPluginConfiguration()}
|
|
1112
|
-
|
|
1102
|
+
playerProps={{
|
|
1103
|
+
ref: this._assignRoot,
|
|
1104
|
+
...nativeEvents,
|
|
1105
|
+
...nativeProps,
|
|
1106
|
+
listener: this.props.listener,
|
|
1107
|
+
style: this.takeParentStyle,
|
|
1108
|
+
controller: this.props.controller,
|
|
1109
|
+
}}
|
|
1110
|
+
audioPlayerProps={{ style: getStyle(styles.playerSize) }}
|
|
1111
|
+
playNextProps={{
|
|
1112
|
+
playerIsDocked: docked,
|
|
1113
|
+
playerId: this.props.playerId,
|
|
1114
|
+
playNextData: this.getPlayNextData(),
|
|
1115
|
+
setNextVideoPreloadThresholdPercentage:
|
|
1116
|
+
this.props.setNextVideoPreloadThresholdPercentage,
|
|
1117
|
+
}}
|
|
1113
1118
|
>
|
|
1114
|
-
{this.
|
|
1115
|
-
</
|
|
1116
|
-
|
|
1117
|
-
{renderTransportControls(this.takeParentStyle)}
|
|
1118
|
-
{!isTvOSPlatform() && (
|
|
1119
|
-
<PlayNextOverlay
|
|
1120
|
-
item={entry}
|
|
1121
|
-
playerIsDocked={docked}
|
|
1122
|
-
pluginConfiguration={this.getPluginConfiguration()}
|
|
1123
|
-
playerId={this.props.playerId}
|
|
1124
|
-
playNextData={this.getPlayNextData()}
|
|
1125
|
-
setNextVideoPreloadThresholdPercentage={
|
|
1126
|
-
this.props.setNextVideoPreloadThresholdPercentage
|
|
1127
|
-
}
|
|
1128
|
-
/>
|
|
1129
|
-
)}
|
|
1119
|
+
{this.props.children}
|
|
1120
|
+
</TVPlayerView>
|
|
1130
1121
|
</View>
|
|
1131
1122
|
);
|
|
1132
1123
|
};
|
package/src/QuickBrickPlayer.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import Player from "./Player";
|
|
|
5
5
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
6
6
|
import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
7
7
|
import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
8
|
+
import { useIsCasting } from "./Player/hooks/useIsCasting";
|
|
8
9
|
|
|
9
10
|
const CHROMECAST_IDENTIFIER = "quick-brick-chromecast-action";
|
|
10
11
|
|
|
@@ -24,6 +25,7 @@ const QuickBrickPlayerComponent = (
|
|
|
24
25
|
}, [props.PlayerComponent]);
|
|
25
26
|
|
|
26
27
|
const castAction = useActions(CHROMECAST_IDENTIFIER);
|
|
28
|
+
const isCasting = useIsCasting();
|
|
27
29
|
|
|
28
30
|
const isModal = Boolean(props?.isModal);
|
|
29
31
|
const isScreenDisabled = !controller?.isFullScreenSupported() || !isModal;
|
|
@@ -48,6 +50,7 @@ const QuickBrickPlayerComponent = (
|
|
|
48
50
|
{...props}
|
|
49
51
|
ref={ref}
|
|
50
52
|
castAction={!props?.disableCastAction && castAction}
|
|
53
|
+
isCasting={isCasting}
|
|
51
54
|
PlayerComponent={props.PlayerComponent}
|
|
52
55
|
openPiP={openPiP}
|
|
53
56
|
closePiP={closePiP}
|