Remove SkiaProgressSlider from PlayerControls and refactor progress handling for improved touch interaction. Update AndroidVideoPlayer and VideoPlayer components to eliminate seekToTime prop and adjust subtitle handling.

This commit is contained in:
tapframe 2025-07-08 17:19:21 +05:30
parent 6433acac77
commit d66764471f
4 changed files with 53 additions and 293 deletions

View file

@ -1195,7 +1195,6 @@ const AndroidVideoPlayer: React.FC = () => {
handleProgressBarDragEnd={handleProgressBarDragEnd}
buffered={buffered}
formatTime={formatTime}
seekToTime={seekToTime}
/>
<CustomSubtitles

View file

@ -141,7 +141,6 @@ const VideoPlayer: React.FC = () => {
const [currentSubtitle, setCurrentSubtitle] = useState<string>('');
const [subtitleSize, setSubtitleSize] = useState<number>(DEFAULT_SUBTITLE_SIZE);
const [useCustomSubtitles, setUseCustomSubtitles] = useState<boolean>(false);
const [subtitleBackground, setSubtitleBackground] = useState<boolean>(true); // Add missing state
const [isLoadingSubtitles, setIsLoadingSubtitles] = useState<boolean>(false);
const [availableSubtitles, setAvailableSubtitles] = useState<WyzieSubtitle[]>([]);
const [showSubtitleLanguageModal, setShowSubtitleLanguageModal] = useState<boolean>(false);
@ -904,20 +903,14 @@ const VideoPlayer: React.FC = () => {
}, []);
const increaseSubtitleSize = () => {
const newSize = Math.min(subtitleSize + 2, 40);
setSubtitleSize(newSize);
const newSize = Math.min(subtitleSize + 2, 32);
saveSubtitleSize(newSize);
};
const decreaseSubtitleSize = () => {
const newSize = Math.max(subtitleSize - 2, 12);
setSubtitleSize(newSize);
const newSize = Math.max(subtitleSize - 2, 8);
saveSubtitleSize(newSize);
};
const toggleSubtitleBackground = () => {
setSubtitleBackground(prev => !prev);
};
useEffect(() => {
if (pendingSeek && isPlayerReady && isVideoLoaded && duration > 0) {
@ -1211,7 +1204,6 @@ const VideoPlayer: React.FC = () => {
handleProgressBarDragEnd={handleProgressBarDragEnd}
buffered={buffered}
formatTime={formatTime}
seekToTime={seekToTime}
/>
<CustomSubtitles
@ -1219,7 +1211,6 @@ const VideoPlayer: React.FC = () => {
currentSubtitle={currentSubtitle}
subtitleSize={subtitleSize}
zoomScale={zoomScale}
subtitleBackground={subtitleBackground}
/>
<ResumeOverlay
@ -1255,13 +1246,11 @@ const VideoPlayer: React.FC = () => {
selectedTextTrack={selectedTextTrack}
useCustomSubtitles={useCustomSubtitles}
subtitleSize={subtitleSize}
subtitleBackground={subtitleBackground}
fetchAvailableSubtitles={fetchAvailableSubtitles}
loadWyzieSubtitle={loadWyzieSubtitle}
selectTextTrack={selectTextTrack}
increaseSubtitleSize={increaseSubtitleSize}
decreaseSubtitleSize={decreaseSubtitleSize}
toggleSubtitleBackground={toggleSubtitleBackground}
/>
<SourcesModal

View file

@ -1,10 +1,9 @@
import React, { useState } from 'react';
import { View, Text, TouchableOpacity, Animated, StyleSheet, Dimensions } from 'react-native';
import React from 'react';
import { View, Text, TouchableOpacity, Animated, StyleSheet } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient';
import { styles } from '../utils/playerStyles';
import { getTrackDisplayName } from '../utils/playerUtils';
import { SkiaProgressSlider } from './SkiaProgressSlider';
interface PlayerControlsProps {
showControls: boolean;
@ -40,11 +39,8 @@ interface PlayerControlsProps {
handleProgressBarDragEnd: () => void;
buffered: number;
formatTime: (seconds: number) => string;
seekToTime?: (time: number) => void;
}
const { width: screenWidth } = Dimensions.get('window');
export const PlayerControls: React.FC<PlayerControlsProps> = ({
showControls,
fadeAnim,
@ -79,61 +75,63 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
handleProgressBarDragEnd,
buffered,
formatTime,
seekToTime,
}) => {
// State for tracking preview time during dragging
const [previewTime, setPreviewTime] = useState<number | null>(null);
const [isDragging, setIsDragging] = useState(false);
// Calculate slider width based on screen width minus padding
const sliderWidth = screenWidth - 40; // 20px padding on each side
const handleSeek = (time: number) => {
if (seekToTime) {
seekToTime(time);
}
};
const handleSeekPreview = (time: number) => {
setPreviewTime(time);
};
const handleSeekStart = () => {
setIsDragging(true);
handleProgressBarDragStart();
};
const handleSeekEnd = (time: number) => {
setIsDragging(false);
setPreviewTime(null);
handleProgressBarDragEnd();
handleSeek(time);
};
// Determine which time to display (preview time while dragging, otherwise current time)
const displayTime = isDragging && previewTime !== null ? previewTime : currentTime;
return (
<Animated.View
style={[StyleSheet.absoluteFill, { opacity: fadeAnim }]}
pointerEvents={showControls ? 'auto' : 'none'}
>
{/* Progress bar with Skia slider */}
{/* Progress bar with enhanced touch handling */}
<View style={styles.sliderContainer}>
<SkiaProgressSlider
currentTime={currentTime}
duration={duration}
buffered={buffered}
onSeek={handleSeek}
onSeekStart={handleSeekStart}
onSeekEnd={handleSeekEnd}
onSeekPreview={handleSeekPreview}
width={sliderWidth}
/>
<View
style={styles.progressTouchArea}
onTouchStart={handleProgressBarDragStart}
onTouchMove={handleProgressBarDragMove}
onTouchEnd={handleProgressBarDragEnd}
>
<TouchableOpacity
activeOpacity={0.8}
onPress={handleProgressBarTouch}
style={{width: '100%'}}
>
<View
ref={progressBarRef}
style={styles.progressBarContainer}
>
{/* Buffered Progress */}
<View style={[styles.bufferProgress, {
width: `${(buffered / (duration || 1)) * 100}%`
}]} />
{/* Animated Progress */}
<Animated.View
style={[
styles.progressBarFill,
{
width: progressAnim.interpolate({
inputRange: [0, 1],
outputRange: ['0%', '100%']
})
}
]}
/>
</View>
{/* Progress Thumb - Moved outside the progressBarContainer */}
<Animated.View
style={[
styles.progressThumb,
{
left: progressAnim.interpolate({
inputRange: [0, 1],
outputRange: ['0%', '100%']
})
}
]}
/>
</TouchableOpacity>
</View>
<View style={styles.timeDisplay}>
<Text style={[styles.duration, isDragging && { color: '#E50914' }]}>
{formatTime(displayTime)}
</Text>
<Text style={styles.duration}>{formatTime(currentTime)}</Text>
<Text style={styles.duration}>{formatTime(duration)}</Text>
</View>
</View>

View file

@ -1,226 +0,0 @@
import React, { useMemo, useEffect } from 'react';
import { View, Dimensions } from 'react-native';
import {
Canvas,
RoundedRect,
Circle,
Group,
Shadow,
} from '@shopify/react-native-skia';
import {
Gesture,
GestureDetector,
GestureHandlerRootView,
} from 'react-native-gesture-handler';
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
runOnJS,
interpolate,
} from 'react-native-reanimated';
interface SkiaProgressSliderProps {
currentTime: number;
duration: number;
buffered: number;
onSeek: (time: number) => void;
onSeekStart?: () => void;
onSeekEnd?: (time: number) => void;
onSeekPreview?: (time: number) => void; // New callback for preview time
width: number;
height?: number;
thumbSize?: number;
progressColor?: string;
backgroundColor?: string;
bufferedColor?: string;
}
export const SkiaProgressSlider: React.FC<SkiaProgressSliderProps> = ({
currentTime,
duration,
buffered,
onSeek,
onSeekStart,
onSeekEnd,
onSeekPreview,
width,
height = 4,
thumbSize = 16,
progressColor = '#E50914',
backgroundColor = 'rgba(255, 255, 255, 0.2)',
bufferedColor = 'rgba(255, 255, 255, 0.4)',
}) => {
const progress = useSharedValue(0);
const isDragging = useSharedValue(false);
const thumbScale = useSharedValue(1);
const progressWidth = useSharedValue(0);
const previewTimeValue = useSharedValue(0);
// Add padding to account for thumb size
const trackPadding = thumbSize / 2;
const trackWidth = width - (trackPadding * 2);
// Update progress when currentTime changes
useEffect(() => {
if (!isDragging.value && duration > 0) {
const newProgress = (currentTime / duration);
progress.value = newProgress;
progressWidth.value = newProgress * trackWidth;
}
}, [currentTime, duration, trackWidth]);
// Calculate buffered width
const bufferedWidth = useMemo(() => {
return duration > 0 ? (buffered / duration) * trackWidth : 0;
}, [buffered, duration, trackWidth]);
// Handle seeking with proper coordinate mapping
const seekToPosition = (gestureX: number, isPreview: boolean = false) => {
'worklet';
// Map gesture coordinates to track coordinates
const trackX = gestureX - trackPadding;
const clampedX = Math.max(0, Math.min(trackX, trackWidth));
const newProgress = clampedX / trackWidth;
progress.value = newProgress;
progressWidth.value = clampedX;
const seekTime = newProgress * duration;
previewTimeValue.value = seekTime;
if (isPreview && onSeekPreview) {
runOnJS(onSeekPreview)(seekTime);
} else if (!isPreview) {
runOnJS(onSeek)(seekTime);
}
};
// Pan gesture for dragging
const panGesture = Gesture.Pan()
.onBegin((e) => {
'worklet';
isDragging.value = true;
thumbScale.value = withSpring(1.2, { damping: 15, stiffness: 400 });
// Process the initial touch position immediately
seekToPosition(e.x, true);
if (onSeekStart) {
runOnJS(onSeekStart)();
}
})
.onUpdate((e) => {
'worklet';
seekToPosition(e.x, true); // Use preview mode during dragging
})
.onFinalize((e) => {
'worklet';
isDragging.value = false;
thumbScale.value = withSpring(1, { damping: 15, stiffness: 400 });
// Use the exact same preview time for the final seek to ensure consistency
if (onSeekEnd) {
runOnJS(onSeekEnd)(previewTimeValue.value);
}
// Final seek when drag ends - use the same calculation as preview
runOnJS(onSeek)(previewTimeValue.value);
});
// Tap gesture for seeking
const tapGesture = Gesture.Tap()
.onEnd((e) => {
'worklet';
seekToPosition(e.x, false); // Direct seek on tap
});
const composedGesture = Gesture.Simultaneous(tapGesture, panGesture);
// Animated styles for thumb
const animatedThumbStyle = useAnimatedStyle(() => {
const thumbX = progress.value * trackWidth + trackPadding;
return {
transform: [
{ translateX: thumbX - thumbSize / 2 },
{ scale: thumbScale.value }
],
};
});
// Animated style for progress width
const animatedProgressStyle = useAnimatedStyle(() => {
return {
width: progressWidth.value,
};
});
return (
<View style={{ width, height: height + thumbSize + 8 }}>
<GestureDetector gesture={composedGesture}>
<View>
<Canvas style={{ width, height: height + thumbSize + 8 }}>
<Group transform={[{ translateY: (thumbSize / 2) + 4 }, { translateX: trackPadding }]}>
{/* Background track */}
<RoundedRect
x={0}
y={0}
width={trackWidth}
height={height}
r={height / 2}
color={backgroundColor}
/>
{/* Buffered progress */}
{bufferedWidth > 0 && (
<RoundedRect
x={0}
y={0}
width={bufferedWidth}
height={height}
r={height / 2}
color={bufferedColor}
/>
)}
</Group>
</Canvas>
{/* Current progress - using regular view for animation */}
<Animated.View
style={[
{
position: 'absolute',
top: (thumbSize / 2) + 4,
left: trackPadding,
height: height,
backgroundColor: progressColor,
borderRadius: height / 2,
},
animatedProgressStyle,
]}
/>
{/* Animated thumb */}
<Animated.View
style={[
{
position: 'absolute',
top: 4,
left: 0,
width: thumbSize,
height: thumbSize,
borderRadius: thumbSize / 2,
backgroundColor: progressColor,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.3,
shadowRadius: 3,
elevation: 4,
},
animatedThumbStyle,
]}
/>
</View>
</GestureDetector>
</View>
);
};