fix(watchlist): never draw a spot before its verdict
A new spot rendered as Needed the instant it arrived and was withdrawn half a second later when the worked answer landed — the list twitched on every burst. Unsettled spots are now invisible everywhere (card lines, counters, the Active/Needed-only filters) until their verdict is in: nobody needs a spot 150 ms early, they need it settled. The answer map is merged rather than replaced, so a settled line can never fall back to unknown, and the query debounce drops to 150 ms since there is nothing on screen waiting for it.
This commit is contained in:
@@ -138,16 +138,25 @@ export function WatchlistTab({ spots, spotStatus, onSpotSelect, onSpotClick }: P
|
||||
if (queries.length === 0) { setWorked({}); return; }
|
||||
try {
|
||||
const res: boolean[] = (await WatchlistWorkedSlots(queries as any)) ?? [];
|
||||
const next: Record<string, boolean> = {};
|
||||
keys.forEach((k, i) => { next[k] = !!res[i]; });
|
||||
setWorked(next);
|
||||
// MERGED, not replaced: replacing made every already-answered key
|
||||
// momentarily unknown on each refresh, which re-hid settled lines.
|
||||
setWorked((prev) => {
|
||||
const next = { ...prev };
|
||||
keys.forEach((k, i) => { next[k] = !!res[i]; });
|
||||
return next;
|
||||
});
|
||||
} catch { /* the badges just stay conservative */ }
|
||||
}, 400) as unknown as number;
|
||||
}, 150) as unknown as number;
|
||||
return () => { if (queryTimer.current) window.clearTimeout(queryTimer.current); };
|
||||
}, [spotsFor, entries]);
|
||||
|
||||
const workedFor = (e: WLEntry, s: ClusterSpot): boolean =>
|
||||
worked[`${s.dx_call}|${s.band ?? ''}|${inferSpotMode(s.comment ?? '', s.freq_hz) || ''}|${e.isContest ? 1 : 0}`] ?? false;
|
||||
const wkey = (e: WLEntry, s: ClusterSpot) =>
|
||||
`${s.dx_call}|${s.band ?? ''}|${inferSpotMode(s.comment ?? '', s.freq_hz) || ''}|${e.isContest ? 1 : 0}`;
|
||||
const workedFor = (e: WLEntry, s: ClusterSpot): boolean => worked[wkey(e, s)] ?? false;
|
||||
// A spot whose verdict has not come back yet is NOT drawn. Showing it as
|
||||
// Needed and withdrawing it half a second later made the list twitch on
|
||||
// every burst — and nobody needs a spot 400 ms early, they need it settled.
|
||||
const settled = (e: WLEntry, s: ClusterSpot): boolean => wkey(e, s) in worked;
|
||||
|
||||
const add = async () => {
|
||||
const c = addCall.trim().toUpperCase();
|
||||
@@ -173,7 +182,7 @@ export function WatchlistTab({ spots, spotStatus, onSpotSelect, onSpotClick }: P
|
||||
if (family === 'normal' && e.isContest) return false;
|
||||
if (family === 'contest' && !e.isContest) return false;
|
||||
if (search && !e.callsign.includes(search.trim().toUpperCase())) return false;
|
||||
const list = (spotsFor.get(e.callsign) ?? []).filter(modeMatches);
|
||||
const list = (spotsFor.get(e.callsign) ?? []).filter(modeMatches).filter((s2) => settled(e, s2));
|
||||
if (activeOnly && list.length === 0) return false;
|
||||
if (neededOnly && !list.some((s2) => !workedFor(e, s2))) return false;
|
||||
return true;
|
||||
@@ -182,7 +191,7 @@ export function WatchlistTab({ spots, spotStatus, onSpotSelect, onSpotClick }: P
|
||||
const counters = useMemo(() => {
|
||||
let active = 0, needed = 0;
|
||||
for (const e of entries) {
|
||||
const list = (spotsFor.get(e.callsign) ?? []).filter(modeMatches);
|
||||
const list = (spotsFor.get(e.callsign) ?? []).filter(modeMatches).filter((s2) => settled(e, s2));
|
||||
if (list.length > 0) active++;
|
||||
if (list.some((s2) => !workedFor(e, s2))) needed++;
|
||||
}
|
||||
@@ -281,7 +290,7 @@ export function WatchlistTab({ spots, spotStatus, onSpotSelect, onSpotClick }: P
|
||||
{entries.length === 0 ? t('wl.empty') : t('wl.noneMatch')}
|
||||
</div>
|
||||
) : shown.map((e) => {
|
||||
const all = (spotsFor.get(e.callsign) ?? []).filter(modeMatches);
|
||||
const all = (spotsFor.get(e.callsign) ?? []).filter(modeMatches).filter((s2) => settled(e, s2));
|
||||
const needed = all.filter((s) => !workedFor(e, s)).length;
|
||||
// Needed-only hides the worked LINES as well as the all-worked cards:
|
||||
// a filter that says needed and still lists five green Worked rows is
|
||||
|
||||
Reference in New Issue
Block a user