import React, { useEffect, useRef, useState } from "react"; import { Text, View, ScrollView, TextInput, ActivityIndicator, } from "react-native"; import { Button } from "~/components/ui/button"; import { useLocalSearchParams, router } from "expo-router"; import BackButton from "~/components/ui/backButton"; import { useAuthWithProfile } from "~/lib/hooks/useAuthWithProfile"; import { TransactionService } from "~/lib/services/transactionService"; import { calculateTotalAmountForSending, calculateProcessingFee, } from "~/lib/utils/feeUtils"; import ScreenWrapper from "~/components/ui/ScreenWrapper"; import ModalToast from "~/components/ui/toast"; import { useTranslation } from "react-i18next"; import { ROUTES } from "~/lib/routes"; import { applyReferral } from "~/lib/services/referralService"; import { awardPoints } from "~/lib/services/pointsService"; export default function TransConfirm() { const { t } = useTranslation(); const params = useLocalSearchParams<{ transactionId?: string; amount?: string; type?: string; recipientName?: string; recipientPhoneNumber?: string; recipientType?: string; recipientId?: string; note?: string; donationSkipped?: string; donationType?: string; donationAmount?: string; donateAnonymously?: string; donationCampaignId?: string; donationCampaignTitle?: string; fromSelectRecipientFlow?: string; }>(); // Parse amount from cents to dollars const amountInCents = parseInt(params.amount || "0"); const amountInDollars = amountInCents / 100; // Donation amount (entered on donation screen) const hasDonation = params.donationSkipped === "false" && !!params.donationAmount; const donationAmountNumber = params.donationAmount ? Number(params.donationAmount) : NaN; const donationAmountDisplay = isNaN(donationAmountNumber) ? null : donationAmountNumber.toFixed(2); const donationAmountDollars = !isNaN(donationAmountNumber) && hasDonation ? donationAmountNumber : 0; // Calculate processing fee (1.25% of the amount) const processingFeeInCents = Math.round(amountInCents * 0.0125); const processingFeeInDollars = processingFeeInCents / 100; // Subtotal = main amount + donation const subtotalInDollars = amountInDollars + donationAmountDollars; // Total = subtotal + processing fee const totalInDollars = subtotalInDollars + processingFeeInDollars; const donationTypeLabel = params.donationType === "monthly" ? "Monthly" : "One-Time"; const transactionDate = new Date(); const formattedDate = transactionDate.toLocaleDateString(undefined, { year: "numeric", month: "short", day: "numeric", }); const { user, wallet, refreshWallet } = useAuthWithProfile(); const [isSending, setIsSending] = useState(false); const [referralCode, setReferralCode] = useState(""); 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 = 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(() => { return () => { if (toastTimeoutRef.current) { clearTimeout(toastTimeoutRef.current); } }; }, []); const handleConfirm = async () => { const isAddCashFlow = params.type === "add_cash"; if (isAddCashFlow) { // If a referral code is provided in the add-cash flow, apply it here if (referralCode.trim() && user?.uid) { try { setIsSending(true); const referralResult = await applyReferral({ referralCode: referralCode.trim(), uid: user.uid, referralReason: "transaction", contextId: params.transactionId || "add_cash", }); console.log( "[TransConfirm] Add-cash referral apply result", referralResult ); if (!referralResult.success) { showToast( t("transconfirm.toastErrorTitle"), referralResult.error || "Failed to apply referral code", "error" ); } } catch (referralError) { console.error( "[TransConfirm] Error while applying add-cash referral code", referralError ); showToast( t("transconfirm.toastErrorTitle"), "Failed to apply referral code", "error" ); } finally { setIsSending(false); } } router.push({ pathname: ROUTES.CHECKOUT, params: { ...params, }, }); return; } if (!params.amount || !user?.uid) { showToast( t("transconfirm.toastErrorTitle"), t("transconfirm.toastMissingDetails"), "error" ); return; } const amountInCents = parseInt(params.amount); if (isNaN(amountInCents) || amountInCents <= 0) { showToast( t("transconfirm.toastErrorTitle"), t("transconfirm.toastInvalidAmount"), "error" ); return; } // Check if user has sufficient balance (including processing fee) const totalRequired = calculateTotalAmountForSending(amountInCents); if (!wallet) { showToast( t("transconfirm.toastErrorTitle"), t("transconfirm.toastWalletNotFound"), "error" ); return; } if (wallet.balance < totalRequired) { const processingFee = calculateProcessingFee(amountInCents); const required = (totalRequired / 100).toFixed(2); const fee = (processingFee / 100).toFixed(2); const available = (wallet.balance / 100).toFixed(2); showToast( t("transconfirm.toastInsufficientBalanceTitle"), t("transconfirm.toastInsufficientBalanceDescription", { required, fee, available, }), "error" ); return; } if ( !params.recipientName || !params.recipientPhoneNumber || !params.recipientType || !params.recipientId ) { showToast( t("transconfirm.toastErrorTitle"), t("transconfirm.toastRecipientMissing"), "error" ); return; } const isFromSelectRecipientFlow = params.fromSelectRecipientFlow === "true"; if (isFromSelectRecipientFlow) { router.push({ pathname: "checkout", params: { ...params, }, }); return; } setIsSending(true); try { const result = await TransactionService.sendMoney(user.uid, { amount: amountInCents, recipientName: params.recipientName, recipientPhoneNumber: params.recipientPhoneNumber, recipientType: params.recipientType as "saved" | "contact", recipientId: params.recipientId, note: params.note?.trim() || "", }); if (result.success) { try { await awardPoints("send_money"); } catch (error) { console.warn( "[TransConfirm] Failed to award send money points", error ); } await refreshWallet(); if (referralCode.trim() && result.transactionId) { try { const referralResult = await applyReferral({ referralCode: referralCode.trim(), uid: user.uid, referralReason: "transaction", contextId: result.transactionId, }); console.log("[TransConfirm] Referral apply result", referralResult); if (!referralResult.success) { console.warn( "[TransConfirm] Failed to apply referral:", referralResult.error ); showToast( t("transconfirm.toastErrorTitle"), referralResult.error || "Failed to apply referral code", "error" ); } } catch (referralError) { console.error( "[TransConfirm] Error while applying referral code", referralError ); showToast( t("transconfirm.toastErrorTitle"), "Failed to apply referral code", "error" ); } } // Navigate to success screen with message router.replace({ pathname: "/(screens)/taskcomp", params: { message: `Transaction completed on your end. $${( amountInCents / 100 ).toFixed(2)} will be claimed by ${ params.recipientName } within 7 days, or the money will revert to your wallet.`, amount: (amountInCents / 100).toFixed(2), recipientName: params.recipientName, recipientPhoneNumber: params.recipientPhoneNumber, }, }); } else { showToast( t("transconfirm.toastErrorTitle"), result.error || t("transconfirm.toastSendFailed"), "error" ); } } catch (error) { console.error("Error sending money:", error); showToast( t("transconfirm.toastErrorTitle"), t("transconfirm.toastSendFailedWithRetry"), "error" ); } finally { setIsSending(false); } }; return ( {/* Centered content: title + card */} {t("transconfirm.title")} ${amountInDollars.toFixed(2)} {params.type === "add_cash" ? "You are planning to add money to your wallet." : t("transconfirm.planningDescription", { recipientName: params.recipientName, })} {t("transconfirm.sectionTitle")} {params.note && ( {t("transconfirm.noteLabel")} {params.note} )} {hasDonation && donationAmountDisplay && ( <> Donation ${donationAmountDisplay} Donation Type {donationTypeLabel} )} Date {formattedDate} {t("transconfirm.processingFeeLabel")} ${processingFeeInDollars.toFixed(2)} {t("transconfirm.subtotalLabel")} ${subtotalInDollars.toFixed(2)} {t("transconfirm.totalLabel")} ${totalInDollars.toFixed(2)} {t("transconfirm.referralCodeLabel", "Referral Code")} ); }