fix: bulk update no longer holds the application hostage, and says what it does
Two faults in the progress bar shipped an hour ago. It was a modal. A hundred QSOs is a minute; a contest log is two thousand and ten minutes, and for all of it the operator could do nothing else — while their radio is on. It is now a card in the corner: same bar, same count, same callsign, none of the imprisonment. And the menu said "Update from QRZ.com" while the code has always walked EVERY configured provider, falling through to HamQTH when QRZ answers not-found. The label is now "Update from the callsign databases", which is what happens. Noted while reading that code, NOT fixed here because it trades against the user's API quota and is their call: this path goes through the lookup cache, so a second run on the same callsigns re-reads the cache rather than reaching the provider. The cache comment already argues the opposite for deliberate clicks — "a lookup the operator asked for by clicking is a deliberate act and must reach the provider" — and a right-click on a selection is exactly that.
This commit is contained in:
+26
-27
@@ -6394,34 +6394,33 @@ export default function App() {
|
||||
</Dialog>
|
||||
)}
|
||||
|
||||
{/* Deliberately NOT a modal. A contest log is thousands of QSOs and one
|
||||
network round trip each, so this runs for ten minutes \u2014 a dialog would
|
||||
hold the whole application hostage for the duration, and the operator
|
||||
has a radio to work. It sits in the corner, above everything, and
|
||||
stays out of the way. */}
|
||||
{bulkProgress && (
|
||||
<Dialog open>
|
||||
<DialogContent className="max-w-sm px-6" hideClose>
|
||||
<DialogHeader className="px-2">
|
||||
<DialogTitle>{t('bulk.progressTitle')}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="px-2 pb-4 space-y-2">
|
||||
{(() => {
|
||||
const { processed, total, call } = bulkProgress;
|
||||
const pct = total > 0 ? Math.min(100, Math.round((processed / total) * 100)) : 0;
|
||||
return (
|
||||
<>
|
||||
<div className="h-2 w-full rounded-full bg-muted overflow-hidden">
|
||||
<div
|
||||
className={cn('h-full bg-primary rounded-full transition-[width] duration-150', total === 0 && 'w-1/3 animate-pulse')}
|
||||
style={total > 0 ? { width: `${pct}%` } : undefined}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-xs text-muted-foreground">
|
||||
<span className="font-mono">{call || '\u00a0'}</span>
|
||||
<span>{total > 0 ? `${processed} / ${total}` : ''}</span>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<div className="fixed bottom-3 right-3 z-50 w-64 rounded-lg border border-border bg-card/95 backdrop-blur shadow-lg px-3 py-2 space-y-1.5">
|
||||
<div className="text-xs font-medium">{t('bulk.progressTitle')}</div>
|
||||
{(() => {
|
||||
const { processed, total, call } = bulkProgress;
|
||||
const pct = total > 0 ? Math.min(100, Math.round((processed / total) * 100)) : 0;
|
||||
return (
|
||||
<>
|
||||
<div className="h-1.5 w-full rounded-full bg-muted overflow-hidden">
|
||||
<div
|
||||
className={cn('h-full bg-primary rounded-full transition-[width] duration-150', total === 0 && 'w-1/3 animate-pulse')}
|
||||
style={total > 0 ? { width: `${pct}%` } : undefined}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-[10px] text-muted-foreground">
|
||||
<span className="font-mono">{call || '\u00a0'}</span>
|
||||
<span className="tabular-nums">{total > 0 ? `${processed} / ${total}` : ''}</span>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{importing && (
|
||||
|
||||
Reference in New Issue
Block a user