feat: day batch — upload results surfaced as toasts (silent LoTW failures from the right-click send looked like nothing was sent); live selection count + Select all/Unselect all toggle in the grid toolbar; active filters bypass the row limit (all matches shown, 10k safety cap); NET Control: same right-click menu on worked-before + drag&drop roster<->on-air (drop on roster logs the QSO); compact mode restores previous window size/position; advanced-filter field renamed Station callsign; optional DXCC-style digital-mode grouping (Settings->General) for matrix badges + cluster colouring; changelog 0.20.9 entries (version NOT bumped)

This commit is contained in:
2026-07-22 15:38:08 +02:00
parent 5aac28f564
commit 4c75680689
10 changed files with 284 additions and 39 deletions
+19 -1
View File
@@ -1371,6 +1371,17 @@ func modeClass(mode string) string {
}
}
// GroupDigitalMode collapses every digital mode into the single "DIG" bucket
// (FT8, FT4, RTTY, PSK… all become one mode) while leaving phone modes and CW
// untouched. Used as the optional slot normaliser when the operator prefers
// DXCC-style mode classes over per-mode slots (Settings → General).
func GroupDigitalMode(mode string) string {
if modeClass(mode) == "DIG" {
return "DIG"
}
return strings.ToUpper(mode)
}
// BandMode is a (band, mode) pair used for the NEW SLOT check.
type BandMode struct {
Band string `json:"band"`
@@ -1802,7 +1813,11 @@ type EntitySlot struct {
// 0 (unresolvable) skips the QSO.
//
// One DB scan regardless of input size. Cheap to call per cluster batch.
func (r *Repo) EntitySlotMap(ctx context.Context, keyFor func(call string, storedDXCC int, country string) int) (map[int]*EntitySlot, error) {
//
// normMode (nil = identity) maps each QSO's mode before it is stored as a
// Modes/Slots key — pass GroupDigitalMode to collapse all digital modes into
// one bucket. Callers must normalise their lookup mode the same way.
func (r *Repo) EntitySlotMap(ctx context.Context, keyFor func(call string, storedDXCC int, country string) int, normMode func(string) string) (map[int]*EntitySlot, error) {
rows, err := r.db.QueryContext(ctx,
`SELECT callsign, coalesce(dxcc,0), lower(coalesce(country,'')), lower(band), upper(mode) FROM qso
WHERE band IS NOT NULL AND band != ''
@@ -1827,6 +1842,9 @@ func (r *Repo) EntitySlotMap(ctx context.Context, keyFor func(call string, store
if key == 0 {
continue
}
if normMode != nil {
mode = normMode(mode)
}
e, ok := out[key]
if !ok {
e = &EntitySlot{