fix(qsl): cancel an in-flight confirmation download on close / new download
The LoTW/QRZ confirmation download ran on the app-lifetime context, so it kept going after the QSL Manager was closed and a new download didn't stop the previous one. A slow QRZ sync therefore bled its "flag cleared" log into a freshly started LoTW download — the operator saw QRZ activity during a LoTW run. Now each download gets a cancellable context: starting one cancels the previous, closing the window cancels it (CancelConfirmations), and the parse loops bail promptly on cancel. (A LoTW http 503 is separate — ARRL's server.)
This commit is contained in:
@@ -622,6 +622,12 @@ type App struct {
|
||||
dvkRecSlot int // slot currently being recorded (DVKStartRecord → DVKStopRecord)
|
||||
dvkPttKeyed bool // we keyed PTT for a voice message; unkey when it ends
|
||||
pttMu sync.Mutex
|
||||
// confDLMu/confDLCancel cancel the in-flight confirmation download (LoTW/QRZ)
|
||||
// so closing the QSL Manager — or starting another download — stops the previous
|
||||
// one instead of leaving it running against the app-lifetime context (which made
|
||||
// a still-running QRZ sync bleed its log into a freshly started LoTW download).
|
||||
confDLMu sync.Mutex
|
||||
confDLCancel context.CancelFunc
|
||||
udpLogMu sync.Mutex // serialises UDP auto-log so concurrent packets can't both pass the dedup check
|
||||
adifMonMu sync.Mutex // guards the ADIF-monitor config (file list + per-file read offsets)
|
||||
relayAutoMu sync.Mutex // serialises relay auto-control evaluation
|
||||
@@ -10180,13 +10186,35 @@ func (a *App) DownloadConfirmations(service string, addNotFound bool, since stri
|
||||
}
|
||||
svc := extsvc.Service(service)
|
||||
cfg := a.loadExternalServices()
|
||||
go a.runDownloadConfirmations(svc, cfg, addNotFound, since)
|
||||
// Cancel any download still running (a slow QRZ sync, say) before starting this
|
||||
// one, so the two don't run at once and interleave their logs — and derive a
|
||||
// cancellable context so closing the QSL Manager can stop it too.
|
||||
a.confDLMu.Lock()
|
||||
if a.confDLCancel != nil {
|
||||
a.confDLCancel()
|
||||
}
|
||||
ctx, cancel := context.WithCancel(a.ctx)
|
||||
a.confDLCancel = cancel
|
||||
a.confDLMu.Unlock()
|
||||
go a.runDownloadConfirmations(ctx, svc, cfg, addNotFound, since)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) runDownloadConfirmations(svc extsvc.Service, cfg extsvc.ExternalServices, addNotFound bool, since string) {
|
||||
// CancelConfirmations stops the in-flight LoTW/QRZ download — the QSL Manager
|
||||
// calls it when the window closes, so work doesn't keep running in the background.
|
||||
func (a *App) CancelConfirmations() {
|
||||
a.confDLMu.Lock()
|
||||
if a.confDLCancel != nil {
|
||||
a.confDLCancel()
|
||||
a.confDLCancel = nil
|
||||
}
|
||||
a.confDLMu.Unlock()
|
||||
}
|
||||
|
||||
func (a *App) runDownloadConfirmations(ctx context.Context, svc extsvc.Service, cfg extsvc.ExternalServices, addNotFound bool, since string) {
|
||||
emit := func(line string) {
|
||||
if a.ctx != nil {
|
||||
// Don't keep logging into a closed/cancelled window.
|
||||
if a.ctx != nil && ctx.Err() == nil {
|
||||
wruntime.EventsEmit(a.ctx, "qslmgr:log", line)
|
||||
}
|
||||
}
|
||||
@@ -10195,7 +10223,6 @@ func (a *App) runDownloadConfirmations(svc extsvc.Service, cfg extsvc.ExternalSe
|
||||
wruntime.EventsEmit(a.ctx, "qslmgr:done", map[string]any{"uploaded": matched, "total": total})
|
||||
}
|
||||
}
|
||||
ctx := a.ctx
|
||||
matched, total, added := 0, 0, 0
|
||||
|
||||
// resolveSince turns the UI's request into a concrete date (or ""):
|
||||
@@ -10247,6 +10274,9 @@ func (a *App) runDownloadConfirmations(svc extsvc.Service, cfg extsvc.ExternalSe
|
||||
var items []ConfirmationItem
|
||||
var unmatched []string
|
||||
perr := adif.Parse(strings.NewReader(adifText), func(rec adif.Record) error {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err() // window closed / superseded
|
||||
}
|
||||
q, ok := adif.RecordToQSO(rec)
|
||||
if !ok {
|
||||
return nil
|
||||
@@ -10429,6 +10459,9 @@ func (a *App) runDownloadConfirmations(svc extsvc.Service, cfg extsvc.ExternalSe
|
||||
// QRZ FETCH returns headerless ADIF (no <EOH>); prepend one so the
|
||||
// parser treats the stream as records.
|
||||
perr := adif.Parse(strings.NewReader("<EOH>\n"+adifText), func(rec adif.Record) error {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err() // window closed / superseded — stop clearing flags
|
||||
}
|
||||
parsed++
|
||||
for k := range rec {
|
||||
allKeys[k] = true
|
||||
@@ -10598,6 +10631,9 @@ func (a *App) runDownloadConfirmations(svc extsvc.Service, cfg extsvc.ExternalSe
|
||||
var items []ConfirmationItem
|
||||
var unmatched []string
|
||||
perr := adif.Parse(strings.NewReader(adifText), func(rec adif.Record) error {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err() // window closed / superseded
|
||||
}
|
||||
q, ok := adif.RecordToQSO(rec)
|
||||
if !ok {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user