TriloopTem_App/src/components/device/GlobalStatusBar.tsx
2026-06-19 22:08:10 +08:00

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',
},
});