mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-04-28 11:26:29 +01:00
fix(tv): improve skip/countdown focus and back button handling
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useState } from "react";
|
||||
import { Platform } from "react-native";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Alert, BackHandler, Platform } from "react-native";
|
||||
import { type SharedValue, useSharedValue } from "react-native-reanimated";
|
||||
|
||||
// TV event handler with fallback for non-TV platforms
|
||||
@@ -23,6 +23,10 @@ interface UseRemoteControlProps {
|
||||
disableSeeking?: boolean;
|
||||
/** Callback for back/menu button press (tvOS: menu, Android TV: back) */
|
||||
onBack?: () => void;
|
||||
/** Callback to hide controls (called on back press when controls are visible) */
|
||||
onHideControls?: () => void;
|
||||
/** Title of the video being played (shown in exit confirmation) */
|
||||
videoTitle?: string;
|
||||
/** Whether the progress bar currently has focus */
|
||||
isProgressBarFocused?: boolean;
|
||||
/** Callback for seeking left when progress bar is focused */
|
||||
@@ -69,6 +73,8 @@ export function useRemoteControl({
|
||||
toggleControls,
|
||||
togglePlay,
|
||||
onBack,
|
||||
onHideControls,
|
||||
videoTitle,
|
||||
isProgressBarFocused,
|
||||
onSeekLeft,
|
||||
onSeekRight,
|
||||
@@ -87,14 +93,73 @@ export function useRemoteControl({
|
||||
const [isSliding] = useState(false);
|
||||
const [time] = useState({ hours: 0, minutes: 0, seconds: 0 });
|
||||
|
||||
// Use refs to avoid stale closures in BackHandler
|
||||
const showControlsRef = useRef(showControls);
|
||||
const onHideControlsRef = useRef(onHideControls);
|
||||
const onBackRef = useRef(onBack);
|
||||
const videoTitleRef = useRef(videoTitle);
|
||||
|
||||
useEffect(() => {
|
||||
showControlsRef.current = showControls;
|
||||
onHideControlsRef.current = onHideControls;
|
||||
onBackRef.current = onBack;
|
||||
videoTitleRef.current = videoTitle;
|
||||
}, [showControls, onHideControls, onBack, videoTitle]);
|
||||
|
||||
// Handle hardware back button (works on both Android TV and tvOS)
|
||||
useEffect(() => {
|
||||
if (!Platform.isTV) return;
|
||||
|
||||
const handleBackPress = () => {
|
||||
if (showControlsRef.current && onHideControlsRef.current) {
|
||||
// Controls are visible - just hide them
|
||||
onHideControlsRef.current();
|
||||
return true; // Prevent default back navigation
|
||||
}
|
||||
if (onBackRef.current) {
|
||||
// Controls are hidden - show confirmation before exiting
|
||||
Alert.alert(
|
||||
"Stop Playback",
|
||||
videoTitleRef.current
|
||||
? `Stop playing "${videoTitleRef.current}"?`
|
||||
: "Are you sure you want to stop playback?",
|
||||
[
|
||||
{ text: "Cancel", style: "cancel" },
|
||||
{ text: "Stop", style: "destructive", onPress: onBackRef.current },
|
||||
],
|
||||
);
|
||||
return true; // Prevent default back navigation
|
||||
}
|
||||
return false; // Let default back navigation happen
|
||||
};
|
||||
|
||||
const subscription = BackHandler.addEventListener(
|
||||
"hardwareBackPress",
|
||||
handleBackPress,
|
||||
);
|
||||
|
||||
return () => subscription.remove();
|
||||
}, []);
|
||||
|
||||
// TV remote control handling (no-op on non-TV platforms)
|
||||
useTVEventHandler((evt) => {
|
||||
if (!evt) return;
|
||||
|
||||
// Handle back/menu button press (tvOS: menu, Android TV: back)
|
||||
if (evt.eventType === "menu" || evt.eventType === "back") {
|
||||
if (onBack) {
|
||||
onBack();
|
||||
// Back/menu is handled by BackHandler above, but keep this for tvOS menu button
|
||||
if (evt.eventType === "menu") {
|
||||
if (showControls && onHideControls) {
|
||||
onHideControls();
|
||||
} else if (onBack) {
|
||||
Alert.alert(
|
||||
"Stop Playback",
|
||||
videoTitle
|
||||
? `Stop playing "${videoTitle}"?`
|
||||
: "Are you sure you want to stop playback?",
|
||||
[
|
||||
{ text: "Cancel", style: "cancel" },
|
||||
{ text: "Stop", style: "destructive", onPress: onBack },
|
||||
],
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -154,8 +219,8 @@ export function useRemoteControl({
|
||||
onVerticalDpad();
|
||||
return;
|
||||
}
|
||||
// For other D-pad presses, show full controls
|
||||
toggleControls();
|
||||
// Ignore all other events (focus/blur, swipes, etc.)
|
||||
// User can press up/down to show controls
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user