fix: bump biome and fix error (#864)
Some checks failed
🤖 Android APK Build / 🏗️ Build Android APK (push) Has been cancelled
🤖 iOS IPA Build / 🏗️ Build iOS IPA (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🕒 Handle Stale Issues / 🗑️ Cleanup Stale Issues (push) Has been cancelled

This commit is contained in:
Gauvain
2025-07-21 09:44:24 +02:00
committed by GitHub
parent 3b2a6bd40a
commit 5f39622ad6
202 changed files with 1858 additions and 1954 deletions

View File

@@ -1,5 +1,3 @@
import { apiAtom } from "@/providers/JellyfinProvider";
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
import type {
BaseItemDto,
BaseItemPerson,
@@ -10,6 +8,8 @@ import type React from "react";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { TouchableOpacity, View, type ViewProps } from "react-native";
import { apiAtom } from "@/providers/JellyfinProvider";
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
import { HorizontalScroll } from "../common/HorrizontalScroll";
import { Text } from "../common/Text";
import { itemRouter } from "../common/TouchableItemRouter";
@@ -48,7 +48,7 @@ export const CastAndCrew: React.FC<Props> = ({ item, loading, ...props }) => {
</Text>
<HorizontalScroll
loading={loading}
keyExtractor={(i, idx) => i.Id.toString()}
keyExtractor={(i, _idx) => i.Id.toString()}
height={247}
data={destinctPeople}
renderItem={(i) => (

View File

@@ -1,11 +1,11 @@
import { apiAtom } from "@/providers/JellyfinProvider";
import { getPrimaryImageUrlById } from "@/utils/jellyfin/image/getPrimaryImageUrlById";
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { router } from "expo-router";
import { useAtom } from "jotai";
import type React from "react";
import { useTranslation } from "react-i18next";
import { TouchableOpacity, View, type ViewProps } from "react-native";
import { apiAtom } from "@/providers/JellyfinProvider";
import { getPrimaryImageUrlById } from "@/utils/jellyfin/image/getPrimaryImageUrlById";
import { HorizontalScroll } from "../common/HorrizontalScroll";
import { Text } from "../common/Text";
import Poster from "../posters/Poster";
@@ -26,7 +26,7 @@ export const CurrentSeries: React.FC<Props> = ({ item, ...props }) => {
<HorizontalScroll
data={[item]}
height={247}
renderItem={(item, index) => (
renderItem={(item, _index) => (
<TouchableOpacity
key={item.Id}
onPress={() => router.push(`/series/${item.SeriesId}`)}

View File

@@ -1,7 +1,7 @@
import { Text } from "@/components/common/Text";
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { useRouter } from "expo-router";
import { TouchableOpacity, View, type ViewProps } from "react-native";
import { Text } from "@/components/common/Text";
interface Props extends ViewProps {
item: BaseItemDto;

View File

@@ -1,21 +1,3 @@
import { Tags } from "@/components/GenreTags";
import { RoundButton } from "@/components/RoundButton";
import { HorizontalScroll } from "@/components/common/HorrizontalScroll";
import { Text } from "@/components/common/Text";
import { dateOpts } from "@/components/jellyseerr/DetailFacts";
import JellyseerrStatusIcon from "@/components/jellyseerr/JellyseerrStatusIcon";
import { textShadowStyle } from "@/components/jellyseerr/discover/GenericSlideCard";
import { useJellyseerr } from "@/hooks/useJellyseerr";
import {
MediaStatus,
MediaType,
} from "@/utils/jellyseerr/server/constants/media";
import type MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest";
import type Season from "@/utils/jellyseerr/server/entity/Season";
import type { MediaRequestBody } from "@/utils/jellyseerr/server/interfaces/api/requestInterfaces";
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
import { TvResult } from "@/utils/jellyseerr/server/models/Search";
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
import { Ionicons } from "@expo/vector-icons";
import { FlashList } from "@shopify/flash-list";
import {
@@ -29,6 +11,23 @@ import { orderBy } from "lodash";
import type React from "react";
import { useCallback, useMemo, useState } from "react";
import { Alert, TouchableOpacity, View } from "react-native";
import { HorizontalScroll } from "@/components/common/HorrizontalScroll";
import { Text } from "@/components/common/Text";
import { Tags } from "@/components/GenreTags";
import { dateOpts } from "@/components/jellyseerr/DetailFacts";
import { textShadowStyle } from "@/components/jellyseerr/discover/GenericSlideCard";
import JellyseerrStatusIcon from "@/components/jellyseerr/JellyseerrStatusIcon";
import { RoundButton } from "@/components/RoundButton";
import { useJellyseerr } from "@/hooks/useJellyseerr";
import {
MediaStatus,
MediaType,
} from "@/utils/jellyseerr/server/constants/media";
import type MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest";
import type Season from "@/utils/jellyseerr/server/entity/Season";
import type { MediaRequestBody } from "@/utils/jellyseerr/server/interfaces/api/requestInterfaces";
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
import { Loader } from "../Loader";
const JellyseerrSeasonEpisodes: React.FC<{
@@ -70,12 +69,11 @@ const RenderItem = ({ item, index }: any) => {
const airDate = item.airDate;
if (airDate) {
const airDateObj = new Date(airDate);
if (new Date() < airDateObj) {
return airDateObj.toLocaleDateString(`${locale}-${region}`, dateOpts);
}
}
}, [item]);
}, [item, locale, region]);
return (
<View className='flex flex-col w-44 mt-2'>
@@ -91,7 +89,7 @@ const RenderItem = ({ item, index }: any) => {
cachePolicy={"memory-disk"}
contentFit='cover'
className='w-full h-full'
onError={(e) => {
onError={(_e) => {
setImageError(true);
}}
/>
@@ -127,7 +125,6 @@ const RenderItem = ({ item, index }: any) => {
{`S${item.seasonNumber}:E${item.episodeNumber}`}
</Text>
</View>
<Text numberOfLines={3} className='text-xs text-neutral-500 shrink'>
{item.overview}
</Text>
@@ -152,40 +149,35 @@ const JellyseerrSeasons: React.FC<{
hasAdvancedRequest,
onAdvancedRequest,
}) => {
if (!details) return null;
const { jellyseerrApi, requestMedia } = useJellyseerr();
const [seasonStates, setSeasonStates] = useState<{
[key: number]: boolean;
}>();
const [seasonStates, setSeasonStates] = useState<{ [key: number]: boolean }>(
{},
);
const seasons = useMemo(() => {
const mediaInfoSeasons = details?.mediaInfo?.seasons?.filter(
if (!details) return [];
const mediaInfoSeasons = details.mediaInfo?.seasons?.filter(
(s: Season) => s.seasonNumber !== 0,
);
const requestedSeasons = details?.mediaInfo?.requests?.flatMap(
(r: MediaRequest) => r.seasons,
);
return details.seasons?.map((season) => {
return {
const requestedSeasons =
details.mediaInfo?.requests?.flatMap((r: MediaRequest) => r.seasons) ??
[];
return (
details.seasons?.map((season) => ({
...season,
status:
// What our library status is
mediaInfoSeasons?.find(
(mediaSeason: Season) =>
mediaSeason.seasonNumber === season.seasonNumber,
)?.status ??
// What our request status is
requestedSeasons?.find(
(s: Season) => s.seasonNumber === season.seasonNumber,
)?.status ??
// Otherwise set it as unknown
MediaStatus.UNKNOWN,
};
});
})) ?? []
);
}, [details]);
const allSeasonsAvailable = useMemo(
() => seasons?.every((season) => season.status === MediaStatus.AVAILABLE),
() => seasons.every((season) => season.status === MediaStatus.AVAILABLE),
[seasons],
);
@@ -201,14 +193,20 @@ const JellyseerrSeasons: React.FC<{
)
.map((s) => s.seasonNumber),
};
if (hasAdvancedRequest) {
return onAdvancedRequest?.(body);
}
requestMedia(details.name, body, refetch);
}
}, [jellyseerrApi, seasons, details, hasAdvancedRequest, onAdvancedRequest]);
}, [
jellyseerrApi,
seasons,
details,
hasAdvancedRequest,
onAdvancedRequest,
requestMedia,
refetch,
]);
const promptRequestAll = useCallback(
() =>
@@ -231,24 +229,24 @@ const JellyseerrSeasons: React.FC<{
const requestSeason = useCallback(
async (canRequest: boolean, seasonNumber: number) => {
if (canRequest) {
if (canRequest && details) {
const body: MediaRequestBody = {
mediaId: details.id,
mediaType: MediaType.TV,
tvdbId: details.externalIds?.tvdbId,
seasons: [seasonNumber],
};
if (hasAdvancedRequest) {
return onAdvancedRequest?.(body);
}
requestMedia(`${details.name}, Season ${seasonNumber}`, body, refetch);
}
},
[requestMedia, hasAdvancedRequest, onAdvancedRequest],
[requestMedia, hasAdvancedRequest, onAdvancedRequest, refetch, details],
);
if (!details) return null;
if (isLoading)
return (
<View>
@@ -269,7 +267,7 @@ const JellyseerrSeasons: React.FC<{
return (
<FlashList
data={orderBy(
details.seasons.filter((s) => s.seasonNumber !== 0),
seasons.filter((s) => s.seasonNumber !== 0),
"seasonNumber",
"desc",
)}
@@ -314,9 +312,7 @@ const JellyseerrSeasons: React.FC<{
]}
/>
{[0].map(() => {
const canRequest =
seasons?.find((s) => s.seasonNumber === season.seasonNumber)
?.status === MediaStatus.UNKNOWN;
const canRequest = season.status === MediaStatus.UNKNOWN;
return (
<JellyseerrStatusIcon
key={0}
@@ -324,11 +320,7 @@ const JellyseerrSeasons: React.FC<{
requestSeason(canRequest, season.seasonNumber)
}
className={canRequest ? "bg-gray-700/40" : undefined}
mediaStatus={
seasons?.find(
(s) => s.seasonNumber === season.seasonNumber,
)?.status
}
mediaStatus={season.status}
showRequestIcon={canRequest}
/>
);

View File

@@ -1,4 +1,3 @@
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { Ionicons } from "@expo/vector-icons";
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
@@ -6,6 +5,7 @@ import { useQuery } from "@tanstack/react-query";
import { useRouter } from "expo-router";
import { useAtom } from "jotai";
import { useMemo } from "react";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { Button } from "../Button";
interface Props extends React.ComponentProps<typeof Button> {

View File

@@ -1,18 +1,15 @@
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { getTvShowsApi } from "@jellyfin/sdk/lib/utils/api";
import { FlashList } from "@shopify/flash-list";
import { useQuery } from "@tanstack/react-query";
import { router } from "expo-router";
import { useAtom } from "jotai";
import type React from "react";
import { useTranslation } from "react-i18next";
import { TouchableOpacity, View } from "react-native";
import { View } from "react-native";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import ContinueWatchingPoster from "../ContinueWatchingPoster";
import { ItemCardText } from "../ItemCardText";
import { HorizontalScroll } from "../common/HorrizontalScroll";
import { Text } from "../common/Text";
import { TouchableItemRouter } from "../common/TouchableItemRouter";
import { ItemCardText } from "../ItemCardText";
export const NextUp: React.FC<{ seriesId: string }> = ({ seriesId }) => {
const [user] = useAtom(userAtom);

View File

@@ -1,7 +1,9 @@
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { useEffect, useMemo } from "react";
import { Platform, TouchableOpacity, View } from "react-native";
const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null;
import { t } from "i18next";
import { Text } from "../common/Text";
@@ -30,7 +32,7 @@ export const SeasonDropdown: React.FC<Props> = ({
state,
onSelect,
}) => {
if (Platform.isTV) return null;
const isTv = Platform.isTV;
const keys = useMemo<SeasonKeys>(
() =>
@@ -50,10 +52,11 @@ export const SeasonDropdown: React.FC<Props> = ({
const seasonIndex = useMemo(
() => state[(item[keys.id] as string) ?? ""],
[state],
[state, item, keys],
);
useEffect(() => {
if (isTv) return;
if (seasons && seasons.length > 0 && seasonIndex === undefined) {
let initialIndex: number | undefined;
@@ -79,16 +82,26 @@ export const SeasonDropdown: React.FC<Props> = ({
const initialSeason = seasons.find(
(season: any) => season[keys.index] === initialIndex,
);
if (initialSeason) onSelect(initialSeason!);
else throw Error("Initial index could not be found!");
}
}
}, [seasons, seasonIndex, item[keys.id], initialSeasonIndex]);
}, [
isTv,
seasons,
seasonIndex,
item,
item[keys.id],
initialSeasonIndex,
keys,
onSelect,
]);
const sortByIndex = (a: BaseItemDto, b: BaseItemDto) =>
Number(a[keys.index]) - Number(b[keys.index]);
if (isTv) return null;
return (
<DropdownMenu.Root>
<DropdownMenu.Trigger>

View File

@@ -1,17 +1,17 @@
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { router } from "expo-router";
import { useAtom } from "jotai";
import { useEffect, useMemo, useRef } from "react";
import { TouchableOpacity, View, type ViewProps } from "react-native";
import { TouchableOpacity, type ViewProps } from "react-native";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
import ContinueWatchingPoster from "../ContinueWatchingPoster";
import { ItemCardText } from "../ItemCardText";
import {
HorizontalScroll,
type HorizontalScrollRef,
} from "../common/HorrizontalScroll";
import { ItemCardText } from "../ItemCardText";
interface Props extends ViewProps {
item?: BaseItemDto | null;
@@ -123,7 +123,7 @@ export const SeasonEpisodesCarousel: React.FC<Props> = ({
data={episodes}
extraData={item}
loading={loading || isLoading || isFetching}
renderItem={(_item, idx) => (
renderItem={(_item, _idx) => (
<TouchableOpacity
key={_item.Id}
onPress={() => {

View File

@@ -1,11 +1,4 @@
import {
SeasonDropdown,
type SeasonIndexState,
} from "@/components/series/SeasonDropdown";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
import { runtimeTicksToSeconds } from "@/utils/time";
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
import { Ionicons } from "@expo/vector-icons";
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { getTvShowsApi } from "@jellyfin/sdk/lib/utils/api";
import { useQuery, useQueryClient } from "@tanstack/react-query";
@@ -13,12 +6,19 @@ import { atom, useAtom } from "jotai";
import { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { View } from "react-native";
import {
SeasonDropdown,
type SeasonIndexState,
} from "@/components/series/SeasonDropdown";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
import { runtimeTicksToSeconds } from "@/utils/time";
import ContinueWatchingPoster from "../ContinueWatchingPoster";
import { Text } from "../common/Text";
import { TouchableItemRouter } from "../common/TouchableItemRouter";
import { DownloadItems, DownloadSingleItem } from "../DownloadItem";
import { Loader } from "../Loader";
import { PlayedStatus } from "../PlayedStatus";
import { Text } from "../common/Text";
import { TouchableItemRouter } from "../common/TouchableItemRouter";
type Props = {
item: BaseItemDto;

View File

@@ -1,5 +1,3 @@
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
import { Ionicons } from "@expo/vector-icons";
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
import { useCallback, useMemo } from "react";
@@ -10,6 +8,8 @@ import {
View,
type ViewProps,
} from "react-native";
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
interface Props extends ViewProps {
item: BaseItemDto | MovieDetails | TvDetails;

View File

@@ -1,8 +1,8 @@
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
import { useMemo } from "react";
import { View } from "react-native";
import { Ratings } from "../Ratings";
import { Text } from "../common/Text";
import { Ratings } from "../Ratings";
import { ItemActions } from "./SeriesActions";
interface Props {