TriloopTem_App/app/connect.tsx
2026-06-06 22:13:00 +08:00

208 lines
6.8 KiB
TypeScript

import React, { useState } from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
ScrollView,
Linking,
ActivityIndicator,
} from 'react-native';
import { router } from 'expo-router';
import { useConnectionStore } from '../src/stores/connectionStore';
import { useDevice } from '../src/hooks/useDevice';
const LOG_MAX = 60;
export default function ConnectScreen() {
const { host, port, status, setHost, setPort } = useConnectionStore();
const { connect, disconnect } = useDevice();
const [logs, setLogs] = useState<string[]>(['准备连接...']);
const [portStr, setPortStr] = useState(String(port));
const addLog = (msg: string) => {
setLogs((prev) => [`[${new Date().toLocaleTimeString()}] ${msg}`, ...prev].slice(0, LOG_MAX));
};
const handleConnect = () => {
const p = parseInt(portStr, 10);
if (!host || isNaN(p)) {
addLog('请检查 IP 和端口');
return;
}
setPort(p);
addLog(`正在连接 ${host}:${p}...`);
// Watch for status changes via the hook (side effect in useDevice)
useConnectionStore.subscribe((s) => {
if (s.status === 'connected') {
addLog('连接成功!');
router.replace('/(tabs)/control');
} else if (s.status === 'error') {
addLog(`错误: ${s.lastError}`);
} else if (s.status === 'reconnecting') {
addLog('断线,正在重连...');
}
});
connect();
};
const handleDisconnect = () => {
disconnect();
addLog('已断开连接');
};
const isConnecting = status === 'connecting' || status === 'reconnecting';
const isConnected = status === 'connected';
return (
<View style={styles.container}>
<Text style={styles.title}>TEM Receiver</Text>
<Text style={styles.subtitle}></Text>
{/* Connection steps */}
<View style={styles.card}>
<Text style={styles.cardTitle}></Text>
<Text style={styles.step}> WiFi </Text>
<TouchableOpacity style={styles.wifiBtn} onPress={() => Linking.openSettings()}>
<Text style={styles.wifiBtnText}> WiFi </Text>
</TouchableOpacity>
<Text style={styles.step}> </Text>
</View>
{/* IP / Port input */}
<View style={styles.card}>
<View style={styles.inputRow}>
<Text style={styles.inputLabel}> IP</Text>
<TextInput
style={styles.input}
value={host}
onChangeText={setHost}
keyboardType="numeric"
placeholder="192.168.4.1"
placeholderTextColor="#555"
autoCapitalize="none"
/>
</View>
<View style={styles.inputRow}>
<Text style={styles.inputLabel}></Text>
<TextInput
style={styles.input}
value={portStr}
onChangeText={setPortStr}
keyboardType="numeric"
placeholder="4321"
placeholderTextColor="#555"
/>
</View>
</View>
{/* Connect / Disconnect button */}
{!isConnected ? (
<TouchableOpacity
style={[styles.connectBtn, isConnecting && styles.connectBtnBusy]}
onPress={handleConnect}
disabled={isConnecting}
>
{isConnecting ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={styles.connectBtnText}> </Text>
)}
</TouchableOpacity>
) : (
<View style={styles.connectedRow}>
<View style={styles.connectedBadge}>
<View style={styles.greenDot} />
<Text style={styles.connectedText}></Text>
</View>
<TouchableOpacity style={styles.goBtn} onPress={() => router.replace('/(tabs)/control')}>
<Text style={styles.goBtnText}> </Text>
</TouchableOpacity>
<TouchableOpacity style={styles.disconnectBtn} onPress={handleDisconnect}>
<Text style={styles.disconnectBtnText}></Text>
</TouchableOpacity>
</View>
)}
{/* Connection log */}
<View style={styles.logCard}>
<Text style={styles.logTitle}></Text>
<ScrollView style={styles.logScroll} nestedScrollEnabled>
{logs.map((l, i) => (
<Text key={i} style={styles.logLine}>{l}</Text>
))}
</ScrollView>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#0d0d0d', padding: 16 },
title: { color: '#4a9eff', fontSize: 28, fontWeight: '700', textAlign: 'center', marginTop: 40 },
subtitle: { color: '#666', fontSize: 13, textAlign: 'center', marginBottom: 24 },
card: {
backgroundColor: '#1a1a1a',
borderRadius: 10,
padding: 14,
marginBottom: 12,
borderWidth: 1,
borderColor: '#2a2a2a',
},
cardTitle: { color: '#4a9eff', fontSize: 13, fontWeight: '600', marginBottom: 8 },
step: { color: '#bbb', fontSize: 13, marginBottom: 6 },
wifiBtn: {
backgroundColor: '#0d2b55',
borderRadius: 6,
paddingVertical: 6,
paddingHorizontal: 12,
alignSelf: 'flex-start',
marginBottom: 10,
},
wifiBtnText: { color: '#4a9eff', fontSize: 12 },
inputRow: { flexDirection: 'row', alignItems: 'center', marginBottom: 8 },
inputLabel: { color: '#888', fontSize: 13, width: 50 },
input: {
flex: 1,
backgroundColor: '#111',
color: '#eee',
borderRadius: 6,
paddingHorizontal: 10,
paddingVertical: 6,
fontSize: 14,
borderWidth: 1,
borderColor: '#333',
},
connectBtn: {
backgroundColor: '#4a9eff',
borderRadius: 10,
paddingVertical: 14,
alignItems: 'center',
marginBottom: 12,
},
connectBtnBusy: { backgroundColor: '#2a5a99' },
connectBtnText: { color: '#fff', fontSize: 16, fontWeight: '700', letterSpacing: 2 },
connectedRow: { flexDirection: 'row', alignItems: 'center', gap: 10, marginBottom: 12 },
connectedBadge: { flexDirection: 'row', alignItems: 'center', gap: 6 },
greenDot: { width: 10, height: 10, borderRadius: 5, backgroundColor: '#44cc44' },
connectedText: { color: '#44cc44', fontWeight: '600' },
goBtn: { flex: 1, backgroundColor: '#1a4a22', borderRadius: 8, padding: 10, alignItems: 'center' },
goBtnText: { color: '#44cc44', fontWeight: '600' },
disconnectBtn: { backgroundColor: '#3a1010', borderRadius: 8, padding: 10 },
disconnectBtnText: { color: '#ff6666' },
logCard: {
flex: 1,
backgroundColor: '#111',
borderRadius: 10,
padding: 10,
borderWidth: 1,
borderColor: '#222',
},
logTitle: { color: '#666', fontSize: 11, marginBottom: 4 },
logScroll: { flex: 1 },
logLine: { color: '#555', fontSize: 10, fontFamily: 'monospace', marginBottom: 2 },
});