mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-06-06 14:08:30 +01:00
wip
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
|
import { ItemImage } from "@/components/common/ItemImage";
|
||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import { TouchableItemRouter } from "@/components/common/TouchableItemRouter";
|
import { TouchableItemRouter } from "@/components/common/TouchableItemRouter";
|
||||||
import { FilterButton } from "@/components/filters/FilterButton";
|
import { FilterButton } from "@/components/filters/FilterButton";
|
||||||
import { ResetFiltersButton } from "@/components/filters/ResetFiltersButton";
|
import { ResetFiltersButton } from "@/components/filters/ResetFiltersButton";
|
||||||
import { ItemCardText } from "@/components/ItemCardText";
|
import { ItemCardText } from "@/components/ItemCardText";
|
||||||
|
import { ItemPoster } from "@/components/posters/ItemPoster";
|
||||||
import MoviePoster from "@/components/posters/MoviePoster";
|
import MoviePoster from "@/components/posters/MoviePoster";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
import {
|
import {
|
||||||
@@ -194,7 +196,8 @@ const page: React.FC = () => {
|
|||||||
width: "89%",
|
width: "89%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MoviePoster item={item} />
|
<ItemPoster item={item} />
|
||||||
|
{/* <MoviePoster item={item} /> */}
|
||||||
<ItemCardText item={item} />
|
<ItemCardText item={item} />
|
||||||
</View>
|
</View>
|
||||||
</MemoizedTouchableItemRouter>
|
</MemoizedTouchableItemRouter>
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ import { FlashList } from "@shopify/flash-list";
|
|||||||
import { Loader } from "@/components/Loader";
|
import { Loader } from "@/components/Loader";
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
import { orientationAtom } from "@/utils/atoms/orientation";
|
import { orientationAtom } from "@/utils/atoms/orientation";
|
||||||
|
import { ItemPoster } from "@/components/posters/ItemPoster";
|
||||||
|
|
||||||
const MemoizedTouchableItemRouter = React.memo(TouchableItemRouter);
|
const MemoizedTouchableItemRouter = React.memo(TouchableItemRouter);
|
||||||
|
|
||||||
@@ -208,7 +209,8 @@ const Page = () => {
|
|||||||
width: "89%",
|
width: "89%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MoviePoster item={item} />
|
{/* <MoviePoster item={item} /> */}
|
||||||
|
<ItemPoster item={item} />
|
||||||
<ItemCardText item={item} />
|
<ItemCardText item={item} />
|
||||||
</View>
|
</View>
|
||||||
</MemoizedTouchableItemRouter>
|
</MemoizedTouchableItemRouter>
|
||||||
|
|||||||
@@ -1,20 +1,31 @@
|
|||||||
import { useImageColors } from "@/hooks/useImageColors";
|
import { useImageColors } from "@/hooks/useImageColors";
|
||||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||||
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||||
import { Image, ImageProps, ImageSource } from "expo-image";
|
import { Image, ImageProps, ImageSource } from "expo-image";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
import { View } from "react-native";
|
||||||
|
|
||||||
interface Props extends ImageProps {
|
interface Props extends ImageProps {
|
||||||
item: BaseItemDto;
|
item: BaseItemDto;
|
||||||
variant?: "Backdrop" | "Primary" | "Thumb" | "Logo";
|
variant?:
|
||||||
|
| "Primary"
|
||||||
|
| "Backdrop"
|
||||||
|
| "ParentBackdrop"
|
||||||
|
| "ParentLogo"
|
||||||
|
| "Logo"
|
||||||
|
| "AlbumPrimary"
|
||||||
|
| "SeriesPrimary"
|
||||||
|
| "Screenshot"
|
||||||
|
| "Thumb";
|
||||||
quality?: number;
|
quality?: number;
|
||||||
width?: number;
|
width?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ItemImage: React.FC<Props> = ({
|
export const ItemImage: React.FC<Props> = ({
|
||||||
item,
|
item,
|
||||||
variant,
|
variant = "Primary",
|
||||||
quality = 90,
|
quality = 90,
|
||||||
width = 1000,
|
width = 1000,
|
||||||
...props
|
...props
|
||||||
@@ -28,6 +39,8 @@ export const ItemImage: React.FC<Props> = ({
|
|||||||
let blurhash: string | null | undefined;
|
let blurhash: string | null | undefined;
|
||||||
let src: ImageSource | null = null;
|
let src: ImageSource | null = null;
|
||||||
|
|
||||||
|
console.log("ImageItem ~ " + variant, item.Name, item.ImageTags);
|
||||||
|
|
||||||
switch (variant) {
|
switch (variant) {
|
||||||
case "Backdrop":
|
case "Backdrop":
|
||||||
if (item.Type === "Episode") {
|
if (item.Type === "Episode") {
|
||||||
@@ -35,7 +48,7 @@ export const ItemImage: React.FC<Props> = ({
|
|||||||
if (!tag) break;
|
if (!tag) break;
|
||||||
blurhash = item.ImageBlurHashes?.Backdrop?.[tag];
|
blurhash = item.ImageBlurHashes?.Backdrop?.[tag];
|
||||||
src = {
|
src = {
|
||||||
uri: `${api.basePath}/Items/${item.ParentBackdropItemId}/Images/Backdrop/0?quality=${quality}&tag=${tag}`,
|
uri: `${api.basePath}/Items/${item.ParentBackdropItemId}/Images/Backdrop/0?quality=${quality}&tag=${tag}&width=${width}`,
|
||||||
blurhash,
|
blurhash,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
@@ -45,7 +58,7 @@ export const ItemImage: React.FC<Props> = ({
|
|||||||
if (!tag) break;
|
if (!tag) break;
|
||||||
blurhash = item.ImageBlurHashes?.Backdrop?.[tag];
|
blurhash = item.ImageBlurHashes?.Backdrop?.[tag];
|
||||||
src = {
|
src = {
|
||||||
uri: `${api.basePath}/Items/${item.Id}/Images/Backdrop/0?quality=${quality}&tag=${tag}`,
|
uri: `${api.basePath}/Items/${item.Id}/Images/Backdrop/0?quality=${quality}&tag=${tag}&width=${width}`,
|
||||||
blurhash,
|
blurhash,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
@@ -55,7 +68,7 @@ export const ItemImage: React.FC<Props> = ({
|
|||||||
blurhash = item.ImageBlurHashes?.Primary?.[tag];
|
blurhash = item.ImageBlurHashes?.Primary?.[tag];
|
||||||
|
|
||||||
src = {
|
src = {
|
||||||
uri: `${api.basePath}/Items/${item.Id}/Images/Primary?quality=${quality}&tag=${tag}`,
|
uri: `${api.basePath}/Items/${item.Id}/Images/Primary?quality=${quality}&tag=${tag}&width=${width}`,
|
||||||
blurhash,
|
blurhash,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
@@ -65,29 +78,42 @@ export const ItemImage: React.FC<Props> = ({
|
|||||||
blurhash = item.ImageBlurHashes?.Thumb?.[tag];
|
blurhash = item.ImageBlurHashes?.Thumb?.[tag];
|
||||||
|
|
||||||
src = {
|
src = {
|
||||||
uri: `${api.basePath}/Items/${item.Id}/Images/Backdrop?quality=${quality}&tag=${tag}`,
|
uri: `${api.basePath}/Items/${item.Id}/Images/Backdrop?quality=${quality}&tag=${tag}&width=${width}`,
|
||||||
blurhash,
|
blurhash,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
tag = item.ImageTags?.["Primary"];
|
tag = item.ImageTags?.["Primary"];
|
||||||
src = {
|
src = {
|
||||||
uri: `${api.basePath}/Items/${item.Id}/Images/Primary?quality=${quality}&tag=${tag}`,
|
uri: `${api.basePath}/Items/${item.Id}/Images/Primary?quality=${quality}&tag=${tag}&width=${width}`,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("src: ", src?.uri?.slice(0, 30));
|
||||||
|
|
||||||
return src;
|
return src;
|
||||||
}, [item.ImageTags]);
|
}, [item.ImageTags]);
|
||||||
|
|
||||||
useImageColors(source?.uri);
|
useImageColors(source?.uri);
|
||||||
|
|
||||||
|
// return placeholder icon if no source
|
||||||
|
if (!source?.uri) return;
|
||||||
|
<View {...props}>
|
||||||
|
<Ionicons name="image-outline" size={24} color="white" />;
|
||||||
|
</View>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Image
|
<Image
|
||||||
|
cachePolicy={"memory-disk"}
|
||||||
transition={300}
|
transition={300}
|
||||||
placeholder={{
|
placeholder={{
|
||||||
blurhash: source?.blurhash,
|
blurhash: source?.blurhash,
|
||||||
}}
|
}}
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
source={{
|
source={{
|
||||||
uri: source?.uri,
|
uri: source?.uri,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export const TouchableItemRouter: React.FC<PropsWithChildren<Props>> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (item.Type === "UserView") {
|
if (item.Type === "UserView") {
|
||||||
Alert.alert("Not implemented");
|
router.push(`/(auth)/(tabs)/${from}/collections/${item.Id}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
47
components/posters/ItemPoster.tsx
Normal file
47
components/posters/ItemPoster.tsx
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { View, ViewProps } from "react-native";
|
||||||
|
import { Text } from "@/components/common/Text";
|
||||||
|
import {
|
||||||
|
BaseItemDto,
|
||||||
|
BaseItemKind,
|
||||||
|
} from "@jellyfin/sdk/lib/generated-client/models";
|
||||||
|
import { ItemImage } from "../common/ItemImage";
|
||||||
|
import { WatchedIndicator } from "../WatchedIndicator";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
interface Props extends ViewProps {
|
||||||
|
item: BaseItemDto;
|
||||||
|
showProgress?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ItemPoster: React.FC<Props> = ({
|
||||||
|
item,
|
||||||
|
showProgress,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
const [progress, setProgress] = useState(
|
||||||
|
item.UserData?.PlayedPercentage || 0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (item.Type === "Movie" || item.Type === "Series" || item.Type === "BoxSet")
|
||||||
|
return (
|
||||||
|
<View className="relative rounded-lg overflow-hidden border border-neutral-900">
|
||||||
|
<ItemImage
|
||||||
|
style={{
|
||||||
|
aspectRatio: "10/15",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
item={item}
|
||||||
|
/>
|
||||||
|
<WatchedIndicator item={item} />
|
||||||
|
{showProgress && progress > 0 && (
|
||||||
|
<View className="h-1 bg-red-600 w-full"></View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="rounded-lg w-full aspect-square overflow-hidden border border-neutral-900">
|
||||||
|
<ItemImage className="w-full aspect-square" item={item} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user