import { useEffect, useRef, useState } from 'react' import type { LiveRCMessage, LiveDriverInfo } from '../../types' import { latestRaceControl, rcFlagClass } from '../../lib/live' import { teamColor } from '../../utils' import { useAutoAnimate } from '@formkit/auto-animate/react' interface Props { messages: LiveRCMessage[] driverInfo?: Record } function FormattedMessage({ text, driverInfo }: { text: string, driverInfo?: Record }) { if (!text) return null; const regex = /(CAR \d+(?: \([A-Z]{3}\))?|DELETED|TRACK LIMITS|INVESTIGATING|NO FURTHER INVESTIGATION|TIME PENALTY|DRIVE THROUGH PENALTY|STOP AND GO PENALTY|BLACK AND WHITE FLAG|DISQUALIFIED)/g; const parts = text.split(regex); return ( {parts.map((part, i) => { if (!part) return null; if (part.startsWith('CAR ')) { let style = {} const match = part.match(/CAR (\d+)/) if (match && driverInfo && driverInfo[match[1]]) { const hex = teamColor(driverInfo[match[1]].TeamColour) style = { backgroundColor: hex, color: '#fff', textShadow: '0 1px 2px rgba(0,0,0,0.5)', border: 'none' } } return {part} } if (part === 'DELETED' || part.includes('PENALTY') || part === 'DISQUALIFIED') return {part}; if (part === 'TRACK LIMITS' || part === 'INVESTIGATING' || part === 'BLACK AND WHITE FLAG') return {part}; if (part === 'NO FURTHER INVESTIGATION') return {part}; return {part}; })} ) } export function RaceControlFeed({ messages, driverInfo }: Props) { const latest = latestRaceControl(messages) const [listRef] = useAutoAnimate() const prevMessagesRef = useRef([]) const [newKeys, setNewKeys] = useState>(new Set()) useEffect(() => { const prev = prevMessagesRef.current if (prev.length > 0 && messages.length > prev.length) { // Find new messages const newlyAdded = messages.filter( (m) => !prev.some((p) => p.Time === m.Time && p.Message === m.Message) ) if (newlyAdded.length > 0) { const keys = newlyAdded.map((m) => `${m.Time}-${m.Message}`) setNewKeys((current) => { const updated = new Set(current) keys.forEach(k => updated.add(k)) return updated }) // Clear highlight after 3 seconds setTimeout(() => { setNewKeys((current) => { const updated = new Set(current) keys.forEach(k => updated.delete(k)) return updated }) }, 3000) } } prevMessagesRef.current = messages }, [messages]) return (
Race Control {messages.length > 0 ? `${messages.length} messages ยท ` : ''}times UTC
{latest.length === 0 ? (
No race control messages in the current live snapshot.
) : (
{latest.map((message, index) => { const key = `${message.Time}-${message.Message}` const isNew = newKeys.has(key) let flashClass = '' if (isNew) { if (message.Flag === 'YELLOW' || message.Flag === 'DOUBLE YELLOW') { flashClass = ' rc-item-yellow' } else if (message.Flag === 'RED') { flashClass = ' rc-item-red' } else { flashClass = ' rc-item-new' } } return (
{message.Time || '--:--'} {message.Lap > 0 && L{message.Lap}} {message.Flag ? {message.Flag} : message.Category && message.Category !== 'Other' ? {message.Category} : null }
) })}
)}
) }