fix(qso list): the grid followed the upload, not the log

Reported as delayed auto-upload breaking Recent QSOs. It does not — the grid was
waiting on the upload for its news.

Nothing reloaded the list on qso:logged. The entry form calls refresh() itself
after AddQSO, so a QSO TYPED in OpsLog always appeared; a QSO logged from
anywhere else — WSJT-X or MSHV over UDP, JTAlert, NET Control, an offline replay
— reached the grid only when something else reloaded it, and the something else
was extsvc:uploaded. With auto-upload set to immediate that arrives a second
later and hides the gap completely.

Set a service to delayed and the QSO appeared one to two minutes late; set it to
upload on close and not until the next launch. So the very thing deferring an
upload exists for — correcting a contact before it goes out — was impossible,
because there was nothing on screen to correct.

The list now follows the log, on the same debounced path the upload events
already use: qso:logged fires twice per contact (insert, then award refs) and a
contest run fires it every few seconds.
This commit is contained in:
2026-08-16 10:48:38 +02:00
parent 3ed2d957e5
commit 46d6531cf5
2 changed files with 26 additions and 6 deletions
+4 -2
View File
@@ -3,10 +3,12 @@
"version": "0.25.6", "version": "0.25.6",
"date": "", "date": "",
"en": [ "en": [
"Right-click: update the US county of the selected contacts from the ULS database, replacing a county since renamed or abolished." "Right-click: update the US county of the selected contacts from the ULS database, replacing a county since renamed or abolished.",
"A QSO logged from WSJT-X, MSHV or a net now appears in Recent QSOs at once, instead of waiting for a delayed auto-upload to send it."
], ],
"fr": [ "fr": [
"Clic droit : mettre à jour le comté US des contacts sélectionnés depuis la base ULS, pour remplacer un comté renommé ou supprimé." "Clic droit : mettre à jour le comté US des contacts sélectionnés depuis la base ULS, pour remplacer un comté renommé ou supprimé.",
"Un QSO logué depuis WSJT-X, MSHV ou un net apparaît aussitôt dans les QSO récents, sans attendre lenvoi dun upload automatique différé."
] ]
}, },
{ {
+22 -4
View File
@@ -2227,16 +2227,34 @@ export default function App() {
} }
}, [buildActiveFilter]); }, [buildActiveFilter]);
// Refresh the Recent QSOs grid after external-service uploads stamp the // Reload the Recent QSOs grid when the log changes.
// sent status (auto-upload via extsvc:uploaded, or manual QSL Manager via //
// qslmgr:done). Debounced so a batch of per-QSO events triggers one reload. // qso:logged is the important one, and it was missing. The entry form calls
// refresh() itself after AddQSO, so a QSO TYPED here always appeared — but a
// QSO logged from anywhere else (WSJT-X / MSHV over UDP, JTAlert, NET
// Control, an offline replay) only reached the grid when something else
// happened to reload it, and the something else was the upload: with
// auto-upload set to immediate, extsvc:uploaded arrived a second later and
// hid the gap entirely.
//
// Which is why this surfaced as "delayed upload breaks the recent list". It
// does not — the grid was waiting on the upload for its news. On a delayed
// service the QSO appeared one to two minutes late, and on close-of-session
// upload not until the next launch: the corrections that deferring the upload
// exists to allow could not be made, because there was nothing to correct on
// screen. The list follows the LOG now, and the upload only stamps a status.
//
// Debounced: qso:logged fires twice per contact (once on insert, once when
// the award refs are materialised), and a contest run fires it every few
// seconds.
useEffect(() => { useEffect(() => {
let t: number | undefined; let t: number | undefined;
const ping = () => { if (t) window.clearTimeout(t); t = window.setTimeout(() => { refresh(); }, 400); }; const ping = () => { if (t) window.clearTimeout(t); t = window.setTimeout(() => { refresh(); }, 400); };
const offLogged = EventsOn('qso:logged', ping);
const offUploaded = EventsOn('extsvc:uploaded', ping); const offUploaded = EventsOn('extsvc:uploaded', ping);
const offDone = EventsOn('qslmgr:done', ping); const offDone = EventsOn('qslmgr:done', ping);
const offEqsl = EventsOn('qsl:sent', ping); const offEqsl = EventsOn('qsl:sent', ping);
return () => { offUploaded(); offDone(); offEqsl(); if (t) window.clearTimeout(t); }; return () => { offLogged(); offUploaded(); offDone(); offEqsl(); if (t) window.clearTimeout(t); };
}, [refresh]); }, [refresh]);
// The backend bulk-recomputed the materialised award_refs (an award definition // The backend bulk-recomputed the materialised award_refs (an award definition