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

@@ -1,7 +1,7 @@
import type { Api } from "@jellyfin/sdk";
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
import { BlurView } from "expo-blur";
import { type FC, useEffect, useRef } from "react";
import { type FC, useEffect, useMemo, useRef } from "react";
import { useTranslation } from "react-i18next";
import { Image, StyleSheet, View } from "react-native";
import Animated, {
@@ -13,7 +13,7 @@ import Animated, {
withTiming,
} from "react-native-reanimated";
import { Text } from "@/components/common/Text";
import { TVTypography } from "@/constants/TVTypography";
import { useScaledTVTypography } from "@/constants/TVTypography";
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
export interface TVNextEpisodeCountdownProps {
@@ -31,6 +31,7 @@ export const TVNextEpisodeCountdown: FC<TVNextEpisodeCountdownProps> = ({
isPlaying,
onFinish,
}) => {
const typography = useScaledTVTypography();
const { t } = useTranslation();
const progress = useSharedValue(0);
const onFinishRef = useRef(onFinish);
@@ -69,6 +70,8 @@ export const TVNextEpisodeCountdown: FC<TVNextEpisodeCountdownProps> = ({
width: `${progress.value * 100}%`,
}));
const styles = useMemo(() => createStyles(typography), [typography]);
if (!show) return null;
return (
@@ -105,57 +108,58 @@ export const TVNextEpisodeCountdown: FC<TVNextEpisodeCountdownProps> = ({
);
};
const styles = StyleSheet.create({
container: {
position: "absolute",
bottom: 180,
right: 80,
zIndex: 100,
},
blur: {
borderRadius: 16,
overflow: "hidden",
},
innerContainer: {
flexDirection: "row",
alignItems: "stretch",
},
thumbnail: {
width: 180,
backgroundColor: "rgba(0,0,0,0.3)",
},
content: {
padding: 16,
justifyContent: "center",
width: 280,
},
label: {
fontSize: TVTypography.callout,
color: "rgba(255,255,255,0.5)",
textTransform: "uppercase",
letterSpacing: 1,
marginBottom: 4,
},
seriesName: {
fontSize: TVTypography.callout,
color: "rgba(255,255,255,0.7)",
marginBottom: 2,
},
episodeInfo: {
fontSize: TVTypography.body,
color: "#fff",
fontWeight: "600",
marginBottom: 12,
},
progressContainer: {
height: 4,
backgroundColor: "rgba(255,255,255,0.2)",
borderRadius: 2,
overflow: "hidden",
},
progressBar: {
height: "100%",
backgroundColor: "#fff",
borderRadius: 2,
},
});
const createStyles = (typography: ReturnType<typeof useScaledTVTypography>) =>
StyleSheet.create({
container: {
position: "absolute",
bottom: 180,
right: 80,
zIndex: 100,
},
blur: {
borderRadius: 16,
overflow: "hidden",
},
innerContainer: {
flexDirection: "row",
alignItems: "stretch",
},
thumbnail: {
width: 180,
backgroundColor: "rgba(0,0,0,0.3)",
},
content: {
padding: 16,
justifyContent: "center",
width: 280,
},
label: {
fontSize: typography.callout,
color: "rgba(255,255,255,0.5)",
textTransform: "uppercase",
letterSpacing: 1,
marginBottom: 4,
},
seriesName: {
fontSize: typography.callout,
color: "rgba(255,255,255,0.7)",
marginBottom: 2,
},
episodeInfo: {
fontSize: typography.body,
color: "#fff",
fontWeight: "600",
marginBottom: 12,
},
progressContainer: {
height: 4,
backgroundColor: "rgba(255,255,255,0.2)",
borderRadius: 2,
overflow: "hidden",
},
progressBar: {
height: "100%",
backgroundColor: "#fff",
borderRadius: 2,
},
});