115 lines
3.4 KiB
TypeScript
115 lines
3.4 KiB
TypeScript
import React from 'react';
|
|
import { View, Text, StyleSheet } from 'react-native';
|
|
import { Colors, Fonts, FontSize, BorderRadius } from '@/constants/theme';
|
|
import PulsingDot from '@/components/PulsingDot';
|
|
import { useTradingData } from '@/providers/TradingDataProvider';
|
|
|
|
export default function StatusBanner() {
|
|
const { botState, connectionState, error, lastUpdatedAt } = useTradingData();
|
|
const controlMode = botState?.health?.tradingControl?.mode ?? 'RUNNING';
|
|
const executionMode = botState?.settings.executionMode || 'Paper';
|
|
const uptimeSeconds = botState?.uptime || 0;
|
|
const uptimeHours = Math.floor(uptimeSeconds / 3600);
|
|
const uptimeMinutes = Math.floor((uptimeSeconds % 3600) / 60);
|
|
const isPaused = controlMode === 'PAUSED';
|
|
const statusText =
|
|
connectionState === 'offline' ? 'OFFLINE' : connectionState === 'degraded' ? 'DEGRADED' : controlMode;
|
|
const statusDetail = error
|
|
? error
|
|
: lastUpdatedAt
|
|
? `Synced ${Math.max(Math.round((Date.now() - lastUpdatedAt) / 60000), 0)}m ago`
|
|
: 'Awaiting sync';
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
<View
|
|
style={[
|
|
styles.badge,
|
|
isPaused && connectionState === 'live' && styles.pausedBadge,
|
|
connectionState === 'degraded' && styles.degradedBadge,
|
|
connectionState === 'offline' && styles.offlineBadge,
|
|
]}
|
|
>
|
|
{connectionState === 'live' && !isPaused ? <PulsingDot size={6} /> : null}
|
|
<Text style={[styles.runningText, isPaused && styles.pausedText]}>
|
|
{statusText}
|
|
</Text>
|
|
</View>
|
|
<View style={[styles.badge, styles.paperBadge]}>
|
|
<Text style={styles.paperText}>{executionMode.toUpperCase()}</Text>
|
|
</View>
|
|
<View style={styles.metaColumn}>
|
|
<Text style={styles.detailText} numberOfLines={1}>{statusDetail}</Text>
|
|
<Text style={styles.uptime}>{uptimeHours}h {uptimeMinutes}m</Text>
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
backgroundColor: Colors.background.card,
|
|
borderRadius: BorderRadius.small,
|
|
padding: 12,
|
|
paddingHorizontal: 16,
|
|
gap: 10,
|
|
borderWidth: 1,
|
|
borderColor: Colors.border.default,
|
|
},
|
|
badge: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 6,
|
|
backgroundColor: 'rgba(0,255,136,0.15)',
|
|
paddingHorizontal: 10,
|
|
paddingVertical: 5,
|
|
borderRadius: 6,
|
|
},
|
|
runningText: {
|
|
fontFamily: Fonts.inter.black,
|
|
fontSize: FontSize.micro,
|
|
color: Colors.accent.green,
|
|
letterSpacing: 1,
|
|
},
|
|
paperBadge: {
|
|
backgroundColor: 'rgba(52,152,219,0.15)',
|
|
},
|
|
paperText: {
|
|
fontFamily: Fonts.inter.black,
|
|
fontSize: FontSize.micro,
|
|
color: Colors.accent.blue,
|
|
letterSpacing: 1,
|
|
},
|
|
pausedBadge: {
|
|
backgroundColor: 'rgba(255,149,0,0.15)',
|
|
},
|
|
degradedBadge: {
|
|
backgroundColor: 'rgba(255,184,0,0.15)',
|
|
},
|
|
offlineBadge: {
|
|
backgroundColor: 'rgba(255,71,87,0.15)',
|
|
},
|
|
pausedText: {
|
|
color: Colors.accent.amber,
|
|
},
|
|
metaColumn: {
|
|
marginLeft: 'auto',
|
|
alignItems: 'flex-end',
|
|
gap: 2,
|
|
minWidth: 110,
|
|
},
|
|
detailText: {
|
|
fontFamily: Fonts.inter.medium,
|
|
fontSize: FontSize.micro,
|
|
color: Colors.text.secondary,
|
|
maxWidth: 140,
|
|
},
|
|
uptime: {
|
|
fontFamily: Fonts.mono.regular,
|
|
fontSize: FontSize.bodySmall,
|
|
color: Colors.text.secondary,
|
|
},
|
|
});
|