Review

On-air host context for community radio board console

KRCL board ops still prop-drill the on-air host through the underwriting queue, request line, and cart log. Lift host + setter into OnAirHostContext so any console panel can read who is live. Shell already drives a requestAnimationFrame VU meter for the peak LEDs; wrap children in the new provider without changing panel APIs.

ReactTier 2reactcontextre-renders

Click a line to flag it, pick one or more labels, then submit. If the change looks correct, approve it.

components/board/BoardConsoleShell.tsx+20-4
11import { createContext, useContext, useEffect, useState, type ReactNode } from "react";
22import { readMeterPeak } from "@/audio/vuBridge";
33import { VuMeter } from "./VuMeter";
44
55type Host = { id: string; callSign: string; shiftEndsAt: string };
66
7+type OnAirHostValue = {
8+ host: Host | null;
9+ setHost: (h: Host | null) => void;
10+};
11+
12+const OnAirHostContext = createContext<OnAirHostValue | null>(null);
13+
14+export function useOnAirHost() {
15+ const ctx = useContext(OnAirHostContext);
16+ if (!ctx) throw new Error("useOnAirHost requires BoardConsoleShell");
17+ return ctx;
18+}
19+
720export function BoardConsoleShell({ children }: { children: ReactNode }) {
21+ const [host, setHost] = useState<Host | null>(null);
822 const [vuPeak, setVuPeak] = useState(0);
923
1024 useEffect(() => {
1125 let frame = 0;
1226 const tick = () => {
1327 setVuPeak(readMeterPeak());
1428 frame = requestAnimationFrame(tick);
1529 };
1630 frame = requestAnimationFrame(tick);
1731 return () => cancelAnimationFrame(frame);
1832 }, []);
1933
2034 return (
21- <div className="board-shell">
22- <VuMeter peak={vuPeak} />
23- {children}
24- </div>
35+ <OnAirHostContext.Provider value={{ host, setHost }}>
36+ <div className="board-shell">
37+ <VuMeter peak={vuPeak} />
38+ {children}
39+ </div>
40+ </OnAirHostContext.Provider>
2541 );
2642}