diff --git a/components/video-player/controls/AudioSlider.tsx b/components/video-player/controls/AudioSlider.tsx index e0d95677..9c65624e 100644 --- a/components/video-player/controls/AudioSlider.tsx +++ b/components/video-player/controls/AudioSlider.tsx @@ -113,7 +113,7 @@ const AudioSlider: React.FC = ({ setVisibility }) => { const styles = StyleSheet.create({ sliderContainer: { - width: 150, + width: 130, display: "flex", flexDirection: "row", justifyContent: "center", diff --git a/components/video-player/controls/BrightnessSlider.tsx b/components/video-player/controls/BrightnessSlider.tsx index 12023a86..330bb3fc 100644 --- a/components/video-player/controls/BrightnessSlider.tsx +++ b/components/video-player/controls/BrightnessSlider.tsx @@ -63,7 +63,7 @@ const BrightnessSlider = () => { const styles = StyleSheet.create({ sliderContainer: { - width: 150, + width: 130, display: "flex", flexDirection: "row", justifyContent: "center", diff --git a/components/video-player/controls/Controls.tsx b/components/video-player/controls/Controls.tsx index d8f660d7..c0d6c801 100644 --- a/components/video-player/controls/Controls.tsx +++ b/components/video-player/controls/Controls.tsx @@ -166,6 +166,9 @@ export const Controls: FC = ({ // Animated opacity for smooth transitions const controlsOpacity = useSharedValue(showControls ? 1 : 0); + // Animated scale for slider + const sliderScale = useSharedValue(1); + const wasPlayingRef = useRef(false); const lastProgressRef = useRef(0); @@ -192,6 +195,13 @@ export const Controls: FC = ({ }; }); + // Animated style for slider scale + const animatedSliderStyle = useAnimatedStyle(() => { + return { + transform: [{ scaleY: sliderScale.value }], + }; + }); + useEffect(() => { prefetchAllTrickplayImages(); }, []); @@ -538,11 +548,35 @@ export const Controls: FC = ({ isSeeking.value = true; }, [showControls, isPlaying, pause]); + const handleTouchStart = useCallback(() => { + if (!showControls) { + return; + } + + // Scale up the slider immediately on touch + sliderScale.value = withTiming(1.4, { duration: 300 }); + }, [showControls]); + + const handleTouchEnd = useCallback(() => { + if (!showControls) { + return; + } + + // Scale down the slider on touch end (only if not sliding, to avoid conflict with onSlidingComplete) + if (!isSliding) { + sliderScale.value = withTiming(1.0, { duration: 300 }); + } + }, [showControls, isSliding]); + const handleSliderComplete = useCallback( async (value: number) => { isSeeking.value = false; progress.value = value; setIsSliding(false); + + // Scale down the slider + sliderScale.value = withTiming(1.0, { duration: 200 }); + seek(Math.max(0, Math.floor(isVlc ? value : ticksToSeconds(value)))); if (wasPlayingRef.current) { play(); @@ -980,7 +1014,9 @@ export const Controls: FC = ({ position: "absolute", right: settings?.safeAreaInControlsEnabled ? insets.right : 0, left: settings?.safeAreaInControlsEnabled ? insets.left : 0, - bottom: settings?.safeAreaInControlsEnabled ? insets.bottom : 0, + bottom: settings?.safeAreaInControlsEnabled + ? Math.max(insets.bottom - 17, 0) + : 0, }, animatedControlsStyle, ]} @@ -1049,39 +1085,70 @@ export const Controls: FC = ({ pointerEvents={showControls ? "box-none" : "none"} > - null} - cache={cacheProgress} - onSlidingStart={handleSliderStart} - onSlidingComplete={handleSliderComplete} - onValueChange={handleSliderChange} - containerStyle={{ - borderRadius: 100, - }} - renderBubble={() => - (isSliding || showRemoteBubble) && memoizedRenderBubble() - } - sliderHeight={10} - thumbWidth={0} - progress={effectiveProgress} - minimumValue={min} - maximumValue={max} - /> + onTouchStart={handleTouchStart} + onTouchEnd={handleTouchEnd} + > + + null} + cache={cacheProgress} + onSlidingStart={handleSliderStart} + onSlidingComplete={handleSliderComplete} + onValueChange={handleSliderChange} + containerStyle={{ + borderRadius: 100, + }} + renderBubble={() => + (isSliding || showRemoteBubble) && + memoizedRenderBubble() + } + sliderHeight={10} + thumbWidth={0} + progress={effectiveProgress} + minimumValue={min} + maximumValue={max} + /> + + {formatTimeString(currentTime, isVlc ? "ms" : "s")} - - -{formatTimeString(remainingTime, isVlc ? "ms" : "s")} - + + + -{formatTimeString(remainingTime, isVlc ? "ms" : "s")} + + + ends at {(() => { + const now = new Date(); + const remainingMs = isVlc + ? remainingTime + : remainingTime * 1000; + const finishTime = new Date( + now.getTime() + remainingMs, + ); + return finishTime.toLocaleTimeString([], { + hour: "2-digit", + minute: "2-digit", + hour12: false, + }); + })()} + +