This commit is contained in:
Mekan1206
2026-06-02 12:10:45 +05:00
parent 01b8d5c988
commit 3cac2d4051
7 changed files with 8598 additions and 269 deletions

View File

@@ -1,98 +1,252 @@
import * as Device from 'expo-device';
import { Platform, StyleSheet } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useState, useEffect, useRef } from 'react';
import { StyleSheet, View, Text, FlatList, TouchableOpacity, PermissionsAndroid, Platform } from 'react-native';
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';
import { AnimatedIcon } from '@/components/animated-icon';
import { HintRow } from '@/components/hint-row';
import { ThemedText } from '@/components/themed-text';
import { ThemedView } from '@/components/themed-view';
import { WebBadge } from '@/components/web-badge';
import { BottomTabInset, MaxContentWidth, Spacing } from '@/constants/theme';
export default function DashboardScreen() {
useKeepAwake(); // Keep the screen awake
const [status, setStatus] = useState('Disconnected');
const [hasPermission, setHasPermission] = useState(false);
const [logs, setLogs] = useState<{id: string, time: string, msg: string}[]>([]);
const socketRef = useRef<any>(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');
} else {
setHasPermission(false);
addLog('SMS permission denied');
}
} catch (err) {
console.warn(err);
addLog(`Permission error: ${err}`);
}
}
};
const addLog = (msg: string) => {
setLogs(prev => [{
id: Math.random().toString(),
time: new Date().toLocaleTimeString(),
msg
}, ...prev].slice(0, 50)); // Keep last 50 logs
};
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');
return;
}
if (socketRef.current) {
socketRef.current.disconnect();
}
setStatus('Connecting...');
const socket = io(endpoint, {
auth: { login, password },
reconnectionAttempts: Infinity, // Keep trying forever
reconnectionDelay: 1000,
reconnectionDelayMax: 5000,
});
socket.on('connect', () => {
setStatus('Connected');
addLog('Connected to backend');
});
socket.on('disconnect', () => {
setStatus('Disconnected');
addLog('Disconnected from backend');
});
socket.on('connect_error', (err) => {
setStatus('Error');
addLog(`Connection error: ${err.message}`);
});
socket.on('send_sms', async (data) => {
addLog(`Received SMS request ID: ${data.id} to ${data.phone}`);
try {
// Default to rule phone if empty, or use the one provided
const targetPhone = data.phone || '+993 61 92 92 48';
if (Platform.OS === 'android') {
SendDirectSms.sendDirectSms(targetPhone, data.message)
.then((res: any) => {
addLog(`SMS sent successfully to ${targetPhone}`);
socket.emit('sms_status', { id: data.id, status: 'sent' });
})
.catch((err: any) => {
addLog(`SMS send failed: ${err}`);
socket.emit('sms_status', { id: data.id, status: 'failed' });
});
} else {
addLog('Direct SMS only supported on Android');
socket.emit('sms_status', { id: data.id, status: 'failed' });
}
} catch (error) {
addLog(`Error sending SMS: ${error}`);
socket.emit('sms_status', { id: data.id, status: 'failed' });
}
});
socketRef.current = socket;
} catch (e) {
addLog(`Setup error: ${e}`);
}
};
function getDevMenuHint() {
if (Platform.OS === 'web') {
return <ThemedText type="small">use browser devtools</ThemedText>;
}
if (Device.isDevice) {
return (
<ThemedText type="small">
shake device or press <ThemedText type="code">m</ThemedText> in terminal
</ThemedText>
);
}
const shortcut = Platform.OS === 'android' ? 'cmd+m (or ctrl+m)' : 'cmd+d';
return (
<ThemedText type="small">
press <ThemedText type="code">{shortcut}</ThemedText>
</ThemedText>
);
}
<View style={styles.container}>
<View style={styles.statusCard}>
<Text style={styles.statusLabel}>Backend Status:</Text>
<Text style={[
styles.statusValue,
{ color: status === 'Connected' ? 'green' : (status === 'Error' || status === 'Disconnected' ? 'red' : 'orange') }
]}>
{status}
</Text>
<View style={styles.buttonRow}>
<TouchableOpacity style={styles.reconnectButton} onPress={connectSocket}>
<Text style={styles.reconnectText}>Reconnect</Text>
</TouchableOpacity>
{!hasPermission && (
<TouchableOpacity style={[styles.reconnectButton, { backgroundColor: '#ffebee', marginLeft: 10 }]} onPress={checkAndRequestPermission}>
<Text style={[styles.reconnectText, { color: '#c62828' }]}>Req Permission</Text>
</TouchableOpacity>
)}
</View>
</View>
export default function HomeScreen() {
return (
<ThemedView style={styles.container}>
<SafeAreaView style={styles.safeArea}>
<ThemedView style={styles.heroSection}>
<AnimatedIcon />
<ThemedText type="title" style={styles.title}>
Welcome to&nbsp;Expo
</ThemedText>
</ThemedView>
<ThemedText type="code" style={styles.code}>
get started
</ThemedText>
<ThemedView type="backgroundElement" style={styles.stepContainer}>
<HintRow
title="Try editing"
hint={<ThemedText type="code">src/app/index.tsx</ThemedText>}
/>
<HintRow title="Dev tools" hint={getDevMenuHint()} />
<HintRow
title="Fresh start"
hint={<ThemedText type="code">npm run reset-project</ThemedText>}
/>
</ThemedView>
{Platform.OS === 'web' && <WebBadge />}
</SafeAreaView>
</ThemedView>
<Text style={styles.logTitle}>Activity Log</Text>
<FlatList
data={logs}
keyExtractor={item => item.id}
style={styles.logList}
renderItem={({item}) => (
<View style={styles.logItem}>
<Text style={styles.logTime}>{item.time}</Text>
<Text style={styles.logMsg}>{item.msg}</Text>
</View>
)}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
headerButton: {
marginRight: 15,
},
headerButtonText: {
color: '#208AEF',
fontSize: 16,
fontWeight: 'bold',
},
statusCard: {
backgroundColor: '#fff',
margin: 15,
padding: 20,
borderRadius: 10,
alignItems: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
statusLabel: {
fontSize: 16,
color: '#666',
},
statusValue: {
fontSize: 24,
fontWeight: 'bold',
marginTop: 5,
},
reconnectButton: {
paddingVertical: 8,
paddingHorizontal: 20,
backgroundColor: '#eee',
borderRadius: 20,
},
buttonRow: {
flexDirection: 'row',
marginTop: 15,
justifyContent: 'center',
},
reconnectText: {
color: '#333',
fontWeight: '600',
},
logTitle: {
fontSize: 18,
fontWeight: 'bold',
marginLeft: 15,
marginBottom: 10,
color: '#333',
},
logList: {
flex: 1,
backgroundColor: '#fff',
},
logItem: {
padding: 15,
borderBottomWidth: 1,
borderBottomColor: '#eee',
flexDirection: 'row',
},
safeArea: {
logTime: {
color: '#888',
fontSize: 12,
width: 80,
},
logMsg: {
flex: 1,
paddingHorizontal: Spacing.four,
alignItems: 'center',
gap: Spacing.three,
paddingBottom: BottomTabInset + Spacing.three,
maxWidth: MaxContentWidth,
},
heroSection: {
alignItems: 'center',
justifyContent: 'center',
flex: 1,
paddingHorizontal: Spacing.four,
gap: Spacing.four,
},
title: {
textAlign: 'center',
},
code: {
textTransform: 'uppercase',
},
stepContainer: {
gap: Spacing.three,
alignSelf: 'stretch',
paddingHorizontal: Spacing.three,
paddingVertical: Spacing.four,
borderRadius: Spacing.four,
},
color: '#333',
fontSize: 14,
}
});