Chore/tv interface scaling (#1591)

Signed-off-by: Lance Chant <13349722+lancechant@users.noreply.github.com>
This commit is contained in:
lance chant
2026-05-24 12:55:28 +02:00
committed by GitHub
parent 52ad1a06e1
commit 4b577b8111
4 changed files with 50 additions and 10 deletions

View File

@@ -14,6 +14,7 @@ import {
} from "react";
import { useTranslation } from "react-i18next";
import {
Pressable,
StyleSheet,
TVFocusGuideView,
useWindowDimensions,
@@ -278,6 +279,9 @@ export const Controls: FC<Props> = ({
null,
);
// Ref for the invisible focus-stealing overlay (prevents hidden buttons from receiving select events)
const focusOverlayRef = useRef<View>(null);
const audioTracks = useMemo(() => {
return mediaSource?.MediaStreams?.filter((s) => s.Type === "Audio") ?? [];
}, [mediaSource]);
@@ -908,6 +912,19 @@ export const Controls: FC<Props> = ({
setFocusPlayButton(false);
}, [setShowControls]);
// When controls hide (and no skip/countdown overlay is visible), move focus
// to the invisible overlay so hidden buttons can't receive select events.
useEffect(() => {
if (!showControls && !isSkipOrCountdownVisible) {
// Small delay to let the controls fade-out animation start and
// the focus engine settle before stealing focus
const t = setTimeout(() => {
focusOverlayRef.current?.focus();
}, 100);
return () => clearTimeout(t);
}
}, [showControls, isSkipOrCountdownVisible]);
const handleBack = useCallback(() => {
router.back();
}, [router]);
@@ -1025,6 +1042,24 @@ export const Controls: FC<Props> = ({
pointerEvents='none'
/>
{/* Invisible overlay that steals focus when controls are hidden.
Prevents hidden control buttons from receiving select/enter events
from the TV remote. Pressing center button here toggles play/pause. */}
<Pressable
ref={focusOverlayRef}
style={styles.focusStealingOverlay}
pointerEvents={
showControls || isSkipOrCountdownVisible ? "none" : "auto"
}
focusable={!showControls && !isSkipOrCountdownVisible}
hasTVPreferredFocus={!showControls && !isSkipOrCountdownVisible}
onPress={() => {
togglePlay();
setShowControls(true);
setFocusPlayButton(true);
}}
/>
{getTechnicalInfo && (
<TechnicalInfoOverlay
showControls={showControls}
@@ -1177,6 +1212,7 @@ export const Controls: FC<Props> = ({
<Animated.View
style={[styles.bottomContainer, bottomAnimatedStyle]}
pointerEvents={showControls ? "auto" : "none"}
focusable={showControls}
>
<View
style={[
@@ -1379,6 +1415,10 @@ const styles = StyleSheet.create({
...StyleSheet.absoluteFillObject,
backgroundColor: "rgba(0, 0, 0, 0.4)",
},
focusStealingOverlay: {
...StyleSheet.absoluteFillObject,
zIndex: 1,
},
bottomContainer: {
position: "absolute",
bottom: 0,