fix(cluster): carry worked_slot to the frontend, and react to the option at once
The "colour the stations not worked on this band and mode" option did nothing. The backend computed WorkedSlot correctly and the generated bindings declared it, but the status map in App.tsx is assembled field by field from the reply, and worked_slot was not on that list. The extra key was simply dropped, so the frontend saw undefined, the === false test never matched, and every spot stayed muted. Silent by construction: nothing warns that a copied-out struct is missing a field. All three assembly sites now carry it, and the state type names it so the compiler catches the next one. The band map also memoised the transformed status map on spotStatusRaw alone, so toggling an option changed nothing until a poll happened to hand over a fresh object. The options are read at render time and are part of the dependencies.
This commit is contained in:
@@ -1580,7 +1580,10 @@ export default function App() {
|
||||
// Cached per-call slot status: "new" | "new-band" | "new-slot" | "worked".
|
||||
// Keyed by `${call}|${band}|${mode}` so two spots of the same call on
|
||||
// different slots don't share the same colour.
|
||||
const [spotStatus, setSpotStatus] = useState<Record<string, { status: string; country?: string; continent?: string; worked_call?: boolean; new_county?: boolean; new_pota?: boolean; new_pfx?: boolean; pfx?: string }>>({});
|
||||
// worked_slot must be carried explicitly like every other field: this map is
|
||||
// assembled field by field, so a backend flag that nobody copies here simply
|
||||
// never reaches the panels — silently, since the extra key is just dropped.
|
||||
const [spotStatus, setSpotStatus] = useState<Record<string, { status: string; country?: string; continent?: string; worked_call?: boolean; worked_slot?: boolean; new_county?: boolean; new_pota?: boolean; new_pfx?: boolean; pfx?: string }>>({});
|
||||
// Live mirror of spotStatus so the incoming-spot buffer can tell which slots
|
||||
// still need resolving without re-subscribing the cluster:spot listener.
|
||||
const spotStatusRef = useRef(spotStatus);
|
||||
@@ -1630,7 +1633,7 @@ export default function App() {
|
||||
const k = `${r.call}|${r.band ?? ''}|${(r.mode ?? '').toUpperCase()}`;
|
||||
next[k] = {
|
||||
status: r.status ?? '', country: r.country, continent: (r as any).continent,
|
||||
worked_call: !!(r as any).worked_call, new_county: !!(r as any).new_county,
|
||||
worked_call: !!(r as any).worked_call, worked_slot: !!(r as any).worked_slot, new_county: !!(r as any).new_county,
|
||||
new_pota: !!(r as any).new_pota, new_pfx: !!(r as any).new_pfx, pfx: (r as any).pfx,
|
||||
};
|
||||
}
|
||||
@@ -2729,6 +2732,7 @@ export default function App() {
|
||||
country: r.country,
|
||||
continent: (r as any).continent,
|
||||
worked_call: !!(r as any).worked_call,
|
||||
worked_slot: !!(r as any).worked_slot,
|
||||
new_county: !!(r as any).new_county,
|
||||
new_pota: !!(r as any).new_pota,
|
||||
new_pfx: !!(r as any).new_pfx,
|
||||
@@ -3196,6 +3200,7 @@ export default function App() {
|
||||
country: r.country,
|
||||
continent: (r as any).continent,
|
||||
worked_call: !!(r as any).worked_call,
|
||||
worked_slot: !!(r as any).worked_slot,
|
||||
new_county: !!(r as any).new_county,
|
||||
new_pota: !!(r as any).new_pota,
|
||||
new_pfx: !!(r as any).new_pfx,
|
||||
|
||||
@@ -243,13 +243,16 @@ export function BandMap({ band, spots, spotStatus: spotStatusRaw, currentFreqHz,
|
||||
// the map and the cluster list share the single rule set in lib/spotDisplay.
|
||||
// Re-derived whenever the poll delivers a new status map, which is also when a
|
||||
// just-changed option takes effect.
|
||||
// Read at render time, not inside the memo: the options must be part of the
|
||||
// dependencies. Keyed only on spotStatusRaw, toggling an option changed
|
||||
// nothing until the next poll happened to hand over a fresh object.
|
||||
const dispOpts = readSpotDisplayOptions();
|
||||
const spotStatus = useMemo(() => {
|
||||
const o = readSpotDisplayOptions();
|
||||
if (!o.muteWorked && !o.slotHighlight) return spotStatusRaw;
|
||||
if (!dispOpts.muteWorked && !dispOpts.slotHighlight) return spotStatusRaw;
|
||||
const out: Record<string, SpotStatusEntry> = {};
|
||||
for (const k of Object.keys(spotStatusRaw)) out[k] = applySpotDisplay(spotStatusRaw[k], o) as SpotStatusEntry;
|
||||
for (const k of Object.keys(spotStatusRaw)) out[k] = applySpotDisplay(spotStatusRaw[k], dispOpts) as SpotStatusEntry;
|
||||
return out;
|
||||
}, [spotStatusRaw]);
|
||||
}, [spotStatusRaw, dispOpts.muteWorked, dispOpts.slotHighlight]);
|
||||
const range = BAND_RANGES[band];
|
||||
const segments = SEGMENT_COLORS[band] ?? [];
|
||||
const [zoomIdx, setZoomIdx] = useState(2); // default 32 px/kHz
|
||||
|
||||
Reference in New Issue
Block a user