Revert "Merge branch 'develop' into chore/expo-52"

This reverts commit 933f3f2f7c, reversing
changes made to f92fee4158.
This commit is contained in:
Fredrik Burmester
2025-01-23 11:34:22 +01:00
parent 933f3f2f7c
commit d5c634b74b
176 changed files with 3718 additions and 6122 deletions

View File

@@ -12,7 +12,6 @@ import { HorizontalScroll } from "../common/HorrizontalScroll";
import { Text } from "../common/Text";
import Poster from "../posters/Poster";
import { itemRouter } from "../common/TouchableItemRouter";
import { useTranslation } from "react-i18next";
interface Props extends ViewProps {
item?: BaseItemDto | null;
@@ -22,7 +21,6 @@ interface Props extends ViewProps {
export const CastAndCrew: React.FC<Props> = ({ item, loading, ...props }) => {
const [api] = useAtom(apiAtom);
const segments = useSegments();
const { t } = useTranslation();
const from = segments[2];
const destinctPeople = useMemo(() => {
@@ -42,7 +40,7 @@ export const CastAndCrew: React.FC<Props> = ({ item, loading, ...props }) => {
return (
<View {...props} className="flex flex-col">
<Text className="text-lg font-bold mb-2 px-4">{t("item_card.cast_and_crew")}</Text>
<Text className="text-lg font-bold mb-2 px-4">Cast & Crew</Text>
<HorizontalScroll
loading={loading}
keyExtractor={(i, idx) => i.Id.toString()}
@@ -57,7 +55,7 @@ export const CastAndCrew: React.FC<Props> = ({ item, loading, ...props }) => {
}}
className="flex flex-col w-28"
>
<Poster id={i.id} url={getPrimaryImageUrl({ api, item: i })} />
<Poster item={i} url={getPrimaryImageUrl({ api, item: i })} />
<Text className="mt-2">{i.Name}</Text>
<Text className="text-xs opacity-50">{i.Role}</Text>
</TouchableOpacity>

View File

@@ -8,7 +8,6 @@ import Poster from "../posters/Poster";
import { HorizontalScroll } from "../common/HorrizontalScroll";
import { Text } from "../common/Text";
import { getPrimaryImageUrlById } from "@/utils/jellyfin/image/getPrimaryImageUrlById";
import { useTranslation } from "react-i18next";
interface Props extends ViewProps {
item?: BaseItemDto | null;
@@ -16,11 +15,10 @@ interface Props extends ViewProps {
export const CurrentSeries: React.FC<Props> = ({ item, ...props }) => {
const [api] = useAtom(apiAtom);
const { t } = useTranslation();
return (
<View {...props}>
<Text className="text-lg font-bold mb-2 px-4">{t("item_card.series")}</Text>
<Text className="text-lg font-bold mb-2 px-4">Series</Text>
<HorizontalScroll
data={[item]}
height={247}
@@ -31,7 +29,7 @@ export const CurrentSeries: React.FC<Props> = ({ item, ...props }) => {
className="flex flex-col space-y-2 w-28"
>
<Poster
id={item.id}
item={item}
url={getPrimaryImageUrlById({ api, id: item.ParentId })}
/>
<Text>{item.SeriesName}</Text>

View File

@@ -5,7 +5,7 @@ import { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
import { FlashList } from "@shopify/flash-list";
import { orderBy } from "lodash";
import { Tags } from "@/components/GenreTags";
import JellyseerrStatusIcon from "@/components/jellyseerr/JellyseerrStatusIcon";
import JellyseerrIconStatus from "@/components/icons/JellyseerrIconStatus";
import Season from "@/utils/jellyseerr/server/entity/Season";
import {
MediaStatus,
@@ -20,9 +20,7 @@ import { HorizontalScroll } from "@/components/common/HorrizontalScroll";
import { Image } from "expo-image";
import MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest";
import { Loader } from "../Loader";
import { t } from "i18next";
import {MovieDetails} from "@/utils/jellyseerr/server/models/Movie";
import {MediaRequestBody} from "@/utils/jellyseerr/server/interfaces/api/requestInterfaces";
const JellyseerrSeasonEpisodes: React.FC<{
details: TvDetails;
@@ -63,7 +61,7 @@ const RenderItem = ({ item, index }: any) => {
key={item.id}
id={item.id}
source={{
uri: jellyseerrApi?.imageProxy(item.stillPath),
uri: jellyseerrApi?.tvStillImageProxy(item.stillPath),
}}
cachePolicy={"memory-disk"}
contentFit="cover"
@@ -103,17 +101,8 @@ const JellyseerrSeasons: React.FC<{
isLoading: boolean;
result?: TvResult;
details?: TvDetails;
hasAdvancedRequest?: boolean,
onAdvancedRequest?: (data: MediaRequestBody) => void;
refetch: (options?: (RefetchOptions | undefined)) => Promise<QueryObserverResult<TvDetails | MovieDetails | undefined, Error>>;
}> = ({
isLoading,
result,
details,
refetch,
hasAdvancedRequest,
onAdvancedRequest,
}) => {
}> = ({ isLoading, result, details, refetch }) => {
if (!details) return null;
const { jellyseerrApi, requestMedia } = useJellyseerr();
@@ -153,7 +142,7 @@ const JellyseerrSeasons: React.FC<{
const requestAll = useCallback(() => {
if (details && jellyseerrApi) {
const body: MediaRequestBody = {
requestMedia(result?.name!!, {
mediaId: details.id,
mediaType: MediaType.TV,
tvdbId: details.externalIds?.tvdbId,
@@ -162,25 +151,19 @@ const JellyseerrSeasons: React.FC<{
(s) => s.status === MediaStatus.UNKNOWN && s.seasonNumber !== 0
)
.map((s) => s.seasonNumber),
}
if (hasAdvancedRequest) {
return onAdvancedRequest?.(body)
}
requestMedia(result?.name!!, body, refetch);
});
}
}, [jellyseerrApi, seasons, details, hasAdvancedRequest, onAdvancedRequest]);
}, [jellyseerrApi, seasons, details]);
const promptRequestAll = useCallback(
() =>
Alert.alert(t("jellyseerr.confirm"), t("jellyseerr.are_you_sure_you_want_to_request_all_seasons"), [
Alert.alert("Confirm", "Are you sure you want to request all seasons?", [
{
text: t("jellyseerr.cancel"),
text: "Cancel",
style: "cancel",
},
{
text: t("jellyseerr.yes"),
text: "Yes",
onPress: requestAll,
},
]),
@@ -189,26 +172,24 @@ const JellyseerrSeasons: React.FC<{
const requestSeason = useCallback(async (canRequest: Boolean, seasonNumber: number) => {
if (canRequest) {
const body: MediaRequestBody = {
mediaId: details.id,
mediaType: MediaType.TV,
tvdbId: details.externalIds?.tvdbId,
seasons: [seasonNumber],
}
if (hasAdvancedRequest) {
return onAdvancedRequest?.(body)
}
requestMedia(`${result?.name!!}, Season ${seasonNumber}`, body, refetch);
requestMedia(
`${result?.name!!}, Season ${seasonNumber}`,
{
mediaId: details.id,
mediaType: MediaType.TV,
tvdbId: details.externalIds?.tvdbId,
seasons: [seasonNumber],
},
refetch
)
}
}, [requestMedia, hasAdvancedRequest, onAdvancedRequest]);
}, [requestMedia]);
if (isLoading)
return (
<View>
<View className="flex flex-row justify-between items-end px-4">
<Text className="text-lg font-bold mb-2">{t("item_card.seasons")}</Text>
<Text className="text-lg font-bold mb-2">Seasons</Text>
{!allSeasonsAvailable && (
<RoundButton className="mb-2 pa-2" onPress={promptRequestAll}>
<Ionicons name="bag-add" color="white" size={26} />
@@ -228,7 +209,7 @@ const JellyseerrSeasons: React.FC<{
)}
ListHeaderComponent={() => (
<View className="flex flex-row justify-between items-end px-4">
<Text className="text-lg font-bold mb-2">{t("item_card.seasons")}</Text>
<Text className="text-lg font-bold mb-2">Seasons</Text>
{!allSeasonsAvailable && (
<RoundButton className="mb-2 pa-2" onPress={promptRequestAll}>
<Ionicons name="bag-add" color="white" size={26} />
@@ -256,8 +237,8 @@ const JellyseerrSeasons: React.FC<{
<Tags
textClass=""
tags={[
t("jellyseerr.season_number", {season_number: season.seasonNumber}),
t("jellyseerr.number_episodes", {episode_number: season.episodeCount}),
`Season ${season.seasonNumber}`,
`${season.episodeCount} Episodes`,
]}
/>
{[0].map(() => {
@@ -265,7 +246,7 @@ const JellyseerrSeasons: React.FC<{
seasons?.find((s) => s.seasonNumber === season.seasonNumber)
?.status === MediaStatus.UNKNOWN;
return (
<JellyseerrStatusIcon
<JellyseerrIconStatus
key={0}
onPress={() => requestSeason(canRequest, season.seasonNumber)}
className={canRequest ? "bg-gray-700/40" : undefined}

View File

@@ -12,12 +12,10 @@ import ContinueWatchingPoster from "../ContinueWatchingPoster";
import { ItemCardText } from "../ItemCardText";
import { TouchableItemRouter } from "../common/TouchableItemRouter";
import { FlashList } from "@shopify/flash-list";
import { useTranslation } from "react-i18next";
export const NextUp: React.FC<{ seriesId: string }> = ({ seriesId }) => {
const [user] = useAtom(userAtom);
const [api] = useAtom(apiAtom);
const { t } = useTranslation();
const { data: items } = useQuery({
queryKey: ["nextUp", seriesId],
@@ -39,14 +37,14 @@ export const NextUp: React.FC<{ seriesId: string }> = ({ seriesId }) => {
if (!items?.length)
return (
<View className="px-4">
<Text className="text-lg font-bold mb-2">{t("item_card.next_up")}</Text>
<Text className="opacity-50">{t("item_card.no_items_to_display")}</Text>
<Text className="text-lg font-bold mb-2">Next up</Text>
<Text className="opacity-50">No items to display</Text>
</View>
);
return (
<View>
<Text className="text-lg font-bold px-4 mb-2">{t("item_card.next_up")}</Text>
<Text className="text-lg font-bold px-4 mb-2">Next up</Text>
<FlashList
contentContainerStyle={{ paddingLeft: 16 }}
horizontal

View File

@@ -3,7 +3,6 @@ import { useEffect, useMemo } from "react";
import { TouchableOpacity, View } from "react-native";
import * as DropdownMenu from "zeego/dropdown-menu";
import { Text } from "../common/Text";
import { t } from "i18next";
type Props = {
item: BaseItemDto;
@@ -92,7 +91,7 @@ export const SeasonDropdown: React.FC<Props> = ({
<DropdownMenu.Trigger>
<View className="flex flex-row">
<TouchableOpacity className="bg-neutral-900 rounded-2xl border-neutral-900 border px-3 py-2 flex flex-row items-center justify-between">
<Text>{t("item_card.season")} {seasonIndex}</Text>
<Text>Season {seasonIndex}</Text>
</TouchableOpacity>
</View>
</DropdownMenu.Trigger>
@@ -105,7 +104,7 @@ export const SeasonDropdown: React.FC<Props> = ({
collisionPadding={8}
sideOffset={8}
>
<DropdownMenu.Label>{t("item_card.seasons")}</DropdownMenu.Label>
<DropdownMenu.Label>Seasons</DropdownMenu.Label>
{seasons?.sort(sortByIndex).map((season: any) => (
<DropdownMenu.Item
key={season[keys.title]}

View File

@@ -17,7 +17,7 @@ import {
SeasonIndexState,
} from "@/components/series/SeasonDropdown";
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
import { useTranslation } from "react-i18next";
type Props = {
item: BaseItemDto;
initialSeasonIndex?: number;
@@ -29,7 +29,6 @@ export const SeasonPicker: React.FC<Props> = ({ item, initialSeasonIndex }) => {
const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom);
const [seasonIndexState, setSeasonIndexState] = useAtom(seasonIndexAtom);
const { t } = useTranslation();
const seasonIndex = useMemo(
() => seasonIndexState[item.Id ?? ""],
@@ -146,7 +145,7 @@ export const SeasonPicker: React.FC<Props> = ({ item, initialSeasonIndex }) => {
/>
{episodes?.length || 0 > 0 ? (
<DownloadItems
title={t("item_card.download.download_season")}
title="Download Season"
className="ml-2"
items={episodes || []}
MissingDownloadIconComponent={() => (
@@ -211,7 +210,7 @@ export const SeasonPicker: React.FC<Props> = ({ item, initialSeasonIndex }) => {
{(episodes?.length || 0) === 0 ? (
<View className="flex flex-col">
<Text className="text-neutral-500">
{t("item_card.no_episodes_for_this_season")}
No episodes for this season
</Text>
</View>
) : null}

View File

@@ -1,45 +1,24 @@
import { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
import { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
import { Ionicons } from "@expo/vector-icons";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
import { useRouter } from "expo-router";
import { useCallback, useMemo } from "react";
import {
Alert,
Linking,
TouchableOpacity,
View,
ViewProps,
} from "react-native";
import { TouchableOpacity, View, ViewProps } from "react-native";
interface Props extends ViewProps {
item: BaseItemDto | MovieDetails | TvDetails;
item: BaseItemDto;
}
export const ItemActions = ({ item, ...props }: Props) => {
const trailerLink = useMemo(() => {
if ("RemoteTrailers" in item && item.RemoteTrailers?.[0]?.Url) {
return item.RemoteTrailers[0].Url;
}
const router = useRouter();
if ("relatedVideos" in item) {
return item.relatedVideos?.find((v) => v.type === "Trailer")?.url;
}
return undefined;
}, [item]);
const trailerLink = useMemo(() => item.RemoteTrailers?.[0]?.Url, [item]);
const openTrailer = useCallback(async () => {
if (!trailerLink) {
Alert.alert("No trailer available");
return;
}
if (!trailerLink) return;
try {
await Linking.openURL(trailerLink);
} catch (err) {
console.error("Failed to open trailer link:", err);
}
}, [trailerLink]);
const encodedTrailerLink = encodeURIComponent(trailerLink);
router.push(`/trailer/page?url=${encodedTrailerLink}`);
}, [router, trailerLink]);
return (
<View className="" {...props}>