fix: stop monitor button greyed out during retune and click-to-tune race

1. Stop Monitor button was disabled during shared monitor retunes
   because _syncMonitorButtons disabled the button whenever
   _startingMonitor was true, even if the monitor was already active.
   Now only disables during initial start (not retunes).

2. Click-to-tune was inconsistent because the shared monitor retune
   (rearm after capture restart) captured the center frequency early
   in _startMonitorInternal, then sent it via POST to /audio/start.
   If the user clicked a new frequency during the async reconnect,
   the POST carried the stale frequency and could override the click.
   Now retunes use the live _monitorFreqMhz and send a WS tune sync
   after reconnecting to ensure the backend has the latest VFO.
This commit is contained in:
Smittix
2026-02-23 22:11:33 +00:00
parent 0a4a0689a0
commit 889c08691f

View File

@@ -2667,7 +2667,10 @@ const Waterfall = (function () {
if (monitorBtn) {
monitorBtn.textContent = _monitoring ? 'Stop Monitor' : 'Monitor';
monitorBtn.classList.toggle('is-active', _monitoring);
monitorBtn.disabled = _startingMonitor;
// Allow clicking Stop Monitor during retunes (monitor already
// active, just reconnecting audio). Only disable when starting
// from scratch so users can't double-click Start.
monitorBtn.disabled = _startingMonitor && !_monitoring;
}
if (muteBtn) {
@@ -2687,7 +2690,9 @@ const Waterfall = (function () {
_resumeWaterfallAfterMonitor = !!wasRunningWaterfall;
}
const centerMhz = _currentCenter();
// For retune-only calls, keep the current VFO frequency so
// user clicks during the async reconnect are not overridden.
const centerMhz = retuneOnly ? _monitorFreqMhz : _currentCenter();
const mode = document.getElementById('wfMonitorMode')?.value || 'wfm';
const squelch = parseInt(document.getElementById('wfMonitorSquelch')?.value, 10) || 0;
const sliderGain = parseInt(document.getElementById('wfMonitorGain')?.value, 10);
@@ -2701,7 +2706,9 @@ const Waterfall = (function () {
const biasT = !!document.getElementById('wfBiasT')?.checked;
const usingSecondaryDevice = !!altDevice;
_monitorFreqMhz = centerMhz;
if (!retuneOnly) {
_monitorFreqMhz = centerMhz;
}
_drawFreqAxis();
_stopSmeter();
_setUnlockVisible(false);
@@ -2716,8 +2723,11 @@ const Waterfall = (function () {
_setMonitorState(`Starting ${centerMhz.toFixed(4)} MHz ${mode.toUpperCase()}...`);
}
// Use live _monitorFreqMhz for retunes so that any user
// clicks that changed the VFO during the async setup are
// picked up rather than overridden.
let { response, payload } = await _requestAudioStart({
frequency: centerMhz,
frequency: retuneOnly ? _monitorFreqMhz : centerMhz,
modulation: mode,
squelch,
gain,
@@ -2802,20 +2812,29 @@ const Waterfall = (function () {
_monitoring = true;
_syncMonitorButtons();
_startSmeter(attach.player);
// Use live VFO for display — user may have clicked a new
// frequency while the retune was reconnecting audio.
const displayMhz = retuneOnly ? _monitorFreqMhz : centerMhz;
if (_monitorSource === 'waterfall') {
_setMonitorState(
`Monitoring ${centerMhz.toFixed(4)} MHz ${mode.toUpperCase()} via shared IQ`
`Monitoring ${displayMhz.toFixed(4)} MHz ${mode.toUpperCase()} via shared IQ`
);
} else if (usingSecondaryDevice) {
_setMonitorState(
`Monitoring ${centerMhz.toFixed(4)} MHz ${mode.toUpperCase()} `
`Monitoring ${displayMhz.toFixed(4)} MHz ${mode.toUpperCase()} `
+ `via ${monitorDevice.sdrType.toUpperCase()} #${monitorDevice.deviceIndex}`
);
} else {
_setMonitorState(`Monitoring ${centerMhz.toFixed(4)} MHz ${mode.toUpperCase()}`);
_setMonitorState(`Monitoring ${displayMhz.toFixed(4)} MHz ${mode.toUpperCase()}`);
}
_setStatus(`Audio monitor active on ${centerMhz.toFixed(4)} MHz (${mode.toUpperCase()})`);
_setStatus(`Audio monitor active on ${displayMhz.toFixed(4)} MHz (${mode.toUpperCase()})`);
_setVisualStatus('MONITOR');
// After a retune reconnect, sync the backend to the latest
// VFO in case the user clicked a new frequency while the
// audio stream was reconnecting.
if (retuneOnly && _monitorSource === 'waterfall' && _ws && _ws.readyState === WebSocket.OPEN) {
_sendWsTuneCmd();
}
} catch (err) {
if (nonce !== _audioConnectNonce) return;
_monitoring = false;