[fix](trx-frontend-http): preserve bookmark bandwidth

Order bookmark mode and bandwidth updates so WFM bookmarks do\nnot race against the backend mode default.\n\nAlso apply saved bookmark bandwidth in the scheduler path so\nscheduled bookmark replays keep the configured filter width.\n\nTested with:\n- cargo test -p trx-frontend-http\n\nCo-authored-by: OpenAI Codex <codex@openai.com>

Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-03-24 23:04:35 +01:00
parent c93e855f0d
commit 6eb0f3a116
2 changed files with 33 additions and 17 deletions
@@ -394,28 +394,31 @@ async function bmApply(bm) {
scheduleSpectrumDraw(); scheduleSpectrumDraw();
} }
// --- Fire-and-forget: send mode, bandwidth, and frequency in parallel --- // Take scheduler control up front, then apply mode before bandwidth so a
// The UI is already updated optimistically above; don't block on the // late SetMode cannot revert a saved WFM bookmark bandwidth to 180 kHz.
// network round-trips so the bookmark click feels instant. const tunePromise = (async () => {
const modePromise = (async () => { if (typeof vchanTakeSchedulerControl === "function") {
await vchanTakeSchedulerControl();
}
const onVirtual = typeof vchanInterceptMode === "function" const onVirtual = typeof vchanInterceptMode === "function"
&& await vchanInterceptMode(bm.mode); && await vchanInterceptMode(bm.mode);
if (!onVirtual) { if (!onVirtual) {
await postPath("/set_mode?mode=" + encodeURIComponent(bm.mode)); await postPath("/set_mode?mode=" + encodeURIComponent(bm.mode));
} }
})();
const bwPromise = bm.bandwidth_hz ? (async () => { if (bm.bandwidth_hz) {
const bwHandledByVchan = typeof vchanInterceptBandwidth === "function" const bwHandledByVchan = typeof vchanInterceptBandwidth === "function"
&& await vchanInterceptBandwidth(bm.bandwidth_hz); && await vchanInterceptBandwidth(bm.bandwidth_hz);
if (!bwHandledByVchan) { if (!bwHandledByVchan) {
await postPath("/set_bandwidth?hz=" + bm.bandwidth_hz); await postPath("/set_bandwidth?hz=" + bm.bandwidth_hz);
} }
})() : Promise.resolve(); }
// setRigFrequency is wrapped by vchan.js to redirect to the channel API // setRigFrequency is wrapped by vchan.js to redirect to the channel API
// when on a virtual channel, so this call works correctly in both cases. // when on a virtual channel, so this call works correctly in both cases.
// It also does its own optimistic update (applyLocalTunedFrequency) but // It also does its own optimistic update (applyLocalTunedFrequency) but
// that's a no-op since we already set the same value above. // that's a no-op since we already set the same value above.
const freqPromise = (async () => {
if (typeof setRigFrequency === "function") { if (typeof setRigFrequency === "function") {
await setRigFrequency(bm.freq_hz); await setRigFrequency(bm.freq_hz);
} else { } else {
@@ -439,7 +442,7 @@ async function bmApply(bm) {
})() : Promise.resolve(); })() : Promise.resolve();
// Don't await — let the network calls settle in the background. // Don't await — let the network calls settle in the background.
// Errors are logged but don't block the UI. // Errors are logged but don't block the UI.
Promise.all([modePromise, bwPromise, freqPromise, decoderPromise]).catch( Promise.all([tunePromise, decoderPromise]).catch(
(err) => console.error("Bookmark apply background error:", err) (err) => console.error("Bookmark apply background error:", err)
); );
} catch (err) { } catch (err) {
@@ -534,6 +534,19 @@ async fn apply_scheduler_target(
) )
.await?; .await?;
if let Some(bandwidth_hz) = bookmark
.bandwidth_hz
.filter(|bw| *bw > 0 && *bw <= u32::MAX as u64)
.map(|bw| bw as u32)
{
scheduler_send(
rig_tx,
RigCommand::SetBandwidth(bandwidth_hz),
remote.to_string(),
)
.await?;
}
apply_scheduler_decoders(rig_tx, remote, &bookmark, &extra_bookmarks).await; apply_scheduler_decoders(rig_tx, remote, &bookmark, &extra_bookmarks).await;
let status = SchedulerStatus { let status = SchedulerStatus {