From d13731c28f795f646be88825db4e72b3a8183be3 Mon Sep 17 00:00:00 2001 From: herrrta <73949927+herrrta@users.noreply.github.com> Date: Sat, 4 Jan 2025 15:51:58 -0500 Subject: [PATCH 01/49] Requesting using purple request button doesnt refresh page fixes #363 --- .../jellyseerr/page.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx index 42edcb59..3f6daae9 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useRef, useState } from "react"; +import React, {useCallback, useMemo, useRef, useState} from "react"; import { useLocalSearchParams } from "expo-router"; import { MovieResult, TvResult } from "@/utils/jellyseerr/server/models/Search"; import { Text } from "@/components/common/Text"; @@ -9,7 +9,7 @@ import { Ionicons } from "@expo/vector-icons"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { OverviewText } from "@/components/OverviewText"; import { GenreTags } from "@/components/GenreTags"; -import { MediaType } from "@/utils/jellyseerr/server/constants/media"; +import {MediaRequestStatus, MediaStatus, MediaType} from "@/utils/jellyseerr/server/constants/media"; import { useQuery } from "@tanstack/react-query"; import { useJellyseerr } from "@/hooks/useJellyseerr"; import { Button } from "@/components/Button"; @@ -27,6 +27,7 @@ import * as DropdownMenu from "zeego/dropdown-menu"; import { TvDetails } from "@/utils/jellyseerr/server/models/Tv"; import JellyseerrSeasons from "@/components/series/JellyseerrSeasons"; import { JellyserrRatings } from "@/components/Ratings"; +import MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest"; const Page: React.FC = () => { const insets = useSafeAreaInsets(); @@ -44,7 +45,6 @@ const Page: React.FC = () => { posterSrc: string; } & Partial; - const canRequest = canRequestString === "true"; const { jellyseerrApi, requestMedia } = useJellyseerr(); const [issueType, setIssueType] = useState(); @@ -72,6 +72,14 @@ const Page: React.FC = () => { }, }); + const canRequest = useMemo(() => { + const pendingRequests = details?.mediaInfo?.requests + ?.some((r: MediaRequest) => r.status == MediaRequestStatus.PENDING || r.status == MediaRequestStatus.APPROVED) + + return (details?.mediaInfo?.status === MediaStatus.UNKNOWN && !pendingRequests) || + (!details?.mediaInfo?.status && canRequestString === "true"); + }, [canRequestString, details]); + const renderBackdrop = useCallback( (props: BottomSheetBackdropProps) => ( Date: Sat, 4 Jan 2025 17:11:01 -0500 Subject: [PATCH 02/49] Add badge to differentiate a movie/series fixes #332 --- components/GenreTags.tsx | 7 ++-- components/jellyseerr/JellyseerrMediaIcon.tsx | 37 +++++++++++++++++++ .../JellyseerrStatusIcon.tsx} | 5 +-- components/posters/JellyseerrPoster.tsx | 16 +++++--- components/series/JellyseerrSeasons.tsx | 4 +- 5 files changed, 55 insertions(+), 14 deletions(-) create mode 100644 components/jellyseerr/JellyseerrMediaIcon.tsx rename components/{icons/JellyseerrIconStatus.tsx => jellyseerr/JellyseerrStatusIcon.tsx} (93%) diff --git a/components/GenreTags.tsx b/components/GenreTags.tsx index cc5db670..35de54a6 100644 --- a/components/GenreTags.tsx +++ b/components/GenreTags.tsx @@ -1,6 +1,6 @@ // GenreTags.tsx import React from "react"; -import {View, ViewProps} from "react-native"; +import {StyleProp, TextStyle, View, ViewProps} from "react-native"; import { Text } from "./common/Text"; interface TagProps { @@ -8,14 +8,15 @@ interface TagProps { textClass?: ViewProps["className"] } -export const Tag: React.FC<{ text: string, textClass?: ViewProps["className"]} & ViewProps> = ({ +export const Tag: React.FC<{ text: string, textClass?: ViewProps["className"], textStyle?: StyleProp} & ViewProps> = ({ text, textClass, + textStyle, ...props }) => { return ( - {text} + {text} ); }; diff --git a/components/jellyseerr/JellyseerrMediaIcon.tsx b/components/jellyseerr/JellyseerrMediaIcon.tsx new file mode 100644 index 00000000..97a5ab69 --- /dev/null +++ b/components/jellyseerr/JellyseerrMediaIcon.tsx @@ -0,0 +1,37 @@ +import {useMemo} from "react"; +import {MediaType} from "@/utils/jellyseerr/server/constants/media"; +import {Feather, MaterialCommunityIcons} from "@expo/vector-icons"; +import {View, ViewProps} from "react-native"; + +const JellyseerrMediaIcon: React.FC<{ mediaType: "tv" | "movie" } & ViewProps> = ({ + mediaType, + className, + ...props +}) => { + const style = useMemo( + () => mediaType === MediaType.MOVIE + ? 'bg-blue-600/90 border-blue-400/40' + : 'bg-purple-600/90 border-purple-400/40', + [mediaType] + ); + return ( + mediaType && + + {mediaType === MediaType.MOVIE ? ( + + ) : ( + + )} + + ) +} + +export default JellyseerrMediaIcon; \ No newline at end of file diff --git a/components/icons/JellyseerrIconStatus.tsx b/components/jellyseerr/JellyseerrStatusIcon.tsx similarity index 93% rename from components/icons/JellyseerrIconStatus.tsx rename to components/jellyseerr/JellyseerrStatusIcon.tsx index 4c1bda37..8fc593fa 100644 --- a/components/icons/JellyseerrIconStatus.tsx +++ b/components/jellyseerr/JellyseerrStatusIcon.tsx @@ -2,7 +2,6 @@ import {useEffect, useState} from "react"; import {MediaStatus} from "@/utils/jellyseerr/server/constants/media"; import {MaterialCommunityIcons} from "@expo/vector-icons"; import {TouchableOpacity, View, ViewProps} from "react-native"; -import {MovieResult, TvResult} from "@/utils/jellyseerr/server/models/Search"; interface Props { mediaStatus?: MediaStatus; @@ -10,7 +9,7 @@ interface Props { onPress?: () => void; } -const JellyseerrIconStatus: React.FC = ({ +const JellyseerrStatusIcon: React.FC = ({ mediaStatus, showRequestIcon, onPress, @@ -69,4 +68,4 @@ const JellyseerrIconStatus: React.FC = ({ ) } -export default JellyseerrIconStatus; \ No newline at end of file +export default JellyseerrStatusIcon; \ No newline at end of file diff --git a/components/posters/JellyseerrPoster.tsx b/components/posters/JellyseerrPoster.tsx index 5a9647ae..0de80a75 100644 --- a/components/posters/JellyseerrPoster.tsx +++ b/components/posters/JellyseerrPoster.tsx @@ -1,14 +1,14 @@ import {View, ViewProps} from "react-native"; import {Image} from "expo-image"; -import {MaterialCommunityIcons} from "@expo/vector-icons"; import {Text} from "@/components/common/Text"; -import {useEffect, useMemo, useState} from "react"; -import {MovieResult, Results, TvResult} from "@/utils/jellyseerr/server/models/Search"; +import {useMemo} from "react"; +import {MovieResult, TvResult} from "@/utils/jellyseerr/server/models/Search"; import {MediaStatus, MediaType} from "@/utils/jellyseerr/server/constants/media"; import {useJellyseerr} from "@/hooks/useJellyseerr"; import {hasPermission, Permission} from "@/utils/jellyseerr/server/lib/permissions"; import {TouchableJellyseerrRouter} from "@/components/common/JellyseerrItemRouter"; -import JellyseerrIconStatus from "@/components/icons/JellyseerrIconStatus"; +import JellyseerrStatusIcon from "@/components/jellyseerr/JellyseerrStatusIcon"; +import JellyseerrMediaIcon from "@/components/jellyseerr/JellyseerrMediaIcon"; interface Props extends ViewProps { item: MovieResult | TvResult; } @@ -72,16 +72,20 @@ const JellyseerrPoster: React.FC = ({ width: "100%", }} /> - + {title} - {releaseYear} + {releaseYear} diff --git a/components/series/JellyseerrSeasons.tsx b/components/series/JellyseerrSeasons.tsx index bcd9b336..d6dedeb6 100644 --- a/components/series/JellyseerrSeasons.tsx +++ b/components/series/JellyseerrSeasons.tsx @@ -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 JellyseerrIconStatus from "@/components/icons/JellyseerrIconStatus"; +import JellyseerrStatusIcon from "@/components/jellyseerr/JellyseerrStatusIcon"; import Season from "@/utils/jellyseerr/server/entity/Season"; import { MediaStatus, @@ -246,7 +246,7 @@ const JellyseerrSeasons: React.FC<{ seasons?.find((s) => s.seasonNumber === season.seasonNumber) ?.status === MediaStatus.UNKNOWN; return ( - requestSeason(canRequest, season.seasonNumber)} className={canRequest ? "bg-gray-700/40" : undefined} From 29bba04fddce8f813cfa847767202e7a8610e0ee Mon Sep 17 00:00:00 2001 From: herrrta <73949927+herrrta@users.noreply.github.com> Date: Sat, 4 Jan 2025 17:22:46 -0500 Subject: [PATCH 03/49] react-native-ios-utilities major having issues locked to 4.5.1 --- bun.lockb | Bin 593313 -> 593709 bytes package.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/bun.lockb b/bun.lockb index fc89caa05b34b9ec9f9de5584bb5bf4d05c6803a..cdb35dc58ab89a60fe58e93e0cb5bd0b008b43f6 100755 GIT binary patch delta 28428 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

=GFLETHbQfMhe~SwM}+ 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 Ja>3I*{|{gMvVs5r diff --git a/package.json b/package.json index 6bbe1d00..1284adac 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "react-native-google-cast": "^4.8.3", "react-native-image-colors": "^2.4.0", "react-native-ios-context-menu": "^2.5.2", - "react-native-ios-utilities": "^4.5.1", + "react-native-ios-utilities": "4.5.1", "react-native-mmkv": "^2.12.2", "react-native-pager-view": "6.3.0", "react-native-progress": "^5.0.1", From d84ed558f3ca8e809d5a330fcea4f4894271c348 Mon Sep 17 00:00:00 2001 From: herrrta <73949927+herrrta@users.noreply.github.com> Date: Sat, 4 Jan 2025 21:04:12 -0500 Subject: [PATCH 04/49] [Jellyseerr] Show facts about media result implements #325 --- .../jellyseerr/page.tsx | 5 + bun.lockb | Bin 593709 -> 594112 bytes components/jellyseerr/DetailFacts.tsx | 167 ++++++++++++++++++ package.json | 1 + 4 files changed, 173 insertions(+) create mode 100644 components/jellyseerr/DetailFacts.tsx diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx index 3f6daae9..25fc8cb8 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx @@ -28,6 +28,7 @@ import { TvDetails } from "@/utils/jellyseerr/server/models/Tv"; import JellyseerrSeasons from "@/components/series/JellyseerrSeasons"; import { JellyserrRatings } from "@/components/Ratings"; import MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest"; +import DetailFacts from "@/components/jellyseerr/DetailFacts"; const Page: React.FC = () => { const insets = useSafeAreaInsets(); @@ -221,6 +222,10 @@ const Page: React.FC = () => { refetch={refetch} /> )} + diff --git a/bun.lockb b/bun.lockb index cdb35dc58ab89a60fe58e93e0cb5bd0b008b43f6..bc0866e3a89645142977532dc97961a5c8d13802 100755 GIT binary patch delta 92497 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 delta 93703 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 = ({title, facts, ...props}) => ( + facts && facts?.length > 0 && ( + + {title} + + + {facts.map((f, idx) => + typeof f === "string" ? {f} : f + )} + + + ) +) + +const Fact: React.FC<{title: string, fact?: string | null} & ViewProps> = ({title, fact, ...props}) => ( + fact && +) + +const DetailFacts: React.FC<{ details?: MovieDetails | TvDetails } & ViewProps> = ({ + details, + className, + ...props +}) => { + const {jellyseerrUser} = useJellyseerr(); + + const locale = useMemo(() => { + return jellyseerrUser?.settings?.locale || 'en' + }, [jellyseerrUser]); + + const region = useMemo( + () => jellyseerrUser?.settings?.region || 'US', + [jellyseerrUser] + ); + + const releases = useMemo( + () => (details as MovieDetails)?.releases?.results.find((r: TmdbRelease) => r.iso_3166_1 === region)?.release_dates as TmdbRelease['release_dates'], + [details] + ); + + // Release date types: + // 1. Premiere + // 2. Theatrical (limited) + // 3. Theatrical + // 4. Digital + // 5. Physical + // 6. TV + const filteredReleases = useMemo( + () => uniqBy(releases?.filter((r: Release) => r.type > 2 && r.type < 6), 'type'), + [releases] + ); + + const firstAirDate = useMemo(() => { + const firstAirDate = (details as TvDetails)?.firstAirDate + if (firstAirDate) { + return new Date(firstAirDate).toLocaleDateString(`${locale}-${region}`, dateOpts) + } + }, [details]); + + const nextAirDate = useMemo(() => { + const firstAirDate = (details as TvDetails)?.firstAirDate + const nextAirDate = (details as TvDetails)?.nextEpisodeToAir?.airDate + if (nextAirDate && firstAirDate !== nextAirDate) { + return new Date(nextAirDate).toLocaleDateString(`${locale}-${region}`, dateOpts) + } + }, [details]); + + const revenue = useMemo( + () => (details as MovieDetails)?.revenue + ?.toLocaleString?.(`${locale}-${region}`, {style: 'currency', currency: "USD"}), + [details] + ); + + const budget = useMemo( + () => (details as MovieDetails)?.budget + ?.toLocaleString?.(`${locale}-${region}`, {style: 'currency', currency: "USD"}), + [details] + ); + + const streamingProviders = useMemo( + () => details?.watchProviders?.find((provider) => provider.iso_3166_1 === region)?.flatrate, + [details] + ); + + const networks = useMemo( + () => (details as TvDetails)?.networks, + [details] + ); + + const spokenLanguage = useMemo( + () => details?.spokenLanguages.find((lng) => lng.iso_639_1 === details.originalLanguage)?.name, + [details] + ); + + return ( + details && + Details + + + + {details.keywords.some((keyword) => keyword.id === ANIME_KEYWORD_ID) && ( + + )} + + + {r.type === 3 ? ( + // Theatrical + + ) : r.type === 4 ? ( + // Digital + + ) : ( + // Physical + + )} + {new Date(r.release_date).toLocaleDateString(`${locale}-${region}`, dateOpts)} + + )} + /> + + + + + + + + + {n.name} + + )}/> + n.name)}/> + n.name)}/> + s.name)}/> + + + ) +} + +export default DetailFacts; \ No newline at end of file diff --git a/package.json b/package.json index 1284adac..b0a0ce75 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "react-native-bottom-tabs": "0.7.1", "react-native-circular-progress": "^1.4.1", "react-native-compressor": "^1.9.0", + "react-native-country-flag": "^2.0.2", "react-native-device-info": "^14.0.1", "react-native-edge-to-edge": "^1.1.3", "react-native-gesture-handler": "~2.16.1", From 734678b1d5ebf4ae48f853fae12c218a80bfce28 Mon Sep 17 00:00:00 2001 From: herrrta <73949927+herrrta@users.noreply.github.com> Date: Sat, 4 Jan 2025 21:28:45 -0500 Subject: [PATCH 05/49] [Jellyseerr] Add external links to trailers implements #328 --- .../jellyseerr/page.tsx | 18 ++++++++++++++++-- components/series/SeriesActions.tsx | 11 +++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx index 25fc8cb8..31334dcc 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx @@ -1,5 +1,5 @@ -import React, {useCallback, useMemo, useRef, useState} from "react"; -import { useLocalSearchParams } from "expo-router"; +import React, {useCallback, useEffect, useMemo, useRef, useState} from "react"; +import {useLocalSearchParams, useNavigation} from "expo-router"; import { MovieResult, TvResult } from "@/utils/jellyseerr/server/models/Search"; import { Text } from "@/components/common/Text"; import { ParallaxScrollView } from "@/components/ParallaxPage"; @@ -29,6 +29,7 @@ import JellyseerrSeasons from "@/components/series/JellyseerrSeasons"; import { JellyserrRatings } from "@/components/Ratings"; import MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest"; import DetailFacts from "@/components/jellyseerr/DetailFacts"; +import {ItemActions} from "@/components/series/SeriesActions"; const Page: React.FC = () => { const insets = useSafeAreaInsets(); @@ -46,6 +47,7 @@ const Page: React.FC = () => { posterSrc: string; } & Partial; + const navigation = useNavigation(); const { jellyseerrApi, requestMedia } = useJellyseerr(); const [issueType, setIssueType] = useState(); @@ -120,6 +122,18 @@ const Page: React.FC = () => { [details, result, requestMedia] ); + useEffect(() => { + if (details) { + navigation.setOptions({ + headerRight: () => + + + + }); + } + + }, [details]); + return ( { const router = useRouter(); - const trailerLink = useMemo(() => item.RemoteTrailers?.[0]?.Url, [item]); + const trailerLink = useMemo(() => { + const url = (item as BaseItemDto).RemoteTrailers?.[0]?.Url + if (url) + return url + return (item as MovieDetails | TvDetails)?.relatedVideos?.find(v => v.type === "Trailer")?.url + }, [item]); const openTrailer = useCallback(async () => { if (!trailerLink) return; From db4046267ff72d69a34d5b6cf36a4b50b354a2ca Mon Sep 17 00:00:00 2001 From: herrrta <73949927+herrrta@users.noreply.github.com> Date: Sun, 5 Jan 2025 02:53:41 -0500 Subject: [PATCH 06/49] [Jellyseerr] Add cast/crew results implements #327 --- .../jellyseerr/[personId].tsx | 206 ++++++++++++++++++ .../jellyseerr/page.tsx | 7 +- app/(auth)/(tabs)/(search)/_layout.tsx | 1 + app/(auth)/(tabs)/(search)/index.tsx | 24 +- components/jellyseerr/Cast.tsx | 34 +++ components/jellyseerr/PersonPoster.tsx | 42 ++++ components/posters/JellyseerrPoster.tsx | 8 +- components/posters/Poster.tsx | 14 +- components/series/CastAndCrew.tsx | 2 +- components/series/CurrentSeries.tsx | 2 +- components/series/JellyseerrSeasons.tsx | 2 +- hooks/useJellyseerr.ts | 39 +++- 12 files changed, 354 insertions(+), 27 deletions(-) create mode 100644 app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/[personId].tsx create mode 100644 components/jellyseerr/Cast.tsx create mode 100644 components/jellyseerr/PersonPoster.tsx diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/[personId].tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/[personId].tsx new file mode 100644 index 00000000..f2219b26 --- /dev/null +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/[personId].tsx @@ -0,0 +1,206 @@ +import {router, useLocalSearchParams, useNavigation, useSegments} from "expo-router"; +import React, {useCallback, useEffect, useMemo, useRef, useState} from "react"; +import {TouchableOpacity, View} from "react-native"; +import {useQuery} from "@tanstack/react-query"; +import {useJellyseerr} from "@/hooks/useJellyseerr"; +import {useSafeAreaInsets} from "react-native-safe-area-context"; +import {ParallaxScrollView} from "@/components/ParallaxPage"; +import {Text} from "@/components/common/Text"; +import {Animated} from "react-native"; +import {Image} from "expo-image"; +import {OverviewText} from "@/components/OverviewText"; +import {orderBy} from "lodash"; +import {FlashList} from "@shopify/flash-list"; +import {PersonCreditCast} from "@/utils/jellyseerr/server/models/Person"; +import Poster from "@/components/posters/Poster"; +import JellyseerrMediaIcon from "@/components/jellyseerr/JellyseerrMediaIcon"; + +const ANIMATION_ENTER = 250 +const ANIMATION_EXIT = 250 +const BACKDROP_DURATION = 5000 + +export default function page() { + const insets = useSafeAreaInsets(); + const local = useLocalSearchParams(); + const segments = useSegments(); + const {jellyseerrApi, jellyseerrUser} = useJellyseerr(); + + const { personId } = local as { personId: string; }; + const from = segments[2]; + + const [currentIndex, setCurrentIndex] = useState(0); + const fadeAnim = useRef(new Animated.Value(0)).current; + + const {data, isLoading, isFetching} = useQuery({ + queryKey: ['jellyseerr', 'person', personId], + queryFn: async () => ({ + details: await jellyseerrApi?.personDetails(personId), + combinedCredits: await jellyseerrApi?.personCombinedCredits(personId) + }), + enabled: !!jellyseerrApi && !!personId + }); + + const locale = useMemo(() => { + return jellyseerrUser?.settings?.locale || 'en' + }, [jellyseerrUser]); + + const region = useMemo( + () => jellyseerrUser?.settings?.region || 'US', + [jellyseerrUser] + ); + + const castedRoles: PersonCreditCast[] = useMemo( + () => orderBy(data?.combinedCredits?.cast, ['voteCount', 'voteAverage'], 'desc'), + [data?.combinedCredits] + ); + + const backdrops = useMemo( + () => castedRoles.map(c => c.backdropPath), + [data?.combinedCredits] + ) + + const enterAnimation = useCallback(() => + Animated.timing(fadeAnim, { + toValue: 1, + duration: ANIMATION_ENTER, + useNativeDriver: true, + }), [fadeAnim]); + + const exitAnimation = useCallback(() => + Animated.timing(fadeAnim, { + toValue: 0, + duration: ANIMATION_EXIT, + useNativeDriver: true, + }), [fadeAnim]); + + useEffect(() => { + if (backdrops?.length) { + enterAnimation().start() + const intervalId = setInterval( + () => { + exitAnimation().start(end => { + if (end.finished) + setCurrentIndex((prevIndex) => (prevIndex + 1) % backdrops?.length) + }) + + }, + BACKDROP_DURATION + ); + + return () => clearInterval(intervalId); + } + }, [backdrops, enterAnimation, exitAnimation, setCurrentIndex, currentIndex]); + + const viewDetails = (credit: PersonCreditCast) => { + router.push({ + //@ts-ignore + pathname: `/(auth)/(tabs)/${from}/jellyseerr/page`, + //@ts-ignore + params: { + ...credit, + mediaTitle: credit.title, + releaseYear: new Date(credit.releaseDate).getFullYear(), + canRequest: "false", + posterSrc: jellyseerrApi?.imageProxy(credit.posterPath, 'w300_and_h450_face') + } + }); + } + + return ( + + + } + logo={ + + } + > + + + + + {data?.details?.name} + + + Born {new Date(data?.details?.birthday!!).toLocaleDateString(`${locale}-${region}`, { + year: 'numeric', + month: 'long', + day: 'numeric', + })} | {data?.details?.placeOfBirth} + + + + + + + + No results + + } + contentInsetAdjustmentBehavior="automatic" + ListHeaderComponent={ + Appearances + } + renderItem={({item}) => + viewDetails(item)} + > + + + {/*{item.title}*/} + as {item.character} + + } + keyExtractor={(item) => item.id.toString()} + estimatedItemSize={255} + numColumns={3} + contentContainerStyle={{paddingBottom: 24}} + ItemSeparatorComponent={() => ( + + )} + /> + + + + + ); +} \ No newline at end of file diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx index 31334dcc..805727fd 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx @@ -30,6 +30,7 @@ import { JellyserrRatings } from "@/components/Ratings"; import MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest"; import DetailFacts from "@/components/jellyseerr/DetailFacts"; import {ItemActions} from "@/components/series/SeriesActions"; +import Cast from "@/components/jellyseerr/Cast"; const Page: React.FC = () => { const insets = useSafeAreaInsets(); @@ -156,7 +157,7 @@ const Page: React.FC = () => { height: "100%", }} source={{ - uri: `https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/${result.backdropPath}`, + uri: jellyseerrApi?.imageProxy(result.backdropPath, 'w1920_and_h800_multi_faces'), }} /> ) : ( @@ -240,6 +241,10 @@ const Page: React.FC = () => { className="p-2 border border-neutral-800 bg-neutral-900 rounded-xl" details={details} /> + diff --git a/app/(auth)/(tabs)/(search)/_layout.tsx b/app/(auth)/(tabs)/(search)/_layout.tsx index 12cbad20..1119e2a4 100644 --- a/app/(auth)/(tabs)/(search)/_layout.tsx +++ b/app/(auth)/(tabs)/(search)/_layout.tsx @@ -36,6 +36,7 @@ export default function SearchLayout() { }} /> + ); } diff --git a/app/(auth)/(tabs)/(search)/index.tsx b/app/(auth)/(tabs)/(search)/index.tsx index 7d9ecebe..fcf8119d 100644 --- a/app/(auth)/(tabs)/(search)/index.tsx +++ b/app/(auth)/(tabs)/(search)/index.tsx @@ -31,12 +31,13 @@ import { Platform, ScrollView, TouchableOpacity, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useDebounce } from "use-debounce"; import { useJellyseerr } from "@/hooks/useJellyseerr"; -import { MovieResult, TvResult } from "@/utils/jellyseerr/server/models/Search"; +import {MovieResult, PersonResult, TvResult} from "@/utils/jellyseerr/server/models/Search"; import { MediaType } from "@/utils/jellyseerr/server/constants/media"; import JellyseerrPoster from "@/components/posters/JellyseerrPoster"; import { Tag } from "@/components/GenreTags"; import DiscoverSlide from "@/components/jellyseerr/DiscoverSlide"; import { sortBy } from "lodash"; +import PersonPoster from "@/components/jellyseerr/PersonPoster"; type SearchType = "Library" | "Discover"; @@ -191,6 +192,14 @@ export default function search() { [jellyseerrResults] ); + const jellyseerrPersonResults: PersonResult[] | undefined = useMemo( + () => + jellyseerrResults?.filter( + (r) => r.mediaType === "person" + ) as PersonResult[], + [jellyseerrResults] + ); + const { data: series, isFetching: l2 } = useQuery({ queryKey: ["search", "series", debouncedSearch], queryFn: () => @@ -486,6 +495,19 @@ export default function search() { )} /> + ( + + )} + /> )} diff --git a/components/jellyseerr/Cast.tsx b/components/jellyseerr/Cast.tsx new file mode 100644 index 00000000..7642382d --- /dev/null +++ b/components/jellyseerr/Cast.tsx @@ -0,0 +1,34 @@ +import {View, ViewProps} from "react-native"; +import {MovieDetails} from "@/utils/jellyseerr/server/models/Movie"; +import {TvDetails} from "@/utils/jellyseerr/server/models/Tv"; +import React from "react"; +import {FlashList} from "@shopify/flash-list"; +import {Text} from "@/components/common/Text"; +import PersonPoster from "@/components/jellyseerr/PersonPoster"; + +const CastSlide: React.FC<{ details?: MovieDetails | TvDetails } & ViewProps> = ({ details, ...props }) => { + return ( + details?.credits?.cast?.length && details?.credits?.cast?.length > 0 && + + Cast + } + estimatedItemSize={15} + keyExtractor={item => item?.id?.toString()} + renderItem={({item}) => + + } + /> + + ) +} + +export default CastSlide; \ No newline at end of file diff --git a/components/jellyseerr/PersonPoster.tsx b/components/jellyseerr/PersonPoster.tsx new file mode 100644 index 00000000..57ff9f58 --- /dev/null +++ b/components/jellyseerr/PersonPoster.tsx @@ -0,0 +1,42 @@ +import {TouchableOpacity, View, ViewProps} from "react-native"; +import React from "react"; +import {Text} from "@/components/common/Text"; +import Poster from "@/components/posters/Poster"; +import {useRouter, useSegments} from "expo-router"; +import {useJellyseerr} from "@/hooks/useJellyseerr"; + +interface Props { + id: string + posterPath?: string + name: string + subName?: string +} + +const PersonPoster: React.FC = ({ + id, + posterPath, + name, + subName, + ...props +}) => { + const {jellyseerrApi} = useJellyseerr(); + const router = useRouter(); + const segments = useSegments(); + const from = segments[2]; + + if (from === "(home)" || from === "(search)" || from === "(libraries)") + return ( + router.push(`/(auth)/(tabs)/${from}/jellyseerr/${id}`)}> + + + {name} + {subName && {subName}} + + + ) +} + +export default PersonPoster; \ No newline at end of file diff --git a/components/posters/JellyseerrPoster.tsx b/components/posters/JellyseerrPoster.tsx index 0de80a75..ad3df50f 100644 --- a/components/posters/JellyseerrPoster.tsx +++ b/components/posters/JellyseerrPoster.tsx @@ -20,10 +20,8 @@ const JellyseerrPoster: React.FC = ({ const {jellyseerrUser, jellyseerrApi} = useJellyseerr(); // const imageSource = - const imageSrc = useMemo(() => - item.posterPath ? - `https://image.tmdb.org/t/p/w300_and_h450_face${item.posterPath}` - : jellyseerrApi?.axios?.defaults.baseURL + `/images/overseerr_poster_not_found_logo_top.png`, + const imageSrc = useMemo( + () => jellyseerrApi?.imageProxy(item.posterPath, 'w300_and_h450_face'), [item, jellyseerrApi] ) const title = useMemo(() => item.mediaType === MediaType.MOVIE ? item.title : item.name, [item]) @@ -57,7 +55,7 @@ const JellyseerrPoster: React.FC = ({ mediaTitle={title} releaseYear={releaseYear} canRequest={canRequest} - posterSrc={imageSrc} + posterSrc={imageSrc!!} > diff --git a/components/posters/Poster.tsx b/components/posters/Poster.tsx index 1787506e..68799f47 100644 --- a/components/posters/Poster.tsx +++ b/components/posters/Poster.tsx @@ -1,19 +1,15 @@ -import { - BaseItemDto, - BaseItemPerson, -} from "@jellyfin/sdk/lib/generated-client/models"; import { Image } from "expo-image"; import { View } from "react-native"; type PosterProps = { - item?: BaseItemDto | BaseItemPerson | null; + id?: string | null; url?: string | null; showProgress?: boolean; blurhash?: string | null; }; -const Poster: React.FC = ({ item, url, blurhash }) => { - if (!item) +const Poster: React.FC = ({ id, url, blurhash }) => { + if (!id && !url) return ( = ({ item, url, blurhash }) => { } : null } - key={item.Id} - id={item.Id} + key={id} + id={id!!} source={ url ? { diff --git a/components/series/CastAndCrew.tsx b/components/series/CastAndCrew.tsx index 2b312f0e..01cd1e84 100644 --- a/components/series/CastAndCrew.tsx +++ b/components/series/CastAndCrew.tsx @@ -55,7 +55,7 @@ export const CastAndCrew: React.FC = ({ item, loading, ...props }) => { }} className="flex flex-col w-28" > - + {i.Name} {i.Role} diff --git a/components/series/CurrentSeries.tsx b/components/series/CurrentSeries.tsx index e573929a..52851533 100644 --- a/components/series/CurrentSeries.tsx +++ b/components/series/CurrentSeries.tsx @@ -29,7 +29,7 @@ export const CurrentSeries: React.FC = ({ item, ...props }) => { className="flex flex-col space-y-2 w-28" > {item.SeriesName} diff --git a/components/series/JellyseerrSeasons.tsx b/components/series/JellyseerrSeasons.tsx index d6dedeb6..80bfd254 100644 --- a/components/series/JellyseerrSeasons.tsx +++ b/components/series/JellyseerrSeasons.tsx @@ -61,7 +61,7 @@ const RenderItem = ({ item, index }: any) => { key={item.id} id={item.id} source={{ - uri: jellyseerrApi?.tvStillImageProxy(item.stillPath), + uri: jellyseerrApi?.imageProxy(item.stillPath), }} cachePolicy={"memory-disk"} contentFit="cover" diff --git a/hooks/useJellyseerr.ts b/hooks/useJellyseerr.ts index 8393798d..4546cee1 100644 --- a/hooks/useJellyseerr.ts +++ b/hooks/useJellyseerr.ts @@ -28,6 +28,10 @@ import Issue from "@/utils/jellyseerr/server/entity/Issue"; import { RTRating } from "@/utils/jellyseerr/server/api/rating/rottentomatoes"; import { writeErrorLog } from "@/utils/log"; import DiscoverSlider from "@/utils/jellyseerr/server/entity/DiscoverSlider"; +import { + CombinedCredit, + PersonDetails +} from "@/utils/jellyseerr/server/models/Person"; interface SearchParams { query: string; @@ -55,6 +59,8 @@ export enum Endpoints { API_V1 = "/api/v1", SEARCH = "/search", REQUEST = "/request", + PERSON = "/person", + COMBINED_CREDITS = "/combined_credits", MOVIE = "/movie", RATINGS = "/ratings", ISSUE = "/issue", @@ -204,6 +210,22 @@ export class JellyseerrApi { }); } + async personDetails(id: number | string): Promise { + return this.axios + ?.get(Endpoints.API_V1 + Endpoints.PERSON + `/${id}`) + .then((response) => { + return response?.data; + }); + } + + async personCombinedCredits(id: number | string): Promise { + return this.axios + ?.get(Endpoints.API_V1 + Endpoints.PERSON + `/${id}` + Endpoints.COMBINED_CREDITS) + .then((response) => { + return response?.data; + }); + } + async movieRatings(id: number) { return this.axios ?.get( @@ -238,14 +260,15 @@ export class JellyseerrApi { }); } - tvStillImageProxy(path: string, width: number = 1920, quality: number = 75) { - return ( - this.axios.defaults.baseURL + - `/_next/image?` + - new URLSearchParams( - `url=https://image.tmdb.org/t/p/original/${path}&w=${width}&q=${quality}` - ).toString() - ); + imageProxy(path?: string, tmdbPath: string = 'original', width: number = 1920, quality: number = 75) { + return path ? ( + this.axios.defaults.baseURL + + `/_next/image?` + + new URLSearchParams( + `url=https://image.tmdb.org/t/p/${tmdbPath}/${path}&w=${width}&q=${quality}` + ).toString() + ) : + this.axios?.defaults.baseURL + `/images/overseerr_poster_not_found_logo_top.png`; } async submitIssue(mediaId: number, issueType: IssueType, message: string) { From 8d4115f5a0a146d2c0c99b4c4d206149d26b65c6 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sun, 5 Jan 2025 09:24:54 +0100 Subject: [PATCH 07/49] fix: app crash on internal yt trailer, link out instead --- app/(auth)/trailer/page.tsx | 45 ----------------------------- app/_layout.tsx | 8 ----- components/series/SeriesActions.tsx | 42 ++++++++++++++++++--------- 3 files changed, 28 insertions(+), 67 deletions(-) delete mode 100644 app/(auth)/trailer/page.tsx diff --git a/app/(auth)/trailer/page.tsx b/app/(auth)/trailer/page.tsx deleted file mode 100644 index 9f331795..00000000 --- a/app/(auth)/trailer/page.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { useGlobalSearchParams } from "expo-router"; -import { useCallback, useEffect, useMemo, useState } from "react"; -import { Alert, Dimensions, View } from "react-native"; -import YoutubePlayer, { PLAYER_STATES } from "react-native-youtube-iframe"; - -export default function page() { - const searchParams = useGlobalSearchParams(); - - const { url } = searchParams as { url: string }; - - const videoId = useMemo(() => { - return url.split("v=")[1]; - }, [url]); - - const [playing, setPlaying] = useState(false); - - const onStateChange = useCallback((state: PLAYER_STATES) => { - if (state === "ended") { - setPlaying(false); - Alert.alert("video has finished playing!"); - } - }, []); - - const togglePlaying = useCallback(() => { - setPlaying((prev) => !prev); - }, []); - - useEffect(() => { - togglePlaying(); - }, []); - - const screenWidth = Dimensions.get("screen").width; - - return ( - - - - ); -} diff --git a/app/_layout.tsx b/app/_layout.tsx index bf779be5..23512523 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -336,14 +336,6 @@ function Layout() { header: () => null, }} /> - { - const router = useRouter(); - const trailerLink = useMemo(() => { - const url = (item as BaseItemDto).RemoteTrailers?.[0]?.Url - if (url) - return url - return (item as MovieDetails | TvDetails)?.relatedVideos?.find(v => v.type === "Trailer")?.url + if ("RemoteTrailers" in item && item.RemoteTrailers?.[0]?.Url) { + return item.RemoteTrailers[0].Url; + } + + if ("relatedVideos" in item) { + return item.relatedVideos?.find((v) => v.type === "Trailer")?.url; + } + + return undefined; }, [item]); const openTrailer = useCallback(async () => { - if (!trailerLink) return; + if (!trailerLink) { + Alert.alert("No trailer available"); + return; + } - const encodedTrailerLink = encodeURIComponent(trailerLink); - router.push(`/trailer/page?url=${encodedTrailerLink}`); - }, [router, trailerLink]); + try { + await Linking.openURL(trailerLink); + } catch (err) { + console.error("Failed to open trailer link:", err); + } + }, [trailerLink]); return ( From 85e21edbf16aa7bb7a9d47a56dc5b972120416c9 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sun, 5 Jan 2025 09:46:30 +0100 Subject: [PATCH 08/49] fix: padding in person view appearences grid --- .../jellyseerr/[personId].tsx | 191 +++++++++++------- 1 file changed, 115 insertions(+), 76 deletions(-) diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/[personId].tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/[personId].tsx index f2219b26..2dc2a478 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/[personId].tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/[personId].tsx @@ -1,91 +1,109 @@ -import {router, useLocalSearchParams, useNavigation, useSegments} from "expo-router"; -import React, {useCallback, useEffect, useMemo, useRef, useState} from "react"; -import {TouchableOpacity, View} from "react-native"; -import {useQuery} from "@tanstack/react-query"; -import {useJellyseerr} from "@/hooks/useJellyseerr"; -import {useSafeAreaInsets} from "react-native-safe-area-context"; -import {ParallaxScrollView} from "@/components/ParallaxPage"; -import {Text} from "@/components/common/Text"; -import {Animated} from "react-native"; -import {Image} from "expo-image"; -import {OverviewText} from "@/components/OverviewText"; -import {orderBy} from "lodash"; -import {FlashList} from "@shopify/flash-list"; -import {PersonCreditCast} from "@/utils/jellyseerr/server/models/Person"; +import { + router, + useLocalSearchParams, + useNavigation, + useSegments, +} from "expo-router"; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import { TouchableOpacity, View } from "react-native"; +import { useQuery } from "@tanstack/react-query"; +import { useJellyseerr } from "@/hooks/useJellyseerr"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { ParallaxScrollView } from "@/components/ParallaxPage"; +import { Text } from "@/components/common/Text"; +import { Animated } from "react-native"; +import { Image } from "expo-image"; +import { OverviewText } from "@/components/OverviewText"; +import { orderBy } from "lodash"; +import { FlashList } from "@shopify/flash-list"; +import { PersonCreditCast } from "@/utils/jellyseerr/server/models/Person"; import Poster from "@/components/posters/Poster"; import JellyseerrMediaIcon from "@/components/jellyseerr/JellyseerrMediaIcon"; -const ANIMATION_ENTER = 250 -const ANIMATION_EXIT = 250 -const BACKDROP_DURATION = 5000 +const ANIMATION_ENTER = 250; +const ANIMATION_EXIT = 250; +const BACKDROP_DURATION = 5000; export default function page() { const insets = useSafeAreaInsets(); const local = useLocalSearchParams(); const segments = useSegments(); - const {jellyseerrApi, jellyseerrUser} = useJellyseerr(); + const { jellyseerrApi, jellyseerrUser } = useJellyseerr(); - const { personId } = local as { personId: string; }; + const { personId } = local as { personId: string }; const from = segments[2]; const [currentIndex, setCurrentIndex] = useState(0); const fadeAnim = useRef(new Animated.Value(0)).current; - const {data, isLoading, isFetching} = useQuery({ - queryKey: ['jellyseerr', 'person', personId], + const { data, isLoading, isFetching } = useQuery({ + queryKey: ["jellyseerr", "person", personId], queryFn: async () => ({ details: await jellyseerrApi?.personDetails(personId), - combinedCredits: await jellyseerrApi?.personCombinedCredits(personId) + combinedCredits: await jellyseerrApi?.personCombinedCredits(personId), }), - enabled: !!jellyseerrApi && !!personId + enabled: !!jellyseerrApi && !!personId, }); const locale = useMemo(() => { - return jellyseerrUser?.settings?.locale || 'en' + return jellyseerrUser?.settings?.locale || "en"; }, [jellyseerrUser]); const region = useMemo( - () => jellyseerrUser?.settings?.region || 'US', + () => jellyseerrUser?.settings?.region || "US", [jellyseerrUser] ); const castedRoles: PersonCreditCast[] = useMemo( - () => orderBy(data?.combinedCredits?.cast, ['voteCount', 'voteAverage'], 'desc'), + () => + orderBy( + data?.combinedCredits?.cast, + ["voteCount", "voteAverage"], + "desc" + ), [data?.combinedCredits] ); const backdrops = useMemo( - () => castedRoles.map(c => c.backdropPath), + () => castedRoles.map((c) => c.backdropPath), [data?.combinedCredits] - ) + ); - const enterAnimation = useCallback(() => - Animated.timing(fadeAnim, { - toValue: 1, - duration: ANIMATION_ENTER, - useNativeDriver: true, - }), [fadeAnim]); + const enterAnimation = useCallback( + () => + Animated.timing(fadeAnim, { + toValue: 1, + duration: ANIMATION_ENTER, + useNativeDriver: true, + }), + [fadeAnim] + ); - const exitAnimation = useCallback(() => - Animated.timing(fadeAnim, { - toValue: 0, - duration: ANIMATION_EXIT, - useNativeDriver: true, - }), [fadeAnim]); + const exitAnimation = useCallback( + () => + Animated.timing(fadeAnim, { + toValue: 0, + duration: ANIMATION_EXIT, + useNativeDriver: true, + }), + [fadeAnim] + ); useEffect(() => { if (backdrops?.length) { - enterAnimation().start() - const intervalId = setInterval( - () => { - exitAnimation().start(end => { - if (end.finished) - setCurrentIndex((prevIndex) => (prevIndex + 1) % backdrops?.length) - }) - - }, - BACKDROP_DURATION - ); + enterAnimation().start(); + const intervalId = setInterval(() => { + exitAnimation().start((end) => { + if (end.finished) + setCurrentIndex((prevIndex) => (prevIndex + 1) % backdrops?.length); + }); + }, BACKDROP_DURATION); return () => clearInterval(intervalId); } @@ -98,13 +116,16 @@ export default function page() { //@ts-ignore params: { ...credit, - mediaTitle: credit.title, - releaseYear: new Date(credit.releaseDate).getFullYear(), + mediaTitle: credit.title, + releaseYear: new Date(credit.releaseDate).getFullYear(), canRequest: "false", - posterSrc: jellyseerrApi?.imageProxy(credit.posterPath, 'w300_and_h450_face') - } + posterSrc: jellyseerrApi?.imageProxy( + credit.posterPath, + "w300_and_h450_face" + ), + }, }); - } + }; return ( } @@ -134,7 +158,12 @@ export default function page() { key={data?.details?.id} id={data?.details?.id.toString()} className="rounded-full bottom-1" - source={{uri: jellyseerrApi?.imageProxy(data?.details?.profilePath, 'w600_and_h600_bestv2'),}} + source={{ + uri: jellyseerrApi?.imageProxy( + data?.details?.profilePath, + "w600_and_h600_bestv2" + ), + }} cachePolicy={"memory-disk"} contentFit="cover" style={{ @@ -151,31 +180,38 @@ export default function page() { {data?.details?.name} - Born {new Date(data?.details?.birthday!!).toLocaleDateString(`${locale}-${region}`, { - year: 'numeric', - month: 'long', - day: 'numeric', - })} | {data?.details?.placeOfBirth} + Born{" "} + {new Date(data?.details?.birthday!!).toLocaleDateString( + `${locale}-${region}`, + { + year: "numeric", + month: "long", + day: "numeric", + } + )}{" "} + | {data?.details?.placeOfBirth} - + - No results + + No results + } contentInsetAdjustmentBehavior="automatic" ListHeaderComponent={ Appearances } - renderItem={({item}) => + renderItem={({ item }) => ( viewDetails(item)} > {/*{item.title}*/} - as {item.character} + + as {item.character} + - } + )} keyExtractor={(item) => item.id.toString()} estimatedItemSize={255} numColumns={3} - contentContainerStyle={{paddingBottom: 24}} - ItemSeparatorComponent={() => ( - - )} + contentContainerStyle={{ paddingBottom: 24 }} + ItemSeparatorComponent={() => } /> ); -} \ No newline at end of file +} From 837051975836d22f28806e9bd6dcd361727fd694 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sun, 5 Jan 2025 09:46:48 +0100 Subject: [PATCH 09/49] chore: formatting --- components/jellyseerr/Cast.tsx | 56 ++++--- components/jellyseerr/DetailFacts.tsx | 229 ++++++++++++++++---------- 2 files changed, 170 insertions(+), 115 deletions(-) diff --git a/components/jellyseerr/Cast.tsx b/components/jellyseerr/Cast.tsx index 7642382d..8f64355d 100644 --- a/components/jellyseerr/Cast.tsx +++ b/components/jellyseerr/Cast.tsx @@ -1,34 +1,38 @@ -import {View, ViewProps} from "react-native"; -import {MovieDetails} from "@/utils/jellyseerr/server/models/Movie"; -import {TvDetails} from "@/utils/jellyseerr/server/models/Tv"; +import { View, ViewProps } from "react-native"; +import { MovieDetails } from "@/utils/jellyseerr/server/models/Movie"; +import { TvDetails } from "@/utils/jellyseerr/server/models/Tv"; import React from "react"; -import {FlashList} from "@shopify/flash-list"; -import {Text} from "@/components/common/Text"; +import { FlashList } from "@shopify/flash-list"; +import { Text } from "@/components/common/Text"; import PersonPoster from "@/components/jellyseerr/PersonPoster"; -const CastSlide: React.FC<{ details?: MovieDetails | TvDetails } & ViewProps> = ({ details, ...props }) => { +const CastSlide: React.FC< + { details?: MovieDetails | TvDetails } & ViewProps +> = ({ details, ...props }) => { return ( - details?.credits?.cast?.length && details?.credits?.cast?.length > 0 && - + details?.credits?.cast?.length && + details?.credits?.cast?.length > 0 && ( + Cast } - estimatedItemSize={15} - keyExtractor={item => item?.id?.toString()} - renderItem={({item}) => - - } + horizontal + showsHorizontalScrollIndicator={false} + data={details?.credits.cast} + ItemSeparatorComponent={() => } + estimatedItemSize={15} + keyExtractor={(item) => item?.id?.toString()} + renderItem={({ item }) => ( + + )} /> - - ) -} + + ) + ); +}; -export default CastSlide; \ No newline at end of file +export default CastSlide; diff --git a/components/jellyseerr/DetailFacts.tsx b/components/jellyseerr/DetailFacts.tsx index aa0e7885..782ede8b 100644 --- a/components/jellyseerr/DetailFacts.tsx +++ b/components/jellyseerr/DetailFacts.tsx @@ -1,14 +1,14 @@ -import {View, ViewProps} from "react-native"; -import {MovieDetails} from "@/utils/jellyseerr/server/models/Movie"; -import {TvDetails} from "@/utils/jellyseerr/server/models/Tv"; -import {Text} from "@/components/common/Text"; -import {useMemo} from "react"; -import {useJellyseerr} from "@/hooks/useJellyseerr"; -import {uniqBy} from "lodash"; -import {TmdbRelease} from "@/utils/jellyseerr/server/api/themoviedb/interfaces"; -import {Ionicons, MaterialCommunityIcons} from "@expo/vector-icons"; +import { View, ViewProps } from "react-native"; +import { MovieDetails } from "@/utils/jellyseerr/server/models/Movie"; +import { TvDetails } from "@/utils/jellyseerr/server/models/Tv"; +import { Text } from "@/components/common/Text"; +import { useMemo } from "react"; +import { useJellyseerr } from "@/hooks/useJellyseerr"; +import { uniqBy } from "lodash"; +import { TmdbRelease } from "@/utils/jellyseerr/server/api/themoviedb/interfaces"; +import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons"; import CountryFlag from "react-native-country-flag"; -import {ANIME_KEYWORD_ID} from "@/utils/jellyseerr/server/api/themoviedb/constants"; +import { ANIME_KEYWORD_ID } from "@/utils/jellyseerr/server/api/themoviedb/constants"; interface Release { certification: string; @@ -19,13 +19,16 @@ interface Release { } const dateOpts: Intl.DateTimeFormatOptions = { - year: 'numeric', - month: 'long', - day: 'numeric', -} + year: "numeric", + month: "long", + day: "numeric", +}; -const Facts: React.FC<{title: string, facts?: string[] | React.ReactNode[]} & ViewProps> = ({title, facts, ...props}) => ( - facts && facts?.length > 0 && ( +const Facts: React.FC< + { title: string; facts?: string[] | React.ReactNode[] } & ViewProps +> = ({ title, facts, ...props }) => + facts && + facts?.length > 0 && ( {title} @@ -35,31 +38,33 @@ const Facts: React.FC<{title: string, facts?: string[] | React.ReactNode[]} & Vi )} - ) -) + ); -const Fact: React.FC<{title: string, fact?: string | null} & ViewProps> = ({title, fact, ...props}) => ( - fact && -) - -const DetailFacts: React.FC<{ details?: MovieDetails | TvDetails } & ViewProps> = ({ - details, - className, +const Fact: React.FC<{ title: string; fact?: string | null } & ViewProps> = ({ + title, + fact, ...props -}) => { - const {jellyseerrUser} = useJellyseerr(); +}) => fact && ; + +const DetailFacts: React.FC< + { details?: MovieDetails | TvDetails } & ViewProps +> = ({ details, className, ...props }) => { + const { jellyseerrUser } = useJellyseerr(); const locale = useMemo(() => { - return jellyseerrUser?.settings?.locale || 'en' + return jellyseerrUser?.settings?.locale || "en"; }, [jellyseerrUser]); const region = useMemo( - () => jellyseerrUser?.settings?.region || 'US', + () => jellyseerrUser?.settings?.region || "US", [jellyseerrUser] ); const releases = useMemo( - () => (details as MovieDetails)?.releases?.results.find((r: TmdbRelease) => r.iso_3166_1 === region)?.release_dates as TmdbRelease['release_dates'], + () => + (details as MovieDetails)?.releases?.results.find( + (r: TmdbRelease) => r.iso_3166_1 === region + )?.release_dates as TmdbRelease["release_dates"], [details] ); @@ -71,97 +76,143 @@ const DetailFacts: React.FC<{ details?: MovieDetails | TvDetails } & ViewProps> // 5. Physical // 6. TV const filteredReleases = useMemo( - () => uniqBy(releases?.filter((r: Release) => r.type > 2 && r.type < 6), 'type'), + () => + uniqBy( + releases?.filter((r: Release) => r.type > 2 && r.type < 6), + "type" + ), [releases] ); const firstAirDate = useMemo(() => { - const firstAirDate = (details as TvDetails)?.firstAirDate + const firstAirDate = (details as TvDetails)?.firstAirDate; if (firstAirDate) { - return new Date(firstAirDate).toLocaleDateString(`${locale}-${region}`, dateOpts) + return new Date(firstAirDate).toLocaleDateString( + `${locale}-${region}`, + dateOpts + ); } }, [details]); const nextAirDate = useMemo(() => { - const firstAirDate = (details as TvDetails)?.firstAirDate - const nextAirDate = (details as TvDetails)?.nextEpisodeToAir?.airDate + const firstAirDate = (details as TvDetails)?.firstAirDate; + const nextAirDate = (details as TvDetails)?.nextEpisodeToAir?.airDate; if (nextAirDate && firstAirDate !== nextAirDate) { - return new Date(nextAirDate).toLocaleDateString(`${locale}-${region}`, dateOpts) + return new Date(nextAirDate).toLocaleDateString( + `${locale}-${region}`, + dateOpts + ); } }, [details]); const revenue = useMemo( - () => (details as MovieDetails)?.revenue - ?.toLocaleString?.(`${locale}-${region}`, {style: 'currency', currency: "USD"}), + () => + (details as MovieDetails)?.revenue?.toLocaleString?.( + `${locale}-${region}`, + { style: "currency", currency: "USD" } + ), [details] ); const budget = useMemo( - () => (details as MovieDetails)?.budget - ?.toLocaleString?.(`${locale}-${region}`, {style: 'currency', currency: "USD"}), + () => + (details as MovieDetails)?.budget?.toLocaleString?.( + `${locale}-${region}`, + { style: "currency", currency: "USD" } + ), [details] ); const streamingProviders = useMemo( - () => details?.watchProviders?.find((provider) => provider.iso_3166_1 === region)?.flatrate, + () => + details?.watchProviders?.find( + (provider) => provider.iso_3166_1 === region + )?.flatrate, [details] ); - const networks = useMemo( - () => (details as TvDetails)?.networks, - [details] - ); + const networks = useMemo(() => (details as TvDetails)?.networks, [details]); const spokenLanguage = useMemo( - () => details?.spokenLanguages.find((lng) => lng.iso_639_1 === details.originalLanguage)?.name, + () => + details?.spokenLanguages.find( + (lng) => lng.iso_639_1 === details.originalLanguage + )?.name, [details] ); return ( - details && + details && ( + Details - - - - {details.keywords.some((keyword) => keyword.id === ANIME_KEYWORD_ID) && ( - - )} - - - {r.type === 3 ? ( - // Theatrical - - ) : r.type === 4 ? ( - // Digital - - ) : ( - // Physical - - )} - {new Date(r.release_date).toLocaleDateString(`${locale}-${region}`, dateOpts)} - - )} + + + + {details.keywords.some( + (keyword) => keyword.id === ANIME_KEYWORD_ID + ) && } + ( + + {r.type === 3 ? ( + // Theatrical + + ) : r.type === 4 ? ( + // Digital + + ) : ( + // Physical + + )} + + {new Date(r.release_date).toLocaleDateString( + `${locale}-${region}`, + dateOpts + )} + + + ))} + /> + + + + + + ( + + + {n.name} + + ))} + /> + n.name + )} + /> + n.name)} /> + s.name)} /> - - - - - - - - - {n.name} - - )}/> - n.name)}/> - n.name)}/> - s.name)}/> - - ) -} + + ) + ); +}; -export default DetailFacts; \ No newline at end of file +export default DetailFacts; From 7342b7eb929c2459dc38e98f168d3acf93578925 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sun, 5 Jan 2025 09:46:51 +0100 Subject: [PATCH 10/49] chore: deps --- bun.lockb | Bin 594112 -> 594152 bytes package.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/bun.lockb b/bun.lockb index bc0866e3a89645142977532dc97961a5c8d13802..3a1947ce30c440a8d00177577d1bbabe3f3ebd0e 100755 GIT binary patch delta 4785 zcmXY!33!cH8prRA+${Dh5)xZtD^De*j4jncTcnmGsK#JWRQgzBDQas3kw>*MmZMZh zsEnoa5KAv3rgVzf8MV_UmMPPEB1KJUYkn{P^LTUK@ArT2|2yCL&N=tmm)G!CUc*6EvZ^vl-w(^D@0mg4sgKau=obCquAV$u$UZT}*%*Rpz>EA+ngaQx>xvJcED7dS8{xI<}B)f^W*!PmoI6zuVM zJswX8VXepwo#2b;a9Fq;sKHag<2fQm=Xpu&sF;uIKZ+f51!i$lQYd(Uq*JgU@-Skc z*uPa5&h=oiGtN5^i?fm{fP*SN)6R+6WjV3)VwGU)t(bPfrJBV>BzuS|#0E)MR9+Rf zN$jfX{IJbp#V*e*N>mnXF2@y^#X?EhNJ)}D z5L*E2DYi%~2bL`Mq4Unf;v-25k$Ov7EcO8`gC&_npDVVAe1cZ&UtFqLER$qkWm||| z#B!KD++w1xY*(l*7xsX;u(RZ2muD8MBrO4bt7-Z~Y$@y?FgrI^i`h5i9Nyp1Nn2qdX;&rk0uG1_m@+GZ8Iz?`6gDgHFU!dpSD7G55R&0~Y zH48hbg28_!3d!yC+M@C`>D=~}hDqja0Wb28xL+ms1!D2gQ zu^yH#7H}|ph#ez84$Gwf5oWf>i4ii^!R++9=kmfd)bkAyOSv6AKf~N3nf(&y8)}pc=GA1$l@mX3Bp=c*J3Ufeo5bhLNtnMVz*%SJ~j~%VzjpV!MK zOck_8|AnyDTdX$MOSoG&eIKzp2+Au%H-+aot z@++T<1fZWjO@;PTbtyNt0rZF2`>`W<8O*c+s_U4;x~6S)z8v3kfhIY=8~#AeRlYSL zem@UrlN&+Ughsl(dA@~_75H>Cmr=xLJjQw=-=(hgC6+I?Z*f}ni!LO`*Eq0it*=$< z@NRYh(^du^T<|pv4fX!{#f0VwFS^)b-wAKa?9yUiptLw7D=qw1yJs6YJutmp=mBr| fio|EvDcMPhp@F2tu&^<1Z6GW@Fl$3t=k)&rhUrB) delta 4757 zcmXY!3!IKs9>?F;%xh-IWn5+qh8a|Rk~L{vlGd#<<y{r=4FeEjG9pYQMg{+{!k_nh-SPL#FURMu)< zyX=GGn;orl)6iA@2W6fcwfiWk)kP_5&t%n{;MiyCKWl58Jb1?BMU(&OH16y1BUndtCX$fx?tG zHjXcUvu4xRf2;G@ypO*rI^MHW{Ek@jUB?>id;aMiwMw>4Pl#`up4jc%YQN12iDP1Y zQ+FmtBGE`BQk_uS8AgnW)$g{;I04l1f)I)9HlvGNZB}j;*$O zrK2vd3wzb9!gZ;z*UUZ-WwJQt^7=@RTRLvm0QR)m7iJA%lg+*ir(|)$QaaK*HTXk2 zY1W8*n9KibmH|uCoYKAuJ;q`2y`?6g7M4!KG@VU}jIFV-z{lPtkX9}J7?C4d^(}QormdG7ZbAywQ2}jYL^gm z1ZW9nmy$nufkYySFg_k>O+00mTvb;)(gqOqm{LMfA&*tV9+x2<@|bGEG&609K4!Jd zE{Els)d_oLQP&oKK)Q=iOEqgxzQ7Buo>>m;X|wtvdps5m?A`&Xp(TBVdg3dIG_!QG zKf=<@8ifK`WZ0q;Qii1_F7FI$Y}VAQ3#^G*X4ou?W=NX%u0)olY};zjRyS*Di*B&n zv~6guLh1x8F1DoYJk!!8X5C@uyr?cU>j66t({5@VM#!QKl4idbu>z?c{beriP5uM9 zT3gp$OMb?zU05lL_FQV}t|!h~$}#H$i~7L20;a{1OC*?e4AF^LbVAZ7ZX{}2>SBw& zum)yVne~IEnRN|$vbfsP0Hmgtx|!Vs%Qm|PrfJV3E;j2C%4E^Y<%5tqSn6#y7}nYB zT3g%+;b^S6RB><@dsL{-`}*b{}~gZ-fWKURe}adH|`0r7>m?!qQ+m z2*#Qfknd;fX=jfM*^{s+v@{m99!YJy**NkIW)sW`VJR@};)$U^77tmP0GirOug@hUgS5{%&aw(m+e|&E~=iSdzK)3(OuTALAAKRLGOX zGnVvIwu0zSJPXsq%_ExGcA@J^VP}{Noh8qQGFiM}X+G$Pr|Ctr1+ceaIyYW2(=X(D zv$AkX7B5>0NWC~phS0xa_6&KhkC!DdjqF+C2D7E1$0JxQv$PQDLvl48J$l^diCKQ` z*UkO`TV%F8l*-}_q(tzG#0GMmUaMUG61mO}wbk?*MH%rX5AqlKHK9@#Z@GLi=r*k< zTHNeq^5JG{ZSe{$-|X#>GZl;VF8?Reg^$o{G`izbVhN!W?OoR`BiHMs+Qv{Mi%m!} zeT~pN@a^d)whc4f0wj9>kbFnS#mBn^jXicP1eh2*ym%mBg)@-NQN?1GF?h4t{ zu-I*BHPU+w=T7=^vwxAdjgLKdG}+JZEg15NFi*;exReHb1$+Xk!6{OAn*BCM3fmzK7J^!uvw@&r9I8+o75 zZ>dhSuUz*5c|WtSLv%V8-&op-RL;Ux`=8k^^4+i^`fp*f-A#<*L5k_W4|%dU?eaaK z0;C!AdO2dOkq-%dOEc;9awN8w7z)$r^4(#Q5;ckgL{Ac(C3+9jayv*I)>G2-9;Uj_ zh)-b8(I=W6B7YC>7wBVEpRFA^%!RgyU{WY8=CP9PdlYoP#!ahXRzbcD-^KJ7*|(B> ziSJ)4RLY{Z%Rfh&W2vs$F<4#3xP-o*Esm3y6KeHC&ZAi96;V&}B~jg@XlQlv#U6fTN&t+94d)#tz79~#e&wTM?Nie1?;CF?*H z9TU$y5Nnl`q`T8XpNd#{G^ZrJG8Rv-tX@1cWmA``ecC;~xJ%NuXv%|KtFBv@6m(6B b7j#WdzAr2olav*vElI8wU$Qj0Xa4^I(l|km diff --git a/package.json b/package.json index b0a0ce75..b4d94f08 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "react-native-google-cast": "^4.8.3", "react-native-image-colors": "^2.4.0", "react-native-ios-context-menu": "^2.5.2", - "react-native-ios-utilities": "4.5.1", + "react-native-ios-utilities": "4.5.3", "react-native-mmkv": "^2.12.2", "react-native-pager-view": "6.3.0", "react-native-progress": "^5.0.1", From a488c686339ada856cfd4cc0f94f0f014ea1b46c Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sun, 5 Jan 2025 09:49:57 +0100 Subject: [PATCH 11/49] fix: sizing --- .../jellyseerr/page.tsx | 94 +++++++++++-------- 1 file changed, 54 insertions(+), 40 deletions(-) diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx index 805727fd..678680c7 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx @@ -1,22 +1,33 @@ -import React, {useCallback, useEffect, useMemo, useRef, useState} from "react"; -import {useLocalSearchParams, useNavigation} from "expo-router"; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import { useLocalSearchParams, useNavigation } from "expo-router"; import { MovieResult, TvResult } from "@/utils/jellyseerr/server/models/Search"; import { Text } from "@/components/common/Text"; import { ParallaxScrollView } from "@/components/ParallaxPage"; import { Image } from "expo-image"; -import { TouchableOpacity, View} from "react-native"; +import { TouchableOpacity, View } from "react-native"; import { Ionicons } from "@expo/vector-icons"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { OverviewText } from "@/components/OverviewText"; import { GenreTags } from "@/components/GenreTags"; -import {MediaRequestStatus, MediaStatus, MediaType} from "@/utils/jellyseerr/server/constants/media"; +import { + MediaRequestStatus, + MediaStatus, + MediaType, +} from "@/utils/jellyseerr/server/constants/media"; import { useQuery } from "@tanstack/react-query"; import { useJellyseerr } from "@/hooks/useJellyseerr"; import { Button } from "@/components/Button"; import { BottomSheetBackdrop, BottomSheetBackdropProps, - BottomSheetModal, BottomSheetTextInput, + BottomSheetModal, + BottomSheetTextInput, BottomSheetView, } from "@gorhom/bottom-sheet"; import { @@ -29,7 +40,7 @@ import JellyseerrSeasons from "@/components/series/JellyseerrSeasons"; import { JellyserrRatings } from "@/components/Ratings"; import MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest"; import DetailFacts from "@/components/jellyseerr/DetailFacts"; -import {ItemActions} from "@/components/series/SeriesActions"; +import { ItemActions } from "@/components/series/SeriesActions"; import Cast from "@/components/jellyseerr/Cast"; const Page: React.FC = () => { @@ -59,7 +70,7 @@ const Page: React.FC = () => { data: details, isFetching, isLoading, - refetch + refetch, } = useQuery({ enabled: !!jellyseerrApi && !!result && !!result.id, queryKey: ["jellyseerr", "detail", result.mediaType, result.id], @@ -77,11 +88,17 @@ const Page: React.FC = () => { }); const canRequest = useMemo(() => { - const pendingRequests = details?.mediaInfo?.requests - ?.some((r: MediaRequest) => r.status == MediaRequestStatus.PENDING || r.status == MediaRequestStatus.APPROVED) + const pendingRequests = details?.mediaInfo?.requests?.some( + (r: MediaRequest) => + r.status == MediaRequestStatus.PENDING || + r.status == MediaRequestStatus.APPROVED + ); - return (details?.mediaInfo?.status === MediaStatus.UNKNOWN && !pendingRequests) || - (!details?.mediaInfo?.status && canRequestString === "true"); + return ( + (details?.mediaInfo?.status === MediaStatus.UNKNOWN && + !pendingRequests) || + (!details?.mediaInfo?.status && canRequestString === "true") + ); }, [canRequestString, details]); const renderBackdrop = useCallback( @@ -107,32 +124,31 @@ const Page: React.FC = () => { } }, [jellyseerrApi, details, result, issueType, issueMessage]); - const request = useCallback( - async () => { - requestMedia(mediaTitle, { - mediaId: Number(result.id!!), - mediaType: result.mediaType!!, - tvdbId: details?.externalIds?.tvdbId, - seasons: (details as TvDetails)?.seasons - ?.filter?.((s) => s.seasonNumber !== 0) - ?.map?.((s) => s.seasonNumber), - }, - refetch - ) - }, - [details, result, requestMedia] - ); + const request = useCallback(async () => { + requestMedia( + mediaTitle, + { + mediaId: Number(result.id!!), + mediaType: result.mediaType!!, + tvdbId: details?.externalIds?.tvdbId, + seasons: (details as TvDetails)?.seasons + ?.filter?.((s) => s.seasonNumber !== 0) + ?.map?.((s) => s.seasonNumber), + }, + refetch + ); + }, [details, result, requestMedia]); useEffect(() => { if (details) { navigation.setOptions({ - headerRight: () => - - - + headerRight: () => ( + + + + ), }); } - }, [details]); return ( @@ -157,7 +173,10 @@ const Page: React.FC = () => { height: "100%", }} source={{ - uri: jellyseerrApi?.imageProxy(result.backdropPath, 'w1920_and_h800_multi_faces'), + uri: jellyseerrApi?.imageProxy( + result.backdropPath, + "w1920_and_h800_multi_faces" + ), }} /> ) : ( @@ -241,10 +260,7 @@ const Page: React.FC = () => { className="p-2 border border-neutral-800 bg-neutral-900 rounded-xl" details={details} /> - + @@ -311,13 +327,11 @@ const Page: React.FC = () => { - + Date: Sun, 5 Jan 2025 09:51:35 +0100 Subject: [PATCH 12/49] chore: deps --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index b4d94f08..e5ebb82a 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,6 @@ "react-native-volume-manager": "^1.10.0", "react-native-web": "~0.19.13", "react-native-webview": "13.8.6", - "react-native-youtube-iframe": "^2.3.0", "sonner-native": "^0.14.2", "tailwindcss": "3.3.2", "use-debounce": "^10.0.4", From 95cf25234940a0d8338952efc26bba828ecdc4f7 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sun, 5 Jan 2025 10:29:59 +0100 Subject: [PATCH 13/49] Delete svenska_kyrkan.sql --- svenska_kyrkan.sql | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 svenska_kyrkan.sql diff --git a/svenska_kyrkan.sql b/svenska_kyrkan.sql deleted file mode 100644 index e69de29b..00000000 From 2169bea03174bcd27c62fb8a394f7dace68750e7 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sun, 5 Jan 2025 11:55:05 +0100 Subject: [PATCH 14/49] fix: add loading and refactor permissions --- .../jellyseerr/page.tsx | 38 ++++++------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx index 678680c7..c33b1ee7 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx @@ -42,22 +42,18 @@ import MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest"; import DetailFacts from "@/components/jellyseerr/DetailFacts"; import { ItemActions } from "@/components/series/SeriesActions"; import Cast from "@/components/jellyseerr/Cast"; +import { useJellyseerrCanRequest } from "@/utils/_jellyseerr/useJellyseerrCanRequest"; const Page: React.FC = () => { const insets = useSafeAreaInsets(); const params = useLocalSearchParams(); - const { - mediaTitle, - releaseYear, - canRequest: canRequestString, - posterSrc, - ...result - } = params as unknown as { - mediaTitle: string; - releaseYear: number; - canRequest: string; - posterSrc: string; - } & Partial; + const { mediaTitle, releaseYear, posterSrc, ...result } = + params as unknown as { + mediaTitle: string; + releaseYear: number; + canRequest: string; + posterSrc: string; + } & Partial; const navigation = useNavigation(); const { jellyseerrApi, requestMedia } = useJellyseerr(); @@ -87,19 +83,7 @@ const Page: React.FC = () => { }, }); - const canRequest = useMemo(() => { - const pendingRequests = details?.mediaInfo?.requests?.some( - (r: MediaRequest) => - r.status == MediaRequestStatus.PENDING || - r.status == MediaRequestStatus.APPROVED - ); - - return ( - (details?.mediaInfo?.status === MediaStatus.UNKNOWN && - !pendingRequests) || - (!details?.mediaInfo?.status && canRequestString === "true") - ); - }, [canRequestString, details]); + const canRequest = useJellyseerrCanRequest(details); const renderBackdrop = useCallback( (props: BottomSheetBackdropProps) => ( @@ -225,7 +209,9 @@ const Page: React.FC = () => { g.name) || []} /> - {canRequest ? ( + {isLoading || isFetching ? ( + + ) : canRequest ? ( From 5ee1a9cabba8246c3a191bc12f3d605447ac330f Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sun, 5 Jan 2025 11:55:22 +0100 Subject: [PATCH 15/49] fix: change keys --- app/(auth)/(tabs)/(search)/index.tsx | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/app/(auth)/(tabs)/(search)/index.tsx b/app/(auth)/(tabs)/(search)/index.tsx index fcf8119d..92dc00c5 100644 --- a/app/(auth)/(tabs)/(search)/index.tsx +++ b/app/(auth)/(tabs)/(search)/index.tsx @@ -31,13 +31,18 @@ import { Platform, ScrollView, TouchableOpacity, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useDebounce } from "use-debounce"; import { useJellyseerr } from "@/hooks/useJellyseerr"; -import {MovieResult, PersonResult, TvResult} from "@/utils/jellyseerr/server/models/Search"; +import { + MovieResult, + PersonResult, + TvResult, +} from "@/utils/jellyseerr/server/models/Search"; import { MediaType } from "@/utils/jellyseerr/server/constants/media"; import JellyseerrPoster from "@/components/posters/JellyseerrPoster"; import { Tag } from "@/components/GenreTags"; import DiscoverSlide from "@/components/jellyseerr/DiscoverSlide"; import { sortBy } from "lodash"; import PersonPoster from "@/components/jellyseerr/PersonPoster"; +import { useReactNavigationQuery } from "@/utils/useReactNavigationQuery"; type SearchType = "Library" | "Discover"; @@ -150,8 +155,8 @@ export default function search() { enabled: searchType === "Library" && debouncedSearch.length > 0, }); - const { data: jellyseerrResults, isFetching: j1 } = useQuery({ - queryKey: ["search", "jellyseerrResults", debouncedSearch], + const { data: jellyseerrResults, isFetching: j1 } = useReactNavigationQuery({ + queryKey: ["search", "jellyseerr", "results", debouncedSearch], queryFn: async () => { const response = await jellyseerrApi?.search({ query: new URLSearchParams(debouncedSearch).toString(), @@ -167,14 +172,15 @@ export default function search() { debouncedSearch.length > 0, }); - const { data: jellyseerrDiscoverSettings, isFetching: j2 } = useQuery({ - queryKey: ["search", "jellyseerrDiscoverSettings", debouncedSearch], - queryFn: async () => jellyseerrApi?.discoverSettings(), - enabled: - !!jellyseerrApi && - searchType === "Discover" && - debouncedSearch.length == 0, - }); + const { data: jellyseerrDiscoverSettings, isFetching: j2 } = + useReactNavigationQuery({ + queryKey: ["search", "jellyseerr", "discoverSettings", debouncedSearch], + queryFn: async () => jellyseerrApi?.discoverSettings(), + enabled: + !!jellyseerrApi && + searchType === "Discover" && + debouncedSearch.length == 0, + }); const jellyseerrMovieResults: MovieResult[] | undefined = useMemo( () => From b2786325819c69bc90ea7b836655821186b37059 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sun, 5 Jan 2025 11:55:29 +0100 Subject: [PATCH 16/49] fix: height on loading button --- components/Button.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/Button.tsx b/components/Button.tsx index 1a73ad01..fb685f4c 100644 --- a/components/Button.tsx +++ b/components/Button.tsx @@ -61,7 +61,9 @@ export const Button: React.FC> = ({ {...props} > {loading ? ( - + + + ) : ( Date: Sun, 5 Jan 2025 11:55:41 +0100 Subject: [PATCH 17/49] fix: refactor permissions --- components/posters/JellyseerrPoster.tsx | 81 +++++++++++-------------- 1 file changed, 37 insertions(+), 44 deletions(-) diff --git a/components/posters/JellyseerrPoster.tsx b/components/posters/JellyseerrPoster.tsx index ad3df50f..5f363d42 100644 --- a/components/posters/JellyseerrPoster.tsx +++ b/components/posters/JellyseerrPoster.tsx @@ -1,53 +1,47 @@ -import {View, ViewProps} from "react-native"; -import {Image} from "expo-image"; -import {Text} from "@/components/common/Text"; -import {useMemo} from "react"; -import {MovieResult, TvResult} from "@/utils/jellyseerr/server/models/Search"; -import {MediaStatus, MediaType} from "@/utils/jellyseerr/server/constants/media"; -import {useJellyseerr} from "@/hooks/useJellyseerr"; -import {hasPermission, Permission} from "@/utils/jellyseerr/server/lib/permissions"; -import {TouchableJellyseerrRouter} from "@/components/common/JellyseerrItemRouter"; +import { View, ViewProps } from "react-native"; +import { Image } from "expo-image"; +import { Text } from "@/components/common/Text"; +import { useMemo } from "react"; +import { MovieResult, TvResult } from "@/utils/jellyseerr/server/models/Search"; +import { + MediaStatus, + MediaType, +} from "@/utils/jellyseerr/server/constants/media"; +import { useJellyseerr } from "@/hooks/useJellyseerr"; +import { + hasPermission, + Permission, +} from "@/utils/jellyseerr/server/lib/permissions"; +import { TouchableJellyseerrRouter } from "@/components/common/JellyseerrItemRouter"; import JellyseerrStatusIcon from "@/components/jellyseerr/JellyseerrStatusIcon"; import JellyseerrMediaIcon from "@/components/jellyseerr/JellyseerrMediaIcon"; +import { useJellyseerrCanRequest } from "@/utils/_jellyseerr/useJellyseerrCanRequest"; interface Props extends ViewProps { item: MovieResult | TvResult; } -const JellyseerrPoster: React.FC = ({ - item, - ...props -}) => { - const {jellyseerrUser, jellyseerrApi} = useJellyseerr(); - // const imageSource = +const JellyseerrPoster: React.FC = ({ item, ...props }) => { + const { jellyseerrApi } = useJellyseerr(); const imageSrc = useMemo( - () => jellyseerrApi?.imageProxy(item.posterPath, 'w300_and_h450_face'), + () => jellyseerrApi?.imageProxy(item.posterPath, "w300_and_h450_face"), [item, jellyseerrApi] - ) - const title = useMemo(() => item.mediaType === MediaType.MOVIE ? item.title : item.name, [item]) - const releaseYear = useMemo(() => - new Date(item.mediaType === MediaType.MOVIE ? item.releaseDate : item.firstAirDate).getFullYear(), + ); + const title = useMemo( + () => (item.mediaType === MediaType.MOVIE ? item.title : item.name), [item] - ) + ); + const releaseYear = useMemo( + () => + new Date( + item.mediaType === MediaType.MOVIE + ? item.releaseDate + : item.firstAirDate + ).getFullYear(), + [item] + ); - const showRequestButton = useMemo(() => - jellyseerrUser && hasPermission( - [ - Permission.REQUEST, - item.mediaType === 'movie' - ? Permission.REQUEST_MOVIE - : Permission.REQUEST_TV, - ], - jellyseerrUser.permissions, - {type: 'or'} - ), - [item, jellyseerrUser] - ) - - const canRequest = useMemo(() => { - const status = item?.mediaInfo?.status - return showRequestButton && !status || status === MediaStatus.UNKNOWN - }, [item]) + const canRequest = useJellyseerrCanRequest(item); return ( = ({ = ({ - ) -} + ); +}; - -export default JellyseerrPoster; \ No newline at end of file +export default JellyseerrPoster; From cab6257fb2c92440b6bf9132eba586f15999c73a Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sun, 5 Jan 2025 11:55:52 +0100 Subject: [PATCH 18/49] feat: hook for request permissions --- utils/_jellyseerr/useJellyseerrCanRequest.ts | 52 ++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 utils/_jellyseerr/useJellyseerrCanRequest.ts diff --git a/utils/_jellyseerr/useJellyseerrCanRequest.ts b/utils/_jellyseerr/useJellyseerrCanRequest.ts new file mode 100644 index 00000000..ba692df3 --- /dev/null +++ b/utils/_jellyseerr/useJellyseerrCanRequest.ts @@ -0,0 +1,52 @@ +import { useJellyseerr } from "@/hooks/useJellyseerr"; +import { + MediaRequestStatus, + MediaStatus, +} from "@/utils/jellyseerr/server/constants/media"; +import { + hasPermission, + Permission, +} from "@/utils/jellyseerr/server/lib/permissions"; +import { MovieResult, TvResult } from "@/utils/jellyseerr/server/models/Search"; +import { useMemo } from "react"; +import MediaRequest from "../jellyseerr/server/entity/MediaRequest"; +import { MovieDetails } from "../jellyseerr/server/models/Movie"; +import { TvDetails } from "../jellyseerr/server/models/Tv"; + +export const useJellyseerrCanRequest = ( + item?: MovieResult | TvResult | MovieDetails | TvDetails +) => { + const { jellyseerrUser } = useJellyseerr(); + + const canRequest = useMemo(() => { + if (!jellyseerrUser || !item) return false; + + const canNotRequest = + item?.mediaInfo?.requests?.some( + (r: MediaRequest) => + r.status == MediaRequestStatus.PENDING || + r.status == MediaRequestStatus.APPROVED + ) || + item.mediaInfo?.status === MediaStatus.AVAILABLE || + item.mediaInfo?.status === MediaStatus.BLACKLISTED || + item.mediaInfo?.status === MediaStatus.PENDING || + item.mediaInfo?.status === MediaStatus.PROCESSING; + + if (canNotRequest) return false; + + const userHasPermission = hasPermission( + [ + Permission.REQUEST, + item?.mediaInfo?.mediaType + ? Permission.REQUEST_MOVIE + : Permission.REQUEST_TV, + ], + jellyseerrUser.permissions, + { type: "or" } + ); + + return userHasPermission && !canNotRequest; + }, [item, jellyseerrUser]); + + return canRequest; +}; From adfde1a7cdd71065617a9901a3e3cfe3ddaa3fc2 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sun, 5 Jan 2025 11:56:16 +0100 Subject: [PATCH 19/49] fix: update request status in poster on search scree --- hooks/useJellyseerr.ts | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/hooks/useJellyseerr.ts b/hooks/useJellyseerr.ts index 4546cee1..815510fa 100644 --- a/hooks/useJellyseerr.ts +++ b/hooks/useJellyseerr.ts @@ -30,8 +30,9 @@ import { writeErrorLog } from "@/utils/log"; import DiscoverSlider from "@/utils/jellyseerr/server/entity/DiscoverSlider"; import { CombinedCredit, - PersonDetails + PersonDetails, } from "@/utils/jellyseerr/server/models/Person"; +import { useQueryClient } from "@tanstack/react-query"; interface SearchParams { query: string; @@ -220,7 +221,12 @@ export class JellyseerrApi { async personCombinedCredits(id: number | string): Promise { return this.axios - ?.get(Endpoints.API_V1 + Endpoints.PERSON + `/${id}` + Endpoints.COMBINED_CREDITS) + ?.get( + Endpoints.API_V1 + + Endpoints.PERSON + + `/${id}` + + Endpoints.COMBINED_CREDITS + ) .then((response) => { return response?.data; }); @@ -260,15 +266,20 @@ export class JellyseerrApi { }); } - imageProxy(path?: string, tmdbPath: string = 'original', width: number = 1920, quality: number = 75) { - return path ? ( - this.axios.defaults.baseURL + - `/_next/image?` + - new URLSearchParams( - `url=https://image.tmdb.org/t/p/${tmdbPath}/${path}&w=${width}&q=${quality}` - ).toString() - ) : - this.axios?.defaults.baseURL + `/images/overseerr_poster_not_found_logo_top.png`; + imageProxy( + path?: string, + tmdbPath: string = "original", + width: number = 1920, + quality: number = 75 + ) { + return path + ? this.axios.defaults.baseURL + + `/_next/image?` + + new URLSearchParams( + `url=https://image.tmdb.org/t/p/${tmdbPath}/${path}&w=${width}&q=${quality}` + ).toString() + : this.axios?.defaults.baseURL + + `/images/overseerr_poster_not_found_logo_top.png`; } async submitIssue(mediaId: number, issueType: IssueType, message: string) { @@ -344,6 +355,7 @@ const jellyseerrUserAtom = atom(storage.get(JELLYSEERR_USER)); export const useJellyseerr = () => { const [jellyseerrUser, setJellyseerrUser] = useAtom(jellyseerrUserAtom); const [settings, updateSettings] = useSettings(); + const queryClient = useQueryClient(); const jellyseerrApi = useMemo(() => { const cookies = storage.get(JELLYSEERR_COOKIES); @@ -361,12 +373,16 @@ export const useJellyseerr = () => { const requestMedia = useCallback( (title: string, request: MediaRequestBody, onSuccess?: () => void) => { - jellyseerrApi?.request?.(request)?.then((mediaRequest) => { + jellyseerrApi?.request?.(request)?.then(async (mediaRequest) => { + await queryClient.invalidateQueries({ + queryKey: ["search", "jellyseerr"], + }); + switch (mediaRequest.status) { case MediaRequestStatus.PENDING: case MediaRequestStatus.APPROVED: toast.success(`Requested ${title}!`); - onSuccess?.() + onSuccess?.(); break; case MediaRequestStatus.DECLINED: toast.error(`You don't have permission to request!`); From e9336e9a6727e06db01c6e15fb3ff4e98e6e8235 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sun, 5 Jan 2025 11:56:46 +0100 Subject: [PATCH 20/49] feat: new useQuery specifically for react navigation to invalidate !enabled queries on screen re-mount --- utils/useReactNavigationQuery.ts | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 utils/useReactNavigationQuery.ts diff --git a/utils/useReactNavigationQuery.ts b/utils/useReactNavigationQuery.ts new file mode 100644 index 00000000..a0c5b307 --- /dev/null +++ b/utils/useReactNavigationQuery.ts @@ -0,0 +1,32 @@ +import { useFocusEffect } from "@react-navigation/core"; +import { + QueryKey, + useQuery, + UseQueryOptions, + UseQueryResult, +} from "@tanstack/react-query"; +import { useCallback } from "react"; + +export function useReactNavigationQuery< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey +>( + options: UseQueryOptions +): UseQueryResult { + const useQueryReturn = useQuery(options); + + useFocusEffect( + useCallback(() => { + if ( + ((options.refetchOnWindowFocus && useQueryReturn.isStale) || + options.refetchOnWindowFocus === "always") && + options.enabled !== false + ) + useQueryReturn.refetch(); + }, [options.enabled, options.refetchOnWindowFocus]) + ); + + return useQueryReturn; +} From 39bb3a93708b92694e27a787802d9f46b43a523f Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sun, 5 Jan 2025 12:03:56 +0100 Subject: [PATCH 21/49] fix: padding --- .../jellyseerr/[personId].tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/[personId].tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/[personId].tsx index 2dc2a478..5a930982 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/[personId].tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/[personId].tsx @@ -223,12 +223,14 @@ export default function page() { mediaType={item.mediaType as "movie" | "tv"} /> {/*{item.title}*/} - - as {item.character} - + {item.character && ( + + as {item.character} + + )} )} keyExtractor={(item) => item.id.toString()} From 0773f773baf0504670819f2dd6a0acd1fa2fed05 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sun, 5 Jan 2025 12:04:02 +0100 Subject: [PATCH 22/49] fix: padding --- .../(home,libraries,search,favorites)/jellyseerr/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx index c33b1ee7..b839708d 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx @@ -246,7 +246,7 @@ const Page: React.FC = () => { className="p-2 border border-neutral-800 bg-neutral-900 rounded-xl" details={details} /> - + From 0fb6f2fb308f32cce941013b96a64a084e323a51 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sun, 5 Jan 2025 12:04:07 +0100 Subject: [PATCH 23/49] fix: padding --- app/(auth)/(tabs)/(search)/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(auth)/(tabs)/(search)/index.tsx b/app/(auth)/(tabs)/(search)/index.tsx index 92dc00c5..1ae0059c 100644 --- a/app/(auth)/(tabs)/(search)/index.tsx +++ b/app/(auth)/(tabs)/(search)/index.tsx @@ -315,7 +315,7 @@ export default function search() { paddingRight: insets.right, }} > - + {Platform.OS === "android" && ( Date: Sun, 5 Jan 2025 12:04:11 +0100 Subject: [PATCH 24/49] fix: padding --- components/jellyseerr/Cast.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/jellyseerr/Cast.tsx b/components/jellyseerr/Cast.tsx index 8f64355d..f5474caf 100644 --- a/components/jellyseerr/Cast.tsx +++ b/components/jellyseerr/Cast.tsx @@ -13,7 +13,7 @@ const CastSlide: React.FC< details?.credits?.cast?.length && details?.credits?.cast?.length > 0 && ( - Cast + Cast } estimatedItemSize={15} keyExtractor={(item) => item?.id?.toString()} + contentContainerStyle={{ paddingHorizontal: 16 }} renderItem={({ item }) => ( Date: Sun, 5 Jan 2025 15:29:39 +0100 Subject: [PATCH 25/49] fix: overlapping buttons with navbar fixes #373 --- app/login.tsx | 74 +++++++++++++++++++++---------------- components/Button.tsx | 2 +- components/common/Input.tsx | 2 +- 3 files changed, 44 insertions(+), 34 deletions(-) diff --git a/app/login.tsx b/app/login.tsx index 54aa03a8..614ad793 100644 --- a/app/login.tsx +++ b/app/login.tsx @@ -2,8 +2,13 @@ import { Button } from "@/components/Button"; import { Input } from "@/components/common/Input"; import { Text } from "@/components/common/Text"; import { PreviousServersList } from "@/components/PreviousServersList"; +import { Colors } from "@/constants/Colors"; import { apiAtom, useJellyfin } from "@/providers/JellyfinProvider"; -import { Ionicons } from "@expo/vector-icons"; +import { + Ionicons, + MaterialCommunityIcons, + MaterialIcons, +} from "@expo/vector-icons"; import { PublicSystemInfo } from "@jellyfin/sdk/lib/generated-client"; import { getSystemApi } from "@jellyfin/sdk/lib/utils/api"; import { Image } from "expo-image"; @@ -39,7 +44,6 @@ const Login: React.FC = () => { const [serverURL, setServerURL] = useState(_apiUrl); const [serverName, setServerName] = useState(""); - const [error, setError] = useState(""); const [credentials, setCredentials] = useState<{ username: string; password: string; @@ -77,8 +81,10 @@ const Login: React.FC = () => { onPress={() => { removeServer(); }} + className="flex flex-row items-center" > - + + Change server ) : null, }); @@ -95,9 +101,9 @@ const Login: React.FC = () => { } } catch (error) { if (error instanceof Error) { - setError(error.message); + Alert.alert("Connection failed", error.message); } else { - setError("An unexpected error occurred"); + Alert.alert("Connection failed", "An unexpected error occurred"); } } finally { setLoading(false); @@ -136,6 +142,8 @@ const Login: React.FC = () => { return url; } + return undefined; + } catch { return undefined; } finally { setLoadingServerCheck(false); @@ -230,7 +238,6 @@ const Login: React.FC = () => { /> setCredentials({ ...credentials, password: text }) @@ -244,28 +251,34 @@ const Login: React.FC = () => { clearButtonMode="while-editing" maxLength={500} /> + + + + + + - - {error} - - - - + ) : ( <> - + { Enter the URL to your Jellyfin server { textContentType="URL" maxLength={500} /> - - Make sure to include http or https - - { - handleConnect(s.address); - }} - /> - - + + { + handleConnect(s.address); + }} + /> diff --git a/components/Button.tsx b/components/Button.tsx index fb685f4c..1bb8aa92 100644 --- a/components/Button.tsx +++ b/components/Button.tsx @@ -37,7 +37,7 @@ export const Button: React.FC> = ({ case "red": return "bg-red-600"; case "black": - return "bg-neutral-900 border border-neutral-800"; + return "bg-neutral-900"; case "transparent": return "bg-transparent"; } diff --git a/components/common/Input.tsx b/components/common/Input.tsx index ba4ab45b..d82a3225 100644 --- a/components/common/Input.tsx +++ b/components/common/Input.tsx @@ -7,7 +7,7 @@ export function Input(props: TextInputProps) { return ( Date: Sun, 5 Jan 2025 15:46:44 +0100 Subject: [PATCH 26/49] feat: hide libraries --- app/(auth)/(tabs)/(home)/_layout.tsx | 6 + .../(home)/settings/hide-libraries/page.tsx | 60 +++++++++ app/(auth)/(tabs)/(libraries)/index.tsx | 22 ++-- components/common/TouchableItemRouter.tsx | 115 ++++++------------ components/settings/OtherSettings.tsx | 9 +- utils/atoms/settings.ts | 2 + 6 files changed, 125 insertions(+), 89 deletions(-) create mode 100644 app/(auth)/(tabs)/(home)/settings/hide-libraries/page.tsx diff --git a/app/(auth)/(tabs)/(home)/_layout.tsx b/app/(auth)/(tabs)/(home)/_layout.tsx index 3509be51..98ee5c0d 100644 --- a/app/(auth)/(tabs)/(home)/_layout.tsx +++ b/app/(auth)/(tabs)/(home)/_layout.tsx @@ -77,6 +77,12 @@ export default function IndexLayout() { title: "", }} /> + {Object.entries(nestedTabPageScreenOptions).map(([name, options]) => ( ))} diff --git a/app/(auth)/(tabs)/(home)/settings/hide-libraries/page.tsx b/app/(auth)/(tabs)/(home)/settings/hide-libraries/page.tsx new file mode 100644 index 00000000..bddf62b8 --- /dev/null +++ b/app/(auth)/(tabs)/(home)/settings/hide-libraries/page.tsx @@ -0,0 +1,60 @@ +import { Text } from "@/components/common/Text"; +import { ListGroup } from "@/components/list/ListGroup"; +import { ListItem } from "@/components/list/ListItem"; +import { Loader } from "@/components/Loader"; +import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; +import { useSettings } from "@/utils/atoms/settings"; +import { getUserViewsApi } from "@jellyfin/sdk/lib/utils/api"; +import { useQuery } from "@tanstack/react-query"; +import { useAtomValue } from "jotai"; +import { Switch, View } from "react-native"; + +export default function page() { + const [settings, updateSettings] = useSettings(); + const user = useAtomValue(userAtom); + const api = useAtomValue(apiAtom); + + const { data, isLoading: isLoading } = useQuery({ + queryKey: ["user-views", user?.Id], + queryFn: async () => { + const response = await getUserViewsApi(api!).getUserViews({ + userId: user?.Id, + }); + + return response.data.Items || null; + }, + }); + + if (!settings) return null; + + if (isLoading) + return ( + + + + ); + + return ( + + + {data?.map((view) => ( + {}}> + { + updateSettings({ + hiddenLibraries: value + ? [...(settings.hiddenLibraries || []), view.Id!] + : settings.hiddenLibraries?.filter((id) => id !== view.Id), + }); + }} + /> + + ))} + + + Select the libraries you want to hide from the Library tab. + + + ); +} diff --git a/app/(auth)/(tabs)/(libraries)/index.tsx b/app/(auth)/(tabs)/(libraries)/index.tsx index ef729254..e8c3f766 100644 --- a/app/(auth)/(tabs)/(libraries)/index.tsx +++ b/app/(auth)/(tabs)/(libraries)/index.tsx @@ -10,7 +10,7 @@ import { import { FlashList } from "@shopify/flash-list"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useAtom } from "jotai"; -import { useEffect } from "react"; +import { useEffect, useMemo } from "react"; import { StyleSheet, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; @@ -23,20 +23,20 @@ export default function index() { const { data, isLoading: isLoading } = useQuery({ queryKey: ["user-views", user?.Id], queryFn: async () => { - if (!api || !user?.Id) { - return null; - } - - const response = await getUserViewsApi(api).getUserViews({ - userId: user.Id, + const response = await getUserViewsApi(api!).getUserViews({ + userId: user?.Id, }); return response.data.Items || null; }, - enabled: !!api && !!user?.Id, - staleTime: 60 * 1000 * 60, + staleTime: 60, }); + const libraries = useMemo( + () => data?.filter((l) => !settings?.hiddenLibraries?.includes(l.Id!)), + [data, settings?.hiddenLibraries] + ); + useEffect(() => { for (const item of data || []) { queryClient.prefetchQuery({ @@ -63,7 +63,7 @@ export default function index() { ); - if (!data) + if (!libraries) return ( No libraries found @@ -81,7 +81,7 @@ export default function index() { paddingLeft: insets.left, paddingRight: insets.right, }} - data={data} + data={libraries} renderItem={({ item }) => } keyExtractor={(item) => item.Id || ""} ItemSeparatorComponent={() => diff --git a/components/common/TouchableItemRouter.tsx b/components/common/TouchableItemRouter.tsx index c13e9821..04d36dc7 100644 --- a/components/common/TouchableItemRouter.tsx +++ b/components/common/TouchableItemRouter.tsx @@ -4,9 +4,11 @@ import { BaseItemPerson, } from "@jellyfin/sdk/lib/generated-client/models"; import { useRouter, useSegments } from "expo-router"; -import { PropsWithChildren } from "react"; +import { PropsWithChildren, useCallback } from "react"; import { TouchableOpacity, TouchableOpacityProps } from "react-native"; import * as ContextMenu from "zeego/context-menu"; +import { useActionSheet } from "@expo/react-native-action-sheet"; +import * as Haptics from "expo-haptics"; interface Props extends TouchableOpacityProps { item: BaseItemDto; @@ -16,8 +18,6 @@ export const itemRouter = ( item: BaseItemDto | BaseItemPerson, from: string ) => { - console.log(item.Type, item?.CollectionType); - if ("CollectionType" in item && item.CollectionType === "livetv") { return `/(auth)/(tabs)/${from}/livetv`; } @@ -68,10 +68,33 @@ export const TouchableItemRouter: React.FC> = ({ }) => { const router = useRouter(); const segments = useSegments(); + const { showActionSheetWithOptions } = useActionSheet(); + const markAsPlayedStatus = useMarkAsPlayed(item); const from = segments[2]; - const markAsPlayedStatus = useMarkAsPlayed(item); + const showActionSheet = useCallback(() => { + if (!(item.Type === "Movie" || item.Type === "Episode")) return; + + const options = ["Mark as Played", "Mark as Not Played", "Cancel"]; + const cancelButtonIndex = 2; + + showActionSheetWithOptions( + { + options, + cancelButtonIndex, + }, + async (selectedIndex) => { + if (selectedIndex === 0) { + await markAsPlayedStatus(true); + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + } else if (selectedIndex === 1) { + await markAsPlayedStatus(false); + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + } + } + ); + }, [showActionSheetWithOptions, markAsPlayedStatus]); if ( from === "(home)" || @@ -80,78 +103,16 @@ export const TouchableItemRouter: React.FC> = ({ from === "(favorites)" ) return ( - - - { - const url = itemRouter(item, from); - // @ts-ignore - router.push(url); - }} - {...props} - > - {children} - - - - Actions - { - markAsPlayedStatus(true); - }} - shouldDismissMenuOnSelect - > - - Mark as watched - - - - { - markAsPlayedStatus(false); - }} - shouldDismissMenuOnSelect - destructive - > - - Mark as not watched - - - - - + { + const url = itemRouter(item, from); + // @ts-expect-error + router.push(url); + }} + {...props} + > + {children} + ); }; diff --git a/components/settings/OtherSettings.tsx b/components/settings/OtherSettings.tsx index d280a167..8987379a 100644 --- a/components/settings/OtherSettings.tsx +++ b/components/settings/OtherSettings.tsx @@ -15,10 +15,12 @@ import * as DropdownMenu from "zeego/dropdown-menu"; import { Text } from "../common/Text"; import { ListGroup } from "../list/ListGroup"; import { ListItem } from "../list/ListItem"; +import { useRouter } from "expo-router"; interface Props extends ViewProps {} export const OtherSettings: React.FC = () => { + const router = useRouter(); const [settings, updateSettings] = useSettings(); /******************** @@ -54,7 +56,7 @@ export const OtherSettings: React.FC = () => { if (!settings) return null; return ( - + { } /> + router.push("/settings/hide-libraries/page")} + title="Hide Libraries" + showArrow + /> ); }; diff --git a/utils/atoms/settings.ts b/utils/atoms/settings.ts index c37dd4eb..fa201610 100644 --- a/utils/atoms/settings.ts +++ b/utils/atoms/settings.ts @@ -88,6 +88,7 @@ export type Settings = { remuxConcurrentLimit: 1 | 2 | 3 | 4; safeAreaInControlsEnabled: boolean; jellyseerrServerUrl?: string; + hiddenLibraries?: string[]; }; const loadSettings = (): Settings => { @@ -126,6 +127,7 @@ const loadSettings = (): Settings => { remuxConcurrentLimit: 1, safeAreaInControlsEnabled: true, jellyseerrServerUrl: undefined, + hiddenLibraries: [], }; try { From 41db34ed8eca47298734c386e33f364d24f1fafd Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sun, 5 Jan 2025 16:22:32 +0100 Subject: [PATCH 27/49] fix: hide libraries from home sections as well --- app/(auth)/(tabs)/(home)/index.tsx | 7 ++++++- app/(auth)/(tabs)/(home)/settings/hide-libraries/page.tsx | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/(auth)/(tabs)/(home)/index.tsx b/app/(auth)/(tabs)/(home)/index.tsx index 0f777a45..4c585949 100644 --- a/app/(auth)/(tabs)/(home)/index.tsx +++ b/app/(auth)/(tabs)/(home)/index.tsx @@ -116,7 +116,7 @@ export default function index() { }, []); const { - data: userViews, + data, isError: e1, isLoading: l1, } = useQuery({ @@ -136,6 +136,11 @@ export default function index() { staleTime: 60 * 1000, }); + const userViews = useMemo( + () => data?.filter((l) => !settings?.hiddenLibraries?.includes(l.Id!)), + [data, settings?.hiddenLibraries] + ); + const { data: mediaListCollections, isError: e2, diff --git a/app/(auth)/(tabs)/(home)/settings/hide-libraries/page.tsx b/app/(auth)/(tabs)/(home)/settings/hide-libraries/page.tsx index bddf62b8..6919441e 100644 --- a/app/(auth)/(tabs)/(home)/settings/hide-libraries/page.tsx +++ b/app/(auth)/(tabs)/(home)/settings/hide-libraries/page.tsx @@ -53,7 +53,8 @@ export default function page() { ))} - Select the libraries you want to hide from the Library tab. + Select the libraries you want to hide from the Library tab and home page + sections. ); From 9baa4063bdafb043681ff14ce7219ad49e5fb27b Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sun, 5 Jan 2025 16:22:52 +0100 Subject: [PATCH 28/49] chore --- components/settings/OtherSettings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/settings/OtherSettings.tsx b/components/settings/OtherSettings.tsx index 79441782..cbd8fc18 100644 --- a/components/settings/OtherSettings.tsx +++ b/components/settings/OtherSettings.tsx @@ -6,6 +6,7 @@ import { } from "@/utils/background-tasks"; import { Ionicons } from "@expo/vector-icons"; import * as BackgroundFetch from "expo-background-fetch"; +import { useRouter } from "expo-router"; import * as ScreenOrientation from "expo-screen-orientation"; import * as TaskManager from "expo-task-manager"; import React, { useEffect } from "react"; @@ -15,7 +16,6 @@ import * as DropdownMenu from "zeego/dropdown-menu"; import { Text } from "../common/Text"; import { ListGroup } from "../list/ListGroup"; import { ListItem } from "../list/ListItem"; -import { useRouter } from "expo-router"; interface Props extends ViewProps {} From cad03a35665edabbed418832c47bee65ceb9aef9 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sun, 5 Jan 2025 20:59:45 +0100 Subject: [PATCH 29/49] fix: chromecast --- app.json | 3 ++- plugins/withGoogleCastActivity.js | 34 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 plugins/withGoogleCastActivity.js diff --git a/app.json b/app.json index 8a0f4f54..d0023760 100644 --- a/app.json +++ b/app.json @@ -111,7 +111,8 @@ { "android": { "parentTheme": "Material3" } } ], ["react-native-bottom-tabs"], - ["./plugins/withChangeNativeAndroidTextToWhite.js"] + ["./plugins/withChangeNativeAndroidTextToWhite.js"], + ["./plugins/withGoogleCastActivity.js"] ], "experiments": { "typedRoutes": true diff --git a/plugins/withGoogleCastActivity.js b/plugins/withGoogleCastActivity.js new file mode 100644 index 00000000..1a8c0a30 --- /dev/null +++ b/plugins/withGoogleCastActivity.js @@ -0,0 +1,34 @@ +const { withAndroidManifest } = require("@expo/config-plugins"); + +const withGoogleCastActivity = (config) => + withAndroidManifest(config, async (config) => { + const mainApplication = config.modResults.manifest.application[0]; + + // Initialize activity array if it doesn't exist + if (!mainApplication.activity) { + mainApplication.activity = []; + } + + // Check if the activity already exists + const activityExists = mainApplication.activity.some( + (activity) => + activity.$?.["android:name"] === + "com.reactnative.googlecast.RNGCExpandedControllerActivity" + ); + + // Only add the activity if it doesn't already exist + if (!activityExists) { + mainApplication.activity.push({ + $: { + "android:name": + "com.reactnative.googlecast.RNGCExpandedControllerActivity", + "android:theme": "@style/Theme.MaterialComponents.NoActionBar", + "android:launchMode": "singleTask", + }, + }); + } + + return config; + }); + +module.exports = withGoogleCastActivity; From 58ec9156995ab2eb51dc77c219e7ad10058e05be Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sun, 5 Jan 2025 22:17:02 +0100 Subject: [PATCH 30/49] feat: intro page --- app/(auth)/(tabs)/(home)/_layout.tsx | 8 ++ app/(auth)/(tabs)/(home)/index.tsx | 2 +- app/(auth)/(tabs)/(home)/intro/page.tsx | 110 ++++++++++++++++++++++ app/(auth)/(tabs)/(home)/settings.tsx | 10 ++ app/(auth)/(tabs)/_layout.tsx | 26 +++++- assets/icons/jellyseerr-logo.svg | 118 ++++++++++++++++++++++++ 6 files changed, 271 insertions(+), 3 deletions(-) create mode 100644 app/(auth)/(tabs)/(home)/intro/page.tsx create mode 100644 assets/icons/jellyseerr-logo.svg diff --git a/app/(auth)/(tabs)/(home)/_layout.tsx b/app/(auth)/(tabs)/(home)/_layout.tsx index 98ee5c0d..6a1d95ca 100644 --- a/app/(auth)/(tabs)/(home)/_layout.tsx +++ b/app/(auth)/(tabs)/(home)/_layout.tsx @@ -83,6 +83,14 @@ export default function IndexLayout() { title: "", }} /> + {Object.entries(nestedTabPageScreenOptions).map(([name, options]) => ( ))} diff --git a/app/(auth)/(tabs)/(home)/index.tsx b/app/(auth)/(tabs)/(home)/index.tsx index 4c585949..95cfd856 100644 --- a/app/(auth)/(tabs)/(home)/index.tsx +++ b/app/(auth)/(tabs)/(home)/index.tsx @@ -23,7 +23,7 @@ import { getUserViewsApi, } from "@jellyfin/sdk/lib/utils/api"; import NetInfo from "@react-native-community/netinfo"; -import { QueryFunction, useQuery, useQueryClient } from "@tanstack/react-query"; +import { QueryFunction, useQuery } from "@tanstack/react-query"; import { useNavigation, useRouter } from "expo-router"; import { useAtomValue } from "jotai"; import { useCallback, useEffect, useMemo, useState } from "react"; diff --git a/app/(auth)/(tabs)/(home)/intro/page.tsx b/app/(auth)/(tabs)/(home)/intro/page.tsx new file mode 100644 index 00000000..9dbf5dc3 --- /dev/null +++ b/app/(auth)/(tabs)/(home)/intro/page.tsx @@ -0,0 +1,110 @@ +import { Button } from "@/components/Button"; +import { Text } from "@/components/common/Text"; +import { storage } from "@/utils/mmkv"; +import { Feather, Ionicons } from "@expo/vector-icons"; +import { Image } from "expo-image"; +import { useFocusEffect, useRouter } from "expo-router"; +import { useCallback } from "react"; +import { TouchableOpacity, View } from "react-native"; + +export default function page() { + const router = useRouter(); + + useFocusEffect( + useCallback(() => { + storage.set("hasShownIntro", true); + }, []) + ); + + return ( + + + + Welcome to Streamyfin + + + A free and open source client for Jellyfin. + + + + + Features + + Streamyfin has a bunch of features and integrates with a + wide array of software which you can find in the settings menu, these + include: + + + + + Jellyseerr + + Connect to your Jellyseerr instance and request movies directly in + the app. + + + + + + + + + Downloads + + Download movies and tv-shows to view offline. Use either the + default method or install the optimize server to download files in + the background. + + + + + + + + + Chromecast + + Cast movies and tv-shows to your Chromecast devices. + + + + + + + { + router.back(); + router.push("/settings"); + }} + className="mt-4" + > + Go to settings + + + ); +} diff --git a/app/(auth)/(tabs)/(home)/settings.tsx b/app/(auth)/(tabs)/(home)/settings.tsx index 559611a8..576ed3fa 100644 --- a/app/(auth)/(tabs)/(home)/settings.tsx +++ b/app/(auth)/(tabs)/(home)/settings.tsx @@ -18,6 +18,7 @@ import { useNavigation, useRouter } from "expo-router"; import { useEffect } from "react"; import { ScrollView, TouchableOpacity, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { storage } from "@/utils/mmkv"; export default function settings() { const router = useRouter(); @@ -67,6 +68,15 @@ export default function settings() { + + { + router.push("/intro/page"); + }} + title={"Show intro"} + /> + + { + const hasShownIntro = storage.getBoolean("hasShownIntro"); + if (hasShownIntro === false && !hasNavigated.current) { + const timer = setTimeout(() => { + hasNavigated.current = true; + router.push("/intro/page"); + storage.set("hasShownIntro", true); + }, 1000); + + return () => { + clearTimeout(timer); + }; + } + }, []) + ); + return ( <> diff --git a/app/(auth)/(tabs)/_layout.tsx b/app/(auth)/(tabs)/_layout.tsx index 1b0e0617..be8f24b0 100644 --- a/app/(auth)/(tabs)/_layout.tsx +++ b/app/(auth)/(tabs)/_layout.tsx @@ -13,14 +13,13 @@ const { Navigator } = createNativeBottomTabNavigator(); import { BottomTabNavigationOptions } from "@react-navigation/bottom-tabs"; import { Colors } from "@/constants/Colors"; +import { useSettings } from "@/utils/atoms/settings"; +import { storage } from "@/utils/mmkv"; import type { ParamListBase, TabNavigationState, } from "@react-navigation/native"; import { SystemBars } from "react-native-edge-to-edge"; -import { useSettings } from "@/utils/atoms/settings"; -import { useAtom, useAtomValue } from "jotai"; -import { storage } from "@/utils/mmkv"; export const NativeTabs = withLayoutContext< BottomTabNavigationOptions, @@ -32,16 +31,13 @@ export const NativeTabs = withLayoutContext< export default function TabLayout() { const [settings] = useSettings(); const router = useRouter(); - const hasNavigated = useRef(false); useFocusEffect( useCallback(() => { const hasShownIntro = storage.getBoolean("hasShownIntro"); - if (hasShownIntro === false && !hasNavigated.current) { + if (!hasShownIntro) { const timer = setTimeout(() => { - hasNavigated.current = true; router.push("/intro/page"); - storage.set("hasShownIntro", true); }, 1000); return () => { From b243524a7daa90fe0492584ab55bfae8a3f95565 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Mon, 6 Jan 2025 10:13:40 +0100 Subject: [PATCH 32/49] chore --- scripts/automerge.sh | 12 +++++++ scripts/reset-project.js | 73 ---------------------------------------- 2 files changed, 12 insertions(+), 73 deletions(-) create mode 100644 scripts/automerge.sh delete mode 100755 scripts/reset-project.js diff --git a/scripts/automerge.sh b/scripts/automerge.sh new file mode 100644 index 00000000..2d5c277a --- /dev/null +++ b/scripts/automerge.sh @@ -0,0 +1,12 @@ +#!/bin/bash +[[ -z $(git status --porcelain) ]] && +git checkout main && +git pull --ff-only && +git checkout develop && +git merge main && +git push --follow-tags && +git checkout main && +git merge develop --ff-only && +git push && +git checkout develop || +(echo "Error: Failed to merge" && exit 1) \ No newline at end of file diff --git a/scripts/reset-project.js b/scripts/reset-project.js deleted file mode 100755 index 4512e162..00000000 --- a/scripts/reset-project.js +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env node - -/** - * This script is used to reset the project to a blank state. - * It moves the /app directory to /app-example and creates a new /app directory with an index.tsx and _layout.tsx file. - * You can remove the `reset-project` script from package.json and safely delete this file after running it. - */ - -const fs = require('fs'); -const path = require('path'); - -const root = process.cwd(); -const oldDirPath = path.join(root, 'app'); -const newDirPath = path.join(root, 'app-example'); -const newAppDirPath = path.join(root, 'app'); - -const indexContent = `import { Text, View } from "react-native"; - -export default function Index() { - return ( - - Edit app/index.tsx to edit this screen. - - ); -} -`; - -const layoutContent = `import { Stack } from "expo-router"; - -export default function RootLayout() { - return ( - - - - ); -} -`; - -fs.rename(oldDirPath, newDirPath, (error) => { - if (error) { - return console.error(`Error renaming directory: ${error}`); - } - console.log('/app moved to /app-example.'); - - fs.mkdir(newAppDirPath, { recursive: true }, (error) => { - if (error) { - return console.error(`Error creating new app directory: ${error}`); - } - console.log('New /app directory created.'); - - const indexPath = path.join(newAppDirPath, 'index.tsx'); - fs.writeFile(indexPath, indexContent, (error) => { - if (error) { - return console.error(`Error creating index.tsx: ${error}`); - } - console.log('app/index.tsx created.'); - - const layoutPath = path.join(newAppDirPath, '_layout.tsx'); - fs.writeFile(layoutPath, layoutContent, (error) => { - if (error) { - return console.error(`Error creating _layout.tsx: ${error}`); - } - console.log('app/_layout.tsx created.'); - }); - }); - }); -}); From 23d9cd36d14804fe92e8a3fa26d6ed9bd07033a4 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Mon, 6 Jan 2025 10:14:17 +0100 Subject: [PATCH 33/49] chore --- scripts/automerge.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) mode change 100644 => 100755 scripts/automerge.sh diff --git a/scripts/automerge.sh b/scripts/automerge.sh old mode 100644 new mode 100755 index 2d5c277a..d66a0941 --- a/scripts/automerge.sh +++ b/scripts/automerge.sh @@ -1,11 +1,11 @@ #!/bin/bash [[ -z $(git status --porcelain) ]] && -git checkout main && +git checkout master && git pull --ff-only && git checkout develop && -git merge main && +git merge master && git push --follow-tags && -git checkout main && +git checkout master && git merge develop --ff-only && git push && git checkout develop || From ed42371353520574d0bbd884df2168537e114daf Mon Sep 17 00:00:00 2001 From: ryan0204 Date: Mon, 6 Jan 2025 20:27:34 +0800 Subject: [PATCH 34/49] Change ScreenOrientation to landscape right by default --- hooks/useOrientationSettings.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hooks/useOrientationSettings.ts b/hooks/useOrientationSettings.ts index 85b8a113..907e9bf2 100644 --- a/hooks/useOrientationSettings.ts +++ b/hooks/useOrientationSettings.ts @@ -7,7 +7,9 @@ export const useOrientationSettings = () => { useEffect(() => { if (settings?.autoRotate) { - // Don't need to do anything + ScreenOrientation.lockAsync( + ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT + ); } else if (settings?.defaultVideoOrientation) { ScreenOrientation.lockAsync(settings.defaultVideoOrientation); } From c86a8438e56bd040b0f33aebe2e83f29244ac9da Mon Sep 17 00:00:00 2001 From: ryan0204 Date: Mon, 6 Jan 2025 23:15:00 +0800 Subject: [PATCH 35/49] Bring back toggleSafeArea button for all videos --- app/(auth)/player/direct-player.tsx | 5 ++++ components/video-player/controls/Controls.tsx | 24 +++++++++---------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/app/(auth)/player/direct-player.tsx b/app/(auth)/player/direct-player.tsx index 44b0eb34..86851db6 100644 --- a/app/(auth)/player/direct-player.tsx +++ b/app/(auth)/player/direct-player.tsx @@ -48,6 +48,7 @@ import { import { useSharedValue } from "react-native-reanimated"; import settings from "../(tabs)/(home)/settings"; import { useSettings } from "@/utils/atoms/settings"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; export default function page() { const videoRef = useRef(null); @@ -413,6 +414,8 @@ export default function page() { } } + const insets = useSafeAreaInsets(); + if (!item || isLoadingItem || isLoadingStreamUrl || !stream) return ( @@ -437,6 +440,8 @@ export default function page() { position: "relative", flexDirection: "column", justifyContent: "center", + paddingLeft: ignoreSafeAreas ? 0 : insets.left, + paddingRight: ignoreSafeAreas ? 0 : insets.right, }} > = ({ )} - {mediaSource?.TranscodingUrl && ( - - - - )} + {/* {mediaSource?.TranscodingUrl && ( */} + + + + {/* )} */} { lightHapticFeedback(); From f48b26076dea87de075a82ab8937db7e2c633ffd Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Mon, 6 Jan 2025 17:33:27 +0100 Subject: [PATCH 36/49] feat: loading skeleton for search (including jellyseerr) --- app/(auth)/(tabs)/(search)/index.tsx | 246 +++--------------- components/jellyseerr/JellyseerrIndexPage.tsx | 172 ++++++++++++ components/search/LoadingSkeleton.tsx | 66 +++++ components/search/SearchItemWrapper.tsx | 70 +++++ 4 files changed, 351 insertions(+), 203 deletions(-) create mode 100644 components/jellyseerr/JellyseerrIndexPage.tsx create mode 100644 components/search/LoadingSkeleton.tsx create mode 100644 components/search/SearchItemWrapper.tsx diff --git a/app/(auth)/(tabs)/(search)/index.tsx b/app/(auth)/(tabs)/(search)/index.tsx index 1ae0059c..2a7d6a57 100644 --- a/app/(auth)/(tabs)/(search)/index.tsx +++ b/app/(auth)/(tabs)/(search)/index.tsx @@ -43,6 +43,9 @@ import DiscoverSlide from "@/components/jellyseerr/DiscoverSlide"; import { sortBy } from "lodash"; import PersonPoster from "@/components/jellyseerr/PersonPoster"; import { useReactNavigationQuery } from "@/utils/useReactNavigationQuery"; +import { SearchItemWrapper } from "@/components/search/SearchItemWrapper"; +import { JellyserrIndexPage } from "@/components/jellyseerr/JellyseerrIndexPage"; +import { LoadingSkeleton } from "@/components/search/LoadingSkeleton"; type SearchType = "Library" | "Discover"; @@ -155,57 +158,6 @@ export default function search() { enabled: searchType === "Library" && debouncedSearch.length > 0, }); - const { data: jellyseerrResults, isFetching: j1 } = useReactNavigationQuery({ - queryKey: ["search", "jellyseerr", "results", debouncedSearch], - queryFn: async () => { - const response = await jellyseerrApi?.search({ - query: new URLSearchParams(debouncedSearch).toString(), - page: 1, // todo: maybe rework page & page-size if first results are not enough... - language: "en", - }); - - return response?.results; - }, - enabled: - !!jellyseerrApi && - searchType === "Discover" && - debouncedSearch.length > 0, - }); - - const { data: jellyseerrDiscoverSettings, isFetching: j2 } = - useReactNavigationQuery({ - queryKey: ["search", "jellyseerr", "discoverSettings", debouncedSearch], - queryFn: async () => jellyseerrApi?.discoverSettings(), - enabled: - !!jellyseerrApi && - searchType === "Discover" && - debouncedSearch.length == 0, - }); - - const jellyseerrMovieResults: MovieResult[] | undefined = useMemo( - () => - jellyseerrResults?.filter( - (r) => r.mediaType === MediaType.MOVIE - ) as MovieResult[], - [jellyseerrResults] - ); - - const jellyseerrTvResults: TvResult[] | undefined = useMemo( - () => - jellyseerrResults?.filter( - (r) => r.mediaType === MediaType.TV - ) as TvResult[], - [jellyseerrResults] - ); - - const jellyseerrPersonResults: PersonResult[] | undefined = useMemo( - () => - jellyseerrResults?.filter( - (r) => r.mediaType === "person" - ) as PersonResult[], - [jellyseerrResults] - ); - const { data: series, isFetching: l2 } = useQuery({ queryKey: ["search", "series", debouncedSearch], queryFn: () => @@ -285,25 +237,13 @@ export default function search() { episodes?.length || series?.length || collections?.length || - actors?.length || - jellyseerrMovieResults?.length || - jellyseerrTvResults?.length + actors?.length ); - }, [ - artists, - episodes, - albums, - songs, - movies, - series, - collections, - actors, - jellyseerrResults, - ]); + }, [artists, episodes, albums, songs, movies, series, collections, actors]); const loading = useMemo(() => { - return l1 || l2 || l3 || l4 || l5 || l6 || l7 || l8 || j1 || j2; - }, [l1, l2, l3, l4, l5, l6, l7, l8, j1, j2]); + return l1 || l2 || l3 || l4 || l5 || l6 || l7 || l8; + }, [l1, l2, l3, l4, l5, l6, l7, l8]); return ( <> @@ -350,15 +290,13 @@ export default function search() { )} - {!!q && ( - - - Results for {q} - - - )} - {searchType === "Library" && ( - <> + + + + + + {searchType === "Library" ? ( + m.Id!)} @@ -483,139 +421,41 @@ export default function search() { )} /> - - )} - {searchType === "Discover" && ( + + ) : ( <> - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> + )} - {loading ? ( - - - - ) : noResults && debouncedSearch.length > 0 ? ( - - - No results found for - - - "{debouncedSearch}" - - - ) : debouncedSearch.length === 0 && searchType === "Library" ? ( - - {exampleSearches.map((e) => ( - setSearch(e)} - key={e} - className="mb-2" - > - {e} - - ))} - - ) : debouncedSearch.length === 0 && searchType === "Discover" ? ( - - {sortBy?.( - jellyseerrDiscoverSettings?.filter((s) => s.enabled), - "order" - ).map((slide) => ( - - ))} - - ) : null} + {searchType === "Library" && ( + <> + {!loading && noResults && debouncedSearch.length > 0 ? ( + + + No results found for + + + "{debouncedSearch}" + + + ) : debouncedSearch.length === 0 ? ( + + {exampleSearches.map((e) => ( + setSearch(e)} + key={e} + className="mb-2" + > + {e} + + ))} + + ) : null} + + )} ); } - -type Props = { - ids?: string[] | null; - items?: T[]; - renderItem: (item: any) => React.ReactNode; - header?: string; -}; - -const SearchItemWrapper = ({ - ids, - items, - renderItem, - header, -}: PropsWithChildren>) => { - const [api] = useAtom(apiAtom); - const [user] = useAtom(userAtom); - - const { data, isLoading: l1 } = useQuery({ - queryKey: ["items", ids], - queryFn: async () => { - if (!user?.Id || !api || !ids || ids.length === 0) { - return []; - } - - const itemPromises = ids.map((id) => - getUserItemData({ - api, - userId: user.Id, - itemId: id, - }) - ); - - const results = await Promise.all(itemPromises); - - // Filter out null items - return results.filter( - (item) => item !== null - ) as unknown as BaseItemDto[]; - }, - enabled: !!ids && ids.length > 0 && !!api && !!user?.Id, - staleTime: Infinity, - }); - - if (!data && (!items || items.length === 0)) return null; - - return ( - <> - {header} - - {data && data?.length > 0 - ? data.map((item) => renderItem(item)) - : items && items?.length > 0 - ? items.map((i) => renderItem(i)) - : undefined} - - - ); -}; diff --git a/components/jellyseerr/JellyseerrIndexPage.tsx b/components/jellyseerr/JellyseerrIndexPage.tsx new file mode 100644 index 00000000..f85b9807 --- /dev/null +++ b/components/jellyseerr/JellyseerrIndexPage.tsx @@ -0,0 +1,172 @@ +import { useJellyseerr } from "@/hooks/useJellyseerr"; +import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; +import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData"; +import { MediaType } from "@/utils/jellyseerr/server/constants/media"; +import { + MovieResult, + PersonResult, + TvResult, +} from "@/utils/jellyseerr/server/models/Search"; +import { useReactNavigationQuery } from "@/utils/useReactNavigationQuery"; +import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client"; +import { useQuery } from "@tanstack/react-query"; +import { useAtom } from "jotai"; +import { PropsWithChildren, useMemo } from "react"; +import { ScrollView, View, ViewProps } from "react-native"; +import { Text } from "../common/Text"; +import JellyseerrPoster from "../posters/JellyseerrPoster"; +import PersonPoster from "./PersonPoster"; +import { SearchItemWrapper } from "../search/SearchItemWrapper"; +import DiscoverSlide from "./DiscoverSlide"; +import { sortBy } from "lodash"; +import { Loader } from "../Loader"; +import Animated, { + withTiming, + useAnimatedStyle, + withSequence, + useSharedValue, + useAnimatedReaction, +} from "react-native-reanimated"; +import { LoadingSkeleton } from "../search/LoadingSkeleton"; + +interface Props extends ViewProps { + searchQuery: string; +} + +export const JellyserrIndexPage: React.FC = ({ searchQuery }) => { + const { jellyseerrApi } = useJellyseerr(); + const opacity = useSharedValue(1); + + const { + data: jellyseerrDiscoverSettings, + isFetching: f1, + isLoading: l1, + } = useReactNavigationQuery({ + queryKey: ["search", "jellyseerr", "discoverSettings", searchQuery], + queryFn: async () => jellyseerrApi?.discoverSettings(), + enabled: !!jellyseerrApi && searchQuery.length == 0, + }); + + const { + data: jellyseerrResults, + isFetching: f2, + isLoading: l2, + } = useReactNavigationQuery({ + queryKey: ["search", "jellyseerr", "results", searchQuery], + queryFn: async () => { + const response = await jellyseerrApi?.search({ + query: new URLSearchParams(searchQuery).toString(), + page: 1, + language: "en", + }); + return response?.results; + }, + enabled: !!jellyseerrApi && searchQuery.length > 0, + }); + + const animatedStyle = useAnimatedStyle(() => { + return { + opacity: opacity.value, + }; + }); + + useAnimatedReaction( + () => f1 || f2 || l1 || l2, + (isLoading) => { + if (isLoading) { + opacity.value = withTiming(1, { duration: 200 }); + } else { + opacity.value = withTiming(0, { duration: 200 }); + } + } + ); + + const jellyseerrMovieResults = useMemo( + () => + jellyseerrResults?.filter( + (r) => r.mediaType === MediaType.MOVIE + ) as MovieResult[], + [jellyseerrResults] + ); + + const jellyseerrTvResults = useMemo( + () => + jellyseerrResults?.filter( + (r) => r.mediaType === MediaType.TV + ) as TvResult[], + [jellyseerrResults] + ); + + const jellyseerrPersonResults = useMemo( + () => + jellyseerrResults?.filter( + (r) => r.mediaType === "person" + ) as PersonResult[], + [jellyseerrResults] + ); + + if (!searchQuery.length) + return ( + + {sortBy?.( + jellyseerrDiscoverSettings?.filter((s) => s.enabled), + "order" + ).map((slide) => ( + + ))} + + ); + + return ( + + + + {!jellyseerrMovieResults?.length && + !jellyseerrTvResults?.length && + !jellyseerrPersonResults?.length && + !f1 && + !f2 && + !l1 && + !l2 && ( + + + No results found for + + + "{searchQuery}" + + + )} + + + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + + + ); +}; diff --git a/components/search/LoadingSkeleton.tsx b/components/search/LoadingSkeleton.tsx new file mode 100644 index 00000000..8ac38ada --- /dev/null +++ b/components/search/LoadingSkeleton.tsx @@ -0,0 +1,66 @@ +import { View } from "react-native"; +import { Text } from "../common/Text"; +import Animated, { + useAnimatedStyle, + useAnimatedReaction, + useSharedValue, + withTiming, +} from "react-native-reanimated"; + +interface Props { + isLoading: boolean; +} + +export const LoadingSkeleton: React.FC = ({ isLoading }) => { + const opacity = useSharedValue(1); + + const animatedStyle = useAnimatedStyle(() => { + return { + opacity: opacity.value, + }; + }); + + useAnimatedReaction( + () => isLoading, + (loading) => { + if (loading) { + opacity.value = withTiming(1, { duration: 200 }); + } else { + opacity.value = withTiming(0, { duration: 200 }); + } + } + ); + + return ( + + {[1, 2, 3].map((s) => ( + + + + {[1, 2, 3].map((i) => ( + + + + + Nisi mollit voluptate amet. + + + + + Lorem ipsum + + + + ))} + + + ))} + + ); +}; diff --git a/components/search/SearchItemWrapper.tsx b/components/search/SearchItemWrapper.tsx new file mode 100644 index 00000000..45c3e341 --- /dev/null +++ b/components/search/SearchItemWrapper.tsx @@ -0,0 +1,70 @@ +import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; +import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData"; +import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client"; +import { useQuery } from "@tanstack/react-query"; +import { useAtom } from "jotai"; +import { PropsWithChildren } from "react"; +import { ScrollView } from "react-native"; +import { Text } from "../common/Text"; + +type SearchItemWrapperProps = { + ids?: string[] | null; + items?: T[]; + renderItem: (item: any) => React.ReactNode; + header?: string; +}; + +export const SearchItemWrapper = ({ + ids, + items, + renderItem, + header, +}: PropsWithChildren>) => { + const [api] = useAtom(apiAtom); + const [user] = useAtom(userAtom); + + const { data, isLoading: l1 } = useQuery({ + queryKey: ["items", ids], + queryFn: async () => { + if (!user?.Id || !api || !ids || ids.length === 0) { + return []; + } + + const itemPromises = ids.map((id) => + getUserItemData({ + api, + userId: user.Id, + itemId: id, + }) + ); + + const results = await Promise.all(itemPromises); + + // Filter out null items + return results.filter( + (item) => item !== null + ) as unknown as BaseItemDto[]; + }, + enabled: !!ids && ids.length > 0 && !!api && !!user?.Id, + staleTime: Infinity, + }); + + if (!data && (!items || items.length === 0)) return null; + + return ( + <> + {header} + + {data && data?.length > 0 + ? data.map((item) => renderItem(item)) + : items && items?.length > 0 + ? items.map((i) => renderItem(i)) + : undefined} + + + ); +}; From 0df6b8e2a0c3882a5699cc92306aadad88c4ff7a Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Mon, 6 Jan 2025 17:33:49 +0100 Subject: [PATCH 37/49] chore --- app/(auth)/(tabs)/(search)/index.tsx | 28 ++++-------------- components/jellyseerr/JellyseerrIndexPage.tsx | 29 +++++++------------ 2 files changed, 17 insertions(+), 40 deletions(-) diff --git a/app/(auth)/(tabs)/(search)/index.tsx b/app/(auth)/(tabs)/(search)/index.tsx index 2a7d6a57..39bb0b2a 100644 --- a/app/(auth)/(tabs)/(search)/index.tsx +++ b/app/(auth)/(tabs)/(search)/index.tsx @@ -2,14 +2,17 @@ import { Input } from "@/components/common/Input"; import { Text } from "@/components/common/Text"; import { TouchableItemRouter } from "@/components/common/TouchableItemRouter"; import ContinueWatchingPoster from "@/components/ContinueWatchingPoster"; +import { Tag } from "@/components/GenreTags"; import { ItemCardText } from "@/components/ItemCardText"; -import { Loader } from "@/components/Loader"; +import { JellyserrIndexPage } from "@/components/jellyseerr/JellyseerrIndexPage"; import AlbumCover from "@/components/posters/AlbumCover"; import MoviePoster from "@/components/posters/MoviePoster"; import SeriesPoster from "@/components/posters/SeriesPoster"; +import { LoadingSkeleton } from "@/components/search/LoadingSkeleton"; +import { SearchItemWrapper } from "@/components/search/SearchItemWrapper"; +import { useJellyseerr } from "@/hooks/useJellyseerr"; import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; import { useSettings } from "@/utils/atoms/settings"; -import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData"; import { BaseItemDto, BaseItemKind, @@ -20,7 +23,6 @@ import axios from "axios"; import { Href, router, useLocalSearchParams, useNavigation } from "expo-router"; import { useAtom } from "jotai"; import React, { - PropsWithChildren, useCallback, useEffect, useLayoutEffect, @@ -30,22 +32,6 @@ import React, { import { Platform, ScrollView, TouchableOpacity, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useDebounce } from "use-debounce"; -import { useJellyseerr } from "@/hooks/useJellyseerr"; -import { - MovieResult, - PersonResult, - TvResult, -} from "@/utils/jellyseerr/server/models/Search"; -import { MediaType } from "@/utils/jellyseerr/server/constants/media"; -import JellyseerrPoster from "@/components/posters/JellyseerrPoster"; -import { Tag } from "@/components/GenreTags"; -import DiscoverSlide from "@/components/jellyseerr/DiscoverSlide"; -import { sortBy } from "lodash"; -import PersonPoster from "@/components/jellyseerr/PersonPoster"; -import { useReactNavigationQuery } from "@/utils/useReactNavigationQuery"; -import { SearchItemWrapper } from "@/components/search/SearchItemWrapper"; -import { JellyserrIndexPage } from "@/components/jellyseerr/JellyseerrIndexPage"; -import { LoadingSkeleton } from "@/components/search/LoadingSkeleton"; type SearchType = "Library" | "Discover"; @@ -423,9 +409,7 @@ export default function search() { /> ) : ( - <> - - + )} {searchType === "Library" && ( diff --git a/components/jellyseerr/JellyseerrIndexPage.tsx b/components/jellyseerr/JellyseerrIndexPage.tsx index f85b9807..663fa4c7 100644 --- a/components/jellyseerr/JellyseerrIndexPage.tsx +++ b/components/jellyseerr/JellyseerrIndexPage.tsx @@ -1,6 +1,4 @@ import { useJellyseerr } from "@/hooks/useJellyseerr"; -import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; -import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData"; import { MediaType } from "@/utils/jellyseerr/server/constants/media"; import { MovieResult, @@ -8,26 +6,21 @@ import { TvResult, } from "@/utils/jellyseerr/server/models/Search"; import { useReactNavigationQuery } from "@/utils/useReactNavigationQuery"; -import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client"; -import { useQuery } from "@tanstack/react-query"; -import { useAtom } from "jotai"; -import { PropsWithChildren, useMemo } from "react"; -import { ScrollView, View, ViewProps } from "react-native"; +import { sortBy } from "lodash"; +import { useMemo } from "react"; +import { View, ViewProps } from "react-native"; +import { + useAnimatedReaction, + useAnimatedStyle, + useSharedValue, + withTiming, +} from "react-native-reanimated"; import { Text } from "../common/Text"; import JellyseerrPoster from "../posters/JellyseerrPoster"; -import PersonPoster from "./PersonPoster"; +import { LoadingSkeleton } from "../search/LoadingSkeleton"; import { SearchItemWrapper } from "../search/SearchItemWrapper"; import DiscoverSlide from "./DiscoverSlide"; -import { sortBy } from "lodash"; -import { Loader } from "../Loader"; -import Animated, { - withTiming, - useAnimatedStyle, - withSequence, - useSharedValue, - useAnimatedReaction, -} from "react-native-reanimated"; -import { LoadingSkeleton } from "../search/LoadingSkeleton"; +import PersonPoster from "./PersonPoster"; interface Props extends ViewProps { searchQuery: string; From 90ef8ef6f9b7cc7f6b66e502b1c1792b602599b9 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Mon, 6 Jan 2025 17:38:59 +0100 Subject: [PATCH 38/49] feat: fade in images --- components/posters/JellyseerrPoster.tsx | 53 +++++++++++++++++++------ 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/components/posters/JellyseerrPoster.tsx b/components/posters/JellyseerrPoster.tsx index 5f363d42..930712e5 100644 --- a/components/posters/JellyseerrPoster.tsx +++ b/components/posters/JellyseerrPoster.tsx @@ -1,7 +1,7 @@ import { View, ViewProps } from "react-native"; import { Image } from "expo-image"; import { Text } from "@/components/common/Text"; -import { useMemo } from "react"; +import { useMemo, useRef, useState } from "react"; import { MovieResult, TvResult } from "@/utils/jellyseerr/server/models/Search"; import { MediaStatus, @@ -16,21 +16,46 @@ import { TouchableJellyseerrRouter } from "@/components/common/JellyseerrItemRou import JellyseerrStatusIcon from "@/components/jellyseerr/JellyseerrStatusIcon"; import JellyseerrMediaIcon from "@/components/jellyseerr/JellyseerrMediaIcon"; import { useJellyseerrCanRequest } from "@/utils/_jellyseerr/useJellyseerrCanRequest"; +import Animated, { + FadeIn, + useAnimatedStyle, + useSharedValue, + withTiming, +} from "react-native-reanimated"; +import { Ionicons } from "@expo/vector-icons"; + interface Props extends ViewProps { item: MovieResult | TvResult; } const JellyseerrPoster: React.FC = ({ item, ...props }) => { const { jellyseerrApi } = useJellyseerr(); + const loadingOpacity = useSharedValue(1); + const imageOpacity = useSharedValue(0); + + const loadingAnimatedStyle = useAnimatedStyle(() => ({ + opacity: loadingOpacity.value, + })); + + const imageAnimatedStyle = useAnimatedStyle(() => ({ + opacity: imageOpacity.value, + })); + + const handleImageLoad = () => { + loadingOpacity.value = withTiming(0, { duration: 200 }); + imageOpacity.value = withTiming(1, { duration: 300 }); + }; const imageSrc = useMemo( () => jellyseerrApi?.imageProxy(item.posterPath, "w300_and_h450_face"), [item, jellyseerrApi] ); + const title = useMemo( () => (item.mediaType === MediaType.MOVIE ? item.title : item.name), [item] ); + const releaseYear = useMemo( () => new Date( @@ -53,23 +78,25 @@ const JellyseerrPoster: React.FC = ({ item, ...props }) => { > - + + + - Date: Mon, 6 Jan 2025 12:06:58 -0500 Subject: [PATCH 39/49] [Jellyseerr] Show genre/studio/network discover sliders implements #326 --- .../jellyseerr/[personId].tsx | 247 ------------------ .../jellyseerr/company/[companyId].tsx | 136 ++++++++++ .../jellyseerr/genre/[genreId].tsx | 128 +++++++++ .../jellyseerr/person/[personId].tsx | 151 +++++++++++ app/(auth)/(tabs)/(search)/_layout.tsx | 4 +- bun.lockb | Bin 594152 -> 592269 bytes components/ParallaxPage.tsx | 13 +- components/jellyseerr/JellyseerrIndexPage.tsx | 12 +- components/jellyseerr/ParallaxSlideShow.tsx | 155 +++++++++++ components/jellyseerr/PersonPoster.tsx | 2 +- .../jellyseerr/discover/CompanySlide.tsx | 41 +++ components/jellyseerr/discover/Discover.tsx | 47 ++++ .../jellyseerr/discover/GenericSlideCard.tsx | 59 +++++ components/jellyseerr/discover/GenreSlide.tsx | 56 ++++ .../MovieTvSlide.tsx} | 59 ++--- components/jellyseerr/discover/Slide.tsx | 55 ++++ hooks/useJellyseerr.ts | 22 +- utils/jellyseerr | 2 +- 18 files changed, 887 insertions(+), 302 deletions(-) delete mode 100644 app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/[personId].tsx create mode 100644 app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/company/[companyId].tsx create mode 100644 app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/genre/[genreId].tsx create mode 100644 app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/person/[personId].tsx create mode 100644 components/jellyseerr/ParallaxSlideShow.tsx create mode 100644 components/jellyseerr/discover/CompanySlide.tsx create mode 100644 components/jellyseerr/discover/Discover.tsx create mode 100644 components/jellyseerr/discover/GenericSlideCard.tsx create mode 100644 components/jellyseerr/discover/GenreSlide.tsx rename components/jellyseerr/{DiscoverSlide.tsx => discover/MovieTvSlide.tsx} (58%) create mode 100644 components/jellyseerr/discover/Slide.tsx diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/[personId].tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/[personId].tsx deleted file mode 100644 index 5a930982..00000000 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/[personId].tsx +++ /dev/null @@ -1,247 +0,0 @@ -import { - router, - useLocalSearchParams, - useNavigation, - useSegments, -} from "expo-router"; -import React, { - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from "react"; -import { TouchableOpacity, View } from "react-native"; -import { useQuery } from "@tanstack/react-query"; -import { useJellyseerr } from "@/hooks/useJellyseerr"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { ParallaxScrollView } from "@/components/ParallaxPage"; -import { Text } from "@/components/common/Text"; -import { Animated } from "react-native"; -import { Image } from "expo-image"; -import { OverviewText } from "@/components/OverviewText"; -import { orderBy } from "lodash"; -import { FlashList } from "@shopify/flash-list"; -import { PersonCreditCast } from "@/utils/jellyseerr/server/models/Person"; -import Poster from "@/components/posters/Poster"; -import JellyseerrMediaIcon from "@/components/jellyseerr/JellyseerrMediaIcon"; - -const ANIMATION_ENTER = 250; -const ANIMATION_EXIT = 250; -const BACKDROP_DURATION = 5000; - -export default function page() { - const insets = useSafeAreaInsets(); - const local = useLocalSearchParams(); - const segments = useSegments(); - const { jellyseerrApi, jellyseerrUser } = useJellyseerr(); - - const { personId } = local as { personId: string }; - const from = segments[2]; - - const [currentIndex, setCurrentIndex] = useState(0); - const fadeAnim = useRef(new Animated.Value(0)).current; - - const { data, isLoading, isFetching } = useQuery({ - queryKey: ["jellyseerr", "person", personId], - queryFn: async () => ({ - details: await jellyseerrApi?.personDetails(personId), - combinedCredits: await jellyseerrApi?.personCombinedCredits(personId), - }), - enabled: !!jellyseerrApi && !!personId, - }); - - const locale = useMemo(() => { - return jellyseerrUser?.settings?.locale || "en"; - }, [jellyseerrUser]); - - const region = useMemo( - () => jellyseerrUser?.settings?.region || "US", - [jellyseerrUser] - ); - - const castedRoles: PersonCreditCast[] = useMemo( - () => - orderBy( - data?.combinedCredits?.cast, - ["voteCount", "voteAverage"], - "desc" - ), - [data?.combinedCredits] - ); - - const backdrops = useMemo( - () => castedRoles.map((c) => c.backdropPath), - [data?.combinedCredits] - ); - - const enterAnimation = useCallback( - () => - Animated.timing(fadeAnim, { - toValue: 1, - duration: ANIMATION_ENTER, - useNativeDriver: true, - }), - [fadeAnim] - ); - - const exitAnimation = useCallback( - () => - Animated.timing(fadeAnim, { - toValue: 0, - duration: ANIMATION_EXIT, - useNativeDriver: true, - }), - [fadeAnim] - ); - - useEffect(() => { - if (backdrops?.length) { - enterAnimation().start(); - const intervalId = setInterval(() => { - exitAnimation().start((end) => { - if (end.finished) - setCurrentIndex((prevIndex) => (prevIndex + 1) % backdrops?.length); - }); - }, BACKDROP_DURATION); - - return () => clearInterval(intervalId); - } - }, [backdrops, enterAnimation, exitAnimation, setCurrentIndex, currentIndex]); - - const viewDetails = (credit: PersonCreditCast) => { - router.push({ - //@ts-ignore - pathname: `/(auth)/(tabs)/${from}/jellyseerr/page`, - //@ts-ignore - params: { - ...credit, - mediaTitle: credit.title, - releaseYear: new Date(credit.releaseDate).getFullYear(), - canRequest: "false", - posterSrc: jellyseerrApi?.imageProxy( - credit.posterPath, - "w300_and_h450_face" - ), - }, - }); - }; - - return ( - - - } - logo={ - - } - > - - - - - {data?.details?.name} - - - Born{" "} - {new Date(data?.details?.birthday!!).toLocaleDateString( - `${locale}-${region}`, - { - year: "numeric", - month: "long", - day: "numeric", - } - )}{" "} - | {data?.details?.placeOfBirth} - - - - - - - - - No results - - - } - contentInsetAdjustmentBehavior="automatic" - ListHeaderComponent={ - Appearances - } - renderItem={({ item }) => ( - viewDetails(item)} - > - - - {/*{item.title}*/} - {item.character && ( - - as {item.character} - - )} - - )} - keyExtractor={(item) => item.id.toString()} - estimatedItemSize={255} - numColumns={3} - contentContainerStyle={{ paddingBottom: 24 }} - ItemSeparatorComponent={() => } - /> - - - - - ); -} diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/company/[companyId].tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/company/[companyId].tsx new file mode 100644 index 00000000..02762a51 --- /dev/null +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/company/[companyId].tsx @@ -0,0 +1,136 @@ +import {router, useLocalSearchParams, useSegments,} from "expo-router"; +import React, {useMemo,} from "react"; +import {TouchableOpacity} from "react-native"; +import {useInfiniteQuery} from "@tanstack/react-query"; +import {Endpoints, useJellyseerr} from "@/hooks/useJellyseerr"; +import {Text} from "@/components/common/Text"; +import {Image} from "expo-image"; +import Poster from "@/components/posters/Poster"; +import JellyseerrMediaIcon from "@/components/jellyseerr/JellyseerrMediaIcon"; +import {DiscoverSliderType} from "@/utils/jellyseerr/server/constants/discover"; +import ParallaxSlideShow from "@/components/jellyseerr/ParallaxSlideShow"; +import {MovieResult, Results, TvResult} from "@/utils/jellyseerr/server/models/Search"; +import {COMPANY_LOGO_IMAGE_FILTER} from "@/utils/jellyseerr/src/components/Discover/NetworkSlider"; +import {uniqBy} from "lodash"; + +export default function page() { + const local = useLocalSearchParams(); + const segments = useSegments(); + const {jellyseerrApi} = useJellyseerr(); + + const from = segments[2]; + const {companyId, name, image, type} = local as unknown as { + companyId: string, + name: string, + image: string, + type: DiscoverSliderType + }; + + const {data, fetchNextPage, hasNextPage} = useInfiniteQuery({ + queryKey: ["jellyseerr", "company", type, companyId], + queryFn: async ({pageParam}) => { + let params: any = { + page: Number(pageParam), + }; + + return jellyseerrApi?.discover( + ( + type == DiscoverSliderType.NETWORKS + ? Endpoints.DISCOVER_TV_NETWORK + : Endpoints.DISCOVER_MOVIES_STUDIO + ) + `/${companyId}`, + params + ) + }, + enabled: !!jellyseerrApi && !!companyId, + initialPageParam: 1, + getNextPageParam: (lastPage, pages) => + (lastPage?.page || pages?.findLast((p) => p?.results.length)?.page || 1) + + 1, + staleTime: 0, + }); + + const flatData = useMemo( + () => uniqBy(data?.pages?.filter((p) => p?.results.length).flatMap((p) => p?.results ?? []), "id")?? [], + [data] + ); + + const backdrops = useMemo( + () => jellyseerrApi + ? flatData.map((r) => jellyseerrApi.imageProxy((r as TvResult | MovieResult).backdropPath, "w1920_and_h800_multi_faces")) + : [], + [jellyseerrApi, flatData] + ); + + const viewDetails = (result: Results) => { + router.push({ + //@ts-ignore + pathname: `/(auth)/(tabs)/${from}/jellyseerr/page`, + //@ts-ignore + params: { + ...result, + mediaTitle: getName(result), + releaseYear: getYear(result), + canRequest: "false", + posterSrc: jellyseerrApi?.imageProxy( + (result as MovieResult | TvResult).posterPath, + "w300_and_h450_face" + ), + }, + }); + }; + + const getName = (result: Results) => { + return (result as TvResult).name || (result as MovieResult).title + } + + const getYear = (result: Results) => { + return new Date((result as TvResult).firstAirDate || (result as MovieResult).releaseDate).getFullYear() + } + + return ( + item.id.toString()} + onEndReached={() => { + if (hasNextPage) { + fetchNextPage() + } + }} + logo={ + + } + renderItem={(item, index) => ( + viewDetails(item)} + > + + + {getName(item)} + {getYear(item)} + + )} + /> + ); +} diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/genre/[genreId].tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/genre/[genreId].tsx new file mode 100644 index 00000000..34a4fc7b --- /dev/null +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/genre/[genreId].tsx @@ -0,0 +1,128 @@ +import {router, useLocalSearchParams, useSegments,} from "expo-router"; +import React, {useMemo,} from "react"; +import {TouchableOpacity} from "react-native"; +import {useInfiniteQuery} from "@tanstack/react-query"; +import {Endpoints, useJellyseerr} from "@/hooks/useJellyseerr"; +import {Text} from "@/components/common/Text"; +import Poster from "@/components/posters/Poster"; +import JellyseerrMediaIcon from "@/components/jellyseerr/JellyseerrMediaIcon"; +import {DiscoverSliderType} from "@/utils/jellyseerr/server/constants/discover"; +import ParallaxSlideShow from "@/components/jellyseerr/ParallaxSlideShow"; +import {MovieResult, Results, TvResult} from "@/utils/jellyseerr/server/models/Search"; +import {uniqBy} from "lodash"; +import {textShadowStyle} from "@/components/jellyseerr/discover/GenericSlideCard"; + +export default function page() { + const local = useLocalSearchParams(); + const segments = useSegments(); + const {jellyseerrApi} = useJellyseerr(); + + const from = segments[2]; + const {genreId, name, type} = local as unknown as { + genreId: string, + name: string, + type: DiscoverSliderType + }; + + const {data, fetchNextPage, hasNextPage} = useInfiniteQuery({ + queryKey: ["jellyseerr", "company", type, genreId], + queryFn: async ({pageParam}) => { + let params: any = { + page: Number(pageParam), + genre: genreId + }; + + return jellyseerrApi?.discover( + type == DiscoverSliderType.MOVIE_GENRES + ? Endpoints.DISCOVER_MOVIES + : Endpoints.DISCOVER_TV, + params + ) + }, + enabled: !!jellyseerrApi && !!genreId, + initialPageParam: 1, + getNextPageParam: (lastPage, pages) => + (lastPage?.page || pages?.findLast((p) => p?.results.length)?.page || 1) + + 1, + staleTime: 0, + }); + + const flatData = useMemo( + () => uniqBy(data?.pages?.filter((p) => p?.results.length).flatMap((p) => p?.results ?? []), "id")?? [], + [data] + ); + + const backdrops = useMemo( + () => jellyseerrApi + ? flatData.map((r) => jellyseerrApi.imageProxy((r as TvResult | MovieResult).backdropPath, "w1920_and_h800_multi_faces")) + : [], + [jellyseerrApi, flatData] + ); + + const viewDetails = (result: Results) => { + router.push({ + //@ts-ignore + pathname: `/(auth)/(tabs)/${from}/jellyseerr/page`, + //@ts-ignore + params: { + ...result, + mediaTitle: getName(result), + releaseYear: getYear(result), + canRequest: "false", + posterSrc: jellyseerrApi?.imageProxy( + (result as MovieResult | TvResult).posterPath, + "w300_and_h450_face" + ), + }, + }); + }; + + const getName = (result: Results) => { + return (result as TvResult).name || (result as MovieResult).title + } + + const getYear = (result: Results) => { + return new Date((result as TvResult).firstAirDate || (result as MovieResult).releaseDate).getFullYear() + } + + return ( + item.id.toString()} + onEndReached={() => { + if (hasNextPage) { + fetchNextPage() + } + }} + logo={ + + {name} + + } + renderItem={(item, index) => ( + viewDetails(item)} + > + + + {getName(item)} + {getYear(item)} + + )} + /> + ); +} diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/person/[personId].tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/person/[personId].tsx new file mode 100644 index 00000000..84f59f4b --- /dev/null +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/person/[personId].tsx @@ -0,0 +1,151 @@ +import { + router, + useLocalSearchParams, + useSegments, +} from "expo-router"; +import React, { useMemo } from "react"; +import { TouchableOpacity } from "react-native"; +import { useQuery } from "@tanstack/react-query"; +import { useJellyseerr } from "@/hooks/useJellyseerr"; +import { Text } from "@/components/common/Text"; +import { Image } from "expo-image"; +import { OverviewText } from "@/components/OverviewText"; +import { orderBy } from "lodash"; +import { PersonCreditCast } from "@/utils/jellyseerr/server/models/Person"; +import Poster from "@/components/posters/Poster"; +import JellyseerrMediaIcon from "@/components/jellyseerr/JellyseerrMediaIcon"; +import ParallaxSlideShow from "@/components/jellyseerr/ParallaxSlideShow"; + +export default function page() { + const local = useLocalSearchParams(); + const segments = useSegments(); + const { jellyseerrApi, jellyseerrUser } = useJellyseerr(); + + const { personId } = local as { personId: string }; + const from = segments[2]; + + const { data, isLoading, isFetching } = useQuery({ + queryKey: ["jellyseerr", "person", personId], + queryFn: async () => ({ + details: await jellyseerrApi?.personDetails(personId), + combinedCredits: await jellyseerrApi?.personCombinedCredits(personId), + }), + enabled: !!jellyseerrApi && !!personId, + }); + + const locale = useMemo(() => { + return jellyseerrUser?.settings?.locale || "en"; + }, [jellyseerrUser]); + + const region = useMemo( + () => jellyseerrUser?.settings?.region || "US", + [jellyseerrUser] + ); + + const castedRoles: PersonCreditCast[] = useMemo( + () => + orderBy( + data?.combinedCredits?.cast, + ["voteCount", "voteAverage"], + "desc" + ), + [data?.combinedCredits] + ); + const backdrops = useMemo( + () => jellyseerrApi + ? castedRoles.map((c) => jellyseerrApi.imageProxy(c.backdropPath, "w1920_and_h800_multi_faces")) + : [], + [jellyseerrApi, data?.combinedCredits] + ); + + const viewDetails = (credit: PersonCreditCast) => { + router.push({ + //@ts-ignore + pathname: `/(auth)/(tabs)/${from}/jellyseerr/page`, + //@ts-ignore + params: { + ...credit, + mediaTitle: credit.title, + releaseYear: new Date(credit.releaseDate).getFullYear(), + canRequest: "false", + posterSrc: jellyseerrApi?.imageProxy( + credit.posterPath, + "w300_and_h450_face" + ), + }, + }); + }; + + return ( + item.id.toString()} + logo={ + + } + HeaderContent={() => ( + <> + + {data?.details?.name} + + + Born{" "} + {new Date(data?.details?.birthday!!).toLocaleDateString( + `${locale}-${region}`, + { + year: "numeric", + month: "long", + day: "numeric", + } + )}{" "} + | {data?.details?.placeOfBirth} + + + )} + MainContent={() => ( + + )} + renderItem={(item, index) => ( + viewDetails(item)} + > + + + {item.character && ( + + as {item.character} + + )} + + )} + /> + ); +} diff --git a/app/(auth)/(tabs)/(search)/_layout.tsx b/app/(auth)/(tabs)/(search)/_layout.tsx index 1119e2a4..1f6a3c8b 100644 --- a/app/(auth)/(tabs)/(search)/_layout.tsx +++ b/app/(auth)/(tabs)/(search)/_layout.tsx @@ -36,7 +36,9 @@ export default function SearchLayout() { }} /> - + + + ); } diff --git a/bun.lockb b/bun.lockb index 3a1947ce30c440a8d00177577d1bbabe3f3ebd0e..ce47c71ee7edf7cbb7368deee0e1c1e12a8774a9 100755 GIT binary patch delta 118774 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

wk9ph~socSzrc)jIx* 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 U%&EaKi=GU*oc<}t;-1g{0TZ3<%m4rY delta 119525 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 diff --git a/components/ParallaxPage.tsx b/components/ParallaxPage.tsx index daebca6b..5d7b28e0 100644 --- a/components/ParallaxPage.tsx +++ b/components/ParallaxPage.tsx @@ -1,6 +1,6 @@ import { LinearGradient } from "expo-linear-gradient"; import { type PropsWithChildren, type ReactElement } from "react"; -import { View, ViewProps } from "react-native"; +import {NativeScrollEvent, NativeSyntheticEvent, View, ViewProps} from "react-native"; import Animated, { interpolate, useAnimatedRef, @@ -13,6 +13,7 @@ interface Props extends ViewProps { logo?: ReactElement; episodePoster?: ReactElement; headerHeight?: number; + onEndReached?: (() => void) | null | undefined; } export const ParallaxScrollView: React.FC> = ({ @@ -21,6 +22,7 @@ export const ParallaxScrollView: React.FC> = ({ episodePoster, headerHeight = 400, logo, + onEndReached, ...props }: Props) => { const scrollRef = useAnimatedRef(); @@ -47,6 +49,11 @@ export const ParallaxScrollView: React.FC> = ({ }; }); + + function isCloseToBottom({layoutMeasurement, contentOffset, contentSize}: NativeScrollEvent) { + return layoutMeasurement.height + contentOffset.y >= contentSize.height - 20; + } + return ( > = ({ }} ref={scrollRef} scrollEventThrottle={16} + onScroll={e => { + if (isCloseToBottom(e.nativeEvent)) + onEndReached?.() + }} > {logo && ( = ({ searchQuery }) => { if (!searchQuery.length) return ( - {sortBy?.( - jellyseerrDiscoverSettings?.filter((s) => s.enabled), - "order" - ).map((slide) => ( - - ))} + ); diff --git a/components/jellyseerr/ParallaxSlideShow.tsx b/components/jellyseerr/ParallaxSlideShow.tsx new file mode 100644 index 00000000..1e3d3f4f --- /dev/null +++ b/components/jellyseerr/ParallaxSlideShow.tsx @@ -0,0 +1,155 @@ +import React, { + PropsWithChildren, + useCallback, + useEffect, + useRef, + useState, +} from "react"; +import {Dimensions, View, ViewProps} from "react-native"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { ParallaxScrollView } from "@/components/ParallaxPage"; +import { Text } from "@/components/common/Text"; +import { Animated } from "react-native"; +import { FlashList } from "@shopify/flash-list"; +import {useFocusEffect} from "expo-router"; + +const ANIMATION_ENTER = 250; +const ANIMATION_EXIT = 250; +const BACKDROP_DURATION = 5000; + +type Render = React.ComponentType + | React.ReactElement + | null + | undefined; + +interface Props { + data: T[] + images: string[]; + logo?: React.ReactElement; + HeaderContent?: () => React.ReactElement; + MainContent?: () => React.ReactElement; + listHeader: string; + renderItem: (item: T, index: number) => Render; + keyExtractor: (item: T) => string; + onEndReached?: (() => void) | null | undefined; +} + +const ParallaxSlideShow = ({ + data, + images, + logo, + HeaderContent, + MainContent, + listHeader, + renderItem, + keyExtractor, + onEndReached, + ...props +}: PropsWithChildren & ViewProps> +) => { + const insets = useSafeAreaInsets(); + + const [currentIndex, setCurrentIndex] = useState(0); + const [onEnd, setOnEnd] = useState(true); + const fadeAnim = useRef(new Animated.Value(0)).current; + + const enterAnimation = useCallback( + () => + Animated.timing(fadeAnim, { + toValue: 1, + duration: ANIMATION_ENTER, + useNativeDriver: true, + }), + [fadeAnim] + ); + + const exitAnimation = useCallback( + () => + Animated.timing(fadeAnim, { + toValue: 0, + duration: ANIMATION_EXIT, + useNativeDriver: true, + }), + [fadeAnim] + ); + + useEffect(() => { + if (images?.length) { + enterAnimation().start(); + const intervalId = setInterval(() => { + exitAnimation().start((end) => { + if (end.finished) + setCurrentIndex((prevIndex) => (prevIndex + 1) % images?.length); + }); + }, BACKDROP_DURATION); + + return () => clearInterval(intervalId); + } + }, [images, enterAnimation, exitAnimation, setCurrentIndex, currentIndex]); + + return ( + + + } + logo={logo} + > + + + + {HeaderContent && HeaderContent()} + + + {MainContent && MainContent()} + + + + No results + + + } + contentInsetAdjustmentBehavior="automatic" + ListHeaderComponent={ + {listHeader} + } + nestedScrollEnabled + showsVerticalScrollIndicator={false} + //@ts-ignore + renderItem={({ item, index}) => renderItem(item, index)} + keyExtractor={keyExtractor} + numColumns={3} + estimatedItemSize={214} + ItemSeparatorComponent={() => } + /> + + + + + ); +} + +export default ParallaxSlideShow; \ No newline at end of file diff --git a/components/jellyseerr/PersonPoster.tsx b/components/jellyseerr/PersonPoster.tsx index 57ff9f58..6e7d9aa6 100644 --- a/components/jellyseerr/PersonPoster.tsx +++ b/components/jellyseerr/PersonPoster.tsx @@ -26,7 +26,7 @@ const PersonPoster: React.FC = ({ if (from === "(home)" || from === "(search)" || from === "(libraries)") return ( - router.push(`/(auth)/(tabs)/${from}/jellyseerr/${id}`)}> + router.push(`/(auth)/(tabs)/${from}/jellyseerr/person/${id}`)}> = ({ slide, data, ...props }) => { + const segments = useSegments(); + const { jellyseerrApi } = useJellyseerr(); + const from = segments[2]; + + const navigate = useCallback(({id, image, name}: Network | Studio) => router.push({ + pathname: `/(auth)/(tabs)/${from}/jellyseerr/company/${id}`, + params: {id, image, name, type: slide.type } + }), [slide]); + + return ( + item.id.toString()} + renderItem={(item, index) => ( + navigate(item)}> + + + )} + /> + ); +}; + +export default CompanySlide; diff --git a/components/jellyseerr/discover/Discover.tsx b/components/jellyseerr/discover/Discover.tsx new file mode 100644 index 00000000..6270ad2b --- /dev/null +++ b/components/jellyseerr/discover/Discover.tsx @@ -0,0 +1,47 @@ +import React, {useMemo} from "react"; +import DiscoverSlider from "@/utils/jellyseerr/server/entity/DiscoverSlider"; +import {DiscoverSliderType} from "@/utils/jellyseerr/server/constants/discover"; +import {sortBy} from "lodash"; +import MovieTvSlide from "@/components/jellyseerr/discover/MovieTvSlide"; +import CompanySlide from "@/components/jellyseerr/discover/CompanySlide"; +import {View} from "react-native"; +import {networks} from "@/utils/jellyseerr/src/components/Discover/NetworkSlider"; +import {studios} from "@/utils/jellyseerr/src/components/Discover/StudioSlider"; +import GenreSlide from "@/components/jellyseerr/discover/GenreSlide"; + +interface Props { + sliders?: DiscoverSlider[]; +} +const Discover: React.FC = ({ sliders }) => { + if (!sliders) + return; + + const sortedSliders = useMemo( + () => sortBy(sliders.filter((s) => s.enabled), 'order', 'asc'), + [sliders] + ); + + return ( + + {sortedSliders.map(slide => { + switch (slide.type) { + case DiscoverSliderType.NETWORKS: + return + case DiscoverSliderType.STUDIOS: + return + case DiscoverSliderType.MOVIE_GENRES: + case DiscoverSliderType.TV_GENRES: + return + case DiscoverSliderType.TRENDING: + case DiscoverSliderType.POPULAR_MOVIES: + case DiscoverSliderType.UPCOMING_MOVIES: + case DiscoverSliderType.POPULAR_TV: + case DiscoverSliderType.UPCOMING_TV: + return + } + })} + + ) +}; + +export default Discover; diff --git a/components/jellyseerr/discover/GenericSlideCard.tsx b/components/jellyseerr/discover/GenericSlideCard.tsx new file mode 100644 index 00000000..776d1424 --- /dev/null +++ b/components/jellyseerr/discover/GenericSlideCard.tsx @@ -0,0 +1,59 @@ +import React from "react"; +import {StyleSheet, View, ViewProps} from "react-native"; +import {Image, ImageContentFit} from "expo-image"; +import {Text} from "@/components/common/Text"; +import {LinearGradient} from "expo-linear-gradient"; + +export const textShadowStyle = StyleSheet.create({ + shadow: { + shadowColor: "#000", + shadowOffset: { + width: 1, + height: 1, + }, + shadowOpacity: 1, + shadowRadius: .5, + + elevation: 6, + } +}) + +const GenericSlideCard: React.FC<{id: string; url?: string, title?: string, colors?: string[], contentFit?: ImageContentFit} & ViewProps> = ({ + id, + url, + title, + colors = ['#9333ea', 'transparent'], + contentFit = "contain", + ...props +}) => ( + <> + + + + {title && + + {title} + + } + + + +); + +export default GenericSlideCard; \ No newline at end of file diff --git a/components/jellyseerr/discover/GenreSlide.tsx b/components/jellyseerr/discover/GenreSlide.tsx new file mode 100644 index 00000000..551ee2de --- /dev/null +++ b/components/jellyseerr/discover/GenreSlide.tsx @@ -0,0 +1,56 @@ +import React, {useCallback} from "react"; +import {Endpoints, useJellyseerr,} from "@/hooks/useJellyseerr"; +import {TouchableOpacity, ViewProps} from "react-native"; +import Slide, {SlideProps} from "@/components/jellyseerr/discover/Slide"; +import GenericSlideCard from "@/components/jellyseerr/discover/GenericSlideCard"; +import {router, useSegments} from "expo-router"; +import {useQuery} from "@tanstack/react-query"; +import {DiscoverSliderType} from "@/utils/jellyseerr/server/constants/discover"; +import {genreColorMap} from "@/utils/jellyseerr/src/components/Discover/constants"; +import {GenreSliderItem} from "@/utils/jellyseerr/server/interfaces/api/discoverInterfaces"; + +const GenreSlide: React.FC = ({ slide, ...props }) => { + const segments = useSegments(); + const { jellyseerrApi } = useJellyseerr(); + const from = segments[2]; + + const navigate = useCallback((genre: GenreSliderItem) => router.push({ + pathname: `/(auth)/(tabs)/${from}/jellyseerr/genre/${genre.id}`, + params: {type: slide.type, name: genre.name} + }), [slide]); + + const {data, isFetching, isLoading } = useQuery({ + queryKey: ['jellyseerr', 'discover', slide.type, slide.id], + queryFn: async () => { + return jellyseerrApi?.getGenreSliders( + slide.type == DiscoverSliderType.MOVIE_GENRES + ? Endpoints.MOVIE + : Endpoints.TV + ) + }, + enabled: !!jellyseerrApi + }) + + return ( + data && item.id.toString()} + renderItem={(item, index) => ( + navigate(item)}> + + + )} + /> + ); +}; + +export default GenreSlide; diff --git a/components/jellyseerr/DiscoverSlide.tsx b/components/jellyseerr/discover/MovieTvSlide.tsx similarity index 58% rename from components/jellyseerr/DiscoverSlide.tsx rename to components/jellyseerr/discover/MovieTvSlide.tsx index c7112def..723658c8 100644 --- a/components/jellyseerr/DiscoverSlide.tsx +++ b/components/jellyseerr/discover/MovieTvSlide.tsx @@ -1,5 +1,4 @@ -import React, { useMemo } from "react"; -import DiscoverSlider from "@/utils/jellyseerr/server/entity/DiscoverSlider"; +import React, {useMemo} from "react"; import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover"; import { DiscoverEndpoint, @@ -9,17 +8,13 @@ import { import { useInfiniteQuery } from "@tanstack/react-query"; import { MovieResult, TvResult } from "@/utils/jellyseerr/server/models/Search"; import JellyseerrPoster from "@/components/posters/JellyseerrPoster"; -import { Text } from "@/components/common/Text"; -import { FlashList } from "@shopify/flash-list"; -import { View } from "react-native"; +import Slide, {SlideProps} from "@/components/jellyseerr/discover/Slide"; +import {ViewProps} from "react-native"; -interface Props { - slide: DiscoverSlider; -} -const DiscoverSlide: React.FC = ({ slide }) => { +const MovieTvSlide: React.FC = ({ slide, ...props }) => { const { jellyseerrApi } = useJellyseerr(); - const { data, isFetching, fetchNextPage, hasNextPage } = useInfiniteQuery({ + const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({ queryKey: ["jellyseerr", "discover", slide.id], queryFn: async ({ pageParam }) => { let endpoint: DiscoverEndpoint | undefined = undefined; @@ -62,42 +57,28 @@ const DiscoverSlide: React.FC = ({ slide }) => { }); const flatData = useMemo( - () => - data?.pages?.filter((p) => p?.results.length).flatMap((p) => p?.results), + () => data?.pages?.filter((p) => p?.results.length).flatMap((p) => p?.results), [data] ); return ( flatData && flatData?.length > 0 && ( - - - {DiscoverSliderType[slide.type].toString().toTitle()} - - item!!.id.toString()} - estimatedItemSize={250} - data={flatData} - onEndReachedThreshold={1} - onEndReached={() => { - if (hasNextPage) fetchNextPage(); - }} - renderItem={({ item }) => - item ? ( - - ) : ( - <> - ) - } - /> - + item!!.id.toString()} + onEndReached={() => { + if (hasNextPage) + fetchNextPage() + }} + renderItem={(item) => + + } + /> ) ); }; -export default DiscoverSlide; +export default MovieTvSlide; diff --git a/components/jellyseerr/discover/Slide.tsx b/components/jellyseerr/discover/Slide.tsx new file mode 100644 index 00000000..5a593b41 --- /dev/null +++ b/components/jellyseerr/discover/Slide.tsx @@ -0,0 +1,55 @@ +import React, {PropsWithChildren} from "react"; +import DiscoverSlider from "@/utils/jellyseerr/server/entity/DiscoverSlider"; +import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover"; +import { Text } from "@/components/common/Text"; +import { FlashList } from "@shopify/flash-list"; +import {View, ViewProps} from "react-native"; + +export interface SlideProps { + slide: DiscoverSlider; +} + +interface Props extends SlideProps { + data: T[] + renderItem: (item: T, index: number) => + | React.ComponentType + | React.ReactElement + | null + | undefined; + keyExtractor: (item: T) => string; + onEndReached?: (() => void) | null | undefined; +} + +const Slide = ({ + data, + slide, + renderItem, + keyExtractor, + onEndReached, + ...props +}: PropsWithChildren & ViewProps> +) => { + return ( + + + {DiscoverSliderType[slide.type].toString().toTitle()} + + item ? renderItem(item, index) : <>} + /> + + ); +}; + +export default Slide; diff --git a/hooks/useJellyseerr.ts b/hooks/useJellyseerr.ts index 815510fa..8d3eba30 100644 --- a/hooks/useJellyseerr.ts +++ b/hooks/useJellyseerr.ts @@ -33,6 +33,7 @@ import { PersonDetails, } from "@/utils/jellyseerr/server/models/Person"; import { useQueryClient } from "@tanstack/react-query"; +import {GenreSliderItem} from "@/utils/jellyseerr/server/interfaces/api/discoverInterfaces"; interface SearchParams { query: string; @@ -67,14 +68,20 @@ export enum Endpoints { ISSUE = "/issue", TV = "/tv", SETTINGS = "/settings", + NETWORK = "/network", + STUDIO = "/studio", + GENRE_SLIDER = "/genreslider", DISCOVER = "/discover", DISCOVER_TRENDING = DISCOVER + "/trending", DISCOVER_MOVIES = DISCOVER + "/movies", DISCOVER_TV = DISCOVER + TV, + DISCOVER_TV_NETWORK = DISCOVER + TV + NETWORK, + DISCOVER_MOVIES_STUDIO = DISCOVER + `${MOVIE}s` + STUDIO, AUTH_JELLYFIN = "/auth/jellyfin", } export type DiscoverEndpoint = + | Endpoints.DISCOVER_TV_NETWORK | Endpoints.DISCOVER_TRENDING | Endpoints.DISCOVER_MOVIES | Endpoints.DISCOVER_TV; @@ -181,7 +188,7 @@ export class JellyseerrApi { } async discover( - endpoint: DiscoverEndpoint, + endpoint: DiscoverEndpoint | string, params: any ): Promise { return this.axios @@ -189,6 +196,15 @@ export class JellyseerrApi { .then(({ data }) => data); } + async getGenreSliders( + endpoint: Endpoints.TV | Endpoints.MOVIE, + params: any = undefined + ): Promise { + return this.axios + ?.get(Endpoints.API_V1 + Endpoints.DISCOVER + Endpoints.GENRE_SLIDER + endpoint, { params }) + .then(({ data }) => data); + } + async search(params: SearchParams): Promise { const response = await this.axios?.get( Endpoints.API_V1 + Endpoints.SEARCH, @@ -268,7 +284,7 @@ export class JellyseerrApi { imageProxy( path?: string, - tmdbPath: string = "original", + filter: string = "original", width: number = 1920, quality: number = 75 ) { @@ -276,7 +292,7 @@ export class JellyseerrApi { ? this.axios.defaults.baseURL + `/_next/image?` + new URLSearchParams( - `url=https://image.tmdb.org/t/p/${tmdbPath}/${path}&w=${width}&q=${quality}` + `url=https://image.tmdb.org/t/p/${filter}/${path}&w=${width}&q=${quality}` ).toString() : this.axios?.defaults.baseURL + `/images/overseerr_poster_not_found_logo_top.png`; diff --git a/utils/jellyseerr b/utils/jellyseerr index e69d160e..a15f2ab3 160000 --- a/utils/jellyseerr +++ b/utils/jellyseerr @@ -1 +1 @@ -Subproject commit e69d160e25f0962cd77b01c861ce248050e1ad38 +Subproject commit a15f2ab336936f49e38ea37f8b224da40e12588e From 062e6e6c23a27a787dc47b7515b0a4e086f526da Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Mon, 6 Jan 2025 22:21:27 +0100 Subject: [PATCH 40/49] chore --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- app.json | 2 +- eas.json | 4 ++-- providers/JellyfinProvider.tsx | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 1ac06eda..c8f30a95 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -43,7 +43,7 @@ body: label: Version description: What version of Streamyfin are you running? options: - - 0.24.0 + - 0.25.0 - 0.22.0 - 0.21.0 - older diff --git a/app.json b/app.json index d0023760..fc2c4505 100644 --- a/app.json +++ b/app.json @@ -2,7 +2,7 @@ "expo": { "name": "Streamyfin", "slug": "streamyfin", - "version": "0.24.0", + "version": "0.25.0", "orientation": "default", "icon": "./assets/images/icon.png", "scheme": "streamyfin", diff --git a/eas.json b/eas.json index 8ce5fc71..9821cceb 100644 --- a/eas.json +++ b/eas.json @@ -22,13 +22,13 @@ } }, "production": { - "channel": "0.24.0", + "channel": "0.25.0", "android": { "image": "latest" } }, "production-apk": { - "channel": "0.24.0", + "channel": "0.25.0", "android": { "buildType": "apk", "image": "latest" diff --git a/providers/JellyfinProvider.tsx b/providers/JellyfinProvider.tsx index 57dd94c6..f4ccce75 100644 --- a/providers/JellyfinProvider.tsx +++ b/providers/JellyfinProvider.tsx @@ -55,7 +55,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({ setJellyfin( () => new Jellyfin({ - clientInfo: { name: "Streamyfin", version: "0.24.0" }, + clientInfo: { name: "Streamyfin", version: "0.25.0" }, deviceInfo: { name: deviceName, id, @@ -92,7 +92,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({ return { authorization: `MediaBrowser Client="Streamyfin", Device=${ Platform.OS === "android" ? "Android" : "iOS" - }, DeviceId="${deviceId}", Version="0.24.0"`, + }, DeviceId="${deviceId}", Version="0.25.0"`, }; }, [deviceId]); From a640df30bc1c832ef35f438172d60771fcbb4f03 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Mon, 6 Jan 2025 22:32:27 +0100 Subject: [PATCH 41/49] chore --- .../jellyseerr/page.tsx | 61 ++++++++----------- components/jellyseerr/JellyseerrIndexPage.tsx | 2 +- components/posters/JellyseerrPoster.tsx | 25 +++----- 3 files changed, 34 insertions(+), 54 deletions(-) diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx index b839708d..228d67eb 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx @@ -1,28 +1,23 @@ -import React, { - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from "react"; -import { useLocalSearchParams, useNavigation } from "expo-router"; -import { MovieResult, TvResult } from "@/utils/jellyseerr/server/models/Search"; -import { Text } from "@/components/common/Text"; -import { ParallaxScrollView } from "@/components/ParallaxPage"; -import { Image } from "expo-image"; -import { TouchableOpacity, View } from "react-native"; -import { Ionicons } from "@expo/vector-icons"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { OverviewText } from "@/components/OverviewText"; -import { GenreTags } from "@/components/GenreTags"; -import { - MediaRequestStatus, - MediaStatus, - MediaType, -} from "@/utils/jellyseerr/server/constants/media"; -import { useQuery } from "@tanstack/react-query"; -import { useJellyseerr } from "@/hooks/useJellyseerr"; import { Button } from "@/components/Button"; +import { Text } from "@/components/common/Text"; +import { GenreTags } from "@/components/GenreTags"; +import Cast from "@/components/jellyseerr/Cast"; +import DetailFacts from "@/components/jellyseerr/DetailFacts"; +import { OverviewText } from "@/components/OverviewText"; +import { ParallaxScrollView } from "@/components/ParallaxPage"; +import { JellyserrRatings } from "@/components/Ratings"; +import JellyseerrSeasons from "@/components/series/JellyseerrSeasons"; +import { ItemActions } from "@/components/series/SeriesActions"; +import { useJellyseerr } from "@/hooks/useJellyseerr"; +import { useJellyseerrCanRequest } from "@/utils/_jellyseerr/useJellyseerrCanRequest"; +import { + IssueType, + IssueTypeName, +} from "@/utils/jellyseerr/server/constants/issue"; +import { MediaType } from "@/utils/jellyseerr/server/constants/media"; +import { MovieResult, TvResult } from "@/utils/jellyseerr/server/models/Search"; +import { TvDetails } from "@/utils/jellyseerr/server/models/Tv"; +import { Ionicons } from "@expo/vector-icons"; import { BottomSheetBackdrop, BottomSheetBackdropProps, @@ -30,19 +25,13 @@ import { BottomSheetTextInput, BottomSheetView, } from "@gorhom/bottom-sheet"; -import { - IssueType, - IssueTypeName, -} from "@/utils/jellyseerr/server/constants/issue"; +import { useQuery } from "@tanstack/react-query"; +import { Image } from "expo-image"; +import { useLocalSearchParams, useNavigation } from "expo-router"; +import React, { useCallback, useEffect, useRef, useState } from "react"; +import { TouchableOpacity, View } from "react-native"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; import * as DropdownMenu from "zeego/dropdown-menu"; -import { TvDetails } from "@/utils/jellyseerr/server/models/Tv"; -import JellyseerrSeasons from "@/components/series/JellyseerrSeasons"; -import { JellyserrRatings } from "@/components/Ratings"; -import MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest"; -import DetailFacts from "@/components/jellyseerr/DetailFacts"; -import { ItemActions } from "@/components/series/SeriesActions"; -import Cast from "@/components/jellyseerr/Cast"; -import { useJellyseerrCanRequest } from "@/utils/_jellyseerr/useJellyseerrCanRequest"; const Page: React.FC = () => { const insets = useSafeAreaInsets(); diff --git a/components/jellyseerr/JellyseerrIndexPage.tsx b/components/jellyseerr/JellyseerrIndexPage.tsx index 54e64370..4b9a3488 100644 --- a/components/jellyseerr/JellyseerrIndexPage.tsx +++ b/components/jellyseerr/JellyseerrIndexPage.tsx @@ -1,3 +1,4 @@ +import Discover from "@/components/jellyseerr/discover/Discover"; import { useJellyseerr } from "@/hooks/useJellyseerr"; import { MediaType } from "@/utils/jellyseerr/server/constants/media"; import { @@ -19,7 +20,6 @@ import JellyseerrPoster from "../posters/JellyseerrPoster"; import { LoadingSkeleton } from "../search/LoadingSkeleton"; import { SearchItemWrapper } from "../search/SearchItemWrapper"; import PersonPoster from "./PersonPoster"; -import Discover from "@/components/jellyseerr/discover/Discover"; interface Props extends ViewProps { searchQuery: string; diff --git a/components/posters/JellyseerrPoster.tsx b/components/posters/JellyseerrPoster.tsx index 930712e5..11fb4941 100644 --- a/components/posters/JellyseerrPoster.tsx +++ b/components/posters/JellyseerrPoster.tsx @@ -1,28 +1,19 @@ -import { View, ViewProps } from "react-native"; -import { Image } from "expo-image"; -import { Text } from "@/components/common/Text"; -import { useMemo, useRef, useState } from "react"; -import { MovieResult, TvResult } from "@/utils/jellyseerr/server/models/Search"; -import { - MediaStatus, - MediaType, -} from "@/utils/jellyseerr/server/constants/media"; -import { useJellyseerr } from "@/hooks/useJellyseerr"; -import { - hasPermission, - Permission, -} from "@/utils/jellyseerr/server/lib/permissions"; import { TouchableJellyseerrRouter } from "@/components/common/JellyseerrItemRouter"; -import JellyseerrStatusIcon from "@/components/jellyseerr/JellyseerrStatusIcon"; +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 { MediaType } from "@/utils/jellyseerr/server/constants/media"; +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, { - FadeIn, useAnimatedStyle, useSharedValue, withTiming, } from "react-native-reanimated"; -import { Ionicons } from "@expo/vector-icons"; interface Props extends ViewProps { item: MovieResult | TvResult; From a0ce7cc6d0d160cf7ae226863128f34ee0a8e013 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Mon, 6 Jan 2025 22:58:11 +0100 Subject: [PATCH 42/49] chore --- bun.lockb | Bin 592269 -> 592757 bytes package.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/bun.lockb b/bun.lockb index ce47c71ee7edf7cbb7368deee0e1c1e12a8774a9..4459cefd7c8d3bf69ce2257566d4ec69b54b7a33 100755 GIT binary patch delta 16583 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 Rn}Z+5G|bO981q2G{{c62pfmsg delta 16253 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 W>ZQ1b1qa`aJJFz^=DxVUH~Al;TQ Date: Mon, 6 Jan 2025 19:55:09 -0500 Subject: [PATCH 43/49] use JellyseerrPoster component --- .../jellyseerr/company/[companyId].tsx | 49 ++----------------- .../jellyseerr/genre/[genreId].tsx | 49 ++----------------- .../jellyseerr/person/[personId].tsx | 32 +++--------- 3 files changed, 14 insertions(+), 116 deletions(-) diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/company/[companyId].tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/company/[companyId].tsx index 02762a51..c5eda557 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/company/[companyId].tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/company/[companyId].tsx @@ -12,13 +12,12 @@ import ParallaxSlideShow from "@/components/jellyseerr/ParallaxSlideShow"; import {MovieResult, Results, TvResult} from "@/utils/jellyseerr/server/models/Search"; import {COMPANY_LOGO_IMAGE_FILTER} from "@/utils/jellyseerr/src/components/Discover/NetworkSlider"; import {uniqBy} from "lodash"; +import JellyseerrPoster from "@/components/posters/JellyseerrPoster"; export default function page() { const local = useLocalSearchParams(); - const segments = useSegments(); const {jellyseerrApi} = useJellyseerr(); - const from = segments[2]; const {companyId, name, image, type} = local as unknown as { companyId: string, name: string, @@ -62,32 +61,6 @@ export default function page() { [jellyseerrApi, flatData] ); - const viewDetails = (result: Results) => { - router.push({ - //@ts-ignore - pathname: `/(auth)/(tabs)/${from}/jellyseerr/page`, - //@ts-ignore - params: { - ...result, - mediaTitle: getName(result), - releaseYear: getYear(result), - canRequest: "false", - posterSrc: jellyseerrApi?.imageProxy( - (result as MovieResult | TvResult).posterPath, - "w300_and_h450_face" - ), - }, - }); - }; - - const getName = (result: Results) => { - return (result as TvResult).name || (result as MovieResult).title - } - - const getYear = (result: Results) => { - return new Date((result as TvResult).firstAirDate || (result as MovieResult).releaseDate).getFullYear() - } - return ( } - renderItem={(item, index) => ( - viewDetails(item)} - > - - - {getName(item)} - {getYear(item)} - - )} + renderItem={(item, index) => + + } /> ); } diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/genre/[genreId].tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/genre/[genreId].tsx index 34a4fc7b..dbbce320 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/genre/[genreId].tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/genre/[genreId].tsx @@ -11,13 +11,12 @@ import ParallaxSlideShow from "@/components/jellyseerr/ParallaxSlideShow"; import {MovieResult, Results, TvResult} from "@/utils/jellyseerr/server/models/Search"; import {uniqBy} from "lodash"; import {textShadowStyle} from "@/components/jellyseerr/discover/GenericSlideCard"; +import JellyseerrPoster from "@/components/posters/JellyseerrPoster"; export default function page() { const local = useLocalSearchParams(); - const segments = useSegments(); const {jellyseerrApi} = useJellyseerr(); - const from = segments[2]; const {genreId, name, type} = local as unknown as { genreId: string, name: string, @@ -59,32 +58,6 @@ export default function page() { [jellyseerrApi, flatData] ); - const viewDetails = (result: Results) => { - router.push({ - //@ts-ignore - pathname: `/(auth)/(tabs)/${from}/jellyseerr/page`, - //@ts-ignore - params: { - ...result, - mediaTitle: getName(result), - releaseYear: getYear(result), - canRequest: "false", - posterSrc: jellyseerrApi?.imageProxy( - (result as MovieResult | TvResult).posterPath, - "w300_and_h450_face" - ), - }, - }); - }; - - const getName = (result: Results) => { - return (result as TvResult).name || (result as MovieResult).title - } - - const getYear = (result: Results) => { - return new Date((result as TvResult).firstAirDate || (result as MovieResult).releaseDate).getFullYear() - } - return ( } - renderItem={(item, index) => ( - viewDetails(item)} - > - - - {getName(item)} - {getYear(item)} - - )} + renderItem={(item, index) => + + } /> ); } diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/person/[personId].tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/person/[personId].tsx index 84f59f4b..3be8cc50 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/person/[personId].tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/person/[personId].tsx @@ -10,11 +10,13 @@ import { useJellyseerr } from "@/hooks/useJellyseerr"; import { Text } from "@/components/common/Text"; import { Image } from "expo-image"; import { OverviewText } from "@/components/OverviewText"; -import { orderBy } from "lodash"; +import {orderBy, uniqBy} from "lodash"; import { PersonCreditCast } from "@/utils/jellyseerr/server/models/Person"; import Poster from "@/components/posters/Poster"; import JellyseerrMediaIcon from "@/components/jellyseerr/JellyseerrMediaIcon"; import ParallaxSlideShow from "@/components/jellyseerr/ParallaxSlideShow"; +import JellyseerrPoster from "@/components/posters/JellyseerrPoster"; +import {MovieResult, TvResult} from "@/utils/jellyseerr/server/models/Search"; export default function page() { const local = useLocalSearchParams(); @@ -44,11 +46,11 @@ export default function page() { const castedRoles: PersonCreditCast[] = useMemo( () => - orderBy( + uniqBy(orderBy( data?.combinedCredits?.cast, ["voteCount", "voteAverage"], "desc" - ), + ), 'id'), [data?.combinedCredits] ); const backdrops = useMemo( @@ -123,29 +125,7 @@ export default function page() { MainContent={() => ( )} - renderItem={(item, index) => ( - viewDetails(item)} - > - - - {item.character && ( - - as {item.character} - - )} - - )} + renderItem={(item, index) => } /> ); } From cbe3b1822675002015290feb7f5132ea02c4dc84 Mon Sep 17 00:00:00 2001 From: herrrta <73949927+herrrta@users.noreply.github.com> Date: Mon, 6 Jan 2025 20:51:48 -0500 Subject: [PATCH 44/49] fix enter animation --- components/jellyseerr/ParallaxSlideShow.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/components/jellyseerr/ParallaxSlideShow.tsx b/components/jellyseerr/ParallaxSlideShow.tsx index 1e3d3f4f..6a7fcb7f 100644 --- a/components/jellyseerr/ParallaxSlideShow.tsx +++ b/components/jellyseerr/ParallaxSlideShow.tsx @@ -50,7 +50,6 @@ const ParallaxSlideShow = ({ const insets = useSafeAreaInsets(); const [currentIndex, setCurrentIndex] = useState(0); - const [onEnd, setOnEnd] = useState(true); const fadeAnim = useRef(new Animated.Value(0)).current; const enterAnimation = useCallback( @@ -76,16 +75,22 @@ const ParallaxSlideShow = ({ useEffect(() => { if (images?.length) { enterAnimation().start(); + const intervalId = setInterval(() => { - exitAnimation().start((end) => { - if (end.finished) - setCurrentIndex((prevIndex) => (prevIndex + 1) % images?.length); - }); + Animated.sequence([ + enterAnimation(), + exitAnimation() + ]).start(() => { + fadeAnim.setValue(0); + setCurrentIndex((prevIndex) => (prevIndex + 1) % images?.length); + }) }, BACKDROP_DURATION); - return () => clearInterval(intervalId); + return () => { + clearInterval(intervalId) + }; } - }, [images, enterAnimation, exitAnimation, setCurrentIndex, currentIndex]); + }, [fadeAnim, images, enterAnimation, exitAnimation, setCurrentIndex, currentIndex]); return ( Date: Mon, 6 Jan 2025 20:56:59 -0500 Subject: [PATCH 45/49] cleanup --- .../jellyseerr/person/[personId].tsx | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/person/[personId].tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/person/[personId].tsx index 3be8cc50..0ae41049 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/person/[personId].tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/person/[personId].tsx @@ -1,10 +1,8 @@ import { - router, useLocalSearchParams, useSegments, } from "expo-router"; import React, { useMemo } from "react"; -import { TouchableOpacity } from "react-native"; import { useQuery } from "@tanstack/react-query"; import { useJellyseerr } from "@/hooks/useJellyseerr"; import { Text } from "@/components/common/Text"; @@ -12,19 +10,15 @@ import { Image } from "expo-image"; import { OverviewText } from "@/components/OverviewText"; import {orderBy, uniqBy} from "lodash"; import { PersonCreditCast } from "@/utils/jellyseerr/server/models/Person"; -import Poster from "@/components/posters/Poster"; -import JellyseerrMediaIcon from "@/components/jellyseerr/JellyseerrMediaIcon"; import ParallaxSlideShow from "@/components/jellyseerr/ParallaxSlideShow"; import JellyseerrPoster from "@/components/posters/JellyseerrPoster"; import {MovieResult, TvResult} from "@/utils/jellyseerr/server/models/Search"; export default function page() { const local = useLocalSearchParams(); - const segments = useSegments(); const { jellyseerrApi, jellyseerrUser } = useJellyseerr(); const { personId } = local as { personId: string }; - const from = segments[2]; const { data, isLoading, isFetching } = useQuery({ queryKey: ["jellyseerr", "person", personId], @@ -60,24 +54,6 @@ export default function page() { [jellyseerrApi, data?.combinedCredits] ); - const viewDetails = (credit: PersonCreditCast) => { - router.push({ - //@ts-ignore - pathname: `/(auth)/(tabs)/${from}/jellyseerr/page`, - //@ts-ignore - params: { - ...credit, - mediaTitle: credit.title, - releaseYear: new Date(credit.releaseDate).getFullYear(), - canRequest: "false", - posterSrc: jellyseerrApi?.imageProxy( - credit.posterPath, - "w300_and_h450_face" - ), - }, - }); - }; - return ( Date: Tue, 7 Jan 2025 14:13:22 +0800 Subject: [PATCH 46/49] Rotate ScreenOrientation back on exit player --- components/video-player/controls/Controls.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/video-player/controls/Controls.tsx b/components/video-player/controls/Controls.tsx index cef265ec..7ba3426b 100644 --- a/components/video-player/controls/Controls.tsx +++ b/components/video-player/controls/Controls.tsx @@ -56,6 +56,7 @@ import DropdownViewTranscoding from "./dropdown/DropdownViewTranscoding"; import { EpisodeList } from "./EpisodeList"; import NextEpisodeCountDownButton from "./NextEpisodeCountDownButton"; import SkipButton from "./SkipButton"; +import * as ScreenOrientation from "expo-screen-orientation"; interface Props { item: BaseItemDto; @@ -587,6 +588,9 @@ export const Controls: React.FC = ({ { lightHapticFeedback(); + await ScreenOrientation.lockAsync( + ScreenOrientation.OrientationLock.PORTRAIT_UP + ); router.back(); }} className="aspect-square flex flex-col bg-neutral-800/90 rounded-xl items-center justify-center p-2" From 4e80f58823db692786e9314acf8b393d4004557a Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Tue, 7 Jan 2025 10:27:30 +0100 Subject: [PATCH 47/49] fix: backdrop not filling screen --- components/video-player/controls/Controls.tsx | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/components/video-player/controls/Controls.tsx b/components/video-player/controls/Controls.tsx index 7ba3426b..894d07c4 100644 --- a/components/video-player/controls/Controls.tsx +++ b/components/video-player/controls/Controls.tsx @@ -1,8 +1,8 @@ -import React, { useCallback, useEffect, useRef, useState } from "react"; import { Text } from "@/components/common/Text"; import { Loader } from "@/components/Loader"; import { useAdjacentItems } from "@/hooks/useAdjacentEpisodes"; import { useCreditSkipper } from "@/hooks/useCreditSkipper"; +import { useHaptic } from "@/hooks/useHaptic"; import { useIntroSkipper } from "@/hooks/useIntroSkipper"; import { useTrickplay } from "@/hooks/useTrickplay"; import { @@ -29,12 +29,19 @@ import { BaseItemDto, MediaSourceInfo, } from "@jellyfin/sdk/lib/generated-client"; -import { useHaptic } from "@/hooks/useHaptic"; import { Image } from "expo-image"; import { useLocalSearchParams, useRouter } from "expo-router"; +import * as ScreenOrientation from "expo-screen-orientation"; import { useAtom } from "jotai"; import { debounce } from "lodash"; -import { Dimensions, Pressable, TouchableOpacity, View } from "react-native"; +import React, { useCallback, useEffect, useRef, useState } from "react"; +import { + Dimensions, + Pressable, + TouchableOpacity, + useWindowDimensions, + View, +} from "react-native"; import { Slider } from "react-native-awesome-slider"; import { runOnJS, @@ -42,10 +49,7 @@ import { useAnimatedReaction, useSharedValue, } from "react-native-reanimated"; -import { - SafeAreaView, - useSafeAreaInsets, -} from "react-native-safe-area-context"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; import { VideoRef } from "react-native-video"; import AudioSlider from "./AudioSlider"; import BrightnessSlider from "./BrightnessSlider"; @@ -56,7 +60,7 @@ import DropdownViewTranscoding from "./dropdown/DropdownViewTranscoding"; import { EpisodeList } from "./EpisodeList"; import NextEpisodeCountDownButton from "./NextEpisodeCountDownButton"; import SkipButton from "./SkipButton"; -import * as ScreenOrientation from "expo-screen-orientation"; +import { useOrientation } from "@/hooks/useOrientation"; interface Props { item: BaseItemDto; @@ -119,6 +123,7 @@ export const Controls: React.FC = ({ const insets = useSafeAreaInsets(); const [api] = useAtom(apiAtom); + const { height: screenHeight, width: screenWidth } = useWindowDimensions(); const { previousItem, nextItem } = useAdjacentItems({ item }); const { trickPlayUrl, @@ -506,9 +511,13 @@ export const Controls: React.FC = ({ }} style={{ position: "absolute", - width: Dimensions.get("window").width, - height: Dimensions.get("window").height, + width: screenWidth, + height: screenHeight, backgroundColor: "black", + left: 0, + right: 0, + top: 0, + bottom: 0, opacity: showControls ? 0.5 : 0, }} > @@ -520,8 +529,8 @@ export const Controls: React.FC = ({ top: settings?.safeAreaInControlsEnabled ? insets.top : 0, right: settings?.safeAreaInControlsEnabled ? insets.right : 0, width: settings?.safeAreaInControlsEnabled - ? Dimensions.get("window").width - insets.left - insets.right - : Dimensions.get("window").width, + ? screenWidth - insets.left - insets.right + : screenWidth, opacity: showControls ? 1 : 0, }, ]} From c63cea891d2957cf47aebf4b757cf3881450984f Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Tue, 7 Jan 2025 10:27:56 +0100 Subject: [PATCH 48/49] chore: remove imports --- components/video-player/controls/Controls.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/video-player/controls/Controls.tsx b/components/video-player/controls/Controls.tsx index 894d07c4..1f436f7f 100644 --- a/components/video-player/controls/Controls.tsx +++ b/components/video-player/controls/Controls.tsx @@ -36,7 +36,6 @@ import { useAtom } from "jotai"; import { debounce } from "lodash"; import React, { useCallback, useEffect, useRef, useState } from "react"; import { - Dimensions, Pressable, TouchableOpacity, useWindowDimensions, @@ -60,7 +59,6 @@ import DropdownViewTranscoding from "./dropdown/DropdownViewTranscoding"; import { EpisodeList } from "./EpisodeList"; import NextEpisodeCountDownButton from "./NextEpisodeCountDownButton"; import SkipButton from "./SkipButton"; -import { useOrientation } from "@/hooks/useOrientation"; interface Props { item: BaseItemDto; From efa5638b12e53609c9691577c2eabe504b273a98 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Tue, 7 Jan 2025 10:59:21 +0100 Subject: [PATCH 49/49] fix: remove tab sidebar --- app/(auth)/(tabs)/_layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(auth)/(tabs)/_layout.tsx b/app/(auth)/(tabs)/_layout.tsx index be8f24b0..28b2ffa2 100644 --- a/app/(auth)/(tabs)/_layout.tsx +++ b/app/(auth)/(tabs)/_layout.tsx @@ -51,7 +51,7 @@ export default function TabLayout() { <>