Files
streamyfin/components/posters/SeriesPoster.tv.tsx
2026-01-25 17:02:10 +01:00

93 lines
2.2 KiB
TypeScript

import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { Image } from "expo-image";
import { useAtom } from "jotai";
import { useMemo } from "react";
import { View } from "react-native";
import {
GlassPosterView,
isGlassEffectAvailable,
} from "@/modules/glass-poster";
import { apiAtom } from "@/providers/JellyfinProvider";
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
export const TV_POSTER_WIDTH = 260;
type SeriesPosterProps = {
item: BaseItemDto;
showProgress?: boolean;
};
const SeriesPoster: React.FC<SeriesPosterProps> = ({ item }) => {
const [api] = useAtom(apiAtom);
const url = useMemo(() => {
if (item.Type === "Episode") {
return `${api?.basePath}/Items/${item.SeriesId}/Images/Primary?fillHeight=780&quality=80&tag=${item.SeriesPrimaryImageTag}`;
}
return getPrimaryImageUrl({
api,
item,
width: 520, // 2x for quality on large screens
});
}, [api, item]);
const blurhash = useMemo(() => {
const key = item.ImageTags?.Primary as string;
return item.ImageBlurHashes?.Primary?.[key];
}, [item]);
// Use glass effect on tvOS 26+
const useGlass = isGlassEffectAvailable();
if (useGlass) {
return (
<GlassPosterView
imageUrl={url ?? null}
aspectRatio={10 / 15}
cornerRadius={24}
progress={0}
showWatchedIndicator={false}
isFocused={false}
width={TV_POSTER_WIDTH}
style={{ width: TV_POSTER_WIDTH }}
/>
);
}
// Fallback for older tvOS versions
return (
<View
style={{
width: TV_POSTER_WIDTH,
aspectRatio: 10 / 15,
position: "relative",
borderRadius: 24,
overflow: "hidden",
}}
>
<Image
placeholder={{
blurhash,
}}
key={item.Id}
id={item.Id}
source={
url
? {
uri: url,
}
: null
}
cachePolicy={"memory-disk"}
contentFit='cover'
style={{
height: "100%",
width: "100%",
}}
/>
</View>
);
};
export default SeriesPoster;