feat: backfill name/QTH/grid/address from the last QSO when lookup finds nothing
When a callsign resolves only to cty.dat (not on QRZ/HamQTH, or no lookup service configured) — or the lookup errors — enrich the entry from the most recent QSO already in the log with that call. Fills ONLY the fields the provider left empty and the operator hasn't edited (name, QTH, grid, country, address, state, county, lat/lon, zones, continent, email, QSL-via), so a real QRZ/HamQTH hit is never overridden. Uses the worked-before entries (qso_date DESC, [0] = latest) via a live ref so it works regardless of the lookup/worked-before debounce ordering.
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
"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.",
|
||||
@@ -18,6 +19,7 @@
|
||||
"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.",
|
||||
|
||||
@@ -1442,6 +1442,10 @@ export default function App() {
|
||||
// re-populating a field the operator just cleared.
|
||||
const lookupGenRef = useRef(0);
|
||||
const [wb, setWb] = useState<WB | null>(null);
|
||||
// Live mirror of `wb` so the lookup fallback can read the latest worked-before
|
||||
// entries synchronously (the two run on separate debounce timers).
|
||||
const wbRef = useRef<WB | null>(null);
|
||||
useEffect(() => { wbRef.current = wb; }, [wb]);
|
||||
const [wbBusy, setWbBusy] = useState(false);
|
||||
|
||||
// Per-award columns for the Recent QSOs / Worked-before grids: load the award
|
||||
@@ -2854,6 +2858,37 @@ export default function App() {
|
||||
catch { setWb(null); }
|
||||
finally { setWbBusy(false); }
|
||||
}
|
||||
// fillFromLastQso enriches the entry from the LAST QSO we logged with this call
|
||||
// when the live lookup came up short — the callsign isn't on QRZ/HamQTH, or no
|
||||
// lookup service is configured (cty.dat then gives country/zones only). It fills
|
||||
// ONLY the fields the provider left empty and the operator hasn't edited, so a
|
||||
// real QRZ/HamQTH hit is never overridden. `r` is the provider result (omitted
|
||||
// on a lookup error, where every provider field counts as empty).
|
||||
function fillFromLastQso(r?: any) {
|
||||
const last: any = wbRef.current?.entries?.[0]; // entries are qso_date DESC → most recent
|
||||
if (!last) return;
|
||||
const ue = userEditedRef.current;
|
||||
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);
|
||||
}
|
||||
async function runLookup(call: string) {
|
||||
if (call !== lastLookedUpRef.current) resetAutoFill();
|
||||
const gen = lookupGenRef.current; // invalidated by ESC / resetEntry
|
||||
@@ -2901,6 +2936,9 @@ export default function App() {
|
||||
email: d.email || (r.email ?? ''),
|
||||
qsl_via: d.qsl_via || (r.qsl_via ?? ''),
|
||||
}));
|
||||
// Backfill anything the provider didn't supply from the last time we worked
|
||||
// this call (call not found on QRZ/HamQTH, or lookup off → cty.dat only).
|
||||
fillFromLastQso(r);
|
||||
if (r.dxcc && r.dxcc > 0) runWorkedBefore(call, r.dxcc);
|
||||
// The DX location is now known (grid set above) — force the world map to
|
||||
// auto-zoom right away, so it doesn't lag behind the resolved QSO.
|
||||
@@ -2920,6 +2958,8 @@ export default function App() {
|
||||
if (gen === lookupGenRef.current && call === callsignValRef.current.trim().toUpperCase()) {
|
||||
setLookupResult(null);
|
||||
setLookupError(String(e?.message ?? e));
|
||||
// Lookup failed outright — still borrow from the last logged QSO.
|
||||
fillFromLastQso();
|
||||
}
|
||||
} finally {
|
||||
// Only clear the spinner if we're still the current lookup — a newer one
|
||||
|
||||
Reference in New Issue
Block a user