diff --git a/app/(auth)/(tabs)/(home)/settings/account/page.tsx b/app/(auth)/(tabs)/(home)/settings/account/page.tsx index 56bd8a512..f4a7c16d3 100644 --- a/app/(auth)/(tabs)/(home)/settings/account/page.tsx +++ b/app/(auth)/(tabs)/(home)/settings/account/page.tsx @@ -1,4 +1,5 @@ import * as Application from "expo-application"; +import { setStringAsync } from "expo-clipboard"; import { t } from "i18next"; import { useAtom } from "jotai"; import { useState } from "react"; @@ -19,17 +20,9 @@ export default function AccountPage() { const copyToken = async () => { if (!token) return; - try { - // Lazy import: expo-clipboard is a native module. Importing it at module - // top crashes the screen on a dev client built before it was added; the - // dynamic import defers loading until the user taps copy. - const Clipboard = await import("expo-clipboard"); - await Clipboard.setStringAsync(token); - success(); - Alert.alert(t("home.settings.account.copied")); - } catch { - Alert.alert(t("home.settings.account.copy_unavailable")); - } + await setStringAsync(token); + success(); + Alert.alert(t("home.settings.account.copied")); }; return ( diff --git a/components/settings/QuickConnect.tsx b/components/settings/QuickConnect.tsx index d6a60ae50..dbb1c32de 100644 --- a/components/settings/QuickConnect.tsx +++ b/components/settings/QuickConnect.tsx @@ -1,9 +1,3 @@ -import { - BottomSheetBackdrop, - type BottomSheetBackdropProps, - BottomSheetModal, - BottomSheetView, -} from "@gorhom/bottom-sheet"; import { getQuickConnectApi } from "@jellyfin/sdk/lib/utils/api"; import { useAtom } from "jotai"; import { @@ -18,6 +12,11 @@ import { useTranslation } from "react-i18next"; import { Alert, Platform, View } from "react-native"; import { useHaptic } from "@/hooks/useHaptic"; import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; +import { + type BottomSheetMethods, + BottomSheetModal, + BottomSheetView, +} from "@/utils/expoUiBottomSheet"; import { Button } from "../Button"; import { Text } from "../common/Text"; import { PinInput } from "../inputs/PinInput"; @@ -30,7 +29,7 @@ export const QuickConnectSheet = forwardRef( const [api] = useAtom(apiAtom); const [user] = useAtom(userAtom); const [quickConnectCode, setQuickConnectCode] = useState(); - const modalRef = useRef(null); + const modalRef = useRef(null); const successHapticFeedback = useHaptic("success"); const errorHapticFeedback = useHaptic("error"); const snapPoints = useMemo( @@ -51,17 +50,6 @@ export const QuickConnectSheet = forwardRef( [], ); - const renderBackdrop = useCallback( - (props: BottomSheetBackdropProps) => ( - - ), - [], - ); - const authorizeQuickConnect = useCallback(async () => { if (!quickConnectCode) return; try { @@ -76,7 +64,7 @@ export const QuickConnectSheet = forwardRef( t("home.settings.quick_connect.quick_connect_autorized"), ); setQuickConnectCode(undefined); - modalRef.current?.dismiss(); + modalRef.current?.close(); } else { errorHapticFeedback(); Alert.alert( @@ -105,14 +93,12 @@ export const QuickConnectSheet = forwardRef( return ( diff --git a/translations/en.json b/translations/en.json index 645862906..091121182 100644 --- a/translations/en.json +++ b/translations/en.json @@ -146,8 +146,7 @@ "account": { "title": "Account", "copy_token": "Copy token", - "copied": "Copied to clipboard", - "copy_unavailable": "Clipboard unavailable — rebuild the app to enable copy" + "copied": "Copied to clipboard" }, "playback_controls": { "title": "Playback & Controls" diff --git a/utils/expoUiBottomSheet.ts b/utils/expoUiBottomSheet.ts new file mode 100644 index 000000000..258857256 --- /dev/null +++ b/utils/expoUiBottomSheet.ts @@ -0,0 +1,23 @@ +import { Platform } from "react-native"; + +/** + * TV-safe re-exports of `@expo/ui/community/bottom-sheet`. + * + * `@expo/ui` resolves its native bridge at module load via + * `requireNativeModule('ExpoUI')`, which does not exist on tvOS — a static + * top-level import would crash the whole expo-router route tree. We `require()` + * it lazily and only off-TV; on TV the exports are undefined, which is fine + * because every call site early-returns on `Platform.isTV`. + */ +type BottomSheetMod = typeof import("@expo/ui/community/bottom-sheet"); + +const mod: BottomSheetMod = Platform.isTV + ? ({} as BottomSheetMod) + : (require("@expo/ui/community/bottom-sheet") as BottomSheetMod); + +export const BottomSheetModal = mod.BottomSheetModal; +export const BottomSheetView = mod.BottomSheetView; +export const BottomSheetScrollView = mod.BottomSheetScrollView; +export const BottomSheetTextInput = mod.BottomSheetTextInput; + +export type { BottomSheetMethods } from "@expo/ui/community/bottom-sheet";