feat: live operator status goes offline after 5 min idle, online on log

The multi-op live status heartbeated every 15 s, so an operator who left the
log open but stopped working still showed on air. Now it publishes an explicit
online flag: online when a new contact was logged within the last 5 minutes,
offline otherwise. live_status gains `online` + `last_qso_at` columns (added to
existing tables via ALTER). Logging a QSO (manual, UDP, ADIF monitor) stamps the
time and republishes at once, so they flip back online instantly.

At launch the last-QSO time is seeded from the DB (Repo.LastQSOTime), so an
operator who worked someone just before starting OpsLog is shown online right
away rather than offline until their next contact. LiveLastQSOAgeSec exposes it
to the UI badge.
This commit is contained in:
2026-07-19 18:14:43 +02:00
parent 4ab4f70349
commit 901e967b53
3 changed files with 115 additions and 8 deletions
+3
View File
@@ -505,6 +505,7 @@ type App struct {
liveFreqHz int64 // last freq/band/mode the UI reported (fallback when CAT is off)
liveBand string
liveMode string
liveLastQSOAt time.Time // when this operator last logged a NEW contact — drives online/offline
awardSnapMu sync.Mutex // guards the award QSO snapshot
awardSnap []qso.QSO // light-scanned + enriched logbook snapshot reused across award computations
awardSnapRev string // logbook revision the snapshot was built at ("" = none)
@@ -1871,6 +1872,7 @@ func (a *App) AddQSO(q qso.QSO) (id int64, err error) {
if err == nil {
q.ID = id
a.noteWorked(q.Callsign, q.Band, q.Mode) // keep the alert worked-index fresh
a.noteLiveQSO() // multi-op: flip this operator back "online"
// Announce the log so UI widgets can react (e.g. the Flex panel zeroing RIT).
wruntime.EventsEmit(a.ctx, "qso:logged", id)
a.saveQSORecording(&q)
@@ -9287,6 +9289,7 @@ func (a *App) LogUDPLoggedADIF(adifText string) (int64, error) {
return 0, fmt.Errorf("insert qso: %w", err)
}
q.ID = id
a.noteLiveQSO() // multi-op: flip this operator back "online"
a.saveQSORecording(&q)
if a.extsvc != nil {
a.extsvc.OnQSOLogged(id)