import React, { useState, useEffect } from "react"; import { View, ScrollView, InteractionManager } from "react-native"; import { useFocusEffect } from "expo-router"; import { Button } from "~/components/ui/button"; import { Text } from "~/components/ui/text"; import { PhonePinKeypad } from "~/components/ui/PhonePinKeypad"; import { router } from "expo-router"; import { ROUTES } from "~/lib/routes"; import { useAuthWithProfile } from "~/lib/hooks/useAuthWithProfile"; import { useUserWallet } from "~/lib/hooks/useUserWallet"; import { parseDisplayToCents, formatDisplayAmount, } from "~/lib/utils/monetaryUtils"; import { Big } from "big.js"; import BackButton from "~/components/ui/backButton"; import { PinConfirmationModal } from "~/components/ui/pinConfirmationModal"; import { amountSchema, validate } from "~/lib/utils/validationSchemas"; import { showAlert } from "~/lib/utils/alertUtils"; import ScreenWrapper from "~/components/ui/ScreenWrapper"; import ModalToast from "~/components/ui/toast"; import { useTranslation } from "react-i18next"; import FourDotLoader from "~/components/ui/FourDotLoader"; export default function CashOut() { const { user } = useAuthWithProfile(); const { wallet } = useUserWallet(user); const [amount, setAmount] = useState(""); const [showPinModal, setShowPinModal] = useState(false); const [isSecurityVerified, setIsSecurityVerified] = useState(false); const { t } = useTranslation(); const [toastVisible, setToastVisible] = useState(false); const [toastTitle, setToastTitle] = useState(""); const [toastDescription, setToastDescription] = useState( undefined ); const [toastVariant, setToastVariant] = useState< "success" | "error" | "warning" | "info" >("info"); const toastTimeoutRef = React.useRef | null>( null ); const showToast = ( title: string, description?: string, variant: "success" | "error" | "warning" | "info" = "info" ) => { if (toastTimeoutRef.current) { clearTimeout(toastTimeoutRef.current); } setToastTitle(title); setToastDescription(description); setToastVariant(variant); setToastVisible(true); toastTimeoutRef.current = setTimeout(() => { setToastVisible(false); toastTimeoutRef.current = null; }, 2500); }; useEffect(() => { const task = InteractionManager.runAfterInteractions(() => { if (__DEV__) { console.log("CASHOUT PAGE MOUNTED"); } }); return () => task.cancel(); }, []); useEffect(() => { return () => { if (toastTimeoutRef.current) { clearTimeout(toastTimeoutRef.current); } }; }, []); // Reset state and show PIN modal when screen comes into focus // useFocusEffect only runs when screen is focused, so we can safely show modal here useFocusEffect( React.useCallback(() => { setIsSecurityVerified(false); setShowPinModal(true); setAmount(""); // Cleanup: hide modal when screen loses focus return () => { setShowPinModal(false); setIsSecurityVerified(false); }; }, []) ); // Handle number input and special actions (copied from addcash.tsx) const handleNumberPress = (input: string) => { if (input === "clear") { handleClear(); return; } if (input === "backspace") { handleBackspace(); return; } // Handle decimal point if (input === ".") { // Prevent multiple decimals if (amount.includes(".")) return; // If empty, start with "0." if (amount === "") { setAmount("0."); return; } // Add decimal point setAmount(amount + "."); return; } // Handle digit input (0-9) if (!/^[0-9]$/.test(input)) return; // Handle leading zeros if (amount === "0" && input !== ".") { setAmount(input); // Replace leading zero return; } const newAmount = amount + input; // Check decimal places limit (max 2 decimal places) if (newAmount.includes(".")) { const [whole, decimal] = newAmount.split("."); if (decimal && decimal.length > 2) return; } // Check maximum amount (max $999.99) try { const numValue = new Big(newAmount); if (numValue.gt(999.99)) return; } catch (error) { return; } // Check total length to prevent very long inputs if (newAmount.length > 6) return; // Max: 999.99 setAmount(newAmount); }; // Handle backspace const handleBackspace = () => { if (amount.length === 0) return; // Remove last character const newAmount = amount.slice(0, -1); setAmount(newAmount); }; // Clear all input const handleClear = () => { setAmount(""); }; // Validate if amount is valid for submission const isValidAmount = () => { if ( !amount || amount === "" || amount === "0" || amount === "0." || amount === "0.00" ) { return false; } const amountInCents = parseDisplayToCents(amount); if (amountInCents < 1 || amountInCents > 99999) { // 1 cent to $999.99 return false; } // Check if amount is within wallet balance (no processing fee for cashout) const currentBalance = wallet?.balance || 0; // Balance in cents return currentBalance >= amountInCents; }; // Get validation error message const getValidationError = () => { // Validate basic amount using valibot const amountValidationResult = validate( amountSchema({ min: 1, max: 99999 }), amount ); if (!amountValidationResult.success) { return amountValidationResult.error; } const amountInCents = parseDisplayToCents(amount); if (amountInCents < 1) { return t("cashout.validationMinAmount"); } if (amountInCents > 99999) { // $999.99 return t("cashout.validationMaxAmount"); } // Check if amount exceeds wallet balance (no processing fee for cashout) const currentBalance = wallet?.balance || 0; // Balance in cents if (currentBalance < amountInCents) { const balanceInDollars = currentBalance / 100; const requiredInDollars = amountInCents / 100; return t("cashout.validationInsufficientBalance", { required: requiredInDollars.toFixed(2), available: balanceInDollars.toFixed(2), }); } return null; }; // Handle PIN confirmation success const handlePinSuccess = () => { setShowPinModal(false); setIsSecurityVerified(true); }; // Handle cash out action (called after PIN is verified) const handleCashOut = async () => { const validationError = getValidationError(); if (validationError) { showToast(t("cashout.validationErrorTitle"), validationError, "error"); return; } const amountInCents = parseDisplayToCents(amount); console.log("Cashing out:", amountInCents, "cents"); router.push({ pathname: ROUTES.SEND_BANK, params: { amount: amountInCents.toString(), // Pass cents as string recipientName: user?.displayName || "Self", recipientPhoneNumber: user?.phoneNumber || "", recipientType: "saved", recipientId: user?.uid || "", note: "Cash out to bank account", transactionType: "cashout", }, }); }; return ( {!isSecurityVerified && !showPinModal ? ( {t("cashout.verifyingSecurity")} ) : ( {/* Wallet Balance Display */} {t("cashout.availableBalanceLabel")} ${wallet ? (wallet.balance / 100).toFixed(2) : "0.00"} ${formatDisplayAmount(amount)} )} {/* PIN Confirmation Modal */} setShowPinModal(false)} onSuccess={handlePinSuccess} title={t("cashout.pinModalTitle")} /> ); }