fix: close button modals

This commit is contained in:
Fredrik Burmester
2026-01-18 14:14:23 +01:00
parent ec653cae15
commit 60dd00ad7e
4 changed files with 243 additions and 1 deletions

View File

@@ -10,6 +10,7 @@ import * as BackgroundTask from "expo-background-task";
import * as Device from "expo-device";
import { Platform } from "react-native";
import { GlobalModal } from "@/components/GlobalModal";
import i18n from "@/i18n";
import { DownloadProvider } from "@/providers/DownloadProvider";
import { GlobalModalProvider } from "@/providers/GlobalModalProvider";
@@ -443,7 +444,7 @@ function Layout() {
}}
closeButton
/>
<GlobalModal />
{!Platform.isTV && <GlobalModal />}
</ThemeProvider>
</IntroSheetProvider>
</BottomSheetModalProvider>

View File

@@ -25,6 +25,7 @@ import {
Pressable,
ScrollView,
TVFocusGuideView,
useTVEventHandler,
View,
} from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
@@ -301,6 +302,63 @@ const _TVOptionRowModal: React.FC<{
);
};
// Cancel button for TV option selectors
const TVCancelButton: React.FC<{ onPress: () => void }> = ({ onPress }) => {
const { t } = useTranslation();
const [focused, setFocused] = useState(false);
const scale = useRef(new Animated.Value(1)).current;
const animateTo = (v: number) =>
Animated.timing(scale, {
toValue: v,
duration: 120,
easing: Easing.out(Easing.quad),
useNativeDriver: true,
}).start();
return (
<Pressable
onPress={onPress}
onFocus={() => {
setFocused(true);
animateTo(1.05);
}}
onBlur={() => {
setFocused(false);
animateTo(1);
}}
>
<Animated.View
style={{
transform: [{ scale }],
flexDirection: "row",
alignItems: "center",
backgroundColor: focused ? "#fff" : "rgba(255,255,255,0.15)",
borderRadius: 12,
paddingVertical: 12,
paddingHorizontal: 20,
gap: 8,
}}
>
<Ionicons
name='close'
size={20}
color={focused ? "#000" : "rgba(255,255,255,0.8)"}
/>
<Text
style={{
fontSize: 16,
fontWeight: "600",
color: focused ? "#000" : "rgba(255,255,255,0.8)",
}}
>
{t("common.cancel") || "Cancel"}
</Text>
</Animated.View>
</Pressable>
);
};
// TV Option Selector - Bottom sheet with horizontal scrolling (Apple TV style)
const TVOptionSelector = <T,>({
visible,
@@ -452,6 +510,19 @@ const TVOptionSelector = <T,>({
))}
</ScrollView>
)}
{/* Cancel button */}
{isReady && (
<View
style={{
paddingHorizontal: 48,
paddingTop: 16,
alignItems: "flex-start",
}}
>
<TVCancelButton onPress={onClose} />
</View>
)}
</TVFocusGuideView>
</BlurView>
</Animated.View>
@@ -940,6 +1011,15 @@ export const ItemContentTV: React.FC<ItemContentTVProps> = React.memo(
}
}, [isModalOpen]);
// tvOS menu button handler for closing modals
// Note: This may not receive events if React Navigation intercepts them first
useTVEventHandler((evt) => {
if (!evt || !isModalOpen) return;
if (evt.eventType === "menu" || evt.eventType === "back") {
setOpenModal(null);
}
});
// Get available audio tracks
const audioTracks = useMemo(() => {
const streams = selectedOptions?.mediaSource?.MediaStreams?.filter(

View File

@@ -212,6 +212,13 @@ const TVOptionSelector = <T,>({
))}
</ScrollView>
)}
{/* Cancel button */}
{isReady && (
<View style={selectorStyles.cancelButtonContainer}>
<TVCancelButton onPress={onClose} label='Cancel' />
</View>
)}
</TVFocusGuideView>
</BlurView>
</RNAnimated.View>
@@ -219,6 +226,61 @@ const TVOptionSelector = <T,>({
);
};
// Cancel button for TV option selectors
const TVCancelButton: React.FC<{ onPress: () => void; label: string }> = ({
onPress,
label,
}) => {
const [focused, setFocused] = useState(false);
const scale = useRef(new RNAnimated.Value(1)).current;
const animateTo = (v: number) =>
RNAnimated.timing(scale, {
toValue: v,
duration: 120,
easing: RNEasing.out(RNEasing.quad),
useNativeDriver: true,
}).start();
return (
<Pressable
onPress={onPress}
onFocus={() => {
setFocused(true);
animateTo(1.05);
}}
onBlur={() => {
setFocused(false);
animateTo(1);
}}
>
<RNAnimated.View
style={[
selectorStyles.cancelButton,
{
transform: [{ scale }],
backgroundColor: focused ? "#fff" : "rgba(255,255,255,0.15)",
},
]}
>
<Ionicons
name='close'
size={20}
color={focused ? "#000" : "rgba(255,255,255,0.8)"}
/>
<Text
style={[
selectorStyles.cancelButtonText,
{ color: focused ? "#000" : "rgba(255,255,255,0.8)" },
]}
>
{label}
</Text>
</RNAnimated.View>
</Pressable>
);
};
// Option card for horizontal selector (with forwardRef for programmatic focus)
const TVOptionCard = React.forwardRef<
View,
@@ -663,6 +725,23 @@ const selectorStyles = StyleSheet.create({
tabText: {
fontSize: 18,
},
cancelButtonContainer: {
paddingHorizontal: 48,
paddingTop: 16,
alignItems: "flex-start",
},
cancelButton: {
flexDirection: "row",
alignItems: "center",
borderRadius: 12,
paddingVertical: 12,
paddingHorizontal: 20,
gap: 8,
},
cancelButtonText: {
fontSize: 16,
fontWeight: "600",
},
});
// TV Next Episode Countdown component - horizontal layout with animated progress bar

View File

@@ -474,6 +474,61 @@ const SubtitleResultCard = React.forwardRef<
);
});
// Cancel button for TV subtitle sheet
const TVCancelButton: React.FC<{ onPress: () => void; label: string }> = ({
onPress,
label,
}) => {
const [focused, setFocused] = useState(false);
const scale = useRef(new RNAnimated.Value(1)).current;
const animateTo = (v: number) =>
RNAnimated.timing(scale, {
toValue: v,
duration: 120,
easing: RNEasing.out(RNEasing.quad),
useNativeDriver: true,
}).start();
return (
<Pressable
onPress={onPress}
onFocus={() => {
setFocused(true);
animateTo(1.05);
}}
onBlur={() => {
setFocused(false);
animateTo(1);
}}
>
<RNAnimated.View
style={[
styles.cancelButton,
{
transform: [{ scale }],
backgroundColor: focused ? "#fff" : "rgba(255,255,255,0.15)",
},
]}
>
<Ionicons
name='close'
size={20}
color={focused ? "#000" : "rgba(255,255,255,0.8)"}
/>
<Text
style={[
styles.cancelButtonText,
{ color: focused ? "#000" : "rgba(255,255,255,0.8)" },
]}
>
{label}
</Text>
</RNAnimated.View>
</Pressable>
);
};
export const TVSubtitleSheet: React.FC<TVSubtitleSheetProps> = ({
visible,
item,
@@ -847,6 +902,16 @@ export const TVSubtitleSheet: React.FC<TVSubtitleSheetProps> = ({
)}
</>
)}
{/* Cancel button */}
{isReady && (
<View style={styles.cancelButtonContainer}>
<TVCancelButton
onPress={onClose}
label={t("common.cancel") || "Cancel"}
/>
</View>
)}
</TVFocusGuideView>
</BlurView>
</RNAnimated.View>
@@ -1086,4 +1151,21 @@ const styles = StyleSheet.create({
color: "rgba(255,255,255,0.4)",
fontSize: 12,
},
cancelButtonContainer: {
paddingHorizontal: 48,
paddingTop: 20,
alignItems: "flex-start",
},
cancelButton: {
flexDirection: "row",
alignItems: "center",
borderRadius: 12,
paddingVertical: 12,
paddingHorizontal: 20,
gap: 8,
},
cancelButtonText: {
fontSize: 16,
fontWeight: "600",
},
});