()
| 25 | } |
| 26 | |
| 27 | export function Stream() { |
| 28 | const { stream } = useTracker(); |
| 29 | const { state: serverState } = useStream("display"); |
| 30 | const { state, config } = stream; |
| 31 | const mse = useMse(stream.connected, state?.video); |
| 32 | const liveVideo = useLiveVideo(stream.connected, mse.ok ? undefined : state?.video); |
| 33 | useStabilize(mse.videoRef, state?.vision?.detection, { enabled: mse.ok }); |
| 34 | |
| 35 | // Stream options via URL params; route info is opt-IN here (it's wrong |
| 36 | // often enough that the default is to keep it off the broadcast). |
| 37 | const showRoute = useMemo( |
| 38 | () => new URLSearchParams(window.location.search).get("route") === "1", |
| 39 | [], |
| 40 | ); |
| 41 | |
| 42 | const [now, setNow] = useState(Date.now()); |
| 43 | useEffect(() => { |
| 44 | const t = setInterval(() => setNow(Date.now()), 1000); |
| 45 | return () => clearInterval(t); |
| 46 | }, []); |
| 47 | void now; // re-render tick keeps detection age/LIVE pulse fresh |
| 48 | |
| 49 | const target = state?.target; |
| 50 | const targetAc = useMemo( |
| 51 | () => serverState.aircraft.find((a) => a.hex === target?.hex), |
| 52 | [serverState.aircraft, target?.hex], |
| 53 | ); |
| 54 | |
| 55 | if (!state || !config) { |
| 56 | return ( |
| 57 | <div className="stream-boot"> |
| 58 | <div className="stream-boot-mark">SKYLIGHT</div> |
| 59 | </div> |
| 60 | ); |
| 61 | } |
| 62 | |
| 63 | const det = state.vision.detection; |
| 64 | const { from, to } = routeLine(targetAc); |
| 65 | const tracking = Boolean(target?.hex); |
| 66 | |
| 67 | return ( |
| 68 | <div className="stream"> |
| 69 | {/* ---- top: flight card ---- */} |
| 70 | <header className="stream-top"> |
| 71 | <div className="stream-live"> |
| 72 | <span className="stream-live-dot" /> |
| 73 | LIVE · SKY CAMERA OVER SFO |
| 74 | </div> |
| 75 | {tracking ? ( |
| 76 | <div className="stream-card"> |
| 77 | <div className="stream-card-tag"> |
| 78 | {det && det.ageMs < 1500 ? "VISUAL LOCK" : "TRACKING"} |
| 79 | </div> |
| 80 | <div className="stream-flight">{targetAc?.flight ?? target!.hex}</div> |
| 81 | {targetAc?.airline && <div className="stream-airline">{targetAc.airline}</div>} |
| 82 | {showRoute && (from || to) && ( |
| 83 | <div className="stream-route"> |
| 84 | <span>{from ?? "·"}</span> |
nothing calls this directly
no test coverage detected