import { BottomSheetBackdrop, type BottomSheetBackdropProps, BottomSheetModal, BottomSheetView, } from "@gorhom/bottom-sheet"; import type { BottomSheetModalMethods } from "@gorhom/bottom-sheet/lib/typescript/types"; import { useQuery } from "@tanstack/react-query"; import { forwardRef, useCallback, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { View, type ViewProps } from "react-native"; import { Button } from "@/components/Button"; import Dropdown from "@/components/common/Dropdown"; import { Text } from "@/components/common/Text"; import { useJellyseerr } from "@/hooks/useJellyseerr"; import type { QualityProfile, RootFolder, Tag, } from "@/utils/jellyseerr/server/api/servarr/base"; import type { MediaType } from "@/utils/jellyseerr/server/constants/media"; import type { MediaRequestBody } from "@/utils/jellyseerr/server/interfaces/api/requestInterfaces"; import { writeDebugLog } from "@/utils/log"; interface Props { id: number; title: string; requestBody?: MediaRequestBody; type: MediaType; isAnime?: boolean; is4k?: boolean; onRequested?: () => void; onDismiss?: () => void; } const RequestModal = forwardRef< BottomSheetModalMethods, Props & Omit >( ( { id, title, requestBody, type, isAnime = false, onRequested, onDismiss }, ref, ) => { const { jellyseerrApi, jellyseerrUser, requestMedia } = useJellyseerr(); const [requestOverrides, setRequestOverrides] = useState({ mediaId: Number(id), mediaType: type, userId: jellyseerrUser?.id, }); const { t } = useTranslation(); const { data: serviceSettings } = useQuery({ queryKey: ["jellyseerr", "request", type, "service"], queryFn: async () => jellyseerrApi?.service(type === "movie" ? "radarr" : "sonarr"), enabled: !!jellyseerrApi && !!jellyseerrUser, refetchOnMount: "always", }); const { data: users } = useQuery({ queryKey: ["jellyseerr", "users"], queryFn: async () => jellyseerrApi?.user({ take: 1000, sort: "displayname" }), enabled: !!jellyseerrApi && !!jellyseerrUser, refetchOnMount: "always", }); const defaultService = useMemo( () => serviceSettings?.find?.((v) => v.isDefault), [serviceSettings], ); const { data: defaultServiceDetails } = useQuery({ queryKey: [ "jellyseerr", "request", type, "service", "details", defaultService?.id, ], queryFn: async () => { setRequestOverrides((prev) => ({ ...prev, serverId: defaultService?.id, })); return jellyseerrApi?.serviceDetails( type === "movie" ? "radarr" : "sonarr", defaultService!.id, ); }, enabled: !!jellyseerrApi && !!jellyseerrUser && !!defaultService, refetchOnMount: "always", }); const defaultProfile: QualityProfile = useMemo( () => defaultServiceDetails?.profiles.find( (p) => p.id === (isAnime ? defaultServiceDetails.server?.activeAnimeProfileId : defaultServiceDetails.server?.activeProfileId), ), [defaultServiceDetails], ); const defaultFolder: RootFolder = useMemo( () => defaultServiceDetails?.rootFolders.find( (f) => f.path === (isAnime ? defaultServiceDetails?.server.activeAnimeDirectory : defaultServiceDetails.server?.activeDirectory), ), [defaultServiceDetails], ); const defaultTags: Tag[] = useMemo(() => { const tags = defaultServiceDetails?.tags.filter((t) => (isAnime ? defaultServiceDetails?.server.activeAnimeTags : defaultServiceDetails?.server.activeTags )?.includes(t.id), ) ?? []; return tags; }, [defaultServiceDetails]); const seasonTitle = useMemo(() => { if (requestBody?.seasons && requestBody?.seasons?.length > 1) { return t("jellyseerr.season_all"); } return t("jellyseerr.season_number", { season_number: requestBody?.seasons, }); }, [requestBody?.seasons]); const request = useCallback(() => { const body = { is4k: defaultService?.is4k || defaultServiceDetails?.server.is4k, profileId: defaultProfile?.id, rootFolder: defaultFolder?.path, tags: defaultTags.map((t) => t.id), ...requestBody, ...requestOverrides, }; writeDebugLog("Sending Jellyseerr advanced request", body); requestMedia( seasonTitle ? `${title}, ${seasonTitle}` : title, body, onRequested, ); }, [ requestBody, requestOverrides, defaultProfile, defaultFolder, defaultTags, ]); const pathTitleExtractor = (item: RootFolder) => `${item.path} (${item.freeSpace.bytesToReadable()})`; return ( ( )} > {t("jellyseerr.advanced")} {seasonTitle && ( {seasonTitle} )} {defaultService && defaultServiceDetails && users && ( <> item.name} placeholderText={ requestOverrides.profileName || defaultProfile.name } keyExtractor={(item) => item.id.toString()} label={t("jellyseerr.quality_profile")} onSelected={(item) => item && setRequestOverrides((prev) => ({ ...prev, profileId: item?.id, })) } title={t("jellyseerr.quality_profile")} /> item.id.toString()} label={t("jellyseerr.root_folder")} onSelected={(item) => item && setRequestOverrides((prev) => ({ ...prev, rootFolder: item.path, })) } title={t("jellyseerr.root_folder")} /> item.label} placeholderText={defaultTags.map((t) => t.label).join(",")} keyExtractor={(item) => item.id.toString()} label={t("jellyseerr.tags")} onSelected={(...selected) => setRequestOverrides((prev) => ({ ...prev, tags: selected.map((i) => i.id), })) } title={t("jellyseerr.tags")} /> item.displayName} placeholderText={jellyseerrUser!.displayName} keyExtractor={(item) => item.id.toString() || ""} label={t("jellyseerr.request_as")} onSelected={(item) => item && setRequestOverrides((prev) => ({ ...prev, userId: item?.id, })) } title={t("jellyseerr.request_as")} /> )} ); }, ); export default RequestModal;