fix(sat,rda): the dropdown reads the live list, and the RDA buttons answer

SATELLITES: the field read the list once, when the details panel first
mounted — which is at startup. A list saved in Preferences afterwards
therefore did nothing until a restart. It comes from App now, which
already reloads the lists when Preferences close.

RDA COMPARE: a failed comparison wrote its error into the message beside
the FILL DISTRICTS button, a row above — the error appeared under a
button nobody had pressed while the one that had been pressed showed
nothing, which reads as 'the button does nothing'. It has its own message
now, and reports the two results that look like silence: no disagreement
at all, and no Russian contacts to compare. Both buttons say what they
are doing while they do it, and the comparison logs before it starts
reading — on a remote MySQL that read is seconds of quiet.
This commit is contained in:
2026-08-25 19:53:30 +02:00
parent e41a3a4c1c
commit 67c85be23c
6 changed files with 45 additions and 20 deletions
+6 -11
View File
@@ -1,5 +1,5 @@
import { useEffect, useMemo, useState } from 'react';
import { ComputeQSOAwardRefs, IsNewUSCounty, GetListsSettings } from '../../wailsjs/go/main/App';
import { ComputeQSOAwardRefs, IsNewUSCounty } from '../../wailsjs/go/main/App';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Checkbox } from '@/components/ui/checkbox';
@@ -71,6 +71,10 @@ interface Props {
band: string;
mode: string;
bands?: string[]; // configured bands for the worked-before matrix columns
// The station's satellites, for the SAT_NAME dropdown. Passed in rather than
// read here: the list lives in Preferences, and App already reloads it when
// Preferences close — a panel reading it once at mount would need a restart.
satellites?: string[];
// When the entry form is empty and a QSO is selected in the log grid, the
// Stats (F1) matrix shows THAT contact's entity instead of sitting blank.
// Only the matrix is redirected — every other tab still edits the live entry.
@@ -151,18 +155,9 @@ function Field({ label, span = 1, className, children }: { label: string; span?:
);
}
export function DetailsPanel({ callsign, prefix, operatorGrid, remoteGrid, qth, name, country, comment, note, details, onChange, wb, wbBusy, band, mode, bands, slotCall, slotBand, slotMode, slotWb, slotWbBusy, tab, onTab, keyerActive, onEditQso }: Props) {
export function DetailsPanel({ callsign, prefix, operatorGrid, remoteGrid, qth, name, country, comment, note, details, onChange, wb, wbBusy, band, mode, bands, satellites = [], slotCall, slotBand, slotMode, slotWb, slotWbBusy, tab, onTab, keyerActive, onEditQso }: Props) {
const { t } = useI18n();
const [internalOpen, setInternalOpen] = useState<TabName>('stats');
// The station's satellites, read once. Alphabetical because that is the only
// order a list of birds has, and an operator hunting AO-91 in a list sorted
// by when it was added is worse off than with a plain text box.
const [satellites, setSatellites] = useState<string[]>([]);
useEffect(() => {
GetListsSettings()
.then((l: any) => setSatellites([...((l?.satellites ?? []) as string[])].filter(Boolean).sort()))
.catch(() => {});
}, []);
const open = tab ?? internalOpen; // controlled when `tab` is provided
// Live award detection: run the SAME engine used at log time over the current
+19 -5
View File
@@ -1927,10 +1927,23 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
// operator's own log, and answering it must not modify that log.
const [rdaCmp, setRdaCmp] = useState<any>(null);
const [rdaCmpBusy, setRdaCmpBusy] = useState(false);
// Its own message, next to its own button.
//
// A failed comparison used to write into rdaMsg — which is rendered beside
// the FILL DISTRICTS button, a row above. The error appeared under a button
// nobody had pressed, and the one that had been pressed showed nothing at
// all, which reads as "the button does nothing".
const [rdaCmpMsg, setRdaCmpMsg] = useState<string>('');
const runRDACompare = async () => {
setRdaCmpBusy(true); setRdaCmp(null);
try { setRdaCmp(await CompareRDASources()); }
catch (e: any) { setRdaMsg(String(e?.message ?? e)); }
setRdaCmpBusy(true); setRdaCmp(null); setRdaCmpMsg('');
try {
const r: any = await CompareRDASources();
setRdaCmp(r);
// A comparison that finds nothing is a RESULT, and the most likely one on
// a tidy log. Without a word for it the panel looked like it had not run.
if (!r || (r.scanned ?? 0) === 0) setRdaCmpMsg(t('rda.cmpNoRussian'));
else if ((r.disagree ?? 0) === 0) setRdaCmpMsg(t('rda.cmpNoConflict'));
} catch (e: any) { setRdaCmpMsg(String(e?.message ?? e)); }
finally { setRdaCmpBusy(false); }
};
@@ -7421,7 +7434,7 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
<div className="flex items-center gap-3">
<Button size="sm" variant="secondary" onClick={runRDABackfill} disabled={rdaBusy}>
{rdaBusy ? <Loader2 className="size-3.5 animate-spin mr-1.5" /> : null}
{t('rda.backfillRun')}
{rdaBusy ? t('rda.backfillRunning') : t('rda.backfillRun')}
</Button>
{rdaMsg && <span className="text-xs text-muted-foreground">{rdaMsg}</span>}
</div>
@@ -7435,8 +7448,9 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
<div className="flex items-center gap-3">
<Button size="sm" variant="secondary" onClick={runRDACompare} disabled={rdaCmpBusy}>
{rdaCmpBusy ? <Loader2 className="size-3.5 animate-spin mr-1.5" /> : null}
{t('rda.cmpRun')}
{rdaCmpBusy ? t('rda.cmpRunning') : t('rda.cmpRun')}
</Button>
{!!rdaCmpMsg && <span className="text-xs text-muted-foreground">{rdaCmpMsg}</span>}
{rdaCmp && (
<span className="text-xs text-muted-foreground">
{t('rda.cmpDone', {