diff --git a/app.go b/app.go index 78d15f6..c96cb48 100644 --- a/app.go +++ b/app.go @@ -508,6 +508,7 @@ type App struct { liveFreqHz int64 // last freq/band/mode the UI reported (fallback when CAT is off) liveBand string liveMode string + livePublishTimer *time.Timer // debounced live-status publish on activity change liveLastQSOAt time.Time // when this operator last logged a NEW contact — drives online/offline awardSnapMu sync.Mutex // guards the award QSO snapshot awardSnap []qso.QSO // light-scanned + enriched logbook snapshot reused across award computations diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index edbdb48..f3b30a1 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -451,9 +451,9 @@ export default function App() { if (!liveStationsOn) { setLiveStations([]); return; } const load = () => GetLiveStations().then((s) => setLiveStations((s ?? []) as LiveStation[])).catch(() => {}); load(); - const id = window.setInterval(load, 15 * 1000); + const id = window.setInterval(load, 5 * 1000); // Small delay lets the async publishLiveStatus MySQL write land before we read. - const offLogged = EventsOn('qso:logged', () => { window.setTimeout(load, 2000); }); + const offLogged = EventsOn('qso:logged', () => { window.setTimeout(load, 1500); }); return () => { window.clearInterval(id); offLogged?.(); }; }, [liveStationsOn]); // Mode OpsLog shows when the rig reports generic DIG_U/DIG_L. OmniRig diff --git a/livestatus.go b/livestatus.go index ffe04df..292f81e 100644 --- a/livestatus.go +++ b/livestatus.go @@ -159,10 +159,23 @@ func (a *App) liveStatusOperator() (op, station string) { // ReportLiveActivity is called by the UI with the current entry-strip freq/band/ // mode, used as a fallback for live status when the CAT isn't connected. func (a *App) ReportLiveActivity(freqHz int64, band, mode string) { + nb := strings.ToUpper(strings.TrimSpace(band)) + nm := strings.ToUpper(strings.TrimSpace(mode)) a.liveActMu.Lock() + changed := a.liveFreqHz != freqHz || a.liveBand != nb || a.liveMode != nm a.liveFreqHz = freqHz - a.liveBand = strings.ToUpper(strings.TrimSpace(band)) - a.liveMode = strings.ToUpper(strings.TrimSpace(mode)) + a.liveBand = nb + a.liveMode = nm + // Push a fresh row PROMPTLY when the operator's freq/band/mode changes, instead + // of waiting for the next 15 s heartbeat — otherwise "who's on air" showed a + // stale band and took up to a minute to catch up. Debounced so spinning the VFO + // doesn't hammer the shared MySQL. + if changed { + if a.livePublishTimer != nil { + a.livePublishTimer.Stop() + } + a.livePublishTimer = time.AfterFunc(1500*time.Millisecond, func() { a.publishLiveStatus() }) + } a.liveActMu.Unlock() }