chore: Apply linting rules and add git hok (#611)

Co-authored-by: Fredrik Burmester <fredrik.burmester@gmail.com>
This commit is contained in:
lostb1t
2025-03-16 18:01:12 +01:00
committed by GitHub
parent 2688e1b981
commit 92513e234f
268 changed files with 9197 additions and 8394 deletions

View File

@@ -1,11 +1,11 @@
import { WatchedIndicator } from "@/components/WatchedIndicator";
import { apiAtom } from "@/providers/JellyfinProvider";
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { Image } from "expo-image";
import { useAtom } from "jotai";
import { useMemo, useState } from "react";
import { View } from "react-native";
import { WatchedIndicator } from "@/components/WatchedIndicator";
type MoviePosterProps = {
item: BaseItemDto;
@@ -25,7 +25,7 @@ export const EpisodePoster: React.FC<MoviePosterProps> = ({
}, [item]);
const [progress, setProgress] = useState(
item.UserData?.PlayedPercentage || 0
item.UserData?.PlayedPercentage || 0,
);
const blurhash = useMemo(() => {
@@ -34,7 +34,7 @@ export const EpisodePoster: React.FC<MoviePosterProps> = ({
}, [item]);
return (
<View className="relative rounded-lg overflow-hidden border border-neutral-900">
<View className='relative rounded-lg overflow-hidden border border-neutral-900'>
<Image
placeholder={{
blurhash,
@@ -49,7 +49,7 @@ export const EpisodePoster: React.FC<MoviePosterProps> = ({
: null
}
cachePolicy={"memory-disk"}
contentFit="cover"
contentFit='cover'
style={{
aspectRatio: "10/15",
width: "100%",
@@ -57,7 +57,7 @@ export const EpisodePoster: React.FC<MoviePosterProps> = ({
/>
<WatchedIndicator item={item} />
{showProgress && progress > 0 && (
<View className="h-1 bg-red-600 w-full"></View>
<View className='h-1 bg-red-600 w-full'></View>
)}
</View>
);

View File

@@ -1,12 +1,12 @@
import { View, ViewProps } from "react-native";
import { Text } from "@/components/common/Text";
import {
BaseItemDto,
type BaseItemDto,
BaseItemKind,
} from "@jellyfin/sdk/lib/generated-client/models";
import { ItemImage } from "../common/ItemImage";
import { WatchedIndicator } from "../WatchedIndicator";
import { useState } from "react";
import { View, type ViewProps } from "react-native";
import { WatchedIndicator } from "../WatchedIndicator";
import { ItemImage } from "../common/ItemImage";
interface Props extends ViewProps {
item: BaseItemDto;
@@ -19,13 +19,13 @@ export const ItemPoster: React.FC<Props> = ({
...props
}) => {
const [progress, setProgress] = useState(
item.UserData?.PlayedPercentage || 0
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"
className='relative rounded-lg overflow-hidden border border-neutral-900'
{...props}
>
<ItemImage
@@ -37,17 +37,17 @@ export const ItemPoster: React.FC<Props> = ({
/>
<WatchedIndicator item={item} />
{showProgress && progress > 0 && (
<View className="h-1 bg-red-600 w-full"></View>
<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"
className='rounded-lg w-full aspect-square overflow-hidden border border-neutral-900'
{...props}
>
<ItemImage className="w-full aspect-square" item={item} />
<ItemImage className='w-full aspect-square' item={item} />
</View>
);
};

View File

@@ -1,23 +1,30 @@
import {TouchableJellyseerrRouter} from "@/components/common/JellyseerrItemRouter";
import {Text} from "@/components/common/Text";
import { Tag, Tags } from "@/components/GenreTags";
import { TouchableJellyseerrRouter } from "@/components/common/JellyseerrItemRouter";
import { Text } from "@/components/common/Text";
import JellyseerrMediaIcon from "@/components/jellyseerr/JellyseerrMediaIcon";
import JellyseerrStatusIcon from "@/components/jellyseerr/JellyseerrStatusIcon";
import {useJellyseerr} from "@/hooks/useJellyseerr";
import {useJellyseerrCanRequest} from "@/utils/_jellyseerr/useJellyseerrCanRequest";
import {MovieResult, TvResult} from "@/utils/jellyseerr/server/models/Search";
import {Image} from "expo-image";
import {useMemo} from "react";
import {View, ViewProps} from "react-native";
import Animated, {useAnimatedStyle, useSharedValue, withTiming,} from "react-native-reanimated";
import {TvDetails} from "@/utils/jellyseerr/server/models/Tv";
import {MovieDetails} from "@/utils/jellyseerr/server/models/Movie";
import type {DownloadingItem} from "@/utils/jellyseerr/server/lib/downloadtracker";
import MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest";
import {useTranslation} from "react-i18next";
import {MediaStatus} from "@/utils/jellyseerr/server/constants/media";
import {textShadowStyle} from "@/components/jellyseerr/discover/GenericSlideCard";
import {Colors} from "@/constants/Colors";
import {Tag, Tags} from "@/components/GenreTags";
import { textShadowStyle } from "@/components/jellyseerr/discover/GenericSlideCard";
import { Colors } from "@/constants/Colors";
import { useJellyseerr } from "@/hooks/useJellyseerr";
import { useJellyseerrCanRequest } from "@/utils/_jellyseerr/useJellyseerrCanRequest";
import { MediaStatus } from "@/utils/jellyseerr/server/constants/media";
import type MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest";
import type { DownloadingItem } from "@/utils/jellyseerr/server/lib/downloadtracker";
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
import type {
MovieResult,
TvResult,
} from "@/utils/jellyseerr/server/models/Search";
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
import { Image } from "expo-image";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { View, type ViewProps } from "react-native";
import Animated, {
useAnimatedStyle,
useSharedValue,
withTiming,
} from "react-native-reanimated";
interface Props extends ViewProps {
item?: MovieResult | TvResult | MovieDetails | TvDetails;
@@ -36,7 +43,7 @@ const JellyseerrPoster: React.FC<Props> = ({
const { jellyseerrApi, getTitle, getYear, getMediaType } = useJellyseerr();
const loadingOpacity = useSharedValue(1);
const imageOpacity = useSharedValue(0);
const {t} = useTranslation();
const { t } = useTranslation();
const imageAnimatedStyle = useAnimatedStyle(() => ({
opacity: imageOpacity.value,
@@ -48,65 +55,70 @@ const JellyseerrPoster: React.FC<Props> = ({
};
const backdropSrc = useMemo(
() => jellyseerrApi?.imageProxy(item?.backdropPath, "w1920_and_h800_multi_faces"),
[item, jellyseerrApi, horizontal]
() =>
jellyseerrApi?.imageProxy(
item?.backdropPath,
"w1920_and_h800_multi_faces",
),
[item, jellyseerrApi, horizontal],
);
const posterSrc = useMemo(
() => jellyseerrApi?.imageProxy(item?.posterPath, "w300_and_h450_face",),
[item, jellyseerrApi, horizontal]
() => jellyseerrApi?.imageProxy(item?.posterPath, "w300_and_h450_face"),
[item, jellyseerrApi, horizontal],
);
const title = useMemo(() => getTitle(item), [item]);
const releaseYear = useMemo(() => getYear(item), [item]);
const mediaType = useMemo(() => getMediaType(item), [item]);
const size = useMemo(() => horizontal ? 'h-28' : 'w-28', [horizontal])
const ratio = useMemo(() => horizontal ? '15/10' : '10/15', [horizontal])
const size = useMemo(() => (horizontal ? "h-28" : "w-28"), [horizontal]);
const ratio = useMemo(() => (horizontal ? "15/10" : "10/15"), [horizontal]);
const [canRequest] = useJellyseerrCanRequest(item);
const is4k = useMemo(
() => mediaRequest?.is4k === true,
[mediaRequest]
);
const is4k = useMemo(() => mediaRequest?.is4k === true, [mediaRequest]);
const downloadItems = useMemo(
() => (is4k ? mediaRequest?.media.downloadStatus4k : mediaRequest?.media.downloadStatus) || [],
[mediaRequest, is4k]
)
() =>
(is4k
? mediaRequest?.media.downloadStatus4k
: mediaRequest?.media.downloadStatus) || [],
[mediaRequest, is4k],
);
const progress = useMemo(() => {
const [totalSize, sizeLeft] = downloadItems
.reduce((sum: number[], next: DownloadingItem) =>
[sum[0] + next.size, sum[1] + next.sizeLeft],
[0, 0]
);
const [totalSize, sizeLeft] = downloadItems.reduce(
(sum: number[], next: DownloadingItem) => [
sum[0] + next.size,
sum[1] + next.sizeLeft,
],
[0, 0],
);
return (((totalSize - sizeLeft) / totalSize) * 100);
},
[downloadItems]
);
return ((totalSize - sizeLeft) / totalSize) * 100;
}, [downloadItems]);
const requestedSeasons: string[] | undefined = useMemo(
() => {
const seasons = mediaRequest?.seasons?.flatMap(s => s.seasonNumber.toString()) || []
if (seasons.length > 4) {
const [first, second, third, fourth, ...rest] = seasons;
return [first, second, third, fourth, t("home.settings.plugins.jellyseerr.plus_n_more", {n: rest.length })]
}
return seasons
},
[mediaRequest]
);
const requestedSeasons: string[] | undefined = useMemo(() => {
const seasons =
mediaRequest?.seasons?.flatMap((s) => s.seasonNumber.toString()) || [];
if (seasons.length > 4) {
const [first, second, third, fourth, ...rest] = seasons;
return [
first,
second,
third,
fourth,
t("home.settings.plugins.jellyseerr.plus_n_more", { n: rest.length }),
];
}
return seasons;
}, [mediaRequest]);
const available = useMemo(
() => {
const status = mediaRequest?.media?.[is4k ? 'status4k' : 'status'];
return status === MediaStatus.AVAILABLE
},
[mediaRequest, is4k]
);
const available = useMemo(() => {
const status = mediaRequest?.media?.[is4k ? "status4k" : "status"];
return status === MediaStatus.AVAILABLE;
}, [mediaRequest, is4k]);
return (
<TouchableJellyseerrRouter
@@ -114,57 +126,58 @@ const JellyseerrPoster: React.FC<Props> = ({
mediaTitle={title}
releaseYear={releaseYear}
canRequest={canRequest}
posterSrc={posterSrc!!}
posterSrc={posterSrc!}
mediaType={mediaType}
>
<View className={`flex flex-col mr-2 h-auto`}>
<View className={`relative rounded-lg overflow-hidden border border-neutral-900 ${size} aspect-[${ratio}]`}>
<View
className={`relative rounded-lg overflow-hidden border border-neutral-900 ${size} aspect-[${ratio}]`}
>
<Animated.View style={imageAnimatedStyle}>
<Image
className="w-full"
className='w-full'
key={item?.id}
id={item?.id.toString()}
source={{ uri: horizontal ? backdropSrc : posterSrc }}
cachePolicy={"memory-disk"}
contentFit="cover"
contentFit='cover'
style={{
aspectRatio: ratio,
[horizontal ? 'height' : 'width']: "100%"
[horizontal ? "height" : "width"]: "100%",
}}
onLoad={handleImageLoad}
/>
</Animated.View>
{mediaRequest && showDownloadInfo && (
<>
<View className={`absolute w-full h-full bg-black ${!available ? 'opacity-70' : 'opacity-0'}`} />
<View
className={`absolute w-full h-full bg-black ${!available ? "opacity-70" : "opacity-0"}`}
/>
{!available && !Number.isNaN(progress) && (
<>
<View
className="absolute left-0 h-full opacity-40"
className='absolute left-0 h-full opacity-40'
style={{
width: `${progress || 0}%`,
backgroundColor: Colors.primaryRGB,
}}
/>
<View className="absolute w-full h-full justify-center items-center">
<Text
className="font-bold"
style={textShadowStyle.shadow}
>
<View className='absolute w-full h-full justify-center items-center'>
<Text className='font-bold' style={textShadowStyle.shadow}>
{progress?.toFixed(0)}%
</Text>
</View>
</>
)}
<Tag
className="absolute right-1 top-1 text-right bg-black border border-neutral-800/50"
className='absolute right-1 top-1 text-right bg-black border border-neutral-800/50'
text={mediaRequest?.requestedBy.displayName}
/>
{requestedSeasons.length > 0 && (
<Tags
className="absolute bottom-1 left-0.5 w-32"
className='absolute bottom-1 left-0.5 w-32'
tagProps={{
className: "bg-black rounded-full px-1"
className: "bg-black rounded-full px-1",
}}
tags={requestedSeasons}
/>
@@ -172,19 +185,21 @@ const JellyseerrPoster: React.FC<Props> = ({
</>
)}
<JellyseerrStatusIcon
className="absolute bottom-1 right-1"
className='absolute bottom-1 right-1'
showRequestIcon={canRequest}
mediaStatus={mediaRequest?.media?.status || item?.mediaInfo?.status}
/>
<JellyseerrMediaIcon
className="absolute top-1 left-1"
className='absolute top-1 left-1'
mediaType={mediaType}
/>
</View>
</View>
<View className={`mt-2 flex flex-col ${horizontal ? 'w-44' : 'w-28'}`}>
<View className={`mt-2 flex flex-col ${horizontal ? "w-44" : "w-28"}`}>
<Text numberOfLines={2}>{title || ""}</Text>
<Text className="text-xs opacity-50 align-bottom">{releaseYear || ""}</Text>
<Text className='text-xs opacity-50 align-bottom'>
{releaseYear || ""}
</Text>
</View>
</TouchableJellyseerrRouter>
);

View File

@@ -1,7 +1,7 @@
import { WatchedIndicator } from "@/components/WatchedIndicator";
import { apiAtom } from "@/providers/JellyfinProvider";
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { Image } from "expo-image";
import { useAtom } from "jotai";
import { useMemo, useState } from "react";
@@ -27,7 +27,7 @@ const MoviePoster: React.FC<MoviePosterProps> = ({
}, [item]);
const [progress, setProgress] = useState(
item.UserData?.PlayedPercentage || 0
item.UserData?.PlayedPercentage || 0,
);
const blurhash = useMemo(() => {
@@ -36,7 +36,7 @@ const MoviePoster: React.FC<MoviePosterProps> = ({
}, [item]);
return (
<View className="relative rounded-lg overflow-hidden border border-neutral-900 w-28 aspect-[10/15]">
<View className='relative rounded-lg overflow-hidden border border-neutral-900 w-28 aspect-[10/15]'>
<Image
placeholder={{
blurhash,
@@ -51,7 +51,7 @@ const MoviePoster: React.FC<MoviePosterProps> = ({
: null
}
cachePolicy={"memory-disk"}
contentFit="cover"
contentFit='cover'
style={{
aspectRatio: "10/15",
width: "100%",
@@ -59,7 +59,7 @@ const MoviePoster: React.FC<MoviePosterProps> = ({
/>
<WatchedIndicator item={item} />
{showProgress && progress > 0 && (
<View className="h-1 bg-red-600 w-full"></View>
<View className='h-1 bg-red-600 w-full'></View>
)}
</View>
);

View File

@@ -14,13 +14,13 @@ const ParentPoster: React.FC<PosterProps> = ({ id }) => {
const url = useMemo(
() => `${api?.basePath}/Items/${id}/Images/Primary`,
[id]
[id],
);
if (!url || !id)
return (
<View
className="border border-neutral-900"
className='border border-neutral-900'
style={{
aspectRatio: "10/15",
}}
@@ -28,7 +28,7 @@ const ParentPoster: React.FC<PosterProps> = ({ id }) => {
);
return (
<View className="rounded-lg overflow-hidden border border-neutral-900">
<View className='rounded-lg overflow-hidden border border-neutral-900'>
<Image
key={id}
id={id}
@@ -36,7 +36,7 @@ const ParentPoster: React.FC<PosterProps> = ({ id }) => {
uri: url,
}}
cachePolicy={"memory-disk"}
contentFit="cover"
contentFit='cover'
style={{
aspectRatio: "10/15",
}}

View File

@@ -12,7 +12,7 @@ const Poster: React.FC<PosterProps> = ({ id, url, blurhash }) => {
if (!id && !url)
return (
<View
className="border border-neutral-900"
className='border border-neutral-900'
style={{
aspectRatio: "10/15",
}}
@@ -20,7 +20,7 @@ const Poster: React.FC<PosterProps> = ({ id, url, blurhash }) => {
);
return (
<View className="rounded-lg overflow-hidden border border-neutral-900">
<View className='rounded-lg overflow-hidden border border-neutral-900'>
<Image
placeholder={
blurhash
@@ -30,7 +30,7 @@ const Poster: React.FC<PosterProps> = ({ id, url, blurhash }) => {
: null
}
key={id}
id={id!!}
id={id!}
source={
url
? {
@@ -39,7 +39,7 @@ const Poster: React.FC<PosterProps> = ({ id, url, blurhash }) => {
: null
}
cachePolicy={"memory-disk"}
contentFit="cover"
contentFit='cover'
style={{
aspectRatio: "10/15",
}}

View File

@@ -1,11 +1,11 @@
import { WatchedIndicator } from "@/components/WatchedIndicator";
import { apiAtom } from "@/providers/JellyfinProvider";
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { Image } from "expo-image";
import { useAtom } from "jotai";
import { useMemo, useState } from "react";
import { View } from "react-native";
import { WatchedIndicator } from "@/components/WatchedIndicator";
type MoviePosterProps = {
item: BaseItemDto;
@@ -32,7 +32,7 @@ const SeriesPoster: React.FC<MoviePosterProps> = ({ item }) => {
}, [item]);
return (
<View className="w-28 aspect-[10/15] relative rounded-lg overflow-hidden border border-neutral-900 ">
<View className='w-28 aspect-[10/15] relative rounded-lg overflow-hidden border border-neutral-900 '>
<Image
placeholder={{
blurhash,
@@ -47,7 +47,7 @@ const SeriesPoster: React.FC<MoviePosterProps> = ({ item }) => {
: null
}
cachePolicy={"memory-disk"}
contentFit="cover"
contentFit='cover'
style={{
height: "100%",
width: "100%",