feat(tv): add scalable typography with user-configurable text size

This commit is contained in:
Fredrik Burmester
2026-01-25 22:55:44 +01:00
parent 0c6c20f563
commit 875a017e8c
59 changed files with 712 additions and 494 deletions

View File

@@ -7,7 +7,7 @@ import { useTranslation } from "react-i18next";
import { Animated, FlatList, Pressable, View } from "react-native";
import { Text } from "@/components/common/Text";
import { useTVFocusAnimation } from "@/components/tv/hooks/useTVFocusAnimation";
import { TVTypography } from "@/constants/TVTypography";
import { useScaledTVTypography } from "@/constants/TVTypography";
import useRouter from "@/hooks/useAppRouter";
import {
type DiscoverEndpoint,
@@ -33,6 +33,7 @@ const TVDiscoverPoster: React.FC<TVDiscoverPosterProps> = ({
item,
isFirstItem = false,
}) => {
const typography = useScaledTVTypography();
const router = useRouter();
const { jellyseerrApi, getTitle, getYear } = useJellyseerr();
const { focused, handleFocus, handleBlur, animatedStyle } =
@@ -130,7 +131,7 @@ const TVDiscoverPoster: React.FC<TVDiscoverPosterProps> = ({
</View>
<Text
style={{
fontSize: TVTypography.callout,
fontSize: typography.callout,
color: "#fff",
fontWeight: "600",
marginTop: 12,
@@ -142,7 +143,7 @@ const TVDiscoverPoster: React.FC<TVDiscoverPosterProps> = ({
{year && (
<Text
style={{
fontSize: TVTypography.callout,
fontSize: typography.callout,
color: "#9CA3AF",
marginTop: 2,
}}
@@ -164,6 +165,7 @@ export const TVDiscoverSlide: React.FC<TVDiscoverSlideProps> = ({
slide,
isFirstSlide = false,
}) => {
const typography = useScaledTVTypography();
const { t } = useTranslation();
const { jellyseerrApi, isJellyseerrMovieOrTvResult } = useJellyseerr();
@@ -232,7 +234,7 @@ export const TVDiscoverSlide: React.FC<TVDiscoverSlideProps> = ({
<View style={{ marginBottom: 24 }}>
<Text
style={{
fontSize: TVTypography.heading,
fontSize: typography.heading,
fontWeight: "bold",
color: "#FFFFFF",
marginBottom: 16,

View File

@@ -22,7 +22,7 @@ import { Loader } from "@/components/Loader";
import { JellyserrRatings } from "@/components/Ratings";
import { TVButton } from "@/components/tv";
import { useTVFocusAnimation } from "@/components/tv/hooks/useTVFocusAnimation";
import { TVTypography } from "@/constants/TVTypography";
import { useScaledTVTypography } from "@/constants/TVTypography";
import useRouter from "@/hooks/useAppRouter";
import { useJellyseerr } from "@/hooks/useJellyseerr";
import { useTVRequestModal } from "@/hooks/useTVRequestModal";
@@ -68,6 +68,7 @@ const TVCastCard: React.FC<TVCastCardProps> = ({
onPress,
refSetter,
}) => {
const typography = useScaledTVTypography();
const { focused, handleFocus, handleBlur, animatedStyle } =
useTVFocusAnimation({ scaleAmount: 1.08 });
@@ -128,7 +129,7 @@ const TVCastCard: React.FC<TVCastCardProps> = ({
</View>
<Text
style={{
fontSize: TVTypography.callout,
fontSize: typography.callout,
color: focused ? "#fff" : "rgba(255,255,255,0.9)",
fontWeight: "600",
textAlign: "center",
@@ -158,6 +159,7 @@ const TVCastCard: React.FC<TVCastCardProps> = ({
};
export const TVJellyseerrPage: React.FC = () => {
const typography = useScaledTVTypography();
const insets = useSafeAreaInsets();
const params = useLocalSearchParams();
const { t } = useTranslation();
@@ -552,7 +554,7 @@ export const TVJellyseerrPage: React.FC = () => {
{/* Title */}
<Text
style={{
fontSize: TVTypography.display,
fontSize: typography.display,
fontWeight: "bold",
color: "#FFFFFF",
marginTop: 8,
@@ -566,7 +568,7 @@ export const TVJellyseerrPage: React.FC = () => {
{/* Year */}
<Text
style={{
fontSize: TVTypography.body,
fontSize: typography.body,
color: "rgba(255,255,255,0.7)",
marginBottom: 16,
}}
@@ -601,7 +603,7 @@ export const TVJellyseerrPage: React.FC = () => {
>
<Text
style={{
fontSize: TVTypography.body,
fontSize: typography.body,
color: "#E5E7EB",
lineHeight: 32,
}}
@@ -636,7 +638,7 @@ export const TVJellyseerrPage: React.FC = () => {
/>
<Text
style={{
fontSize: TVTypography.callout,
fontSize: typography.callout,
fontWeight: "bold",
color: "#000000",
}}
@@ -663,7 +665,7 @@ export const TVJellyseerrPage: React.FC = () => {
/>
<Text
style={{
fontSize: TVTypography.callout,
fontSize: typography.callout,
fontWeight: "bold",
color: "#FFFFFF",
}}
@@ -698,7 +700,7 @@ export const TVJellyseerrPage: React.FC = () => {
/>
<Text
style={{
fontSize: TVTypography.callout,
fontSize: typography.callout,
fontWeight: "600",
color: "#FFFFFF",
}}
@@ -732,7 +734,7 @@ export const TVJellyseerrPage: React.FC = () => {
/>
<Text
style={{
fontSize: TVTypography.callout,
fontSize: typography.callout,
fontWeight: "600",
color: "#FFFFFF",
}}
@@ -757,7 +759,7 @@ export const TVJellyseerrPage: React.FC = () => {
<Ionicons name='person-outline' size={18} color='#9CA3AF' />
<Text
style={{
fontSize: TVTypography.callout,
fontSize: typography.callout,
color: "#9CA3AF",
marginLeft: 8,
}}
@@ -776,7 +778,7 @@ export const TVJellyseerrPage: React.FC = () => {
/>
<Text
style={{
fontSize: TVTypography.callout,
fontSize: typography.callout,
fontWeight: "600",
color: "#FFFFFF",
}}
@@ -794,7 +796,7 @@ export const TVJellyseerrPage: React.FC = () => {
/>
<Text
style={{
fontSize: TVTypography.callout,
fontSize: typography.callout,
fontWeight: "600",
color: "#FFFFFF",
}}
@@ -813,7 +815,7 @@ export const TVJellyseerrPage: React.FC = () => {
<View style={{ marginTop: 24 }}>
<Text
style={{
fontSize: TVTypography.heading,
fontSize: typography.heading,
fontWeight: "bold",
color: "#FFFFFF",
marginBottom: 16,

View File

@@ -20,7 +20,7 @@ import {
import { Text } from "@/components/common/Text";
import { TVButton, TVOptionSelector } from "@/components/tv";
import type { TVOptionItem } from "@/components/tv/TVOptionSelector";
import { TVTypography } from "@/constants/TVTypography";
import { useScaledTVTypography } from "@/constants/TVTypography";
import { useJellyseerr } from "@/hooks/useJellyseerr";
import type {
QualityProfile,
@@ -51,6 +51,7 @@ export const TVRequestModal: React.FC<TVRequestModalProps> = ({
onClose,
onRequested,
}) => {
const typography = useScaledTVTypography();
const { t } = useTranslation();
const { jellyseerrApi, jellyseerrUser, requestMedia } = useJellyseerr();
@@ -389,7 +390,7 @@ export const TVRequestModal: React.FC<TVRequestModalProps> = ({
>
<Text
style={{
fontSize: TVTypography.heading,
fontSize: typography.heading,
fontWeight: "bold",
color: "#FFFFFF",
marginBottom: 8,
@@ -399,7 +400,7 @@ export const TVRequestModal: React.FC<TVRequestModalProps> = ({
</Text>
<Text
style={{
fontSize: TVTypography.callout,
fontSize: typography.callout,
color: "rgba(255,255,255,0.6)",
marginBottom: 24,
}}
@@ -473,7 +474,7 @@ export const TVRequestModal: React.FC<TVRequestModalProps> = ({
/>
<Text
style={{
fontSize: TVTypography.callout,
fontSize: typography.callout,
fontWeight: "bold",
color: "#FFFFFF",
}}

View File

@@ -3,7 +3,7 @@ import React from "react";
import { Animated, Pressable, View } from "react-native";
import { Text } from "@/components/common/Text";
import { useTVFocusAnimation } from "@/components/tv/hooks/useTVFocusAnimation";
import { TVTypography } from "@/constants/TVTypography";
import { useScaledTVTypography } from "@/constants/TVTypography";
interface TVRequestOptionRowProps {
label: string;
@@ -20,6 +20,7 @@ export const TVRequestOptionRow: React.FC<TVRequestOptionRowProps> = ({
hasTVPreferredFocus = false,
disabled = false,
}) => {
const typography = useScaledTVTypography();
const { focused, handleFocus, handleBlur, animatedStyle } =
useTVFocusAnimation({
scaleAmount: 1.02,
@@ -56,7 +57,7 @@ export const TVRequestOptionRow: React.FC<TVRequestOptionRowProps> = ({
>
<Text
style={{
fontSize: TVTypography.callout,
fontSize: typography.callout,
color: "rgba(255,255,255,0.6)",
}}
>
@@ -65,7 +66,7 @@ export const TVRequestOptionRow: React.FC<TVRequestOptionRowProps> = ({
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
<Text
style={{
fontSize: TVTypography.body,
fontSize: typography.body,
color: focused ? "#FFFFFF" : "rgba(255,255,255,0.9)",
fontWeight: "500",
}}

View File

@@ -2,7 +2,7 @@ import React from "react";
import { Animated, Pressable, ScrollView, View } from "react-native";
import { Text } from "@/components/common/Text";
import { useTVFocusAnimation } from "@/components/tv/hooks/useTVFocusAnimation";
import { TVTypography } from "@/constants/TVTypography";
import { useScaledTVTypography } from "@/constants/TVTypography";
interface ToggleItem {
id: number;
@@ -21,6 +21,7 @@ const TVToggleChip: React.FC<TVToggleChipProps> = ({
onToggle,
disabled = false,
}) => {
const typography = useScaledTVTypography();
const { focused, handleFocus, handleBlur, animatedStyle } =
useTVFocusAnimation({
scaleAmount: 1.08,
@@ -57,7 +58,7 @@ const TVToggleChip: React.FC<TVToggleChipProps> = ({
>
<Text
style={{
fontSize: TVTypography.callout,
fontSize: typography.callout,
color: focused ? "#000" : "#fff",
fontWeight: item.selected || focused ? "600" : "400",
}}
@@ -82,13 +83,14 @@ export const TVToggleOptionRow: React.FC<TVToggleOptionRowProps> = ({
onToggle,
disabled = false,
}) => {
const typography = useScaledTVTypography();
if (items.length === 0) return null;
return (
<View style={{ marginBottom: 16 }}>
<Text
style={{
fontSize: TVTypography.callout,
fontSize: typography.callout,
color: "rgba(255,255,255,0.6)",
marginBottom: 10,
}}