From 7b2f7b9df040c710d038115092ced51167c86fbd Mon Sep 17 00:00:00 2001 From: Eli Mallon Date: Tue, 2 Jun 2026 13:20:51 -0700 Subject: [PATCH] vod: disable native controls; fit video in fullscreen/landscape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turn expo-video's nativeControls off entirely — we render our own controls, and even briefly enabling them flashed the native overlay. Fix the video clipping off the bottom in fullscreen/landscape: a width-100% + aspectRatio box is taller than a landscape screen. When fullscreen or the device is landscape, fill the area with flex:1 and let objectFit:contain letterbox the video to fit. Portrait inline keeps the aspect-ratio box (video on top, metadata below). Co-Authored-By: Claude Opus 4.8 --- js/app/components/mobile/player.tsx | 28 +++++++++++++------ .../mobile-player/video-async.native.tsx | 5 ++-- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/js/app/components/mobile/player.tsx b/js/app/components/mobile/player.tsx index 264b17bc..f107a048 100644 --- a/js/app/components/mobile/player.tsx +++ b/js/app/components/mobile/player.tsx @@ -393,6 +393,12 @@ export function PlayerInner( const showFullDesktopMode = aspectRatio > 1 && screenWidth > 1200; const isLandscape = aspectRatio > 1; + // When fullscreen or the device is rotated to landscape, a width-100% + + // aspectRatio VOD box is taller than the screen and clips off the bottom. + // In those cases fill the area and let objectFit:contain letterbox instead. + const { width: winWidth, height: winHeight } = useWindowDimensions(); + const vodFillScreen = fullscreen || winWidth > winHeight; + const isPlayerRatioGreater = aspectRatio >= 16 / 9; // animated style for offline height transition @@ -434,15 +440,19 @@ export function PlayerInner( width: calculatedWidth, } : props.mode === "vod" - ? { - // Bound the video to its real aspect ratio so it occupies a - // fixed height with the metadata/comments below — never the - // whole window. (A pixel height derived from contentWidth - // collapsed to full-window on Android when contentWidth - // measured 0.) - width: "100%" as any, - aspectRatio: vodAspectRatio, - } + ? vodFillScreen + ? // Fullscreen/landscape: fill the area; objectFit:contain + // letterboxes so the video fits without clipping. + { flex: 1 } + : { + // Portrait inline: bound the video to its real aspect ratio + // so it occupies a fixed height with the metadata below — + // never the whole window. (A pixel height derived from + // contentWidth collapsed to full-window on Android when + // contentWidth measured 0.) + width: "100%" as any, + aspectRatio: vodAspectRatio, + } : { flex: 1, maxHeight: "auto", diff --git a/js/components/src/components/mobile-player/video-async.native.tsx b/js/components/src/components/mobile-player/video-async.native.tsx index 35768474..071b93ba 100644 --- a/js/components/src/components/mobile-player/video-async.native.tsx +++ b/js/components/src/components/mobile-player/video-async.native.tsx @@ -80,7 +80,6 @@ export function NativeVideo(props?: { const muted = useMuted(); const volume = useEffectiveVolume(); const setFullscreen = usePlayerStore((x) => x.setFullscreen); - const fullscreen = usePlayerStore((x) => x.fullscreen); const playerEvent = usePlayerStore((x) => x.playerEvent); const spurl = useStreamplaceStore((x) => x.url); @@ -226,7 +225,9 @@ export function NativeVideo(props?: { { setFullscreen(true); }} -- 2.51.2