@applicaster/quick-brick-player 13.0.0-rc.99 → 13.0.0
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 -7
- 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 +19 -5
- 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/Utils/index.tsx +6 -0
- package/src/Player/hooks/useIsCasting.tsx +57 -0
- package/src/Player/index.tsx +84 -37
- 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
|
};
|
|
@@ -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
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
import React from "react";
|
|
5
5
|
import * as R from "ramda";
|
|
6
6
|
|
|
7
|
+
import { flatten } from "@applicaster/zapp-react-native-utils/utils";
|
|
8
|
+
|
|
7
9
|
import { findNodeHandle, StyleSheet, View } from "react-native";
|
|
8
10
|
import TransportControlsMobile from "@applicaster/quick-brick-mobile-transport-controls";
|
|
9
11
|
import TransportControlsTV from "@applicaster/quick-brick-tv-transport-controls";
|
|
@@ -31,7 +33,6 @@ import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
|
|
|
31
33
|
import { getAllAccessibilityProps } from "@applicaster/zapp-react-native-utils/configurationUtils";
|
|
32
34
|
import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
|
|
33
35
|
import { getTargetScreenDataFromEntry } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useTargetScreenData";
|
|
34
|
-
import { isAppleTV } from "@applicaster/zapp-react-native-ui-components/Helpers/Platform";
|
|
35
36
|
import {
|
|
36
37
|
AnimationComponent,
|
|
37
38
|
ComponentAnimationType,
|
|
@@ -40,6 +41,10 @@ import { PlayerAnalyticsManager } from "@applicaster/zapp-react-native-utils/ana
|
|
|
40
41
|
import { PlayerAnalyticsTrackerI } from "@applicaster/zapp-react-native-utils/analyticsUtils/playerAnalyticsTracker";
|
|
41
42
|
|
|
42
43
|
import { RNPlayerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/nativePlayerManager";
|
|
44
|
+
import {
|
|
45
|
+
isAudioItem as isAudioItemUtil,
|
|
46
|
+
isInlineTV as isInlineTVUtil,
|
|
47
|
+
} from "@applicaster/zapp-react-native-utils/playerUtils";
|
|
43
48
|
|
|
44
49
|
const styles = StyleSheet.create({
|
|
45
50
|
container: { flex: 1 },
|
|
@@ -50,6 +55,7 @@ const styles = StyleSheet.create({
|
|
|
50
55
|
position: "absolute",
|
|
51
56
|
zIndex: 1,
|
|
52
57
|
},
|
|
58
|
+
audioPlayerSize: { width: "100%", height: "100%" },
|
|
53
59
|
playerSize: { width: "100%", height: "100%" },
|
|
54
60
|
});
|
|
55
61
|
|
|
@@ -58,6 +64,10 @@ const logger = createLogger({
|
|
|
58
64
|
subsystem: Subsystem,
|
|
59
65
|
});
|
|
60
66
|
|
|
67
|
+
const isTvOS = isTvOSPlatform();
|
|
68
|
+
|
|
69
|
+
const AUDIO_PLAYER_NATIVE_ID = "audio-player-view";
|
|
70
|
+
|
|
61
71
|
export default class VideoPlayer extends React.Component<
|
|
62
72
|
QuickBrickPlayer.PlayerProps,
|
|
63
73
|
QuickBrickPlayer.NativePlayerState
|
|
@@ -74,6 +84,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
74
84
|
state: QuickBrickPlayer.NativePlayerState = {
|
|
75
85
|
audioTrackId: null,
|
|
76
86
|
textTrackId: null,
|
|
87
|
+
isCasting: this.props.isCasting,
|
|
77
88
|
|
|
78
89
|
// TODO: Can be deleted, on controller logic
|
|
79
90
|
isAd: false,
|
|
@@ -92,6 +103,8 @@ export default class VideoPlayer extends React.Component<
|
|
|
92
103
|
// Player state
|
|
93
104
|
currentTime: 0,
|
|
94
105
|
duration: 0,
|
|
106
|
+
|
|
107
|
+
nativePlayerIsReady: false,
|
|
95
108
|
};
|
|
96
109
|
|
|
97
110
|
private _tracker: PlayerAnalyticsTrackerI;
|
|
@@ -487,6 +500,9 @@ export default class VideoPlayer extends React.Component<
|
|
|
487
500
|
|
|
488
501
|
onVideoFullscreenPlayerDidPresent = () => {
|
|
489
502
|
this.props.listener?.onVideoFullscreenPlayerDidPresent?.();
|
|
503
|
+
|
|
504
|
+
// we have to have a signal that player is ready, in order to show audio_player(for example)
|
|
505
|
+
this.setState({ nativePlayerIsReady: true });
|
|
490
506
|
};
|
|
491
507
|
|
|
492
508
|
onVideoFullscreenPlayerWillDismiss = () => {
|
|
@@ -672,32 +688,57 @@ export default class VideoPlayer extends React.Component<
|
|
|
672
688
|
}
|
|
673
689
|
};
|
|
674
690
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
691
|
+
childrenWithExtensions = () => {
|
|
692
|
+
const { docked, entry, children } = this.props;
|
|
693
|
+
|
|
694
|
+
if (!isTvOS) {
|
|
695
|
+
return children;
|
|
678
696
|
}
|
|
679
697
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
698
|
+
const output = [];
|
|
699
|
+
|
|
700
|
+
// audio-player
|
|
701
|
+
if (isAudioItemUtil(entry)) {
|
|
702
|
+
const { nativePlayerIsReady } = this.state;
|
|
703
|
+
|
|
704
|
+
// we have to wait ready status of native player view, before showing audio_player
|
|
705
|
+
if (nativePlayerIsReady) {
|
|
706
|
+
output.push(
|
|
707
|
+
<View nativeID={AUDIO_PLAYER_NATIVE_ID} key={AUDIO_PLAYER_NATIVE_ID}>
|
|
708
|
+
{children}
|
|
709
|
+
</View>
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
} else {
|
|
713
|
+
output.push(children);
|
|
714
|
+
}
|
|
715
|
+
// audio-player
|
|
716
|
+
|
|
717
|
+
// play-next
|
|
718
|
+
if (this.getPlayNextData()) {
|
|
719
|
+
output.push(
|
|
720
|
+
<PlayNextOverlay
|
|
721
|
+
key="play-next-overlay"
|
|
722
|
+
item={entry}
|
|
723
|
+
playerIsDocked={docked}
|
|
724
|
+
pluginConfiguration={this.getPluginConfiguration()}
|
|
725
|
+
playerId={this.props.playerId}
|
|
726
|
+
playNextData={this.getPlayNextData()}
|
|
727
|
+
setNextVideoPreloadThresholdPercentage={
|
|
728
|
+
this.props.setNextVideoPreloadThresholdPercentage
|
|
729
|
+
}
|
|
730
|
+
/>
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
// play-next
|
|
734
|
+
|
|
735
|
+
return flatten(output); // prevent possible case with nested array
|
|
699
736
|
};
|
|
700
737
|
|
|
738
|
+
get supportNativeCast() {
|
|
739
|
+
return this.props.controller.supportNativeCast();
|
|
740
|
+
}
|
|
741
|
+
|
|
701
742
|
render() {
|
|
702
743
|
const {
|
|
703
744
|
docked,
|
|
@@ -710,18 +751,13 @@ export default class VideoPlayer extends React.Component<
|
|
|
710
751
|
muted,
|
|
711
752
|
style,
|
|
712
753
|
castAction,
|
|
713
|
-
controller,
|
|
714
754
|
startComponentsAnimation,
|
|
715
755
|
} = this.props;
|
|
716
756
|
|
|
717
|
-
const supportNativeCast = controller.supportNativeCast();
|
|
718
|
-
|
|
719
757
|
const { audioTrackId, keyEvent, paused, seek, textTrackId, rate } =
|
|
720
758
|
this.state;
|
|
721
759
|
|
|
722
|
-
const isAudioItem =
|
|
723
|
-
entry?.content?.type?.includes?.("audio") ||
|
|
724
|
-
entry?.type?.value?.includes?.("audio");
|
|
760
|
+
const isAudioItem = isAudioItemUtil(entry);
|
|
725
761
|
|
|
726
762
|
const getStyle = (dimensions) => {
|
|
727
763
|
const generalStyles = isAudioItem ? styles.audio : styles.video;
|
|
@@ -765,7 +801,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
765
801
|
// TODO: Get screen data more reliable way useCurrentScreenData
|
|
766
802
|
const screenData = getTargetScreenDataFromEntry(this.props.playableItem);
|
|
767
803
|
|
|
768
|
-
const isInlineTV = screenData
|
|
804
|
+
const isInlineTV = isInlineTVUtil(screenData);
|
|
769
805
|
|
|
770
806
|
let platformSpecificProps: any = isApplePlatform()
|
|
771
807
|
? {
|
|
@@ -776,7 +812,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
776
812
|
const supportsNativeControls =
|
|
777
813
|
!!this.props.controller?.supportsNativeControls?.();
|
|
778
814
|
|
|
779
|
-
const controls =
|
|
815
|
+
const controls = isTvOS
|
|
780
816
|
? (supportsNativeControls && !isInlineTV) || !!this.getPlayNextData()
|
|
781
817
|
: supportsNativeControls && !this.getPlayNextData();
|
|
782
818
|
|
|
@@ -823,7 +859,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
823
859
|
),
|
|
824
860
|
};
|
|
825
861
|
|
|
826
|
-
if (!
|
|
862
|
+
if (!isTvOS) {
|
|
827
863
|
// TODO: Move to RN method and remove from props.
|
|
828
864
|
// Causes tvOS bug: native player passes rate on fast seek,
|
|
829
865
|
// RN returns it and it breaks playback.
|
|
@@ -995,11 +1031,14 @@ export default class VideoPlayer extends React.Component<
|
|
|
995
1031
|
const backgroundImageKey =
|
|
996
1032
|
this.getPluginConfiguration()?.player_preview_image_key;
|
|
997
1033
|
|
|
1034
|
+
const shouldRenderNativePlayer =
|
|
1035
|
+
!this.props.isCasting || this.supportNativeCast;
|
|
1036
|
+
|
|
998
1037
|
const renderMobilePlayer = () => {
|
|
999
1038
|
return isFullScreenAudioPlayer && isAudioItem ? (
|
|
1000
1039
|
// Ensure the View remains in the native hierarchy for proper rendering and interaction
|
|
1001
1040
|
<View collapsable={false} style={containerStyle}>
|
|
1002
|
-
{
|
|
1041
|
+
{shouldRenderNativePlayer ? (
|
|
1003
1042
|
<this.props.PlayerComponent
|
|
1004
1043
|
ref={this._assignRoot}
|
|
1005
1044
|
{...nativeEvents}
|
|
@@ -1051,7 +1090,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
1051
1090
|
/>
|
|
1052
1091
|
) : null}
|
|
1053
1092
|
|
|
1054
|
-
{
|
|
1093
|
+
{shouldRenderNativePlayer ? (
|
|
1055
1094
|
<AnimationComponent
|
|
1056
1095
|
animationType={ComponentAnimationType.player}
|
|
1057
1096
|
additionalData={{
|
|
@@ -1073,7 +1112,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
1073
1112
|
pluginConfiguration={this.getPluginConfiguration()}
|
|
1074
1113
|
controller={this.props.controller}
|
|
1075
1114
|
>
|
|
1076
|
-
{this.
|
|
1115
|
+
{this.props.children}
|
|
1077
1116
|
</this.props.PlayerComponent>
|
|
1078
1117
|
</AnimationComponent>
|
|
1079
1118
|
) : null}
|
|
@@ -1102,6 +1141,14 @@ export default class VideoPlayer extends React.Component<
|
|
|
1102
1141
|
const renderTVPlayer = () => {
|
|
1103
1142
|
return (
|
|
1104
1143
|
<View style={this.takeParentStyle} testID={"player-view"}>
|
|
1144
|
+
{isAudioItem && (isInlineTV || !isTvOS) ? (
|
|
1145
|
+
<AudioPlayer
|
|
1146
|
+
style={getStyle(styles.audioPlayerSize)}
|
|
1147
|
+
audio_item={entry}
|
|
1148
|
+
plugin_configuration={this.getPluginConfiguration()}
|
|
1149
|
+
/>
|
|
1150
|
+
) : null}
|
|
1151
|
+
|
|
1105
1152
|
<this.props.PlayerComponent
|
|
1106
1153
|
ref={this._assignRoot}
|
|
1107
1154
|
{...nativeEvents}
|
|
@@ -1111,11 +1158,11 @@ export default class VideoPlayer extends React.Component<
|
|
|
1111
1158
|
pluginConfiguration={this.getPluginConfiguration()}
|
|
1112
1159
|
controller={this.props.controller}
|
|
1113
1160
|
>
|
|
1114
|
-
{this.
|
|
1161
|
+
{!isInlineTV && this.childrenWithExtensions()}
|
|
1115
1162
|
</this.props.PlayerComponent>
|
|
1116
1163
|
|
|
1117
1164
|
{renderTransportControls(this.takeParentStyle)}
|
|
1118
|
-
{!
|
|
1165
|
+
{!isTvOS && (
|
|
1119
1166
|
<PlayNextOverlay
|
|
1120
1167
|
item={entry}
|
|
1121
1168
|
playerIsDocked={docked}
|
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}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from "./LinearGradientProxy";
|