import React, { useState, useRef, useEffect } from "react"; import { View, Text, ScrollView, TouchableOpacity, Pressable, FlatList, Platform, ImageBackground, Image, } from "react-native"; import { Button } from "~/components/ui/button"; import ProfileCard from "~/components/ui/profileCard"; import ContactModal from "~/components/ui/contactModal"; import ProtectedRoute from "~/components/other/protectedRoute"; import { useAuthWithProfile } from "~/lib/hooks/useAuthWithProfile"; import { Users, AlertCircle, BellIcon, Plus, ScanBarcode, LucideScan, ScanIcon, ScanBarcodeIcon, ScanLine, } from "lucide-react-native"; import { Link, router, useFocusEffect } from "expo-router"; import { ROUTES } from "~/lib/routes"; import { Contact, useContactsStore } from "~/lib/stores"; import { useTransactions } from "~/lib/hooks/useTransactions"; import ScreenWrapper from "~/components/ui/ScreenWrapper"; import { Icons } from "~/assets/icons"; import Skeleton from "~/components/ui/skeleton"; import { getAuthInstance } from "~/lib/firebase"; import ModalToast from "~/components/ui/toast"; import { useTranslation } from "react-i18next"; import { useSafeAreaInsets } from "react-native-safe-area-context"; // Contacts are only available on native platforms const isContactsSupported = Platform.OS !== "web"; const UPCOMING_REMINDERS = [ { id: "rem-1", clientName: "Abebe Kebede", datetimeLabel: "Today · 4:00 PM", status: "Upcoming", }, { id: "rem-2", clientName: "Sara Alemu", datetimeLabel: "Tomorrow · 9:30 AM", status: "Upcoming", }, { id: "rem-3", clientName: "Hope Community Fund", datetimeLabel: "Fri, 12 Jan · 2:15 PM", status: "Scheduled", }, ]; const EXCHANGE_RATES = [ { code: "USD", name: "US Dollar", rateToETB: 57.2 }, { code: "AUD", name: "Australian Dollar", rateToETB: 38.4 }, { code: "EUR", name: "Euro", rateToETB: 62.9 }, { code: "GBP", name: "British Pound", rateToETB: 73.5 }, ]; const getHomeTxStatusPillClasses = (status: string) => { switch (status) { case "completed": return "bg-emerald-100 text-emerald-700"; case "pending": return "bg-yellow-100 text-yellow-700"; case "failed": default: return "bg-red-100 text-red-700"; } }; const formatHomeTxAmount = (amountInCents: number) => { return `$${(amountInCents / 100).toFixed(2)}`; }; const formatHomeTxTime = (createdAt: Date) => { const d = createdAt instanceof Date ? createdAt : new Date(createdAt); return d.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", }); }; const getHomeTxClientName = (tx: any) => { if (tx.type === "send") return tx.recipientName || "Client"; if (tx.type === "receive") return tx.senderName || "Client"; if (tx.type === "add_cash") return "Card top up"; if (tx.type === "cash_out") return "Cash out"; return "Transaction"; }; const getHomeTxMethodLabel = (tx: any) => { if (tx.type === "send") return "Telebirr"; if (tx.type === "receive") return "Wallet"; if (tx.type === "add_cash") return "Card"; if (tx.type === "cash_out") { const provider = tx.bankProvider ? tx.bankProvider.charAt(0).toUpperCase() + tx.bankProvider.slice(1) : "Bank"; return provider === "Telebirr" ? "Telebirr" : "Bank transfer"; } return "Wallet"; }; export default function Home() { const { user, wallet, walletLoading, walletError } = useAuthWithProfile(); const { t } = useTranslation(); const { profile, profileLoading } = useAuthWithProfile(); const insets = useSafeAreaInsets(); const fullName = profile?.fullName; const firstName = fullName?.split(" ")[0]; const { contacts, loading: contactsLoading, error: contactsError, hasPermission, requestPermission, } = useContactsStore(); const { transactions, loading: transactionsLoading, error: transactionsError, } = useTransactions(user?.uid); const scrollRef = useRef(null); 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 displayName = user?.displayName || ""; const avatarSource = profile?.photoUrl ? { uri: profile.photoUrl } : Icons.avatar; // Log Firebase ID token when Home mounts (for debugging) useEffect(() => { const logIdToken = async () => { try { const auth = getAuthInstance(); const currentUser = auth.currentUser; if (!currentUser) { console.log("HOME: No current Firebase user, cannot get ID token"); return; } const idToken = await currentUser.getIdToken(); console.log("HOME SCREEN ID TOKEN:", idToken); } catch (error) { console.warn("HOME: Failed to get ID token:", error); } }; logIdToken(); }, []); 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); }; // Modal state const [selectedContact, setSelectedContact] = useState(null); const [modalVisible, setModalVisible] = useState(false); const handleContactPress = (contact: Contact) => { setSelectedContact(contact); setModalVisible(true); }; const handleCloseModal = () => { setModalVisible(false); setSelectedContact(null); }; // Scroll to top whenever Home tab regains focus useFocusEffect( React.useCallback(() => { if (scrollRef.current) { scrollRef.current.scrollTo({ y: 0, animated: false }); } }, []) ); const handleCashOut = () => { const balance = wallet?.balance; if (balance === undefined) { showToast( t("home.cashoutErrorTitle"), t("home.cashoutNoBalance"), "error" ); return; } if (balance < 1000) { showToast( t("home.cashoutErrorTitle"), t("home.cashoutMinError"), "warning" ); return; } router.push(ROUTES.CASH_OUT); }; const handleAddCash = () => { router.push(ROUTES.ADD_CASH); }; const handleAddCard = () => { router.push(ROUTES.ADD_CARD); }; return ( {t("components.topbar.greeting")} {profileLoading ? "..." : firstName && firstName.length > 8 ? firstName.substring(0, 8) : firstName} {/* profile */} Available Credits {walletLoading ? "..." : wallet ? (wallet.balance / 100).toFixed(2) : "0.00"} Balance usable for client payments {walletError && ( {walletError} )} {/* Action Pill */} {/* Add Cash */} router.push(ROUTES.ADD_CASH)} > Add Cash {/* Pay */} router.push(ROUTES.SEND_OR_REQUEST_MONEY)} > Pay {/* Send notification */} router.push(ROUTES.SEND_NOTIFICATION)} > {"Send"} {"\n"} {"notification"} {/* Upcoming Reminders */} Upcoming Reminders {UPCOMING_REMINDERS.map((reminder) => ( {reminder.clientName} {reminder.datetimeLabel} {reminder.status} ))} Exchange Rates Live preview {EXCHANGE_RATES.map((item, index) => ( {index > 0 && ( )} {item.code} {item.name} 1 {item.code} {item.rateToETB.toFixed(2)} ≈ {item.rateToETB.toFixed(2)} ETB ))} {t("home.transactionsTitle")} {transactionsLoading ? ( {Array.from({ length: 5 }).map((_, index) => ( ))} ) : transactionsError ? ( {t("home.transactionsError")} {transactionsError} ) : transactions.length === 0 ? ( {t("home.transactionsNoTransactions")} {t("home.transactionsEmptySubtitle")} ) : ( {transactions.slice(0, 5).map((transaction) => { const pillClasses = getHomeTxStatusPillClasses( transaction.status ); const clientName = getHomeTxClientName(transaction); const timeLabel = formatHomeTxTime(transaction.createdAt); const methodLabel = getHomeTxMethodLabel(transaction); const shortRef = transaction.id ? String(transaction.id).slice(-8) : ""; return ( { router.push({ pathname: ROUTES.TRANSACTION_DETAIL, params: { transactionId: transaction.id, amount: transaction.amount.toString(), type: transaction.type, recipientName: clientName, date: transaction.createdAt.toISOString(), status: transaction.status, //@ts-ignore note: transaction?.note || "", fromHistory: "true", }, }); }} > {formatHomeTxAmount(transaction.amount)} {transaction.status === "completed" ? "Success" : transaction.status === "pending" ? "Pending" : "Failed"} {clientName} {timeLabel} {methodLabel} {shortRef ? ( {shortRef} ) : null} ); })} {transactions.length > 5 && ( router.push(ROUTES.HISTORY)}> {t("home.transactionsMore", { count: transactions.length - 5, })} )} )} {/* Contact Modal */} ); }