fix(chase-new): one badge, filters, a way in and a way out — and the frequency

Four things from an operator's first look at the panel, and one of them was
mine.

The frequency column showed "—" on every row. pskr.Spot.FreqHz was declared
and documented in one edit and never assigned in the next, so the payload's
"f" was parsed and dropped. A click therefore filled the callsign and left
the rig where it was, which is half the point of the panel. Assigned, with a
test that runs a real payload through and checks the value survives into the
Spot — the field being declared is what made it look done.

A row now carries ONE indication instead of stacking them. A station can be a
new band and a new prefix at once; the row says the first that matters, in the
order that would make an operator leave what they are doing: entity, band,
mode, slot, then prefix, then square. Two badges on one line made the list
unreadable at a glance, which is the only thing it is for.

Each category has a filter chip in the header, in its own colour, remembered
across sessions. A toolbar button shows the panel and a cross closes it — the
same split the Super Check Partial panel uses, where the setting decides
whether the feature exists and the button whether it is on screen.

The panel is wider and the country column gets what is left, which is more
than it was now that a row carries one badge.
This commit is contained in:
2026-08-14 15:33:15 +02:00
parent e67f57fee7
commit 9b1faada38
6 changed files with 175 additions and 42 deletions
+1
View File
@@ -272,6 +272,7 @@ func (w *Watcher) handle(_ mqtt.Client, m mqtt.Message) {
s := Spot{
Call: call, Band: strings.ToLower(strings.TrimSpace(p.Band)),
Mode: strings.ToUpper(strings.TrimSpace(p.Mode)), Grid: grid[:4],
FreqHz: p.Freq,
DistKm: dist, Bearing: brg,
// Stamped on receipt: the broker's own timestamps vary between payload
// versions, and the window this feeds is measured in minutes.
+29
View File
@@ -0,0 +1,29 @@
package pskr
import (
"encoding/json"
"testing"
)
// The frequency has to survive from the payload to the Spot: the Chase New
// panel is only useful if a click can put the rig on the station, and a row
// showing "—" instead of 14074.0 is a row nobody can act on.
func TestWirePayloadCarriesFrequency(t *testing.T) {
const payload = `{"sq":1,"f":14074123,"md":"FT8","rp":-12,"sc":"VK9XX","sl":"QH30","rc":"F4BPO","rl":"IN95","b":"20m"}`
var p wire
if err := json.Unmarshal([]byte(payload), &p); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if p.Freq != 14074123 {
t.Errorf("Freq = %d, want 14074123", p.Freq)
}
if p.TxCall != "VK9XX" || p.Band != "20m" || p.Mode != "FT8" {
t.Errorf("unexpected decode: %+v", p)
}
// And the Spot the watcher builds must carry it — this is the field that was
// declared, documented, and then never assigned.
s := Spot{Call: p.TxCall, Band: p.Band, Mode: p.Mode, FreqHz: p.Freq}
if s.FreqHz != p.Freq {
t.Errorf("Spot.FreqHz = %d, want %d", s.FreqHz, p.Freq)
}
}