From c3a9b451b6a3e30579902d635d47a2f54788faae Mon Sep 17 00:00:00 2001 From: Uruk Date: Fri, 22 May 2026 09:55:23 +0200 Subject: [PATCH] fix(casting): clamp trickplay bubble via slider bubbleWidth --- app/(auth)/casting-player.tsx | 4 - .../casting/player/CastPlayerProgressBar.tsx | 158 ++---------------- hooks/useCastPlayerProgress.ts | 9 - 3 files changed, 13 insertions(+), 158 deletions(-) diff --git a/app/(auth)/casting-player.tsx b/app/(auth)/casting-player.tsx index 9a9543b43..cc56404b6 100644 --- a/app/(auth)/casting-player.tsx +++ b/app/(auth)/casting-player.tsx @@ -85,8 +85,6 @@ export default function CastingPlayerScreen() { isScrubbing, trickplayTime, setTrickplayTime, - scrubPercentage, - setScrubPercentage, progress, resumePositionRef, trickPlayUrl, @@ -538,8 +536,6 @@ export default function CastingPlayerScreen() { sliderMin={sliderMin} sliderMax={sliderMax} isScrubbing={isScrubbing} - scrubPercentage={scrubPercentage} - setScrubPercentage={setScrubPercentage} trickplayTime={trickplayTime} setTrickplayTime={setTrickplayTime} trickPlayUrl={trickPlayUrl} diff --git a/components/casting/player/CastPlayerProgressBar.tsx b/components/casting/player/CastPlayerProgressBar.tsx index 14d642551..ea6e96729 100644 --- a/components/casting/player/CastPlayerProgressBar.tsx +++ b/components/casting/player/CastPlayerProgressBar.tsx @@ -3,18 +3,14 @@ * Progress slider with trickplay preview bubble and current/end time display. */ -import { Image } from "expo-image"; import type { TFunction } from "i18next"; -import { Dimensions, Text, View } from "react-native"; +import { Text, View } from "react-native"; import { Slider } from "react-native-awesome-slider"; import type { RemoteMediaClient } from "react-native-google-cast"; import type { SharedValue } from "react-native-reanimated"; +import { CastTrickplayBubble } from "@/components/casting/player/CastTrickplayBubble"; import type { useTrickplay } from "@/hooks/useTrickplay"; -import { - calculateEndingTime, - formatTime, - formatTrickplayTime, -} from "@/utils/casting/helpers"; +import { calculateEndingTime, formatTime } from "@/utils/casting/helpers"; import { msToTicks, ticksToSeconds } from "@/utils/time"; type TrickplayReturn = ReturnType; @@ -28,10 +24,6 @@ interface CastPlayerProgressBarProps { sliderMax: SharedValue; /** Mutable ref flag set true while the user is scrubbing. */ isScrubbing: { current: boolean }; - /** Current scrub percentage (0-1), used to position the trickplay bubble. */ - scrubPercentage: number; - /** Updates the scrub percentage. */ - setScrubPercentage: (value: number) => void; /** Trickplay time display state for the bubble. */ trickplayTime: { hours: number; minutes: number; seconds: number }; /** Updates the trickplay time display state. */ @@ -63,8 +55,6 @@ export function CastPlayerProgressBar({ sliderMin, sliderMax, isScrubbing, - scrubPercentage, - setScrubPercentage, trickplayTime, setTrickplayTime, trickPlayUrl, @@ -107,12 +97,6 @@ export function CastPlayerProgressBar({ const minutes = Math.floor((progressInSeconds % 3600) / 60); const seconds = progressInSeconds % 60; setTrickplayTime({ hours, minutes, seconds }); - - // Track scrub percentage for bubble positioning - const durationMs = duration * 1000; - if (durationMs > 0) { - setScrubPercentage(value / durationMs); - } }} onSlidingComplete={(value) => { isScrubbing.current = false; @@ -126,134 +110,18 @@ export function CastPlayerProgressBar({ }); } }} - renderBubble={() => { - // Calculate bubble position with edge clamping - const screenWidth = Dimensions.get("window").width; - const containerPadding = 20; // left/right padding of slider container (matches style) - const thumbWidth = 16; // matches thumbWidth prop on Slider - const sliderWidth = screenWidth - containerPadding * 2; - // Adjust thumb position to account for thumb width affecting travel range - const effectiveTrackWidth = sliderWidth - thumbWidth; - const thumbPosition = - thumbWidth / 2 + scrubPercentage * effectiveTrackWidth; - - if (!trickPlayUrl || !trickplayInfo) { - // Show simple time bubble when no trickplay - const timeBubbleWidth = 80; - // Clamp position so bubble stays on screen - // minLeft prevents going off left edge, maxLeft prevents going off right edge - const minLeft = -thumbPosition; - const maxLeft = sliderWidth - thumbPosition - timeBubbleWidth; - const centeredLeft = -timeBubbleWidth / 2; - const clampedLeft = Math.max( - minLeft, - Math.min(maxLeft, centeredLeft), - ); - - return ( - - - {formatTrickplayTime(trickplayTime)} - - - ); - } - - const { x, y, url } = trickPlayUrl; - const tileWidth = 220; // Larger preview for casting player - const tileHeight = tileWidth / (trickplayInfo.aspectRatio ?? 1.78); - - // Calculate clamped position for trickplay preview - // minLeft: furthest left (when thumb is at left edge) - // maxLeft: furthest right (when thumb is at right edge) - const minLeft = -thumbPosition; - const maxLeft = sliderWidth - thumbPosition - tileWidth; - const centeredLeft = -tileWidth / 2; - const clampedLeft = Math.max( - minLeft, - Math.min(maxLeft, centeredLeft), - ); - - return ( - - {/* Trickplay image preview */} - - - - {/* Time overlay */} - - - {formatTrickplayTime(trickplayTime)} - - - - ); - }} + renderBubble={() => ( + + )} + bubbleWidth={trickPlayUrl && trickplayInfo ? 220 : 64} sliderHeight={6} thumbWidth={16} - panHitSlop={{ top: 30, bottom: 30, left: 10, right: 10 }} + panHitSlop={{ top: 12, bottom: 12, left: 10, right: 10 }} /> diff --git a/hooks/useCastPlayerProgress.ts b/hooks/useCastPlayerProgress.ts index af1187657..9398997f7 100644 --- a/hooks/useCastPlayerProgress.ts +++ b/hooks/useCastPlayerProgress.ts @@ -34,10 +34,6 @@ interface UseCastPlayerProgressResult { trickplayTime: TrickplayTime; /** Updates the trickplay time display state. */ setTrickplayTime: (time: TrickplayTime) => void; - /** Current scrub percentage (0-1), used to position the trickplay bubble. */ - scrubPercentage: number; - /** Updates the scrub percentage. */ - setScrubPercentage: (value: number) => void; /** Current playback progress, in seconds (live-updating). */ progress: number; /** Last stable playback position (seconds), for resuming across reloads. */ @@ -73,9 +69,6 @@ export function useCastPlayerProgress({ seconds: 0, }); - // Track scrub percentage for trickplay bubble positioning - const [scrubPercentage, setScrubPercentage] = useState(0); - // Live progress tracking - update every second const [liveProgress, setLiveProgress] = useState(0); const lastSyncPositionRef = useRef(0); @@ -146,8 +139,6 @@ export function useCastPlayerProgress({ isScrubbing, trickplayTime, setTrickplayTime, - scrubPercentage, - setScrubPercentage, progress, resumePositionRef, trickPlayUrl,