ui changes for next episode

This commit is contained in:
tapframe 2025-08-13 13:56:45 +05:30
parent a9bb8c1131
commit ac504b99c8
3 changed files with 37 additions and 43 deletions

View file

@ -781,15 +781,12 @@ const AndroidVideoPlayer: React.FC = () => {
} }
disableImmersiveMode(); disableImmersiveMode();
// For series, reset to streams screen for current episode to ensure no hidden players remain on the stack // For series, hard reset to a single Streams route to avoid stacking multiple modals/pages
if (type === 'series' && id && episodeId) { if (type === 'series' && id && episodeId) {
(navigation as any).reset({ (navigation as any).reset({
index: 0, index: 0,
routes: [ routes: [
{ { name: 'Streams', params: { id, type: 'series', episodeId } }
name: 'Streams',
params: { id, type: 'series', episodeId }
}
] ]
}); });
} else { } else {
@ -804,15 +801,12 @@ const AndroidVideoPlayer: React.FC = () => {
} }
disableImmersiveMode(); disableImmersiveMode();
// For series, reset to streams screen for current episode to ensure no hidden players remain on the stack // For series, hard reset to a single Streams route to avoid stacking multiple modals/pages
if (type === 'series' && id && episodeId) { if (type === 'series' && id && episodeId) {
(navigation as any).reset({ (navigation as any).reset({
index: 0, index: 0,
routes: [ routes: [
{ { name: 'Streams', params: { id, type: 'series', episodeId } }
name: 'Streams',
params: { id, type: 'series', episodeId }
}
] ]
}); });
} else { } else {
@ -1418,7 +1412,7 @@ const AndroidVideoPlayer: React.FC = () => {
// 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(() => {
if ((type as any) !== 'series' || !nextEpisode || duration <= 0 || isLoadingNextEpisode) { if ((type as any) !== 'series' || !nextEpisode || duration <= 0) {
if (showNextEpisodeButton) { if (showNextEpisodeButton) {
// Hide button with animation // Hide button with animation
Animated.parallel([ Animated.parallel([
@ -1439,9 +1433,9 @@ const AndroidVideoPlayer: React.FC = () => {
return; return;
} }
// Show button when 2.5 minutes (150 seconds) remain // Show button when 1 minute (60 seconds) remains
const timeRemaining = duration - currentTime; const timeRemaining = duration - currentTime;
const shouldShowButton = timeRemaining <= 150 && timeRemaining > 10; // Hide in last 10 seconds const shouldShowButton = timeRemaining <= 60 && timeRemaining > 10; // Hide in last 10 seconds
if (shouldShowButton && !showNextEpisodeButton) { if (shouldShowButton && !showNextEpisodeButton) {
setShowNextEpisodeButton(true); setShowNextEpisodeButton(true);
@ -1474,7 +1468,7 @@ const AndroidVideoPlayer: React.FC = () => {
setShowNextEpisodeButton(false); setShowNextEpisodeButton(false);
}); });
} }
}, [type, nextEpisode, duration, currentTime, showNextEpisodeButton, isLoadingNextEpisode]); }, [type, nextEpisode, duration, currentTime, showNextEpisodeButton]);
useEffect(() => { useEffect(() => {
isMounted.current = true; isMounted.current = true;
@ -1980,11 +1974,11 @@ const AndroidVideoPlayer: React.FC = () => {
{/* Next Episode Button */} {/* Next Episode Button */}
{showNextEpisodeButton && nextEpisode && ( {showNextEpisodeButton && nextEpisode && (
<Animated.View <Animated.View
style={{ style={{
position: 'absolute', position: 'absolute',
bottom: 80 + insets.bottom, bottom: 80 + insets.bottom,
right: 24 + insets.right, right: 8 + insets.right,
opacity: nextEpisodeButtonOpacity, opacity: nextEpisodeButtonOpacity,
transform: [{ scale: nextEpisodeButtonScale }], transform: [{ scale: nextEpisodeButtonScale }],
}} }}
@ -1992,9 +1986,9 @@ const AndroidVideoPlayer: React.FC = () => {
<TouchableOpacity <TouchableOpacity
style={{ style={{
backgroundColor: 'rgba(255,255,255,0.95)', backgroundColor: 'rgba(255,255,255,0.95)',
borderRadius: 25, borderRadius: 18,
paddingHorizontal: 20, paddingHorizontal: 14,
paddingVertical: 12, paddingVertical: 8,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
shadowColor: '#000', shadowColor: '#000',
@ -2004,19 +1998,18 @@ const AndroidVideoPlayer: React.FC = () => {
elevation: 8, elevation: 8,
}} }}
onPress={handlePlayNextEpisode} onPress={handlePlayNextEpisode}
disabled={isLoadingNextEpisode}
activeOpacity={0.8} activeOpacity={0.8}
> >
{isLoadingNextEpisode ? ( {isLoadingNextEpisode ? (
<ActivityIndicator size="small" color="#000000" style={{ marginRight: 8 }} /> <ActivityIndicator size="small" color="#000000" style={{ marginRight: 8 }} />
) : ( ) : (
<MaterialIcons name="skip-next" size={20} color="#000000" style={{ marginRight: 8 }} /> <MaterialIcons name="skip-next" size={18} color="#000000" style={{ marginRight: 8 }} />
)} )}
<View> <View>
<Text style={{ color: '#000000', fontSize: 12, fontWeight: '600', opacity: 0.7 }}> <Text style={{ color: '#000000', fontSize: 11, fontWeight: '700', opacity: 0.8 }}>
{isLoadingNextEpisode ? 'Loading...' : 'Up Next'} {isLoadingNextEpisode ? 'Loading next episode…' : 'Up next'}
</Text> </Text>
<Text style={{ color: '#000000', fontSize: 14, fontWeight: '700' }} numberOfLines={1}> <Text style={{ color: '#000000', fontSize: 13, fontWeight: '700' }} numberOfLines={1}>
S{nextEpisode.season_number}E{nextEpisode.episode_number} S{nextEpisode.season_number}E{nextEpisode.episode_number}
{nextEpisode.name ? `: ${nextEpisode.name}` : ''} {nextEpisode.name ? `: ${nextEpisode.name}` : ''}
</Text> </Text>

View file

@ -825,15 +825,12 @@ const VideoPlayer: React.FC = () => {
// Navigate back with proper handling for fullscreen modal // Navigate back with proper handling for fullscreen modal
try { try {
// For series, reset to streams screen for current episode to ensure no hidden players remain on the stack // For series, hard reset to a single Streams route to avoid stacking multiple modals/pages
if (type === 'series' && id && episodeId) { if (type === 'series' && id && episodeId) {
(navigation as any).reset({ (navigation as any).reset({
index: 0, index: 0,
routes: [ routes: [
{ { name: 'Streams', params: { id, type: 'series', episodeId } }
name: 'Streams',
params: { id, type: 'series', episodeId }
}
] ]
}); });
} else if (navigation.canGoBack()) { } else if (navigation.canGoBack()) {
@ -1319,7 +1316,7 @@ const VideoPlayer: React.FC = () => {
// 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(() => {
if (type !== 'series' || !nextEpisode || duration <= 0 || isLoadingNextEpisode) { if (type !== 'series' || !nextEpisode || duration <= 0) {
if (showNextEpisodeButton) { if (showNextEpisodeButton) {
// Hide button with animation // Hide button with animation
Animated.parallel([ Animated.parallel([
@ -1340,9 +1337,9 @@ const VideoPlayer: React.FC = () => {
return; return;
} }
// Show button when 2.5 minutes (150 seconds) remain // Show button when 1 minute (60 seconds) remains
const timeRemaining = duration - currentTime; const timeRemaining = duration - currentTime;
const shouldShowButton = timeRemaining <= 150 && timeRemaining > 10; // Hide in last 10 seconds const shouldShowButton = timeRemaining <= 60 && timeRemaining > 10; // Hide in last 10 seconds
if (shouldShowButton && !showNextEpisodeButton) { if (shouldShowButton && !showNextEpisodeButton) {
setShowNextEpisodeButton(true); setShowNextEpisodeButton(true);
@ -1375,7 +1372,7 @@ const VideoPlayer: React.FC = () => {
setShowNextEpisodeButton(false); setShowNextEpisodeButton(false);
}); });
} }
}, [type, nextEpisode, duration, currentTime, showNextEpisodeButton, isLoadingNextEpisode]); }, [type, nextEpisode, duration, currentTime, showNextEpisodeButton]);
useEffect(() => { useEffect(() => {
isMounted.current = true; isMounted.current = true;
@ -1888,7 +1885,7 @@ const VideoPlayer: React.FC = () => {
style={{ style={{
position: 'absolute', position: 'absolute',
bottom: 80 + insets.bottom, bottom: 80 + insets.bottom,
right: 24 + insets.right, right: 8 + insets.right,
opacity: nextEpisodeButtonOpacity, opacity: nextEpisodeButtonOpacity,
transform: [{ scale: nextEpisodeButtonScale }], transform: [{ scale: nextEpisodeButtonScale }],
}} }}
@ -1896,9 +1893,9 @@ const VideoPlayer: React.FC = () => {
<TouchableOpacity <TouchableOpacity
style={{ style={{
backgroundColor: 'rgba(255,255,255,0.95)', backgroundColor: 'rgba(255,255,255,0.95)',
borderRadius: 25, borderRadius: 18,
paddingHorizontal: 20, paddingHorizontal: 14,
paddingVertical: 12, paddingVertical: 8,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
shadowColor: '#000', shadowColor: '#000',
@ -1908,19 +1905,18 @@ const VideoPlayer: React.FC = () => {
elevation: 8, elevation: 8,
}} }}
onPress={handlePlayNextEpisode} onPress={handlePlayNextEpisode}
disabled={isLoadingNextEpisode}
activeOpacity={0.8} activeOpacity={0.8}
> >
{isLoadingNextEpisode ? ( {isLoadingNextEpisode ? (
<ActivityIndicator size="small" color="#000000" style={{ marginRight: 8 }} /> <ActivityIndicator size="small" color="#000000" style={{ marginRight: 8 }} />
) : ( ) : (
<MaterialIcons name="skip-next" size={20} color="#000000" style={{ marginRight: 8 }} /> <MaterialIcons name="skip-next" size={18} color="#000000" style={{ marginRight: 8 }} />
)} )}
<View> <View>
<Text style={{ color: '#000000', fontSize: 12, fontWeight: '600', opacity: 0.7 }}> <Text style={{ color: '#000000', fontSize: 11, fontWeight: '700', opacity: 0.8 }}>
{isLoadingNextEpisode ? 'Loading...' : 'Up Next'} {isLoadingNextEpisode ? 'Loading next episode…' : 'Up next'}
</Text> </Text>
<Text style={{ color: '#000000', fontSize: 14, fontWeight: '700' }} numberOfLines={1}> <Text style={{ color: '#000000', fontSize: 13, fontWeight: '700' }} numberOfLines={1}>
S{nextEpisode.season_number}E{nextEpisode.episode_number} S{nextEpisode.season_number}E{nextEpisode.episode_number}
{nextEpisode.name ? `: ${nextEpisode.name}` : ''} {nextEpisode.name ? `: ${nextEpisode.name}` : ''}
</Text> </Text>

View file

@ -16,6 +16,7 @@ import {
Linking, Linking,
Clipboard, Clipboard,
} from 'react-native'; } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import * as ScreenOrientation from 'expo-screen-orientation'; import * as ScreenOrientation from 'expo-screen-orientation';
import { useRoute, useNavigation, useFocusEffect } from '@react-navigation/native'; import { useRoute, useNavigation, useFocusEffect } from '@react-navigation/native';
@ -349,6 +350,7 @@ const ProviderFilter = memo(({
}); });
export const StreamsScreen = () => { export const StreamsScreen = () => {
const insets = useSafeAreaInsets();
const route = useRoute<RouteProp<RootStackParamList, 'Streams'>>(); const route = useRoute<RouteProp<RootStackParamList, 'Streams'>>();
const navigation = useNavigation<RootStackNavigationProp>(); const navigation = useNavigation<RootStackNavigationProp>();
const { id, type, episodeId, episodeThumbnail } = route.params; const { id, type, episodeId, episodeThumbnail } = route.params;
@ -1381,7 +1383,10 @@ export const StreamsScreen = () => {
style={[styles.backButtonContainer]} style={[styles.backButtonContainer]}
> >
<TouchableOpacity <TouchableOpacity
style={styles.backButton} style={[
styles.backButton,
Platform.OS === 'ios' ? { paddingTop: Math.max(insets.top, 12) + 6 } : null
]}
onPress={handleBack} onPress={handleBack}
activeOpacity={0.7} activeOpacity={0.7}
> >