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:
@@ -1895,6 +1895,32 @@ func (r *Repo) Count(ctx context.Context) (int64, error) {
|
||||
return n, err
|
||||
}
|
||||
|
||||
// LastQSOTime returns the start time of the most recently LOGGED QSO for an
|
||||
// operator (highest id wins) — used to seed the live "on air" state at launch so an
|
||||
// operator who just worked someone before (re)starting OpsLog shows online right
|
||||
// away instead of waiting for their next QSO. Empty operator matches every QSO.
|
||||
func (r *Repo) LastQSOTime(ctx context.Context, operator string) (time.Time, bool) {
|
||||
rows, err := r.db.QueryContext(ctx, `SELECT operator, qso_date FROM qso ORDER BY id DESC LIMIT 400`)
|
||||
if err != nil {
|
||||
return time.Time{}, false
|
||||
}
|
||||
defer rows.Close()
|
||||
opFilter := strings.ToUpper(strings.TrimSpace(operator))
|
||||
for rows.Next() {
|
||||
var oper, dateStr sql.NullString
|
||||
if err := rows.Scan(&oper, &dateStr); err != nil {
|
||||
return time.Time{}, false
|
||||
}
|
||||
if strings.ToUpper(strings.TrimSpace(oper.String)) != opFilter {
|
||||
continue
|
||||
}
|
||||
if t := parseTimeLoose(dateStr.String).UTC(); !t.IsZero() {
|
||||
return t, true
|
||||
}
|
||||
}
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
// RecentRate counts QSOs whose start time falls within each trailing window from
|
||||
// `now` — the live "QSO rate" meter shown in the header. When operator is non-empty
|
||||
// (multi-op on a shared logbook) only that operator's QSOs are counted, so each op
|
||||
|
||||
Reference in New Issue
Block a user