feat(appearance): colour log rows by QSL status, and fix UDP QSO numbering
New Settings → Appearance section: whole-row colouring in the log grid driven by QSL / LoTW state, each rule with its own colour from a palette or a free picker. What Logger32 does, with one difference that matters — the colour is applied as a 24% tint, not a fill. Logger32's grid is white; this one is dark, and a saturated user-picked colour behind white text is unreadable at exactly the moment the operator is scanning for what still needs sending. Rules are ORDERED and the first match wins, because a contact is usually several of these at once: one confirmed on LoTW and by card is confirmed, not "sent, awaiting reply". The order lives in the data so the panel can show the rules in the order they actually apply, numbered. The colour is interpolated into a CSS color-mix(), so anything that is not plainly #rrggbb is refused on the way in and falls back to the default. Also fixes the QSO number column, which was empty for contacts logged from WSJT-X: that path inserts through the repo directly and never reached AddQSO. Rather than chase each of the remaining bulk-insert paths — the POTA hunter import and the LoTW/QRZ "add what I was missing" passes, which insert OLD dates and so shift every number after them — the index now checks its length against a COUNT and rebuilds when they disagree. One indexed count beats remembering to invalidate in a place that does not exist yet.
This commit is contained in:
@@ -237,6 +237,7 @@ const (
|
||||
keyMotorTrackMode = "motor.track_mode" // "always" | "step" | "band"
|
||||
keyMotorBandFreqs = "motor.band_freqs" // per-band tune frequency: "40m=7100,20m=14150"
|
||||
keyChaseNewGrids = "cluster.chase_grids" // "1" → persist learnt locators across restarts
|
||||
keyRowColors = "appearance.row_colors"
|
||||
keyMotorType = "ultrabeam.type" // "ultrabeam" | "steppir" (default ultrabeam)
|
||||
keyMotorTransport = "ultrabeam.transport" // "tcp" | "serial" (default tcp)
|
||||
keyMotorCOM = "ultrabeam.com" // serial device name (COM3, /dev/ttyUSB0)
|
||||
@@ -5831,13 +5832,34 @@ func (a *App) stampQSONumbers(list []qso.QSO) {
|
||||
}
|
||||
}
|
||||
|
||||
// countQSOForNumbering is Count with the nil-repo guard the numbering needs —
|
||||
// it runs before the database is up on a fresh start.
|
||||
func (a *App) countQSOForNumbering() (int64, error) {
|
||||
if a.qso == nil {
|
||||
return 0, fmt.Errorf("db not initialized")
|
||||
}
|
||||
return a.qso.Count(a.ctx)
|
||||
}
|
||||
|
||||
// qsoNumberIndex returns id → chronological position, building it once and
|
||||
// keeping it until the log changes (invalidateAwardStats drops it).
|
||||
func (a *App) qsoNumberIndex() map[int64]int {
|
||||
a.qsoNumMu.Lock()
|
||||
defer a.qsoNumMu.Unlock()
|
||||
if a.qsoNumbers != nil {
|
||||
return a.qsoNumbers
|
||||
// Cheap consistency check rather than trusting every insert path to have
|
||||
// remembered to invalidate. Several bulk paths — the POTA hunter import,
|
||||
// the LoTW and QRZ "add contacts I was missing" passes — insert straight
|
||||
// through the repo, and they add contacts with OLD dates, which lands them
|
||||
// in the MIDDLE of the order and shifts every number after them. A stale
|
||||
// map there is not merely incomplete, it is wrong.
|
||||
//
|
||||
// One indexed COUNT against a map length, versus rereading every id.
|
||||
if n, err := a.countQSOForNumbering(); err == nil && int(n) != len(a.qsoNumbers) {
|
||||
a.qsoNumbers = nil
|
||||
} else {
|
||||
return a.qsoNumbers
|
||||
}
|
||||
}
|
||||
if a.qso == nil {
|
||||
return nil
|
||||
@@ -11991,6 +12013,10 @@ func (a *App) LogUDPLoggedADIF(adifText string) (int64, error) {
|
||||
return 0, fmt.Errorf("insert qso: %w", err)
|
||||
}
|
||||
q.ID = id
|
||||
// Same as the manual path: give the contact its number without rereading the
|
||||
// log. This insert bypasses AddQSO entirely, which is why UDP-logged contacts
|
||||
// came out unnumbered while hand-logged ones did not.
|
||||
a.noteQSONumbered(id, q.QSODate)
|
||||
a.noteLiveQSO() // multi-op: flip this operator back "online" (publishes async)
|
||||
// Announce the log AT ONCE so the grid / ON-AIR badge / stations-on-air widget
|
||||
// refresh immediately, then run the DB-heavy enrichment off the critical path
|
||||
|
||||
Reference in New Issue
Block a user