From 5ede3f30d088bbfed7a5e9dbbdb566df3162540f Mon Sep 17 00:00:00 2001 From: Lance Chant <13349722+lancechant@users.noreply.github.com> Date: Mon, 25 May 2026 15:12:44 +0200 Subject: [PATCH] chore: more scaling fixes and selection improve Fixed the scaling in the direct player controls to use the scaleTV settings Fixed 2 items in settings not being selectable (added style:flex:1) Signed-off-by: Lance Chant <13349722+lancechant@users.noreply.github.com> --- app/(auth)/tv-option-modal.tsx | 48 ++- app/(auth)/tv-subtitle-modal.tsx | 312 +++++++++++-------- components/tv/TVOptionCard.tsx | 2 +- components/tv/settings/TVSettingsStepper.tsx | 1 + 4 files changed, 216 insertions(+), 147 deletions(-) diff --git a/app/(auth)/tv-option-modal.tsx b/app/(auth)/tv-option-modal.tsx index 330885a1..180228e3 100644 --- a/app/(auth)/tv-option-modal.tsx +++ b/app/(auth)/tv-option-modal.tsx @@ -1,6 +1,6 @@ import { BlurView } from "expo-blur"; import { useAtomValue } from "jotai"; -import { useEffect, useMemo, useRef, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Animated, Easing, @@ -11,13 +11,17 @@ import { } from "react-native"; import { Text } from "@/components/common/Text"; import { TVOptionCard } from "@/components/tv"; +import { useScaledTVTypography } from "@/constants/TVTypography"; import useRouter from "@/hooks/useAppRouter"; +import { useTVBackPress } from "@/hooks/useTVBackPress"; import { tvOptionModalAtom } from "@/utils/atoms/tvOptionModal"; +import { scaleSize } from "@/utils/scaleSize"; import { store } from "@/utils/store"; export default function TVOptionModal() { const router = useRouter(); const modalState = useAtomValue(tvOptionModalAtom); + const typography = useScaledTVTypography(); const [isReady, setIsReady] = useState(false); const firstCardRef = useRef(null); @@ -76,12 +80,25 @@ export default function TVOptionModal() { router.back(); }; + const handleClose = useCallback(() => { + store.set(tvOptionModalAtom, null); + router.back(); + }, [router]); + + // Intercept back/menu press to close the modal instead of the player + useTVBackPress(() => { + handleClose(); + return true; + }, [handleClose]); + // If no modal state, just go back (shouldn't happen in normal usage) if (!modalState) { return null; } - const { title, options, cardWidth = 160, cardHeight = 75 } = modalState; + const { title, options } = modalState; + const scaledCardWidth = scaleSize(160); + const scaledCardHeight = scaleSize(75); return ( @@ -100,7 +117,9 @@ export default function TVOptionModal() { trapFocusRight style={styles.content} > - {title} + + {title} + {isReady && ( handleSelect(option.value)} - width={cardWidth} - height={cardHeight} + width={scaledCardWidth} + height={scaledCardHeight} /> ))} @@ -142,21 +161,20 @@ const styles = StyleSheet.create({ width: "100%", }, blurContainer: { - borderTopLeftRadius: 24, - borderTopRightRadius: 24, + borderTopLeftRadius: scaleSize(24), + borderTopRightRadius: scaleSize(24), overflow: "hidden", }, content: { - paddingTop: 24, - paddingBottom: 50, + paddingTop: scaleSize(24), + paddingBottom: scaleSize(50), overflow: "visible", }, title: { - fontSize: 18, fontWeight: "500", color: "rgba(255,255,255,0.6)", - marginBottom: 16, - paddingHorizontal: 48, + marginBottom: scaleSize(16), + paddingHorizontal: scaleSize(48), textTransform: "uppercase", letterSpacing: 1, }, @@ -164,8 +182,8 @@ const styles = StyleSheet.create({ overflow: "visible", }, scrollContent: { - paddingHorizontal: 48, - paddingVertical: 20, - gap: 12, + paddingHorizontal: scaleSize(48), + paddingVertical: scaleSize(20), + gap: scaleSize(12), }, }); diff --git a/app/(auth)/tv-subtitle-modal.tsx b/app/(auth)/tv-subtitle-modal.tsx index e597a782..c59952ba 100644 --- a/app/(auth)/tv-subtitle-modal.tsx +++ b/app/(auth)/tv-subtitle-modal.tsx @@ -22,14 +22,17 @@ import { import { Text } from "@/components/common/Text"; import { TVTabButton, useTVFocusAnimation } from "@/components/tv"; import type { Track } from "@/components/video-player/controls/types"; +import { useScaledTVTypography } from "@/constants/TVTypography"; import useRouter from "@/hooks/useAppRouter"; import { type SubtitleSearchResult, useRemoteSubtitles, } from "@/hooks/useRemoteSubtitles"; +import { useTVBackPress } from "@/hooks/useTVBackPress"; import { useSettings } from "@/utils/atoms/settings"; import { tvSubtitleModalAtom } from "@/utils/atoms/tvSubtitleModal"; import { COMMON_SUBTITLE_LANGUAGES } from "@/utils/opensubtitles/api"; +import { scaleSize } from "@/utils/scaleSize"; import { store } from "@/utils/store"; type TabType = "tracks" | "download" | "settings"; @@ -72,10 +75,10 @@ const TVTrackCard = React.forwardRef< {label} @@ -83,7 +86,10 @@ const TVTrackCard = React.forwardRef< @@ -94,7 +100,7 @@ const TVTrackCard = React.forwardRef< @@ -142,7 +148,7 @@ const LanguageCard = React.forwardRef< {code.toUpperCase()} @@ -161,7 +170,7 @@ const LanguageCard = React.forwardRef< @@ -219,7 +228,10 @@ const SubtitleResultCard = React.forwardRef< {result.providerName} @@ -228,7 +240,10 @@ const SubtitleResultCard = React.forwardRef< {/* Name */} {result.name} @@ -240,7 +255,10 @@ const SubtitleResultCard = React.forwardRef< {result.format?.toUpperCase()} @@ -252,7 +270,7 @@ const SubtitleResultCard = React.forwardRef< @@ -275,7 +294,7 @@ const SubtitleResultCard = React.forwardRef< @@ -307,7 +327,9 @@ const SubtitleResultCard = React.forwardRef< }, ]} > - Hash Match + + Hash Match + )} {result.hearingImpaired && ( @@ -323,7 +345,7 @@ const SubtitleResultCard = React.forwardRef< > @@ -339,7 +361,9 @@ const SubtitleResultCard = React.forwardRef< }, ]} > - AI + + AI + )} @@ -389,7 +413,7 @@ const TVStepperButton: React.FC<{ > @@ -485,7 +509,7 @@ const TVAlignmentCard: React.FC<{ @@ -495,7 +519,7 @@ const TVAlignmentCard: React.FC<{ @@ -510,6 +534,7 @@ export default function TVSubtitleModal() { const { t } = useTranslation(); const modalState = useAtomValue(tvSubtitleModalAtom); const { settings, updateSettings } = useSettings(); + const typography = useScaledTVTypography(); const [activeTab, setActiveTab] = useState("tracks"); const [selectedLanguage, setSelectedLanguage] = useState("eng"); @@ -604,6 +629,12 @@ export default function TVSubtitleModal() { router.back(); }, [router]); + // Intercept back/menu press to close the modal instead of the player + useTVBackPress(() => { + handleClose(); + return true; + }, [handleClose]); + const handleLanguageSelect = useCallback( (code: string) => { setSelectedLanguage(code); @@ -745,7 +776,7 @@ export default function TVSubtitleModal() { > {/* Header with tabs */} - + {t("item_card.subtitles.label") || "Subtitles"} @@ -802,7 +833,9 @@ export default function TVSubtitleModal() { <> {/* Language Selector */} - + {t("player.language") || "Language"} - + {t("player.results") || "Results"} {searchResults && ` (${searchResults.length})`} @@ -846,13 +881,17 @@ export default function TVSubtitleModal() { - + {t("player.search_failed") || "Search failed"} - + {!hasOpenSubtitlesApiKey ? t("player.no_subtitle_provider") || "No subtitle provider configured on server" @@ -869,10 +908,15 @@ export default function TVSubtitleModal() { - + {t("player.no_subtitles_found") || "No subtitles found"} @@ -907,10 +951,15 @@ export default function TVSubtitleModal() { - + {t("player.add_opensubtitles_key_hint") || "Add OpenSubtitles API key in settings for client-side fallback"} @@ -942,7 +991,12 @@ export default function TVSubtitleModal() { }} hasTVPreferredFocus={true} /> - + {t("home.settings.subtitles.mpv_subtitle_scale") || "Subtitle Scale"} @@ -960,7 +1014,12 @@ export default function TVSubtitleModal() { updateSettings({ mpvSubtitleMarginY: newValue }); }} /> - + {t("home.settings.subtitles.mpv_subtitle_margin_y") || "Vertical Margin"} @@ -984,7 +1043,12 @@ export default function TVSubtitleModal() { /> ))} - + {t("home.settings.subtitles.mpv_subtitle_align_x") || "Horizontal Align"} @@ -1008,7 +1072,12 @@ export default function TVSubtitleModal() { /> ))} - + {t("home.settings.subtitles.mpv_subtitle_align_y") || "Vertical Align"} @@ -1033,218 +1102,201 @@ const styles = StyleSheet.create({ maxHeight: "70%", }, blurContainer: { - borderTopLeftRadius: 24, - borderTopRightRadius: 24, + borderTopLeftRadius: scaleSize(24), + borderTopRightRadius: scaleSize(24), overflow: "hidden", }, content: { - paddingTop: 24, - paddingBottom: 48, + paddingTop: scaleSize(24), + paddingBottom: scaleSize(48), }, header: { - paddingHorizontal: 48, - marginBottom: 20, + paddingHorizontal: scaleSize(48), + marginBottom: scaleSize(20), }, title: { - fontSize: 24, fontWeight: "600", color: "#fff", - marginBottom: 16, + marginBottom: scaleSize(16), }, tabRow: { flexDirection: "row", - gap: 24, + gap: scaleSize(24), }, section: { - marginBottom: 20, + marginBottom: scaleSize(20), }, sectionTitle: { - fontSize: 14, fontWeight: "500", color: "rgba(255,255,255,0.5)", textTransform: "uppercase", letterSpacing: 1, - marginBottom: 12, - paddingHorizontal: 48, + marginBottom: scaleSize(12), + paddingHorizontal: scaleSize(48), }, tracksScroll: { overflow: "visible", }, tracksScrollContent: { - paddingHorizontal: 48, - paddingVertical: 8, - gap: 12, + paddingHorizontal: scaleSize(48), + paddingVertical: scaleSize(8), + gap: scaleSize(12), }, trackCard: { - width: 180, - height: 80, - borderRadius: 14, + width: scaleSize(180), + height: scaleSize(80), + borderRadius: scaleSize(14), justifyContent: "center", alignItems: "center", - paddingHorizontal: 12, + paddingHorizontal: scaleSize(12), }, trackCardText: { - fontSize: 16, textAlign: "center", }, trackCardSublabel: { - fontSize: 12, - marginTop: 2, + marginTop: scaleSize(2), }, checkmark: { position: "absolute", - top: 8, - right: 8, + top: scaleSize(8), + right: scaleSize(8), }, languageScroll: { overflow: "visible", }, languageScrollContent: { - paddingHorizontal: 48, - paddingVertical: 8, - gap: 10, + paddingHorizontal: scaleSize(48), + paddingVertical: scaleSize(8), + gap: scaleSize(10), }, languageCard: { - width: 120, - height: 60, - borderRadius: 12, + width: scaleSize(120), + height: scaleSize(60), + borderRadius: scaleSize(12), justifyContent: "center", alignItems: "center", - paddingHorizontal: 12, + paddingHorizontal: scaleSize(12), }, languageCardText: { - fontSize: 15, fontWeight: "500", }, languageCardCode: { - fontSize: 11, - marginTop: 2, + marginTop: scaleSize(2), }, resultsScroll: { overflow: "visible", }, resultsScrollContent: { - paddingHorizontal: 48, - paddingVertical: 8, - gap: 12, + paddingHorizontal: scaleSize(48), + paddingVertical: scaleSize(8), + gap: scaleSize(12), }, resultCard: { - width: 220, - height: 130, - borderRadius: 14, - padding: 14, + width: scaleSize(220), + height: scaleSize(130), + borderRadius: scaleSize(14), + padding: scaleSize(14), borderWidth: 1, overflow: "hidden", }, providerBadge: { alignSelf: "flex-start", - paddingHorizontal: 8, - paddingVertical: 3, - borderRadius: 6, - marginBottom: 8, + paddingHorizontal: scaleSize(8), + paddingVertical: scaleSize(3), + borderRadius: scaleSize(6), + marginBottom: scaleSize(8), }, providerText: { - fontSize: 11, fontWeight: "600", textTransform: "uppercase", letterSpacing: 0.5, }, resultName: { - fontSize: 14, fontWeight: "500", - marginBottom: 8, - lineHeight: 18, + marginBottom: scaleSize(8), + lineHeight: scaleSize(18), }, resultMeta: { flexDirection: "row", alignItems: "center", - gap: 12, - marginBottom: 8, - }, - resultMetaText: { - fontSize: 12, + gap: scaleSize(12), + marginBottom: scaleSize(8), }, + resultMetaText: {}, ratingContainer: { flexDirection: "row", alignItems: "center", - gap: 3, + gap: scaleSize(3), }, downloadCountContainer: { flexDirection: "row", alignItems: "center", - gap: 3, + gap: scaleSize(3), }, flagsContainer: { flexDirection: "row", - gap: 6, + gap: scaleSize(6), flexWrap: "wrap", }, flag: { - paddingHorizontal: 6, - paddingVertical: 2, - borderRadius: 4, + paddingHorizontal: scaleSize(6), + paddingVertical: scaleSize(2), + borderRadius: scaleSize(4), }, flagText: { - fontSize: 10, fontWeight: "600", color: "#fff", }, downloadingOverlay: { ...StyleSheet.absoluteFillObject, backgroundColor: "rgba(0,0,0,0.5)", - borderRadius: 14, + borderRadius: scaleSize(14), justifyContent: "center", alignItems: "center", }, loadingContainer: { - paddingVertical: 20, + paddingVertical: scaleSize(20), alignItems: "center", }, errorContainer: { - paddingVertical: 40, - paddingHorizontal: 48, + paddingVertical: scaleSize(40), + paddingHorizontal: scaleSize(48), alignItems: "center", }, errorText: { color: "rgba(255,100,100,0.9)", - marginTop: 8, - fontSize: 16, + marginTop: scaleSize(8), fontWeight: "500", }, errorHint: { color: "rgba(255,255,255,0.5)", - marginTop: 4, - fontSize: 13, + marginTop: scaleSize(4), textAlign: "center", }, emptyContainer: { - paddingVertical: 40, + paddingVertical: scaleSize(40), alignItems: "center", }, emptyText: { color: "rgba(255,255,255,0.5)", - marginTop: 8, - fontSize: 14, + marginTop: scaleSize(8), }, apiKeyHint: { flexDirection: "row", alignItems: "center", - gap: 8, - paddingHorizontal: 48, - paddingTop: 8, - }, - apiKeyHintText: { - color: "rgba(255,255,255,0.4)", - fontSize: 12, + gap: scaleSize(8), + paddingHorizontal: scaleSize(48), + paddingTop: scaleSize(8), }, + apiKeyHintText: {}, // Settings tab styles settingsScroll: { - maxHeight: 300, + maxHeight: scaleSize(300), }, settingsScrollContent: { - paddingHorizontal: 48, - paddingVertical: 8, - gap: 24, + paddingHorizontal: scaleSize(48), + paddingVertical: scaleSize(8), + gap: scaleSize(24), }, settingRow: { flexDirection: "row", @@ -1252,49 +1304,47 @@ const styles = StyleSheet.create({ justifyContent: "space-between", }, settingLabel: { - fontSize: 18, fontWeight: "500", color: "#fff", }, sizeControlContainer: { flexDirection: "row", alignItems: "center", - gap: 16, + gap: scaleSize(16), }, stepperButton: { - width: 56, - height: 56, - borderRadius: 14, + width: scaleSize(56), + height: scaleSize(56), + borderRadius: scaleSize(14), justifyContent: "center", alignItems: "center", }, sizeValueContainer: { - width: 80, + width: scaleSize(80), alignItems: "center", }, sizeValueText: { - fontSize: 24, fontWeight: "600", color: "#fff", + fontSize: scaleSize(24), }, alignmentRow: { flexDirection: "row", - gap: 10, + gap: scaleSize(10), }, alignmentCard: { - paddingHorizontal: 20, - paddingVertical: 14, - borderRadius: 12, - minWidth: 90, + paddingHorizontal: scaleSize(20), + paddingVertical: scaleSize(14), + borderRadius: scaleSize(12), + minWidth: scaleSize(90), alignItems: "center", }, alignmentCardText: { - fontSize: 15, textTransform: "capitalize", }, alignmentCheckmark: { position: "absolute", - top: 6, - right: 6, + top: scaleSize(6), + right: scaleSize(6), }, }); diff --git a/components/tv/TVOptionCard.tsx b/components/tv/TVOptionCard.tsx index 525a7b62..200f2a9f 100644 --- a/components/tv/TVOptionCard.tsx +++ b/components/tv/TVOptionCard.tsx @@ -68,7 +68,7 @@ export const TVOptionCard = React.forwardRef( fontWeight: focused || selected ? "600" : "400", textAlign: "center", }} - numberOfLines={2} + numberOfLines={4} > {label} diff --git a/components/tv/settings/TVSettingsStepper.tsx b/components/tv/settings/TVSettingsStepper.tsx index c63430be..d0ba7b2c 100644 --- a/components/tv/settings/TVSettingsStepper.tsx +++ b/components/tv/settings/TVSettingsStepper.tsx @@ -49,6 +49,7 @@ export const TVSettingsStepper: React.FC = ({ }} >