From 66f7e82887141c041947cb57d1a09376524d60eb Mon Sep 17 00:00:00 2001 From: lambda Date: Mon, 26 Oct 2026 13:42:23 +0300 Subject: [PATCH] fix: telemetry live updates, input validation, connection race in UI --- src/main.js | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/main.js b/src/main.js index 60be2e4..73aad92 100644 --- a/src/main.js +++ b/src/main.js @@ -13,6 +13,7 @@ const groups = { const deviceCards = new Map(); // device_id -> li const selected = {}; // category -> { btn, card } подключённого датчика +const pendingStatus = {}; // category -> status (пришёл до установки selected) function el(tag, className, text) { const node = document.createElement(tag); @@ -108,6 +109,10 @@ async function connect(deviceId, btn, category, card) { btn.textContent = "✓"; btn.classList.add("device__connect--done"); selected[category] = { btn, card }; + if (pendingStatus[category]) { + applyStatus(selected[category], category, pendingStatus[category]); + delete pendingStatus[category]; + } if (category === "trainer") startBtn.disabled = false; } catch (e) { console.error(e); @@ -140,17 +145,14 @@ once("battery", (event) => { if (card) card.querySelector(".device__head").append(batteryIcon(percent)); }); -once("telemetry", (event) => { +listen("telemetry", (event) => { const t = event.payload.telemetry; if (t.Power) setMetric("power-value", t.Power.watts); else if (t.Cadence) setMetric("cadence-value", Math.round(t.Cadence.rpm)); else if (t.HeartRate) setMetric("hr-value", t.HeartRate.bpm); }); -listen("connection", (event) => { - const { category, status } = event.payload; - const sel = selected[category]; - if (!sel) return; +function applyStatus(sel, category, status) { const { btn } = sel; if (status === "reconnecting") { btn.textContent = "переподключение…"; @@ -165,6 +167,16 @@ listen("connection", (event) => { btn.disabled = false; selected[category] = null; } +} + +listen("connection", (event) => { + const { category, status } = event.payload; + const sel = selected[category]; + if (!sel) { + pendingStatus[category] = status; + return; + } + applyStatus(sel, category, status); }); // --- Таймер --- @@ -212,8 +224,12 @@ timerBtn.addEventListener("click", async () => { // --- Целевая мощность/каденс --- document.querySelector("#apply-btn").addEventListener("click", async () => { - const watts = Number(document.querySelector("#power-input").value); - const rpm = Number(document.querySelector("#cadence-input").value); + const powerRaw = document.querySelector("#power-input").value.trim(); + const cadenceRaw = document.querySelector("#cadence-input").value.trim(); + if (powerRaw === "" || cadenceRaw === "") return; + const watts = Number(powerRaw); + const rpm = Number(cadenceRaw); + if (!Number.isFinite(watts) || !Number.isFinite(rpm)) return; try { await invoke("set_target_power", { watts }); await invoke("set_target_cadence", { rpm });