import React, { useState } from "react"; import { View, ScrollView, Pressable, PermissionsAndroid, Platform, ActivityIndicator, } from "react-native"; import { Text } from "@/components/ui/text"; import { Card } from "@/components/ui/card"; import { ScreenWrapper } from "@/components/ScreenWrapper"; import { toast } from "@/lib/toast-store"; import { ArrowLeft, MessageSquare, RefreshCw } from "@/lib/icons"; import { useColorScheme } from "nativewind"; import { useSirouRouter } from "@sirou/react-native"; import { AppRoutes } from "@/lib/routes"; // Installed via: npm install react-native-get-sms-android --legacy-peer-deps // Android only — iOS does not permit reading SMS let SmsAndroid: any = null; try { SmsAndroid = require("react-native-get-sms-android").default; } catch (_) {} // Keywords to match Ethiopian banking SMS messages const BANK_KEYWORDS = ["CBE", "DashenBank", "Dashen", "127", "telebirr"]; interface SmsMessage { _id: string; address: string; body: string; date: number; date_sent: number; } export default function SmsScanScreen() { const nav = useSirouRouter(); const { colorScheme } = useColorScheme(); const isDark = colorScheme === "dark"; const [messages, setMessages] = useState([]); const [loading, setLoading] = useState(false); const [scanned, setScanned] = useState(false); const scanSms = async () => { if (Platform.OS !== "android") { toast.error("Android Only", "SMS reading is only supported on Android."); return; } if (!SmsAndroid) { toast.error( "Package Missing", "Run: npm install react-native-get-sms-android", ); return; } setLoading(true); try { // Request SMS permission const granted = await PermissionsAndroid.request( PermissionsAndroid.PERMISSIONS.READ_SMS, { title: "SMS Access Required", message: "Yaltopia needs access to read your banking SMS messages to match payments.", buttonPositive: "Allow", buttonNegative: "Deny", }, ); if (granted !== PermissionsAndroid.RESULTS.GRANTED) { toast.error("Permission Denied", "SMS access was not granted."); return; } // Only look at messages from the past 5 minutes const fiveMinutesAgo = Date.now() - 5 * 60 * 1000; const filter = { box: "inbox", minDate: fiveMinutesAgo, maxCount: 50, }; SmsAndroid.list( JSON.stringify(filter), (fail: string) => { console.error("[SMS] Failed to read:", fail); toast.error("Read Failed", "Could not read SMS messages."); setLoading(false); }, (count: number, smsList: string) => { const allMessages: SmsMessage[] = JSON.parse(smsList); // Filter for banking messages only const bankMessages = allMessages.filter((sms) => { const body = sms.body?.toUpperCase() || ""; const address = sms.address?.toUpperCase() || ""; return BANK_KEYWORDS.some( (kw) => body.includes(kw.toUpperCase()) || address.includes(kw.toUpperCase()), ); }); setMessages(bankMessages); setScanned(true); setLoading(false); if (bankMessages.length === 0) { toast.info( "No Matches", "No banking SMS found in the last 5 minutes.", ); } else { toast.success( "Found!", `${bankMessages.length} banking message(s) detected.`, ); } }, ); } catch (err: any) { console.error("[SMS] Error:", err); toast.error("Error", err.message); setLoading(false); } }; const formatTime = (timestamp: number) => { const date = new Date(timestamp); return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); }; const getBankLabel = (sms: SmsMessage) => { const text = (sms.body + sms.address).toUpperCase(); if (text.includes("CBE")) return { name: "CBE", color: "#16a34a" }; if (text.includes("DASHEN")) return { name: "Dashen Bank", color: "#1d4ed8" }; if (text.includes("127") || text.includes("TELEBIRR")) return { name: "Telebirr", color: "#7c3aed" }; return { name: "Bank", color: "#ea580c" }; }; return ( {/* Header */} nav.back()} className="h-10 w-10 rounded-[10px] bg-card items-center justify-center border border-border" > Scan SMS {/* Spacer */} Scan SMS Finds banking messages from the last 5 minutes {/* Scan Button */} {loading ? ( ) : ( <> {scanned ? "Scan Again" : "Scan Now"} )} {!scanned && !loading && ( Tap "Scan Now" to search for CBE, Dashen Bank, and Telebirr messages from the last 5 minutes. )} {scanned && messages.length === 0 && ( No banking messages found in the last 5 minutes. )} {messages.map((sms) => { const bank = getBankLabel(sms); return ( {bank.name} {formatTime(sms.date)} {sms.body} From: {sms.address} ); })} ); }