import { useState, useEffect, useRef } from 'react'; import { StyleSheet, View, Text, FlatList, TouchableOpacity, PermissionsAndroid, Platform, ScrollView } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { Stack, router } from 'expo-router'; import { useKeepAwake } from 'expo-keep-awake'; import io from 'socket.io-client'; import { SendDirectSms } from 'react-native-send-direct-sms'; const COLORS = { background: '#121416', surface: '#1e2022', surfaceHigh: '#282a2c', primary: '#fefcff', onPrimary: '#213148', text: '#e2e2e5', textMuted: '#c5c6cd', success: '#69fc9b', successDim: '#003919', error: '#ffb4ab', border: '#44474d', }; export default function DashboardScreen() { useKeepAwake(); const [status, setStatus] = useState('Disconnected'); const [hasPermission, setHasPermission] = useState(false); const [logs, setLogs] = useState<{id: string, time: string, msg: string, type: 'info' | 'success' | 'error'}[]>([]); const [latestSms, setLatestSms] = useState<{phone: string, message: string} | null>(null); const socketRef = useRef(null); useEffect(() => { checkAndRequestPermission(); connectSocket(); return () => { if (socketRef.current) { socketRef.current.disconnect(); } }; }, []); const checkAndRequestPermission = async () => { if (Platform.OS === 'android') { try { const granted = await PermissionsAndroid.request( PermissionsAndroid.PERMISSIONS.SEND_SMS, { title: 'SMS Permission', message: 'This app needs access to send SMS messages directly.', buttonNeutral: 'Ask Me Later', buttonNegative: 'Cancel', buttonPositive: 'OK', }, ); if (granted === PermissionsAndroid.RESULTS.GRANTED) { setHasPermission(true); addLog('SMS permission granted', 'success'); } else { setHasPermission(false); addLog('SMS permission denied', 'error'); } } catch (err) { console.warn(err); addLog(`Permission error: ${err}`, 'error'); } } }; const addLog = (msg: string, type: 'info' | 'success' | 'error' = 'info') => { setLogs(prev => [{ id: Math.random().toString(), time: new Date().toLocaleTimeString('en-US', { hour12: false }), msg, type }, ...prev].slice(0, 50)); }; const connectSocket = async () => { try { const endpoint = await AsyncStorage.getItem('endpoint'); const login = await AsyncStorage.getItem('login'); const password = await AsyncStorage.getItem('password'); if (!endpoint || !login || !password) { setStatus('Missing Settings'); addLog('Please configure settings first', 'error'); return; } if (socketRef.current) { socketRef.current.disconnect(); } setStatus('Connecting...'); addLog('Attempting socket handshake', 'info'); const socket = io(endpoint, { auth: { login, password }, reconnectionAttempts: Infinity, reconnectionDelay: 1000, reconnectionDelayMax: 5000, }); socket.on('connect', () => { setStatus('Connected'); addLog('Connected to backend', 'success'); }); socket.on('disconnect', () => { setStatus('Disconnected'); addLog('Connection lost: Retrying...', 'error'); }); socket.on('connect_error', (err) => { setStatus('Error'); addLog(`Connection error: ${err.message}`, 'error'); }); socket.on('send_sms', async (data) => { addLog(`OTP received: ${data.message}`, 'info'); setLatestSms({ phone: data.phone || '+993 61 92 92 48', message: data.message }); try { const targetPhone = data.phone || '+993 61 92 92 48'; if (Platform.OS === 'android') { SendDirectSms(targetPhone, data.message) .then((res: any) => { addLog(`OTP forwarded successfully`, 'success'); socket.emit('sms_status', { id: data.id, status: 'sent' }); }) .catch((err: any) => { addLog(`SMS send failed: ${err}`, 'error'); socket.emit('sms_status', { id: data.id, status: 'failed' }); }); } else { addLog('Direct SMS only supported on Android', 'error'); socket.emit('sms_status', { id: data.id, status: 'failed' }); } } catch (error) { addLog(`Error sending SMS: ${error}`, 'error'); socket.emit('sms_status', { id: data.id, status: 'failed' }); } }); socketRef.current = socket; } catch (e) { addLog(`Setup error: ${e}`, 'error'); } }; return ( 🛡️ Secure Relay router.push('/settings')}> ⚙️ {/* Status Card */} {status === 'Connected' ? '✓' : (status === 'Connecting...' ? '⏳' : '✕')} {status} {status === 'Connected' && '✅'} {status === 'Connected' ? 'Service is active and monitoring incoming SMS.' : 'Service is currently unavailable.'} {/* System Status */} System Status 💬 SMS Perm {hasPermission ? 'GRANTED' : 'DENIED'} 📁 Storage GRANTED ↻ Reconnect {/* Latest SMS */} {latestSms && ( <> Latest SMS Request 📱 Phone: {latestSms.phone} 💬 Message: '{latestSms.message}' )} {/* Logs */} Recent Logs setLogs([])} style={styles.clearButton}> Clear Logs {logs.map((item) => ( [{item.time}] {item.msg} ))} {logs.length === 0 && ( No recent activity. )} ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: COLORS.background, }, header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingHorizontal: 24, paddingVertical: 16, marginTop: 8, }, headerLeft: { flexDirection: 'row', alignItems: 'center', }, headerIcon: { fontSize: 24, marginRight: 8, }, headerTitle: { color: COLORS.text, fontSize: 20, fontWeight: 'bold', }, settingsIcon: { fontSize: 24, color: COLORS.textMuted, }, scrollContent: { flex: 1, }, scrollContentContainer: { padding: 24, paddingTop: 8, }, mainStatusCard: { backgroundColor: COLORS.surface, borderRadius: 24, padding: 32, alignItems: 'center', marginBottom: 24, }, statusIconContainer: { width: 48, height: 48, borderRadius: 24, alignItems: 'center', justifyContent: 'center', marginBottom: 16, }, statusIcon: { fontSize: 24, color: COLORS.success, }, statusValue: { fontSize: 28, fontWeight: 'bold', marginBottom: 8, }, statusDesc: { color: COLORS.textMuted, fontSize: 14, textAlign: 'center', }, sectionTitle: { color: COLORS.text, fontSize: 16, fontWeight: '600', marginBottom: 12, marginTop: 8, }, systemStatusRow: { flexDirection: 'row', justifyContent: 'space-between', marginBottom: 24, }, systemStatusCard: { backgroundColor: COLORS.surface, borderRadius: 16, padding: 16, flexDirection: 'row', alignItems: 'center', flex: 0.48, }, systemStatusIcon: { fontSize: 20, marginRight: 12, }, systemStatusLabel: { color: COLORS.text, fontSize: 14, fontWeight: '500', }, systemStatusValue: { fontSize: 12, fontWeight: 'bold', marginTop: 2, }, reconnectButton: { backgroundColor: COLORS.primary, borderRadius: 9999, paddingVertical: 16, alignItems: 'center', marginBottom: 24, }, reconnectText: { color: COLORS.onPrimary, fontSize: 16, fontWeight: 'bold', }, latestSmsCard: { backgroundColor: COLORS.surface, borderRadius: 24, padding: 20, marginBottom: 24, }, latestSmsText: { color: COLORS.textMuted, fontSize: 14, marginBottom: 8, lineHeight: 20, }, latestSmsHighlight: { color: COLORS.text, fontWeight: 'bold', fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace', }, logHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }, clearButton: { backgroundColor: COLORS.surfaceHigh, paddingHorizontal: 12, paddingVertical: 6, borderRadius: 12, }, clearButtonText: { color: COLORS.text, fontSize: 12, fontWeight: '500', }, logsCard: { backgroundColor: COLORS.surface, borderRadius: 24, padding: 20, minHeight: 150, }, logItem: { flexDirection: 'row', marginBottom: 8, }, logTime: { fontSize: 13, fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace', marginRight: 8, }, logMsg: { flex: 1, fontSize: 13, fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace', }, emptyLogs: { color: COLORS.textMuted, fontSize: 13, fontStyle: 'italic', textAlign: 'center', marginTop: 20, } });