diff --git a/app.go b/app.go index a838173..abb2f2d 100644 --- a/app.go +++ b/app.go @@ -19717,10 +19717,11 @@ type SpotStatus struct { // that already holds the pointer keeps reading valid (stale-by-one-log) data // while a newer snapshot is being built. See clusterStatusMaps. type clusterStatusCache struct { - entities map[int]*qso.EntitySlot - workedCalls map[string]struct{} - workedCallSlots map[string]struct{} // nil unless the "same slot" option is on - workedCounties map[string]struct{} + entities map[int]*qso.EntitySlot + workedCalls map[string]struct{} + workedCallSlots map[string]struct{} + workedCallSlotsDig map[string]struct{} // same set, digital modes folded to DIG // nil unless the "same slot" option is on + workedCounties map[string]struct{} // callCounties holds callsign → "STATE,County" for stations already logged // with a county, so a spot shows the county the entry panel showed rather // than the one derived from the licence ZIP. See qso.CallCounties. @@ -19807,6 +19808,15 @@ func (a *App) clusterStatusMaps() *clusterStatusCache { // needs this set whatever the cluster's own display options say, and the // scan already runs at cache-rebuild cadence, not per batch. c.workedCallSlots, _ = a.qso.WorkedCallSlotKeys(a.ctx, c.normMode) + // And once more at digital-CLASS grain, for the spots whose mode the band + // plan can only call "DATA": a generic mode cannot be matched exactly, so it + // is judged against "any digital mode of this call on this band". When the + // grouping option is on, workedCallSlots is already at that grain. + if c.normMode != nil { + c.workedCallSlotsDig = c.workedCallSlots + } else { + c.workedCallSlotsDig, _ = a.qso.WorkedCallSlotKeys(a.ctx, qso.GroupDigitalMode) + } // Orthogonal dimensions: worked US counties (for the ULS callsign→county // lookup) and worked POTA parks. c.workedCounties, _ = a.qso.WorkedCountyKeys(a.ctx, award.USCountyKey) diff --git a/app_watchlist.go b/app_watchlist.go index e7de6ee..64c9d88 100644 --- a/app_watchlist.go +++ b/app_watchlist.go @@ -11,6 +11,7 @@ import ( "strings" "time" + "hamlog/internal/qso" "hamlog/internal/watchlist" wruntime "github.com/wailsapp/wails/v2/pkg/runtime" @@ -99,6 +100,21 @@ func (a *App) WatchlistWorkedSlots(queries []WatchlistSlotQuery) []bool { } return up + "|" + b } + // A spot whose mode the band plan could only call "DATA" (an F/H DXpedition + // off the standard dials, a comment with no mode) cannot be matched exactly: + // "DATA" is in nobody's log. Such a spot is judged at digital-CLASS grain — + // worked if ANY digital mode of that call is logged on that band. Claiming + // NEW MODE because the label differs from FT8 was the reported bug. + generic := func(m string) bool { + switch strings.ToUpper(strings.TrimSpace(m)) { + case "", "DATA", "DIG", "DIGI", "DIGITAL": + return true + } + return false + } + digKey := func(call, band string) string { + return strings.ToUpper(strings.TrimSpace(call)) + "|" + strings.ToLower(strings.TrimSpace(band)) + "|DIG" + } needToday := false for _, q := range queries { if q.Contest { @@ -113,15 +129,23 @@ func (a *App) WatchlistWorkedSlots(queries []WatchlistSlotQuery) []bool { if err == nil { for _, r := range rows { today[slotKey(r.Callsign, r.Band, r.Mode)] = true + today[digKey(r.Callsign, r.Band)] = qso.ModeClass(r.Mode) == "DIG" || today[digKey(r.Callsign, r.Band)] } } } for i, q := range queries { - key := slotKey(q.Call, q.Band, q.Mode) if q.Contest { - out[i] = today[key] + if generic(q.Mode) { + out[i] = today[digKey(q.Call, q.Band)] + } else { + out[i] = today[slotKey(q.Call, q.Band, q.Mode)] + } + } else if generic(q.Mode) { + if idx.workedCallSlotsDig != nil { + _, out[i] = idx.workedCallSlotsDig[digKey(q.Call, q.Band)] + } } else if idx.workedCallSlots != nil { - _, out[i] = idx.workedCallSlots[key] + _, out[i] = idx.workedCallSlots[slotKey(q.Call, q.Band, q.Mode)] } } return out diff --git a/frontend/src/components/WatchlistTab.tsx b/frontend/src/components/WatchlistTab.tsx index b16607d..f17e16f 100644 --- a/frontend/src/components/WatchlistTab.tsx +++ b/frontend/src/components/WatchlistTab.tsx @@ -179,7 +179,7 @@ export function WatchlistTab({ spots, spotStatus, onSpotSelect, onSpotClick }: P // lines stretched across a 34-inch window are not readable, they are long.