diff --git a/src/screens/CaptureScreen.tsx b/src/screens/CaptureScreen.tsx index e97f834..dcddef7 100644 --- a/src/screens/CaptureScreen.tsx +++ b/src/screens/CaptureScreen.tsx @@ -9,6 +9,8 @@ import { } from 'lucide-react-native'; import { type HeartRateMonitorState } from '../ble/useHeartRateMonitor'; import { ActionButton } from '../components/ActionButton'; +import { DialTile } from '../components/DialTile'; +import { LiveDot } from '../components/LiveDot'; import { TrainingChart } from '../components/TrainingChart'; import { colors, radius, spacing, typography } from '../theme'; import { @@ -35,7 +37,7 @@ export function CaptureScreen({ () => monitor.startedAt ? monitor.samples.filter( - sample => sample.timestamp >= monitor.startedAt!, + (sample) => sample.timestamp >= monitor.startedAt!, ) : monitor.samples, [monitor.samples, monitor.startedAt], @@ -48,20 +50,35 @@ export function CaptureScreen({ ? 'Fechar janela de treino' : 'Iniciar janela de treino'; + const isReceiving = monitor.phase === 'receiving'; + const isLive = monitor.isMonitoring || monitor.isCapturing; + return ( - {status.label} + + + {'\•'} + + {status.label} + {formatDeviceName(monitor)} - - - {monitor.isCapturing - ? formatDuration(workoutElapsedMs) - : formatDuration(monitorElapsedMs)} - + + + + {monitor.isCapturing + ? formatDuration(workoutElapsedMs) + : formatDuration(monitorElapsedMs)} + + {monitor.isCapturing ? 'janela' : 'monitor'} @@ -69,7 +86,11 @@ export function CaptureScreen({ - {monitor.currentBpm ?? BPM_EMPTY} + + + {monitor.currentBpm ?? BPM_EMPTY} + + BPM @@ -80,7 +101,7 @@ export function CaptureScreen({ - - + - - + @@ -124,10 +139,10 @@ export function CaptureScreen({ style={styles.batteryPrompt} > - Manter com tela bloqueada + Manter com tela bloq - Toque para abrir a configuração de bateria e remover restrições do + Toque para abrir a configura\ç\ão de bateria e remover restri\ç\ões do FightRate. @@ -137,7 +152,7 @@ export function CaptureScreen({ {!monitor.isMonitoring ? ( { onMonitorAction().catch(() => undefined); }} @@ -149,15 +164,15 @@ export function CaptureScreen({ onPress={() => { onCaptureAction().catch(() => undefined); }} - variant={monitor.isCapturing ? 'stop' : 'primary'} + variant={monitor.isCapturing ? 'stopAlert' : 'primary'} /> - Enquanto a cinta estiver reconhecida, todo BPM real é salvo. O botão - de treino só marca a janela para análise. + Cinta reconhecida = cada BPM real \é salvo. Toque em Iniciar apenas + para destacar uma janela para an\álise. @@ -185,36 +200,11 @@ function RailMetric({ ); } -function SystemSignal({ - icon, - label, - value, - tone = 'amber', -}: { - readonly icon: 'radio' | 'battery' | 'shield'; - readonly label: string; - readonly value: string; - readonly tone?: 'amber' | 'green'; -}) { - const color = tone === 'green' ? colors.success : colors.warning; - const Icon = - icon === 'radio' ? RadioTower : icon === 'battery' ? Battery : ShieldCheck; - return ( - - - - {label} - {value} - - - ); -} - function getStatusCopy(monitor: HeartRateMonitorState) { if (monitor.isCapturing) { return { label: 'JANELA DE TREINO', - detail: 'Treino destacado enquanto o monitor contínuo segue gravando.', + detail: 'Treino destacado enquanto o monitor cont\ínuo segue gravando.', }; } if (monitor.phase === 'receiving') { @@ -226,20 +216,20 @@ function getStatusCopy(monitor: HeartRateMonitorState) { if (monitor.phase === 'scanning') { return { label: 'PROCURANDO CINTA', - detail: 'O serviço continua procurando até encontrar a cinta.', + detail: 'O servi\ço continua procurando at\é encontrar a cinta.', }; } if (monitor.phase === 'connecting') { return { label: 'CONECTANDO 2A37', detail: - 'Abrindo Heart Rate Service 180D e armando notify da característica 2A37.', + 'Abrindo Heart Rate Service 180D e armando notify da caracter\ística 2A37.', }; } if (monitor.phase === 'permission-denied') { return { - label: 'PERMISSÃO NEGADA', - detail: 'Autorize Bluetooth, Localização e Notificações.', + label: 'PERMISS\ÃO NEGADA', + detail: 'Autorize Bluetooth, Localiza\ç\ão e Notifica\ç\ões.', }; } if (monitor.phase === 'bluetooth-off') { @@ -251,21 +241,21 @@ function getStatusCopy(monitor: HeartRateMonitorState) { if (monitor.phase === 'error') { return { label: 'ERRO DE CAPTURA', - detail: monitor.errorMessage ?? 'Falha no serviço BLE.', + detail: monitor.errorMessage ?? 'Falha no servi\ço BLE.', }; } return { label: 'MONITOR DESATIVADO', - detail: 'Ative o monitor contínuo; depois marque a janela de treino.', + detail: 'Ative o monitor cont\ínuo; depois marque a janela de treino.', }; } function formatDeviceName(monitor: HeartRateMonitorState) { const name = monitor.connectedDevice?.name; if (!name) return 'Magene H003 ou cinta BLE 180D'; - if (/magene|h003/i.test(name)) return 'Magene H003 · cinta cardíaca'; - if (/heart|hrm/i.test(name)) return `${name} · cinta cardíaca`; - return `${name} · BLE`; + if (/magene|h003/i.test(name)) return 'Magene H003 \· cinta card\íaca'; + if (/heart|hrm/i.test(name)) return `${name} \· cinta card\íaca`; + return `${name} \· BLE`; } function getBleShortState( @@ -319,6 +309,16 @@ const styles = StyleSheet.create({ stateBlock: { flex: 1, }, + eyebrowRow: { + flexDirection: 'row', + alignItems: 'center', + gap: 6, + }, + eyebrowDot: { + fontSize: 18, + lineHeight: 18, + fontWeight: '900', + }, eyebrow: { ...typography.label, color: colors.action, @@ -336,6 +336,11 @@ const styles = StyleSheet.create({ gap: spacing.xxs, minWidth: 84, }, + timerRow: { + flexDirection: 'row', + alignItems: 'center', + gap: 6, + }, timerText: { ...typography.label, color: colors.text, @@ -350,11 +355,11 @@ const styles = StyleSheet.create({ alignItems: 'flex-end', gap: spacing.md, }, + bpmBlock: { + flex: 1, + }, bpmValue: { - ...typography.display, - fontSize: 98, - lineHeight: 96, - color: colors.text, + ...typography.displayNumeric, }, bpmSide: { flex: 1, @@ -381,11 +386,7 @@ const styles = StyleSheet.create({ paddingRight: spacing.sm, }, railValue: { - color: colors.text, - fontSize: 28, - lineHeight: 30, - fontWeight: '900', - letterSpacing: -1.2, + ...typography.metricNumeric, }, railValueHot: { color: colors.action, @@ -395,29 +396,10 @@ const styles = StyleSheet.create({ color: colors.textMuted, marginTop: spacing.xxs, }, - systemStrip: { - flexDirection: 'row', - gap: spacing.xs, - }, - systemSignal: { - flex: 1, + dialColumn: { borderTopWidth: 1, - borderTopColor: colors.line, - paddingTop: spacing.sm, - flexDirection: 'row', - gap: spacing.xs, - alignItems: 'center', - }, - signalLabel: { - ...typography.label, - color: colors.textDim, - fontSize: 9, - }, - signalValue: { - color: colors.text, - fontSize: 13, - lineHeight: 17, - fontWeight: '800', + borderBottomWidth: 1, + borderColor: colors.line, }, batteryPrompt: { borderWidth: 1,