From 304cb06e0d25a620927b1d9fdb87e7c3bdfe1c92 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Mon, 29 Jun 2026 12:05:36 +0200 Subject: [PATCH] fix(tv): use navigation option modal for advanced request selectors The advanced request modal opened the quality-profile / root-folder / request-as pickers as inline TVOptionSelector overlays (visible-prop), which stacked on top of the modal and broke TV focus. Switch them to the navigation-based useTVOptionModal().showOptions pattern (the same one settings uses) so each picker is its own route with proper focus, and returns to the request modal on select. Removes the activeSelector state and the inline selectors. Claude-Session: https://claude.ai/code/session_016Hhu5DruGLPhdP4LAoy1Xd --- app/(auth)/tv-request-modal.tsx | 63 +++++++++++++-------------------- 1 file changed, 25 insertions(+), 38 deletions(-) diff --git a/app/(auth)/tv-request-modal.tsx b/app/(auth)/tv-request-modal.tsx index 372bd542..098507c7 100644 --- a/app/(auth)/tv-request-modal.tsx +++ b/app/(auth)/tv-request-modal.tsx @@ -15,11 +15,12 @@ import { import { Text } from "@/components/common/Text"; import { TVRequestOptionRow } from "@/components/jellyseerr/tv/TVRequestOptionRow"; import { TVToggleOptionRow } from "@/components/jellyseerr/tv/TVToggleOptionRow"; -import { TVButton, TVOptionSelector } from "@/components/tv"; +import { TVButton } from "@/components/tv"; import type { TVOptionItem } from "@/components/tv/TVOptionSelector"; import { useScaledTVTypography } from "@/constants/TVTypography"; import useRouter from "@/hooks/useAppRouter"; import { useJellyseerr } from "@/hooks/useJellyseerr"; +import { useTVOptionModal } from "@/hooks/useTVOptionModal"; import { tvRequestModalAtom } from "@/utils/atoms/tvRequestModal"; import type { QualityProfile, @@ -35,6 +36,7 @@ export default function TVRequestModalPage() { const modalState = useAtomValue(tvRequestModalAtom); const { t } = useTranslation(); const { jellyseerrApi, jellyseerrUser, requestMedia } = useJellyseerr(); + const { showOptions } = useTVOptionModal(); const [isReady, setIsReady] = useState(false); const [requestOverrides, setRequestOverrides] = useState({ @@ -43,10 +45,6 @@ export default function TVRequestModalPage() { userId: jellyseerrUser?.id, }); - const [activeSelector, setActiveSelector] = useState< - "profile" | "folder" | "user" | null - >(null); - const overlayOpacity = useRef(new Animated.Value(0)).current; const sheetTranslateY = useRef(new Animated.Value(200)).current; @@ -242,17 +240,14 @@ export default function TVRequestModalPage() { // Handlers const handleProfileChange = useCallback((profileId: number) => { setRequestOverrides((prev) => ({ ...prev, profileId })); - setActiveSelector(null); }, []); const handleFolderChange = useCallback((rootFolder: string) => { setRequestOverrides((prev) => ({ ...prev, rootFolder })); - setActiveSelector(null); }, []); const handleUserChange = useCallback((userId: number) => { setRequestOverrides((prev) => ({ ...prev, userId })); - setActiveSelector(null); }, []); const handleTagToggle = useCallback( @@ -353,18 +348,37 @@ export default function TVRequestModalPage() { setActiveSelector("profile")} + onPress={() => + showOptions({ + title: t("jellyseerr.quality_profile"), + options: qualityProfileOptions, + onSelect: handleProfileChange, + }) + } hasTVPreferredFocus /> setActiveSelector("folder")} + onPress={() => + showOptions({ + title: t("jellyseerr.root_folder"), + options: rootFolderOptions, + onSelect: handleFolderChange, + cardWidth: 280, + }) + } /> setActiveSelector("user")} + onPress={() => + showOptions({ + title: t("jellyseerr.request_as"), + options: userOptions, + onSelect: handleUserChange, + }) + } /> {tagItems.length > 0 && ( @@ -409,33 +423,6 @@ export default function TVRequestModalPage() { - - {/* Sub-selectors */} - setActiveSelector(null)} - cancelLabel={t("jellyseerr.cancel")} - /> - setActiveSelector(null)} - cancelLabel={t("jellyseerr.cancel")} - cardWidth={280} - /> - setActiveSelector(null)} - cancelLabel={t("jellyseerr.cancel")} - /> ); }