feat(tv): add scalable poster sizes synchronized with typography settings

This commit is contained in:
Fredrik Burmester
2026-01-26 18:04:22 +01:00
parent bbd7854287
commit d51cf47eb4
18 changed files with 176 additions and 104 deletions

View File

@@ -4,6 +4,7 @@ import { useAtom } from "jotai";
import { useMemo } from "react";
import { View } from "react-native";
import { WatchedIndicator } from "@/components/WatchedIndicator";
import { useScaledTVPosterSizes } from "@/constants/TVPosterSizes";
import {
GlassPosterView,
isGlassEffectAvailable,
@@ -11,8 +12,6 @@ import {
import { apiAtom } from "@/providers/JellyfinProvider";
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
export const TV_POSTER_WIDTH = 260;
type MoviePosterProps = {
item: BaseItemDto;
showProgress?: boolean;
@@ -23,14 +22,15 @@ const MoviePoster: React.FC<MoviePosterProps> = ({
showProgress = false,
}) => {
const [api] = useAtom(apiAtom);
const posterSizes = useScaledTVPosterSizes();
const url = useMemo(() => {
return getPrimaryImageUrl({
api,
item,
width: 520, // 2x for quality on large screens
width: posterSizes.poster * 2, // 2x for quality on large screens
});
}, [api, item]);
}, [api, item, posterSizes.poster]);
const progress = item.UserData?.PlayedPercentage || 0;
const isWatched = item.UserData?.Played === true;
@@ -52,8 +52,8 @@ const MoviePoster: React.FC<MoviePosterProps> = ({
progress={showProgress ? progress : 0}
showWatchedIndicator={isWatched}
isFocused={false}
width={TV_POSTER_WIDTH}
style={{ width: TV_POSTER_WIDTH }}
width={posterSizes.poster}
style={{ width: posterSizes.poster }}
/>
);
}
@@ -65,7 +65,7 @@ const MoviePoster: React.FC<MoviePosterProps> = ({
position: "relative",
borderRadius: 24,
overflow: "hidden",
width: TV_POSTER_WIDTH,
width: posterSizes.poster,
aspectRatio: 10 / 15,
}}
>

View File

@@ -3,6 +3,7 @@ import { Image } from "expo-image";
import { useAtom } from "jotai";
import { useMemo } from "react";
import { View } from "react-native";
import { useScaledTVPosterSizes } from "@/constants/TVPosterSizes";
import {
GlassPosterView,
isGlassEffectAvailable,
@@ -10,8 +11,6 @@ import {
import { apiAtom } from "@/providers/JellyfinProvider";
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
export const TV_POSTER_WIDTH = 260;
type SeriesPosterProps = {
item: BaseItemDto;
showProgress?: boolean;
@@ -19,17 +18,18 @@ type SeriesPosterProps = {
const SeriesPoster: React.FC<SeriesPosterProps> = ({ item }) => {
const [api] = useAtom(apiAtom);
const posterSizes = useScaledTVPosterSizes();
const url = useMemo(() => {
if (item.Type === "Episode") {
return `${api?.basePath}/Items/${item.SeriesId}/Images/Primary?fillHeight=780&quality=80&tag=${item.SeriesPrimaryImageTag}`;
return `${api?.basePath}/Items/${item.SeriesId}/Images/Primary?fillHeight=${posterSizes.poster * 3}&quality=80&tag=${item.SeriesPrimaryImageTag}`;
}
return getPrimaryImageUrl({
api,
item,
width: 520, // 2x for quality on large screens
width: posterSizes.poster * 2, // 2x for quality on large screens
});
}, [api, item]);
}, [api, item, posterSizes.poster]);
const blurhash = useMemo(() => {
const key = item.ImageTags?.Primary as string;
@@ -48,8 +48,8 @@ const SeriesPoster: React.FC<SeriesPosterProps> = ({ item }) => {
progress={0}
showWatchedIndicator={false}
isFocused={false}
width={TV_POSTER_WIDTH}
style={{ width: TV_POSTER_WIDTH }}
width={posterSizes.poster}
style={{ width: posterSizes.poster }}
/>
);
}
@@ -58,7 +58,7 @@ const SeriesPoster: React.FC<SeriesPosterProps> = ({ item }) => {
return (
<View
style={{
width: TV_POSTER_WIDTH,
width: posterSizes.poster,
aspectRatio: 10 / 15,
position: "relative",
borderRadius: 24,