fix: ON AIR badge correct at launch + faster to appear

At launch nothing is in memory yet, so the badge showed offline even with a
recent QSO. liveLastQSOTime is now authoritative: it takes the most recent of
the in-memory stamp AND the DB (this operator's last QSO — covers a contact
from the shared logbook or one logged before launch), used by both the
published status and the badge. The badge polls the backend every 5 s (down
from 15) and on each qso:logged, so it shows on air within a few seconds and
flips online instantly on a new contact.
This commit is contained in:
2026-07-19 18:35:18 +02:00
parent 14a22ddb66
commit 24eaf597fd
2 changed files with 30 additions and 19 deletions
+9 -15
View File
@@ -1096,23 +1096,17 @@ export default function App() {
// offline. Only shown when live-status publishing is enabled (Settings→General).
const [liveStatusOn, setLiveStatusOn] = useState(false);
const [onAir, setOnAir] = useState(false);
const lastQsoAtRef = useRef(0);
useEffect(() => { if (!showSettings) GetLiveStatusEnabled().then((v) => setLiveStatusOn(!!v)).catch(() => {}); }, [showSettings]);
useEffect(() => {
const LIVE_WINDOW = 5 * 60 * 1000; // 5 min, matches the backend
const evalOnAir = () => setOnAir(liveStatusOn && lastQsoAtRef.current > 0 && (Date.now() - lastQsoAtRef.current) < LIVE_WINDOW);
const off = EventsOn('qso:logged', () => { lastQsoAtRef.current = Date.now(); evalOnAir(); });
// Seed from the DB at launch so a QSO logged just before starting OpsLog still
// counts (otherwise the badge showed offline until the next contact).
LiveLastQSOAgeSec().then((sec: number) => {
if (typeof sec === 'number' && sec >= 0) {
const at = Date.now() - sec * 1000;
if (at > lastQsoAtRef.current) lastQsoAtRef.current = at;
evalOnAir();
}
}).catch(() => {});
evalOnAir();
const id = window.setInterval(evalOnAir, 10 * 1000); // flip to offline within ~10s of the window elapsing
// Read the ON-AIR state straight from the backend (single source of truth:
// liveLastQSOAt, stamped on every log and seeded from the DB at launch). Poll
// it + refresh on each logged QSO — no fragile frontend timestamp to drift.
const refresh = () => LiveLastQSOAgeSec()
.then((sec: number) => setOnAir(liveStatusOn && typeof sec === 'number' && sec >= 0 && sec < 300))
.catch(() => {});
refresh();
const off = EventsOn('qso:logged', refresh);
const id = window.setInterval(refresh, 5 * 1000); // responsive without hammering (cheap 400-row scan)
return () => { off(); window.clearInterval(id); };
}, [liveStatusOn]);
// QSO-rate meter (10/60 min) in the header — opt-in via Settings→General.