fix(tv): improve skip/countdown focus and back button handling

This commit is contained in:
Fredrik Burmester
2026-02-01 14:03:20 +01:00
parent 2775075187
commit fb7cee7718
5 changed files with 199 additions and 162 deletions

View File

@@ -31,8 +31,6 @@ export interface TVNextEpisodeCountdownProps {
onFinish: () => void;
/** Called when user presses the card to skip to next episode */
onPlayNext?: () => void;
/** Whether this card should capture focus when visible */
hasFocus?: boolean;
/** Whether controls are visible - affects card position */
controlsVisible?: boolean;
}
@@ -48,7 +46,6 @@ export const TVNextEpisodeCountdown: FC<TVNextEpisodeCountdownProps> = ({
isPlaying,
onFinish,
onPlayNext,
hasFocus = false,
controlsVisible = false,
}) => {
const typography = useScaledTVTypography();
@@ -141,7 +138,7 @@ export const TVNextEpisodeCountdown: FC<TVNextEpisodeCountdownProps> = ({
onPress={onPlayNext}
onFocus={handleFocus}
onBlur={handleBlur}
hasTVPreferredFocus={hasFocus}
hasTVPreferredFocus={true}
focusable={true}
>
<RNAnimated.View style={[animatedStyle, focused && styles.focusedCard]}>

View File

@@ -1,12 +1,8 @@
import { Ionicons } from "@expo/vector-icons";
import { type FC, useEffect, useRef } from "react";
import type { FC } from "react";
import { useEffect } from "react";
import { useTranslation } from "react-i18next";
import {
Pressable,
Animated as RNAnimated,
StyleSheet,
View,
} from "react-native";
import { Pressable, Animated as RNAnimated, StyleSheet } from "react-native";
import Animated, {
Easing,
useAnimatedStyle,
@@ -20,8 +16,6 @@ export interface TVSkipSegmentCardProps {
show: boolean;
onPress: () => void;
type: "intro" | "credits";
/** Whether this card should capture focus when visible */
hasFocus?: boolean;
/** Whether controls are visible - affects card position */
controlsVisible?: boolean;
}
@@ -34,30 +28,15 @@ export const TVSkipSegmentCard: FC<TVSkipSegmentCardProps> = ({
show,
onPress,
type,
hasFocus = false,
controlsVisible = false,
}) => {
const { t } = useTranslation();
const pressableRef = useRef<View>(null);
const { focused, handleFocus, handleBlur, animatedStyle } =
useTVFocusAnimation({
scaleAmount: 1.1,
duration: 120,
});
// Programmatically request focus when card appears with hasFocus=true
useEffect(() => {
if (!show || !hasFocus || !pressableRef.current) return;
const timer = setTimeout(() => {
// Use setNativeProps to trigger focus update on tvOS
(pressableRef.current as any)?.setNativeProps?.({
hasTVPreferredFocus: true,
});
}, 50);
return () => clearTimeout(timer);
}, [show, hasFocus]);
// Animated position based on controls visibility
const bottomPosition = useSharedValue(
controlsVisible ? BOTTOM_WITH_CONTROLS : BOTTOM_WITHOUT_CONTROLS,
@@ -88,11 +67,10 @@ export const TVSkipSegmentCard: FC<TVSkipSegmentCardProps> = ({
pointerEvents='box-none'
>
<Pressable
ref={pressableRef}
onPress={onPress}
onFocus={handleFocus}
onBlur={handleBlur}
hasTVPreferredFocus={hasFocus}
hasTVPreferredFocus={true}
>
<RNAnimated.View
style={[