feat(hamlog): confirmation defaults and grid columns; drop a settings paragraph

Preferences → Confirmations gains a HAMLOG.online row, sent and received, with
the same defaults every other service has: a new QSO can start at N so the
backlog is meaningful from the first contact. The defaults reach it through a
new setExtraDefault — fill() writes through a pointer to a string field and
these two live in the extras map, which no pointer reaches.

The QSO grid gains four HAMLOG columns (status and date, both directions),
hidden by default: a column per service shown to everyone is how a grid becomes
unreadable.

And the paragraph explaining what "Sent" means is gone from that page.
This commit is contained in:
2026-08-23 13:25:20 +02:00
parent f8fdfab031
commit 086581851b
6 changed files with 68 additions and 9 deletions
+34
View File
@@ -344,6 +344,8 @@ const (
keyQSLDefaultHRDLogStatus = "qsl.hrdlog_status"
keyQSLDefaultQRZComStatus = "qsl.qrzcom_status"
keyQSLDefaultQRZComCfm = "qsl.qrzcom_confirmed"
keyQSLDefaultHamlogStatus = "qsl.hamlog_status"
keyQSLDefaultHamlogCfm = "qsl.hamlog_confirmed"
// External services (logbook upload). QRZ.com first; Clublog / LoTW
// will add their own keys under the same extsvc.* prefix.
@@ -420,6 +422,12 @@ type QSLDefaults struct {
HRDLogStatus string `json:"hrdlog_status"`
QRZComStatus string `json:"qrzcom_status"`
QRZComCfm string `json:"qrzcom_confirmed"` // QRZ.com download/confirmed status
// HAMLOG.online lives in the ADIF extras rather than a column — the standard
// names a field for hamlog.EU and none for hamlog.ONLINE — but a default is
// a default: an operator who uploads everything wants new contacts to start
// at "N" here exactly as they do for Club Log.
HamlogStatus string `json:"hamlog_status"`
HamlogCfm string `json:"hamlog_confirmed"`
}
// CATSettings is the user-tweakable rig-control configuration. Stored as
@@ -10521,6 +10529,7 @@ func (a *App) GetQSLDefaults() (QSLDefaults, error) {
keyQSLDefaultEQSLSent, keyQSLDefaultEQSLRcvd,
keyQSLDefaultClublogStatus, keyQSLDefaultHRDLogStatus,
keyQSLDefaultQRZComStatus, keyQSLDefaultQRZComCfm,
keyQSLDefaultHamlogStatus, keyQSLDefaultHamlogCfm,
)
if err != nil {
return out, err
@@ -10535,6 +10544,8 @@ func (a *App) GetQSLDefaults() (QSLDefaults, error) {
out.HRDLogStatus = m[keyQSLDefaultHRDLogStatus]
out.QRZComStatus = m[keyQSLDefaultQRZComStatus]
out.QRZComCfm = m[keyQSLDefaultQRZComCfm]
out.HamlogStatus = m[keyQSLDefaultHamlogStatus]
out.HamlogCfm = m[keyQSLDefaultHamlogCfm]
return out, nil
}
@@ -10556,6 +10567,8 @@ func (a *App) SaveQSLDefaults(d QSLDefaults) error {
keyQSLDefaultHRDLogStatus: strings.ToUpper(strings.TrimSpace(d.HRDLogStatus)),
keyQSLDefaultQRZComStatus: strings.ToUpper(strings.TrimSpace(d.QRZComStatus)),
keyQSLDefaultQRZComCfm: strings.ToUpper(strings.TrimSpace(d.QRZComCfm)),
keyQSLDefaultHamlogStatus: strings.ToUpper(strings.TrimSpace(d.HamlogStatus)),
keyQSLDefaultHamlogCfm: strings.ToUpper(strings.TrimSpace(d.HamlogCfm)),
} {
if err := a.settings.Set(a.ctx, scope+k, v); err != nil {
return err
@@ -10606,6 +10619,27 @@ func applyQSLDefaultsTo(q *qso.QSO, d QSLDefaults) {
fill(&q.HRDLogUploadStatus, d.HRDLogStatus)
fill(&q.QRZComUploadStatus, d.QRZComStatus)
fill(&q.QRZComDownloadStatus, d.QRZComCfm)
// The extras, which fill() cannot reach: it writes through a pointer to a
// string field, and these two are map entries. Only set when the QSO does not
// already carry them, which is the same rule fill() applies.
setExtraDefault(q, hamlogSentKey, d.HamlogStatus)
setExtraDefault(q, award.HamlogQSLKey, d.HamlogCfm)
}
// setExtraDefault writes a default into an ADIF extras key, leaving an existing
// value alone. A blank default means "say nothing", not "write an empty string":
// an empty extra would be carried into every export.
func setExtraDefault(q *qso.QSO, key, def string) {
def = strings.ToUpper(strings.TrimSpace(def))
if def == "" {
return
}
if q.Extras == nil {
q.Extras = map[string]string{}
}
if strings.TrimSpace(q.Extras[key]) == "" {
q.Extras[key] = def
}
}
// ── External services (logbook upload) ─────────────────────────────────