feat(matrix): mark the slots already worked with the callsign in hand

The worked-before cell answered one question with two facts fused into it.
bandStatusCode takes the highest of them, so a confirmed entity outranks a
worked callsign — right for awards, wrong while chasing: a slot worked with the
DXpedition this morning reads "entity confirmed" and says nothing about it.

BandStatus now carries the callsign's own answer alongside the entity's, and the
cell draws a dot for it. The dot is deliberately identical everywhere — same
shape, same colour, over all five backgrounds — so it is read as one thing
rather than as five variants of the colour underneath it.
This commit is contained in:
2026-08-27 08:55:37 +02:00
parent 32dbfbd04e
commit 965ed4c792
5 changed files with 81 additions and 11 deletions
+25
View File
@@ -1901,10 +1901,24 @@ type WorkedBefore struct {
}
// BandStatus is one cell in the worked-before grid.
//
// Status is the single highest thing true of the cell, which is what colours
// it. Call is the SAME cell's answer to a different question — "have I worked
// THIS callsign here" — kept separately because the two are asked at the same
// moment and one was hiding the other.
//
// Chasing an expedition, an operator needs both: whether the slot is still
// missing for the entity (does this fill a DXCC hole) and whether this
// expedition has already been worked on it (would this be a dupe). A confirmed
// entity outranks a worked callsign in Status — correctly, for awards — so a
// slot worked with the DX yesterday can read "entity confirmed" and say nothing
// at all about yesterday.
type BandStatus struct {
Band string `json:"band"` // ADIF lowercase band, e.g. "20m"
Class string `json:"class"` // "PH" | "CW" | "DIG"
Status string `json:"status"` // "call_c" | "call_w" | "dxcc_c" | "dxcc_w"
// Call is "", "w" (worked with this callsign) or "c" (confirmed with it).
Call string `json:"call,omitempty"`
}
// Band-status codes, lowest first. The ORDER is the rule: a cell shows the
@@ -2243,6 +2257,8 @@ func (r *Repo) WorkedBefore(ctx context.Context, callsign string, dxccHint int,
}
type cellKey struct{ band, class string }
best := map[cellKey]int{}
// The call's own answer per cell, independent of the ladder above.
callByCell := map[cellKey]string{}
for statusRows.Next() {
var band, mode string
var callW, callC, dxccConfirmed int
@@ -2255,12 +2271,21 @@ func (r *Repo) WorkedBefore(ctx context.Context, callsign string, dxccHint int,
if cur, ok := best[k]; !ok || code > cur {
best[k] = code
}
// Confirmed beats worked here too, and neither is ever erased by the
// entity: this is only ever about the callsign.
switch {
case callC == 1:
callByCell[k] = "c"
case callW == 1 && callByCell[k] == "":
callByCell[k] = "w"
}
}
statusRows.Close()
codeStr := bandStatusNames
for k, code := range best {
wb.BandStatus = append(wb.BandStatus, BandStatus{
Band: k.band, Class: k.class, Status: codeStr[code],
Call: callByCell[k],
})
}
return wb, nil