From 7e08553e6ee02321b9f2842b29d3ba86ea7f66c8 Mon Sep 17 00:00:00 2001 From: rouggy Date: Sat, 25 Jul 2026 15:54:25 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20prefer=20the=20last=20QSO's=20precise?= =?UTF-8?q?=20locator=20over=20the=20cty.dat=20centroid;=20changelog=20?= =?UTF-8?q?=E2=86=92=200.21.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For an entity resolved only via cty.dat (e.g. a French call not on QRZ/HamQTH), the provider block sets a coarse country-centroid grid. The worked-before backfill now overrides that with the precise locator from the last QSO with this call, and takes that QSO's lat/lon (derived from the grid when the record stored none) so the map and saved record stay consistent. A real provider grid still wins. Moved the (unreleased) worked-before backfill entry into a new 0.21.2 changelog block, since 0.21.1 is already out. --- changelog.json | 12 +++++++++-- frontend/src/App.tsx | 47 +++++++++++++++++++++++++++++--------------- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/changelog.json b/changelog.json index 673eb08..da1af08 100644 --- a/changelog.json +++ b/changelog.json @@ -1,9 +1,18 @@ [ + { + "version": "0.21.2", + "date": "2026-07-25", + "en": [ + "When a callsign isn't found on QRZ/HamQTH (or you don't use a lookup service), the name, QTH, locator and address are recovered from the last time you worked that station — and the precise locator from that QSO is used instead of the coarse cty.dat country centroid. A real QRZ/HamQTH hit still wins." + ], + "fr": [ + "Quand un indicatif est introuvable sur QRZ/HamQTH (ou si tu n'utilises pas de service de lookup), le nom, le QTH, le locator et l'adresse sont récupérés du dernier QSO avec cette station — et c'est le locator précis de ce QSO qui est utilisé, pas le centroïde du pays de cty.dat. Un vrai résultat QRZ/HamQTH reste prioritaire." + ] + }, { "version": "0.21.1", "date": "2026-07-24", "en": [ - "When a callsign isn't found on QRZ/HamQTH (or you don't use a lookup service), the name, QTH, grid and address are now recovered from the last time you worked that station — provider data still wins when there is a hit.", "Ctrl + mouse wheel zoom is now remembered across restarts (Ctrl+0 resets to 100%).", "Log grid: award column widths are now saved like the other columns, so a width you set survives a restart.", "CW keyer widget: added an F9 macro slot, and empty macros are now hidden (like the voice keyer) — fill them in Settings → CW Keyer.", @@ -19,7 +28,6 @@ "Fixed award references (in the entry strip's Awards tab) not clearing when the callsign changes — clicking one spot then another no longer keeps the previous station's references." ], "fr": [ - "Quand un indicatif est introuvable sur QRZ/HamQTH (ou si tu n'utilises pas de service de lookup), le nom, le QTH, le locator et l'adresse sont maintenant récupérés du dernier QSO avec cette station — les données du service restent prioritaires en cas de résultat.", "Le zoom Ctrl + molette est maintenant conservé après un redémarrage (Ctrl+0 remet à 100 %).", "Grille du log : les largeurs des colonnes de diplômes sont désormais sauvegardées comme les autres colonnes, une largeur réglée survit au redémarrage.", "Widget keyer CW : ajout d'un emplacement de macro F9, et les macros vides sont maintenant masquées (comme le keyer vocal) — remplis-les dans Réglages → Keyer CW.", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index f7bc4d7..3348e09 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -2871,23 +2871,38 @@ export default function App() { const empty = (v: any) => (v ?? '') === ''; if (!ue.has('name') && empty(r?.name) && last.name) setName(last.name); if (!ue.has('qth') && empty(r?.qth) && last.qth) setQth(last.qth); - if (!ue.has('grid') && empty(r?.grid) && !(r?.lat || r?.lon) && last.grid) setGrid(last.grid); if (!ue.has('country') && empty(r?.country) && last.country) setCountry(last.country); - setDetails((d) => ({ - ...d, - address: d.address || last.address || '', - state: d.state || last.state || '', - cnty: d.cnty || last.cnty || '', - lat: d.lat ?? (last.lat ?? undefined), - lon: d.lon ?? (last.lon ?? undefined), - dxcc: d.dxcc ?? (last.dxcc || undefined), - cqz: d.cqz ?? (last.cqz || undefined), - ituz: d.ituz ?? (last.ituz || undefined), - cont: d.cont || last.cont || '', - email: d.email || last.email || '', - qsl_via: d.qsl_via || last.qsl_via || '', - })); - if (last.grid || last.lat) setMapZoomSignal((n) => n + 1); + // Grid: a REAL provider grid always wins. Otherwise the last QSO's precise + // locator beats the coarse cty.dat entity centroid the provider block set — so + // a French call resolves to its real JNxx, not the country's JN16 centroid. + const adoptLastGrid = !ue.has('grid') && empty(r?.grid) && !!last.grid; + if (adoptLastGrid) setGrid(last.grid); + setDetails((d) => { + // When we adopt the last QSO's locator, take its coordinates too (derive them + // from the grid if that QSO didn't store any) so the map + saved record match. + let lat = d.lat, lon = d.lon; + if (adoptLastGrid) { + if (last.lat != null && last.lon != null) { lat = last.lat; lon = last.lon; } + else { const ll = gridToLatLon(last.grid); if (ll) { lat = ll.lat; lon = ll.lon; } } + } else { + lat = d.lat ?? (last.lat ?? undefined); + lon = d.lon ?? (last.lon ?? undefined); + } + return { + ...d, + address: d.address || last.address || '', + state: d.state || last.state || '', + cnty: d.cnty || last.cnty || '', + lat, lon, + dxcc: d.dxcc ?? (last.dxcc || undefined), + cqz: d.cqz ?? (last.cqz || undefined), + ituz: d.ituz ?? (last.ituz || undefined), + cont: d.cont || last.cont || '', + email: d.email || last.email || '', + qsl_via: d.qsl_via || last.qsl_via || '', + }; + }); + if (adoptLastGrid || last.grid || last.lat) setMapZoomSignal((n) => n + 1); } async function runLookup(call: string) { if (call !== lastLookedUpRef.current) resetAutoFill();