fix: offline operators removed from live_status + award-column visibility
Live status: when an operator goes offline (no QSO in the window) OpsLog now DELETEs its live_status row instead of just flipping online=0, so a status page that lists present/recent rows shows them as gone without having to read the online column — the whole point of the feature. The row reappears on the next QSO. Recent QSOs columns: toggling an award column (e.g. DDFM) rebuilt columnDefs and made AG Grid re-apply every colDef hide default, so hidden non-award columns (QTH/Grid) came back and restoringRef stayed stuck true (saving off). The restore-saved-state effect now also runs on awardShown changes, so the other columns keep the user's visibility.
This commit is contained in:
@@ -377,11 +377,13 @@ export function RecentQSOsGrid({ rows, selectAllSignal, storageKey, onRowDoubleC
|
|||||||
if (state) saveState(colStateKey, stripAwardCols(state));
|
if (state) saveState(colStateKey, stripAwardCols(state));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// The award columns load asynchronously; when they arrive (or change) the
|
// columnDefs is rebuilt whenever the award columns load OR the user toggles an
|
||||||
// columnDefs memo is rebuilt and AG Grid re-applies each colDef's `hide`
|
// award column (both change the memo → restoringRef flips true at line 316). Each
|
||||||
// default — wiping the user's saved visibility (award columns reappear,
|
// rebuild makes AG Grid re-apply every colDef's `hide` default, wiping the user's
|
||||||
// manually-shown ones like LoTW sent vanish). Re-apply the saved state after
|
// saved visibility of the NON-award columns (QTH/Grid reappear, a manually-shown
|
||||||
// every rebuild so the user's choices win. No-op before the grid is ready.
|
// LoTW-sent vanishes). Re-apply the saved (award-stripped) state after EVERY such
|
||||||
|
// rebuild — hence awardShown in the deps, not just awardCols; without it, toggling
|
||||||
|
// an award reset the other columns AND left restoringRef stuck true (saving off).
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const api = gridRef.current?.api;
|
const api = gridRef.current?.api;
|
||||||
const local = loadLocal(colStateKey);
|
const local = loadLocal(colStateKey);
|
||||||
@@ -389,7 +391,7 @@ export function RecentQSOsGrid({ rows, selectAllSignal, storageKey, onRowDoubleC
|
|||||||
// Re-enable saving once AG Grid has settled the column events from the rebuild.
|
// Re-enable saving once AG Grid has settled the column events from the rebuild.
|
||||||
const t = window.setTimeout(() => { restoringRef.current = false; }, 0);
|
const t = window.setTimeout(() => { restoringRef.current = false; }, 0);
|
||||||
return () => window.clearTimeout(t);
|
return () => window.clearTimeout(t);
|
||||||
}, [awardCols]);
|
}, [awardCols, awardShown]);
|
||||||
|
|
||||||
function handleRowDoubleClicked(e: RowDoubleClickedEvent<QSOForm>) {
|
function handleRowDoubleClicked(e: RowDoubleClickedEvent<QSOForm>) {
|
||||||
if (e.data && onRowDoubleClicked) onRowDoubleClicked(e.data);
|
if (e.data && onRowDoubleClicked) onRowDoubleClicked(e.data);
|
||||||
|
|||||||
+17
-13
@@ -202,33 +202,37 @@ func (a *App) publishLiveStatus() {
|
|||||||
}
|
}
|
||||||
a.liveActMu.Unlock()
|
a.liveActMu.Unlock()
|
||||||
lastQSO := a.liveLastQSOTime() // authoritative (in-memory OR shared DB)
|
lastQSO := a.liveLastQSOTime() // authoritative (in-memory OR shared DB)
|
||||||
// Online = a new contact was logged within the window. An operator who leaves
|
// On air = a new contact was logged within the window. An operator who leaves
|
||||||
// the log open but stops working shows offline after `liveOnlineWindow`; the
|
// the log open but stops working goes offline after `liveOnlineWindow`; the next
|
||||||
// next QSO flips them back on. never-logged (zero time) → offline.
|
// QSO puts them back on. never-logged (zero time) → offline.
|
||||||
online := 0
|
online := !lastQSO.IsZero() && time.Since(lastQSO) < liveOnlineWindow
|
||||||
var lastQSOArg any
|
|
||||||
if !lastQSO.IsZero() {
|
|
||||||
lastQSOArg = lastQSO.UTC()
|
|
||||||
if time.Since(lastQSO) < liveOnlineWindow {
|
|
||||||
online = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := a.ensureLiveStatusTable(); err != nil {
|
if err := a.ensureLiveStatusTable(); err != nil {
|
||||||
applog.Printf("livestatus: CREATE TABLE failed: %v", err)
|
applog.Printf("livestatus: CREATE TABLE failed: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Offline → REMOVE the row entirely, not just flip a flag: a status page that
|
||||||
|
// lists the present rows (the common case, keyed on updated_at) then shows the
|
||||||
|
// operator as gone without having to read the online column. The row reappears
|
||||||
|
// on the next QSO. This is the whole point — no one shows on air when they're not.
|
||||||
|
if !online {
|
||||||
|
if _, err := a.logDb.ExecContext(a.ctx, "DELETE FROM live_status WHERE operator=?", op); err != nil {
|
||||||
|
applog.Printf("livestatus: offline DELETE failed: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lastQSOArg := lastQSO.UTC()
|
||||||
_, err := a.logDb.ExecContext(a.ctx,
|
_, err := a.logDb.ExecContext(a.ctx,
|
||||||
"INSERT INTO live_status (operator, station, freq_hz, band, mode, online, version, last_qso_at, updated_at) "+
|
"INSERT INTO live_status (operator, station, freq_hz, band, mode, online, version, last_qso_at, updated_at) "+
|
||||||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, UTC_TIMESTAMP()) "+
|
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, UTC_TIMESTAMP()) "+
|
||||||
"ON DUPLICATE KEY UPDATE station=VALUES(station), freq_hz=VALUES(freq_hz), "+
|
"ON DUPLICATE KEY UPDATE station=VALUES(station), freq_hz=VALUES(freq_hz), "+
|
||||||
"band=VALUES(band), mode=VALUES(mode), online=VALUES(online), version=VALUES(version), "+
|
"band=VALUES(band), mode=VALUES(mode), online=VALUES(online), version=VALUES(version), "+
|
||||||
"last_qso_at=VALUES(last_qso_at), updated_at=UTC_TIMESTAMP()",
|
"last_qso_at=VALUES(last_qso_at), updated_at=UTC_TIMESTAMP()",
|
||||||
op, station, freqHz, band, mode, online, appVersion, lastQSOArg)
|
op, station, freqHz, band, mode, 1, appVersion, lastQSOArg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
applog.Printf("livestatus: INSERT failed: %v", err)
|
applog.Printf("livestatus: INSERT failed: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
applog.Printf("livestatus: published op=%s station=%s %dHz %s %s online=%d", op, station, freqHz, band, mode, online)
|
applog.Printf("livestatus: published op=%s station=%s %dHz %s %s ON AIR", op, station, freqHz, band, mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LiveStation is one operator's live status for the multi-op "who's on air" widget.
|
// LiveStation is one operator's live status for the multi-op "who's on air" widget.
|
||||||
|
|||||||
Reference in New Issue
Block a user