some UI changes to metadat overlay on videoplayers
This commit is contained in:
parent
9ae1a32989
commit
3886f615c9
4 changed files with 189 additions and 125 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState, useCallback } from 'react';
|
||||||
import { Platform } from 'react-native';
|
import { Platform, Animated, TouchableWithoutFeedback, View } from 'react-native';
|
||||||
import Video, { VideoRef, SelectedTrack, BufferingStrategyType, ResizeMode } from 'react-native-video';
|
import Video, { VideoRef, SelectedTrack, BufferingStrategyType, ResizeMode } from 'react-native-video';
|
||||||
|
|
||||||
interface VideoPlayerProps {
|
interface VideoPlayerProps {
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit c176aabb4edd73a709ebdc097688e780b65b651a
|
Subproject commit 6591e3bc12a64a191367829d3fa5eb3783085c95
|
||||||
|
|
@ -1372,6 +1372,40 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
}
|
}
|
||||||
}, [nextEpisode, id, isLoadingNextEpisode, navigation, metadata, imdbId, backdrop]);
|
}, [nextEpisode, id, isLoadingNextEpisode, navigation, metadata, imdbId, backdrop]);
|
||||||
|
|
||||||
|
// Function to hide pause overlay and show controls
|
||||||
|
const hidePauseOverlay = useCallback(() => {
|
||||||
|
if (showPauseOverlay) {
|
||||||
|
Animated.parallel([
|
||||||
|
Animated.timing(pauseOverlayOpacity, {
|
||||||
|
toValue: 0,
|
||||||
|
duration: 220,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
Animated.timing(pauseOverlayTranslateY, {
|
||||||
|
toValue: 8,
|
||||||
|
duration: 220,
|
||||||
|
useNativeDriver: true,
|
||||||
|
})
|
||||||
|
]).start(() => setShowPauseOverlay(false));
|
||||||
|
|
||||||
|
// Show controls when overlay is touched
|
||||||
|
if (!showControls) {
|
||||||
|
setShowControls(true);
|
||||||
|
Animated.timing(fadeAnim, {
|
||||||
|
toValue: 1,
|
||||||
|
duration: 300,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
// Auto-hide controls after 5 seconds
|
||||||
|
if (controlsTimeout.current) {
|
||||||
|
clearTimeout(controlsTimeout.current);
|
||||||
|
}
|
||||||
|
controlsTimeout.current = setTimeout(hideControls, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [showPauseOverlay, pauseOverlayOpacity, pauseOverlayTranslateY, showControls, fadeAnim, controlsTimeout, hideControls]);
|
||||||
|
|
||||||
// Handle paused overlay after 5 seconds of being paused
|
// Handle paused overlay after 5 seconds of being paused
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (paused) {
|
if (paused) {
|
||||||
|
|
@ -1400,20 +1434,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
clearTimeout(pauseOverlayTimerRef.current);
|
clearTimeout(pauseOverlayTimerRef.current);
|
||||||
pauseOverlayTimerRef.current = null;
|
pauseOverlayTimerRef.current = null;
|
||||||
}
|
}
|
||||||
if (showPauseOverlay) {
|
hidePauseOverlay();
|
||||||
Animated.parallel([
|
|
||||||
Animated.timing(pauseOverlayOpacity, {
|
|
||||||
toValue: 0,
|
|
||||||
duration: 220,
|
|
||||||
useNativeDriver: true,
|
|
||||||
}),
|
|
||||||
Animated.timing(pauseOverlayTranslateY, {
|
|
||||||
toValue: 8,
|
|
||||||
duration: 220,
|
|
||||||
useNativeDriver: true,
|
|
||||||
})
|
|
||||||
]).start(() => setShowPauseOverlay(false));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
if (pauseOverlayTimerRef.current) {
|
if (pauseOverlayTimerRef.current) {
|
||||||
|
|
@ -1421,7 +1442,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
pauseOverlayTimerRef.current = null;
|
pauseOverlayTimerRef.current = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [paused]);
|
}, [paused, hidePauseOverlay]);
|
||||||
|
|
||||||
// Handle next episode button visibility based on current time and next episode availability
|
// Handle next episode button visibility based on current time and next episode availability
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -1924,65 +1945,76 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{showPauseOverlay && (
|
{showPauseOverlay && (
|
||||||
<Animated.View
|
<TouchableOpacity
|
||||||
pointerEvents="none"
|
activeOpacity={1}
|
||||||
|
onPress={hidePauseOverlay}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
opacity: pauseOverlayOpacity,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Strong horizontal fade from left side */}
|
<Animated.View
|
||||||
<View style={{ position: 'absolute', top: 0, left: 0, bottom: 0, width: screenDimensions.width * 0.7 }}>
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
opacity: pauseOverlayOpacity,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Strong horizontal fade from left side */}
|
||||||
|
<View style={{ position: 'absolute', top: 0, left: 0, bottom: 0, width: screenDimensions.width * 0.7 }}>
|
||||||
|
<LinearGradient
|
||||||
|
start={{ x: 0, y: 0.5 }}
|
||||||
|
end={{ x: 1, y: 0.5 }}
|
||||||
|
colors={[ 'rgba(0,0,0,0.85)', 'rgba(0,0,0,0.0)' ]}
|
||||||
|
locations={[0, 1]}
|
||||||
|
style={StyleSheet.absoluteFill}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
start={{ x: 0, y: 0.5 }}
|
colors={[
|
||||||
end={{ x: 1, y: 0.5 }}
|
'rgba(0,0,0,0.6)',
|
||||||
colors={[ 'rgba(0,0,0,0.85)', 'rgba(0,0,0,0.0)' ]}
|
'rgba(0,0,0,0.4)',
|
||||||
locations={[0, 1]}
|
'rgba(0,0,0,0.2)',
|
||||||
|
'rgba(0,0,0,0.0)'
|
||||||
|
]}
|
||||||
|
locations={[0, 0.3, 0.6, 1]}
|
||||||
style={StyleSheet.absoluteFill}
|
style={StyleSheet.absoluteFill}
|
||||||
/>
|
/>
|
||||||
</View>
|
<Animated.View style={{
|
||||||
<LinearGradient
|
position: 'absolute',
|
||||||
colors={[
|
left: 24 + insets.left,
|
||||||
'rgba(0,0,0,0.6)',
|
right: 24 + insets.right,
|
||||||
'rgba(0,0,0,0.4)',
|
bottom: 110 + insets.bottom,
|
||||||
'rgba(0,0,0,0.2)',
|
transform: [{ translateY: pauseOverlayTranslateY }]
|
||||||
'rgba(0,0,0,0.0)'
|
}}>
|
||||||
]}
|
<Text style={{ color: '#B8B8B8', fontSize: 18, marginBottom: 8 }}>You're watching</Text>
|
||||||
locations={[0, 0.3, 0.6, 1]}
|
<Text style={{ color: '#FFFFFF', fontSize: 48, fontWeight: '800', marginBottom: 10 }} numberOfLines={1}>
|
||||||
style={StyleSheet.absoluteFill}
|
{title}
|
||||||
/>
|
|
||||||
<Animated.View style={{
|
|
||||||
position: 'absolute',
|
|
||||||
left: 24 + insets.left,
|
|
||||||
right: 24 + insets.right,
|
|
||||||
bottom: 110 + insets.bottom,
|
|
||||||
transform: [{ translateY: pauseOverlayTranslateY }]
|
|
||||||
}}>
|
|
||||||
<Text style={{ color: '#B8B8B8', fontSize: 18, marginBottom: 8 }}>You're watching</Text>
|
|
||||||
<Text style={{ color: '#FFFFFF', fontSize: 48, fontWeight: '800', marginBottom: 10 }} numberOfLines={1}>
|
|
||||||
{title}
|
|
||||||
</Text>
|
|
||||||
{!!year && (
|
|
||||||
<Text style={{ color: '#CCCCCC', fontSize: 18, marginBottom: 8 }} numberOfLines={1}>
|
|
||||||
{`${year}${type === 'series' && season && episode ? ` • S${season}E${episode}` : ''}`}
|
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
{!!year && (
|
||||||
{!!episodeTitle && (
|
<Text style={{ color: '#CCCCCC', fontSize: 18, marginBottom: 8 }} numberOfLines={1}>
|
||||||
<Text style={{ color: '#FFFFFF', fontSize: 20, fontWeight: '600', marginBottom: 8 }} numberOfLines={1}>
|
{`${year}${type === 'series' && season && episode ? ` • S${season}E${episode}` : ''}`}
|
||||||
{episodeTitle}
|
</Text>
|
||||||
</Text>
|
)}
|
||||||
)}
|
{!!episodeTitle && (
|
||||||
{(currentEpisodeDescription || metadata?.description) && (
|
<Text style={{ color: '#FFFFFF', fontSize: 20, fontWeight: '600', marginBottom: 8 }} numberOfLines={1}>
|
||||||
<Text style={{ color: '#D6D6D6', fontSize: 18, lineHeight: 24 }} numberOfLines={3}>
|
{episodeTitle}
|
||||||
{(type as any) === 'series' ? (currentEpisodeDescription || metadata?.description || '') : (metadata?.description || '')}
|
</Text>
|
||||||
</Text>
|
)}
|
||||||
)}
|
{(currentEpisodeDescription || metadata?.description) && (
|
||||||
|
<Text style={{ color: '#D6D6D6', fontSize: 18, lineHeight: 24 }} numberOfLines={3}>
|
||||||
|
{(type as any) === 'series' ? (currentEpisodeDescription || metadata?.description || '') : (metadata?.description || '')}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Animated.View>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
</Animated.View>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Next Episode Button */}
|
{/* Next Episode Button */}
|
||||||
|
|
|
||||||
|
|
@ -1272,6 +1272,40 @@ const VideoPlayer: React.FC = () => {
|
||||||
}
|
}
|
||||||
}, [nextEpisode, id, isLoadingNextEpisode, navigation, metadata, imdbId, backdrop]);
|
}, [nextEpisode, id, isLoadingNextEpisode, navigation, metadata, imdbId, backdrop]);
|
||||||
|
|
||||||
|
// Function to hide pause overlay and show controls
|
||||||
|
const hidePauseOverlay = useCallback(() => {
|
||||||
|
if (showPauseOverlay) {
|
||||||
|
Animated.parallel([
|
||||||
|
Animated.timing(pauseOverlayOpacity, {
|
||||||
|
toValue: 0,
|
||||||
|
duration: 220,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}),
|
||||||
|
Animated.timing(pauseOverlayTranslateY, {
|
||||||
|
toValue: 8,
|
||||||
|
duration: 220,
|
||||||
|
useNativeDriver: true,
|
||||||
|
})
|
||||||
|
]).start(() => setShowPauseOverlay(false));
|
||||||
|
|
||||||
|
// Show controls when overlay is touched
|
||||||
|
if (!showControls) {
|
||||||
|
setShowControls(true);
|
||||||
|
Animated.timing(fadeAnim, {
|
||||||
|
toValue: 1,
|
||||||
|
duration: 300,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
// Auto-hide controls after 5 seconds
|
||||||
|
if (controlsTimeout.current) {
|
||||||
|
clearTimeout(controlsTimeout.current);
|
||||||
|
}
|
||||||
|
controlsTimeout.current = setTimeout(hideControls, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [showPauseOverlay, pauseOverlayOpacity, pauseOverlayTranslateY, showControls, fadeAnim, controlsTimeout, hideControls]);
|
||||||
|
|
||||||
// Handle paused overlay after 5 seconds of being paused
|
// Handle paused overlay after 5 seconds of being paused
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (paused) {
|
if (paused) {
|
||||||
|
|
@ -1300,20 +1334,7 @@ const VideoPlayer: React.FC = () => {
|
||||||
clearTimeout(pauseOverlayTimerRef.current);
|
clearTimeout(pauseOverlayTimerRef.current);
|
||||||
pauseOverlayTimerRef.current = null;
|
pauseOverlayTimerRef.current = null;
|
||||||
}
|
}
|
||||||
if (showPauseOverlay) {
|
hidePauseOverlay();
|
||||||
Animated.parallel([
|
|
||||||
Animated.timing(pauseOverlayOpacity, {
|
|
||||||
toValue: 0,
|
|
||||||
duration: 220,
|
|
||||||
useNativeDriver: true,
|
|
||||||
}),
|
|
||||||
Animated.timing(pauseOverlayTranslateY, {
|
|
||||||
toValue: 8,
|
|
||||||
duration: 220,
|
|
||||||
useNativeDriver: true,
|
|
||||||
})
|
|
||||||
]).start(() => setShowPauseOverlay(false));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
if (pauseOverlayTimerRef.current) {
|
if (pauseOverlayTimerRef.current) {
|
||||||
|
|
@ -1321,7 +1342,7 @@ const VideoPlayer: React.FC = () => {
|
||||||
pauseOverlayTimerRef.current = null;
|
pauseOverlayTimerRef.current = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [paused]);
|
}, [paused, hidePauseOverlay]);
|
||||||
|
|
||||||
// Handle next episode button visibility based on current time and next episode availability
|
// Handle next episode button visibility based on current time and next episode availability
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -1827,65 +1848,76 @@ const VideoPlayer: React.FC = () => {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{showPauseOverlay && (
|
{showPauseOverlay && (
|
||||||
<Animated.View
|
<TouchableOpacity
|
||||||
pointerEvents="none"
|
activeOpacity={1}
|
||||||
|
onPress={hidePauseOverlay}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
opacity: pauseOverlayOpacity,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Strong horizontal fade from left side */}
|
<Animated.View
|
||||||
<View style={{ position: 'absolute', top: 0, left: 0, bottom: 0, width: screenDimensions.width * 0.7 }}>
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
opacity: pauseOverlayOpacity,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Strong horizontal fade from left side */}
|
||||||
|
<View style={{ position: 'absolute', top: 0, left: 0, bottom: 0, width: screenDimensions.width * 0.7 }}>
|
||||||
|
<LinearGradient
|
||||||
|
start={{ x: 0, y: 0.5 }}
|
||||||
|
end={{ x: 1, y: 0.5 }}
|
||||||
|
colors={[ 'rgba(0,0,0,0.85)', 'rgba(0,0,0,0.0)' ]}
|
||||||
|
locations={[0, 1]}
|
||||||
|
style={StyleSheet.absoluteFill}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
start={{ x: 0, y: 0.5 }}
|
colors={[
|
||||||
end={{ x: 1, y: 0.5 }}
|
'rgba(0,0,0,0.6)',
|
||||||
colors={[ 'rgba(0,0,0,0.85)', 'rgba(0,0,0,0.0)' ]}
|
'rgba(0,0,0,0.4)',
|
||||||
locations={[0, 1]}
|
'rgba(0,0,0,0.2)',
|
||||||
|
'rgba(0,0,0,0.0)'
|
||||||
|
]}
|
||||||
|
locations={[0, 0.3, 0.6, 1]}
|
||||||
style={StyleSheet.absoluteFill}
|
style={StyleSheet.absoluteFill}
|
||||||
/>
|
/>
|
||||||
</View>
|
<Animated.View style={{
|
||||||
<LinearGradient
|
position: 'absolute',
|
||||||
colors={[
|
left: 24 + insets.left,
|
||||||
'rgba(0,0,0,0.6)',
|
right: 24 + insets.right,
|
||||||
'rgba(0,0,0,0.4)',
|
bottom: 110 + insets.bottom,
|
||||||
'rgba(0,0,0,0.2)',
|
transform: [{ translateY: pauseOverlayTranslateY }]
|
||||||
'rgba(0,0,0,0.0)'
|
}}>
|
||||||
]}
|
<Text style={{ color: '#B8B8B8', fontSize: 18, marginBottom: 8 }}>You're watching</Text>
|
||||||
locations={[0, 0.3, 0.6, 1]}
|
<Text style={{ color: '#FFFFFF', fontSize: 48, fontWeight: '800', marginBottom: 10 }} numberOfLines={1}>
|
||||||
style={StyleSheet.absoluteFill}
|
{title}
|
||||||
/>
|
|
||||||
<Animated.View style={{
|
|
||||||
position: 'absolute',
|
|
||||||
left: 24 + insets.left,
|
|
||||||
right: 24 + insets.right,
|
|
||||||
bottom: 110 + insets.bottom,
|
|
||||||
transform: [{ translateY: pauseOverlayTranslateY }]
|
|
||||||
}}>
|
|
||||||
<Text style={{ color: '#B8B8B8', fontSize: 18, marginBottom: 8 }}>You're watching</Text>
|
|
||||||
<Text style={{ color: '#FFFFFF', fontSize: 48, fontWeight: '800', marginBottom: 10 }} numberOfLines={1}>
|
|
||||||
{title}
|
|
||||||
</Text>
|
|
||||||
{!!year && (
|
|
||||||
<Text style={{ color: '#CCCCCC', fontSize: 18, marginBottom: 8 }} numberOfLines={1}>
|
|
||||||
{`${year}${type === 'series' && season && episode ? ` • S${season}E${episode}` : ''}`}
|
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
{!!year && (
|
||||||
{!!episodeTitle && (
|
<Text style={{ color: '#CCCCCC', fontSize: 18, marginBottom: 8 }} numberOfLines={1}>
|
||||||
<Text style={{ color: '#FFFFFF', fontSize: 20, fontWeight: '600', marginBottom: 8 }} numberOfLines={1}>
|
{`${year}${type === 'series' && season && episode ? ` • S${season}E${episode}` : ''}`}
|
||||||
{episodeTitle}
|
</Text>
|
||||||
</Text>
|
)}
|
||||||
)}
|
{!!episodeTitle && (
|
||||||
{(currentEpisodeDescription || metadata?.description) && (
|
<Text style={{ color: '#FFFFFF', fontSize: 20, fontWeight: '600', marginBottom: 8 }} numberOfLines={1}>
|
||||||
<Text style={{ color: '#D6D6D6', fontSize: 18, lineHeight: 24 }} numberOfLines={3}>
|
{episodeTitle}
|
||||||
{type === 'series' ? (currentEpisodeDescription || metadata?.description || '') : (metadata?.description || '')}
|
</Text>
|
||||||
</Text>
|
)}
|
||||||
)}
|
{(currentEpisodeDescription || metadata?.description) && (
|
||||||
|
<Text style={{ color: '#D6D6D6', fontSize: 18, lineHeight: 24 }} numberOfLines={3}>
|
||||||
|
{type === 'series' ? (currentEpisodeDescription || metadata?.description || '') : (metadata?.description || '')}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Animated.View>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
</Animated.View>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Next Episode Button */}
|
{/* Next Episode Button */}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue