import { Ionicons } from "@expo/vector-icons"; import { BottomSheetBackdrop, type BottomSheetBackdropProps, BottomSheetModal, BottomSheetView, } from "@gorhom/bottom-sheet"; import { requireOptionalNativeModule } from "expo-modules-core"; import type React from "react"; import { useCallback, useEffect, useMemo, useRef } from "react"; import { useTranslation } from "react-i18next"; import { TouchableOpacity, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { toast } from "sonner-native"; import { Button } from "../Button"; import { Text } from "../common/Text"; interface Props { /** The Quick Connect code to display, or null when hidden. */ code: string | null; onClose: () => void; } /** * Shows the Quick Connect code while the app polls for authorization. * In-app sheet instead of a native Alert so it can dismiss itself once the * session is authorized — a native alert has no programmatic dismiss and * lingers over the app after login completes. */ export const QuickConnectCodeModal: React.FC = ({ code, onClose }) => { const { t } = useTranslation(); const insets = useSafeAreaInsets(); const bottomSheetModalRef = useRef(null); const snapPoints = useMemo(() => ["50%"], []); const isPresentedRef = useRef(false); // Keep the last code around so the dismiss animation doesn't flash empty // when the parent clears the code to close the sheet. const lastCodeRef = useRef(null); if (code) lastCodeRef.current = code; useEffect(() => { if (code) { bottomSheetModalRef.current?.present(); } else if (isPresentedRef.current) { bottomSheetModalRef.current?.dismiss(); isPresentedRef.current = false; } }, [code]); const handleSheetChanges = useCallback( (index: number) => { if (index >= 0) { isPresentedRef.current = true; } else if (index === -1 && isPresentedRef.current) { isPresentedRef.current = false; onClose(); } }, [onClose], ); const renderBackdrop = useCallback( (props: BottomSheetBackdropProps) => ( ), [], ); const copyCode = useCallback(async () => { const value = code ?? lastCodeRef.current; if (!value) return; // Builds that don't ship the expo-clipboard native module yet: probe with // requireOptionalNativeModule (returns null instead of throwing/logging) // and skip — importing the JS wrapper there would error out. if (!requireOptionalNativeModule("ExpoClipboard")) return; const Clipboard = await import("expo-clipboard"); await Clipboard.setStringAsync(value); toast.success(t("login.code_copied")); }, [code, t]); return ( {t("login.quick_connect")} {code ?? lastCodeRef.current} {t("login.tap_code_to_copy")} {t("login.quick_connect_instructions")} ); };