121 lines
4.1 KiB
TypeScript
121 lines
4.1 KiB
TypeScript
import React from 'react';
|
|
import { View, Text, StyleSheet, TouchableOpacity, Platform } from 'react-native';
|
|
import { useConnectionStore } from '../../stores/connectionStore';
|
|
import { useDeviceStore } from '../../stores/deviceStore';
|
|
import { useDataStore } from '../../stores/dataStore';
|
|
import { formatBattery, formatTemperature } from '../../utils/format';
|
|
import { Colors } from '../../design/tokens';
|
|
|
|
function Pill({
|
|
label,
|
|
value,
|
|
color,
|
|
onPress,
|
|
}: {
|
|
label: string;
|
|
value: string;
|
|
color: string;
|
|
onPress?: () => void;
|
|
}) {
|
|
const inner = (
|
|
<View style={[S.pill, { borderColor: color + '44' }]}>
|
|
<View style={[S.dot, { backgroundColor: color }]} />
|
|
<Text style={S.pillLabel}>{label}</Text>
|
|
<Text style={[S.pillValue, { color }]}>{value}</Text>
|
|
</View>
|
|
);
|
|
if (onPress) return <TouchableOpacity onPress={onPress} activeOpacity={0.7}>{inner}</TouchableOpacity>;
|
|
return inner;
|
|
}
|
|
|
|
export function GlobalStatusBar() {
|
|
const { status, showModal } = useConnectionStore();
|
|
const { deviceStatus, batteryVolt, temperature, gpsStatus, sdStatus } = useDeviceStore();
|
|
const frame = useDataStore((s) => s.currentFrame);
|
|
const meta = frame?.meta;
|
|
|
|
const connected = status === 'connected';
|
|
const connColor = connected ? Colors.green.fg : status === 'connecting' ? Colors.amber.fg : Colors.text.ghost;
|
|
const connLabel = connected ? '已连接' : status === 'connecting' ? '连接中' : status === 'reconnecting' ? '重连' : '未连接';
|
|
|
|
const hasGps = gpsStatus > 0 || (meta?.gpsStatus ?? 0) > 0;
|
|
const hasSd = sdStatus > 0;
|
|
|
|
const running = deviceStatus === 'running';
|
|
const single = deviceStatus === 'single';
|
|
const stateColor = running ? Colors.green.fg : single ? Colors.teal.fg : Colors.text.ghost;
|
|
const stateLabel = running ? '● 运行' : single ? '◎ 单次' : '○ 停止';
|
|
|
|
return (
|
|
<View style={S.bar}>
|
|
{/* Connection — tappable */}
|
|
<Pill label="TCP" value={connLabel} color={connColor} onPress={showModal} />
|
|
|
|
{/* Device run state */}
|
|
{connected && (
|
|
<View style={[S.pill, { borderColor: stateColor + '44' }]}>
|
|
<Text style={[S.stateText, { color: stateColor }]}>{stateLabel}</Text>
|
|
</View>
|
|
)}
|
|
|
|
{/* GPS */}
|
|
<Pill label="GPS" value={hasGps ? '定位' : '--'} color={hasGps ? Colors.green.fg : Colors.text.ghost} />
|
|
|
|
{/* SD */}
|
|
<Pill label="SD" value={hasSd ? 'OK' : '--'} color={hasSd ? Colors.blue.fg : Colors.text.ghost} />
|
|
|
|
{/* Battery */}
|
|
{batteryVolt > 0 && (
|
|
<Pill label="电量" value={formatBattery(batteryVolt)} color={Colors.text.muted} />
|
|
)}
|
|
|
|
{/* Temperature */}
|
|
{temperature > 0 && (
|
|
<Pill label="温度" value={formatTemperature(temperature)} color={Colors.text.muted} />
|
|
)}
|
|
|
|
{/* Frame counter — right-aligned */}
|
|
{frame && (
|
|
<View style={S.frameCount}>
|
|
<Text style={S.frameCountText}>#{frame.frameId}</Text>
|
|
</View>
|
|
)}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const S = StyleSheet.create({
|
|
bar: {
|
|
flexDirection: 'row',
|
|
backgroundColor: Colors.bg.void,
|
|
paddingHorizontal: 10,
|
|
paddingVertical: 7,
|
|
alignItems: 'center',
|
|
flexWrap: 'wrap',
|
|
gap: 5,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: Colors.bg.border,
|
|
},
|
|
pill: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 4,
|
|
backgroundColor: Colors.bg.raised,
|
|
borderRadius: 5,
|
|
borderWidth: 1,
|
|
borderColor: Colors.bg.border,
|
|
paddingHorizontal: 6,
|
|
paddingVertical: 2,
|
|
},
|
|
dot: { width: 5, height: 5, borderRadius: 3 },
|
|
pillLabel: { color: Colors.text.ghost, fontSize: 9, fontWeight: '600', letterSpacing: 0.5 },
|
|
pillValue: { fontSize: 9, fontWeight: '700', fontFamily: Platform.OS === 'ios' ? 'Courier' : 'monospace' },
|
|
stateText: { fontSize: 10, fontWeight: '700', letterSpacing: 0.3 },
|
|
frameCount: { marginLeft: 'auto' as any },
|
|
frameCountText: {
|
|
color: Colors.text.ghost,
|
|
fontSize: 10,
|
|
fontFamily: Platform.OS === 'ios' ? 'Courier' : 'monospace',
|
|
},
|
|
});
|