slider change

This commit is contained in:
tapframe 2025-07-29 14:39:49 +05:30
parent 6405fd2c71
commit 17339d82a6
3 changed files with 65 additions and 146 deletions

View file

@ -124,8 +124,7 @@ const AndroidVideoPlayer: React.FC = () => {
const [rnVideoAudioTracks, setRnVideoAudioTracks] = useState<Array<{id: number, name: string, language?: string}>>([]); const [rnVideoAudioTracks, setRnVideoAudioTracks] = useState<Array<{id: number, name: string, language?: string}>>([]);
const [rnVideoTextTracks, setRnVideoTextTracks] = useState<Array<{id: number, name: string, language?: string}>>([]); const [rnVideoTextTracks, setRnVideoTextTracks] = useState<Array<{id: number, name: string, language?: string}>>([]);
const [isPlayerReady, setIsPlayerReady] = useState(false); const [isPlayerReady, setIsPlayerReady] = useState(false);
const progressAnim = useRef(new Animated.Value(0)).current; // Removed progressAnim and progressBarRef - no longer needed with React Native Community Slider
const progressBarRef = useRef<View>(null);
const [isDragging, setIsDragging] = useState(false); const [isDragging, setIsDragging] = useState(false);
const isSeeking = useRef(false); const isSeeking = useRef(false);
const seekDebounceTimer = useRef<NodeJS.Timeout | null>(null); const seekDebounceTimer = useRef<NodeJS.Timeout | null>(null);
@ -454,44 +453,29 @@ const AndroidVideoPlayer: React.FC = () => {
} }
}, [seekTime, duration]); }, [seekTime, duration]);
const handleProgressBarTouch = (event: any) => { // Slider callback functions for React Native Community Slider
if (duration > 0) { const handleSliderValueChange = (value: number) => {
const { locationX } = event.nativeEvent; if (isDragging && duration > 0) {
processProgressTouch(locationX); const seekTime = Math.min(value, duration - END_EPSILON);
setCurrentTime(seekTime);
pendingSeekValue.current = seekTime;
} }
}; };
const handleProgressBarDragStart = () => { const handleSlidingStart = () => {
setIsDragging(true); setIsDragging(true);
}; };
const handleProgressBarDragMove = (event: any) => { const handleSlidingComplete = (value: number) => {
if (!isDragging || !duration || duration <= 0) return;
const { locationX } = event.nativeEvent;
processProgressTouch(locationX, true);
};
const handleProgressBarDragEnd = () => {
setIsDragging(false); setIsDragging(false);
if (pendingSeekValue.current !== null) { if (duration > 0) {
seekToTime(pendingSeekValue.current); const seekTime = Math.min(value, duration - END_EPSILON);
seekToTime(seekTime);
pendingSeekValue.current = null; pendingSeekValue.current = null;
} }
}; };
const processProgressTouch = (locationX: number, isDragging = false) => { // Removed processProgressTouch - no longer needed with React Native Community Slider
progressBarRef.current?.measure((x, y, width, height, pageX, pageY) => {
const percentage = Math.max(0, Math.min(locationX / width, 0.999));
const seekTime = Math.min(percentage * duration, duration - END_EPSILON);
progressAnim.setValue(percentage);
if (isDragging) {
pendingSeekValue.current = seekTime;
setCurrentTime(seekTime);
} else {
seekToTime(seekTime);
}
});
};
const handleProgress = (data: any) => { const handleProgress = (data: any) => {
if (isDragging || isSeeking.current) return; if (isDragging || isSeeking.current) return;
@ -501,12 +485,7 @@ const AndroidVideoPlayer: React.FC = () => {
// Update time more frequently for subtitle synchronization (0.1s threshold) // Update time more frequently for subtitle synchronization (0.1s threshold)
if (Math.abs(currentTimeInSeconds - currentTime) > 0.1) { if (Math.abs(currentTimeInSeconds - currentTime) > 0.1) {
safeSetState(() => setCurrentTime(currentTimeInSeconds)); safeSetState(() => setCurrentTime(currentTimeInSeconds));
const progressPercent = duration > 0 ? currentTimeInSeconds / duration : 0; // Removed progressAnim animation - no longer needed with React Native Community Slider
Animated.timing(progressAnim, {
toValue: progressPercent,
duration: 100,
useNativeDriver: false,
}).start();
const bufferedTime = data.playableDuration || currentTimeInSeconds; const bufferedTime = data.playableDuration || currentTimeInSeconds;
safeSetState(() => setBuffered(bufferedTime)); safeSetState(() => setBuffered(bufferedTime));
} }
@ -1199,12 +1178,9 @@ const AndroidVideoPlayer: React.FC = () => {
setShowAudioModal={setShowAudioModal} setShowAudioModal={setShowAudioModal}
setShowSubtitleModal={setShowSubtitleModal} setShowSubtitleModal={setShowSubtitleModal}
setShowSourcesModal={setShowSourcesModal} setShowSourcesModal={setShowSourcesModal}
progressBarRef={progressBarRef} onSliderValueChange={handleSliderValueChange}
progressAnim={progressAnim} onSlidingStart={handleSlidingStart}
handleProgressBarTouch={handleProgressBarTouch} onSlidingComplete={handleSlidingComplete}
handleProgressBarDragStart={handleProgressBarDragStart}
handleProgressBarDragMove={handleProgressBarDragMove}
handleProgressBarDragEnd={handleProgressBarDragEnd}
buffered={buffered} buffered={buffered}
formatTime={formatTime} formatTime={formatTime}
/> />

View file

@ -11,6 +11,7 @@ import { logger } from '../../utils/logger';
import AsyncStorage from '@react-native-async-storage/async-storage'; import AsyncStorage from '@react-native-async-storage/async-storage';
import { MaterialIcons } from '@expo/vector-icons'; import { MaterialIcons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient'; import { LinearGradient } from 'expo-linear-gradient';
import Slider from '@react-native-community/slider';
import AndroidVideoPlayer from './AndroidVideoPlayer'; import AndroidVideoPlayer from './AndroidVideoPlayer';
import { useTraktAutosync } from '../../hooks/useTraktAutosync'; import { useTraktAutosync } from '../../hooks/useTraktAutosync';
import { useTraktAutosyncSettings } from '../../hooks/useTraktAutosyncSettings'; import { useTraktAutosyncSettings } from '../../hooks/useTraktAutosyncSettings';
@ -127,8 +128,7 @@ const VideoPlayer: React.FC = () => {
const [vlcAudioTracks, setVlcAudioTracks] = useState<Array<{ id: number, name: string, language?: string }>>([]); const [vlcAudioTracks, setVlcAudioTracks] = useState<Array<{ id: number, name: string, language?: string }>>([]);
const [vlcTextTracks, setVlcTextTracks] = useState<Array<{ id: number, name: string, language?: string }>>([]); const [vlcTextTracks, setVlcTextTracks] = useState<Array<{ id: number, name: string, language?: string }>>([]);
const [isPlayerReady, setIsPlayerReady] = useState(false); const [isPlayerReady, setIsPlayerReady] = useState(false);
const progressAnim = useRef(new Animated.Value(0)).current; // Removed progressAnim and progressBarRef - no longer needed with React Native Community Slider
const progressBarRef = useRef<View>(null);
const [isDragging, setIsDragging] = useState(false); const [isDragging, setIsDragging] = useState(false);
const isSeeking = useRef(false); const isSeeking = useRef(false);
const seekDebounceTimer = useRef<NodeJS.Timeout | null>(null); const seekDebounceTimer = useRef<NodeJS.Timeout | null>(null);
@ -515,44 +515,29 @@ const VideoPlayer: React.FC = () => {
} }
}; };
const handleProgressBarTouch = (event: any) => { // Slider callback functions for React Native Community Slider
if (duration > 0) { const handleSliderValueChange = (value: number) => {
const { locationX } = event.nativeEvent; if (isDragging && duration > 0) {
processProgressTouch(locationX); const seekTime = Math.min(value, duration - END_EPSILON);
setCurrentTime(seekTime);
pendingSeekValue.current = seekTime;
} }
}; };
const handleProgressBarDragStart = () => { const handleSlidingStart = () => {
setIsDragging(true); setIsDragging(true);
}; };
const handleProgressBarDragMove = (event: any) => { const handleSlidingComplete = (value: number) => {
if (!isDragging || !duration || duration <= 0) return;
const { locationX } = event.nativeEvent;
processProgressTouch(locationX, true);
};
const handleProgressBarDragEnd = () => {
setIsDragging(false); setIsDragging(false);
if (pendingSeekValue.current !== null) { if (duration > 0) {
seekToTime(pendingSeekValue.current); const seekTime = Math.min(value, duration - END_EPSILON);
seekToTime(seekTime);
pendingSeekValue.current = null; pendingSeekValue.current = null;
} }
}; };
const processProgressTouch = (locationX: number, isDragging = false) => { // Removed processProgressTouch - no longer needed with React Native Community Slider
progressBarRef.current?.measure((x, y, width, height, pageX, pageY) => {
const percentage = Math.max(0, Math.min(locationX / width, 0.999));
const seekTime = Math.min(percentage * duration, duration - END_EPSILON);
progressAnim.setValue(percentage);
if (isDragging) {
pendingSeekValue.current = seekTime;
setCurrentTime(seekTime);
} else {
seekToTime(seekTime);
}
});
};
const handleProgress = (event: any) => { const handleProgress = (event: any) => {
if (isDragging || isSeeking.current) return; if (isDragging || isSeeking.current) return;
@ -562,12 +547,7 @@ const VideoPlayer: React.FC = () => {
// Only update if there's a significant change to avoid unnecessary updates // Only update if there's a significant change to avoid unnecessary updates
if (Math.abs(currentTimeInSeconds - currentTime) > 0.5) { if (Math.abs(currentTimeInSeconds - currentTime) > 0.5) {
safeSetState(() => setCurrentTime(currentTimeInSeconds)); safeSetState(() => setCurrentTime(currentTimeInSeconds));
const progressPercent = duration > 0 ? currentTimeInSeconds / duration : 0; // Removed progressAnim animation - no longer needed with React Native Community Slider
Animated.timing(progressAnim, {
toValue: progressPercent,
duration: 250,
useNativeDriver: false,
}).start();
const bufferedTime = event.bufferTime / 1000 || currentTimeInSeconds; const bufferedTime = event.bufferTime / 1000 || currentTimeInSeconds;
safeSetState(() => setBuffered(bufferedTime)); safeSetState(() => setBuffered(bufferedTime));
} }
@ -1273,12 +1253,9 @@ const VideoPlayer: React.FC = () => {
setShowAudioModal={setShowAudioModal} setShowAudioModal={setShowAudioModal}
setShowSubtitleModal={setShowSubtitleModal} setShowSubtitleModal={setShowSubtitleModal}
setShowSourcesModal={setShowSourcesModal} setShowSourcesModal={setShowSourcesModal}
progressBarRef={progressBarRef} onSliderValueChange={handleSliderValueChange}
progressAnim={progressAnim} onSlidingStart={handleSlidingStart}
handleProgressBarTouch={handleProgressBarTouch} onSlidingComplete={handleSlidingComplete}
handleProgressBarDragStart={handleProgressBarDragStart}
handleProgressBarDragMove={handleProgressBarDragMove}
handleProgressBarDragEnd={handleProgressBarDragEnd}
buffered={buffered} buffered={buffered}
formatTime={formatTime} formatTime={formatTime}
/> />

View file

@ -1,7 +1,8 @@
import React from 'react'; import React from 'react';
import { View, Text, TouchableOpacity, Animated, StyleSheet } from 'react-native'; import { View, Text, TouchableOpacity, Animated, StyleSheet, Platform } from 'react-native';
import { Ionicons } from '@expo/vector-icons'; import { Ionicons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient'; import { LinearGradient } from 'expo-linear-gradient';
import Slider from '@react-native-community/slider';
import { styles } from '../utils/playerStyles'; import { styles } from '../utils/playerStyles';
import { getTrackDisplayName } from '../utils/playerUtils'; import { getTrackDisplayName } from '../utils/playerUtils';
@ -31,12 +32,10 @@ interface PlayerControlsProps {
setShowAudioModal: (show: boolean) => void; setShowAudioModal: (show: boolean) => void;
setShowSubtitleModal: (show: boolean) => void; setShowSubtitleModal: (show: boolean) => void;
setShowSourcesModal?: (show: boolean) => void; setShowSourcesModal?: (show: boolean) => void;
progressBarRef: React.RefObject<View>; // Slider-specific props
progressAnim: Animated.Value; onSliderValueChange: (value: number) => void;
handleProgressBarTouch: (event: any) => void; onSlidingStart: () => void;
handleProgressBarDragStart: () => void; onSlidingComplete: (value: number) => void;
handleProgressBarDragMove: (event: any) => void;
handleProgressBarDragEnd: () => void;
buffered: number; buffered: number;
formatTime: (seconds: number) => string; formatTime: (seconds: number) => string;
} }
@ -67,12 +66,9 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
setShowAudioModal, setShowAudioModal,
setShowSubtitleModal, setShowSubtitleModal,
setShowSourcesModal, setShowSourcesModal,
progressBarRef, onSliderValueChange,
progressAnim, onSlidingStart,
handleProgressBarTouch, onSlidingComplete,
handleProgressBarDragStart,
handleProgressBarDragMove,
handleProgressBarDragEnd,
buffered, buffered,
formatTime, formatTime,
}) => { }) => {
@ -81,55 +77,25 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
style={[StyleSheet.absoluteFill, { opacity: fadeAnim }]} style={[StyleSheet.absoluteFill, { opacity: fadeAnim }]}
pointerEvents={showControls ? 'auto' : 'none'} pointerEvents={showControls ? 'auto' : 'none'}
> >
{/* Progress bar with enhanced touch handling */} {/* Progress slider with native iOS slider */}
<View style={styles.sliderContainer}> <View style={styles.sliderContainer}>
<View <Slider
style={styles.progressTouchArea} style={{
onTouchStart={handleProgressBarDragStart} width: '100%',
onTouchMove={handleProgressBarDragMove} height: 40,
onTouchEnd={handleProgressBarDragEnd} marginHorizontal: 0,
> }}
<TouchableOpacity minimumValue={0}
activeOpacity={0.8} maximumValue={duration || 1}
onPress={handleProgressBarTouch} value={currentTime}
style={{width: '100%'}} onValueChange={onSliderValueChange}
> onSlidingStart={onSlidingStart}
<View onSlidingComplete={onSlidingComplete}
ref={progressBarRef} minimumTrackTintColor="#FFFFFF"
style={styles.progressBarContainer} maximumTrackTintColor="rgba(255, 255, 255, 0.3)"
> thumbTintColor={Platform.OS === 'android' ? '#FFFFFF' : undefined}
{/* Buffered Progress */} tapToSeek={Platform.OS === 'ios'}
<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}> <View style={styles.timeDisplay}>
<Text style={styles.duration}>{formatTime(currentTime)}</Text> <Text style={styles.duration}>{formatTime(currentTime)}</Text>
<Text style={styles.duration}>{formatTime(duration)}</Text> <Text style={styles.duration}>{formatTime(duration)}</Text>
@ -244,4 +210,4 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
); );
}; };
export default PlayerControls; export default PlayerControls;