Fix WebSocket status label not updating and move it into the topbar
The onopen/onclose handlers only toggled the CSS class on the status dot, never the separate text span, so it stayed "offline" after a reconnect. Replace the querySelector-based toggling with an updateWsIndicator() helper that updates both the dot and its label from stored element refs. The indicator now lives in the topbar (via renderTopbar's extra slot) instead of next to the competition name, with its own .ws-indicator flex wrapper so the dot and label line up vertically.
This commit is contained in:
+17
-7
@@ -1313,12 +1313,26 @@
|
|||||||
|
|
||||||
// ---- Main render -------------------------------------------------------
|
// ---- Main render -------------------------------------------------------
|
||||||
|
|
||||||
|
function wsIndicator() {
|
||||||
|
state.wsDot = el("span", { class: "connection-status " + (state.wsOnline ? "online" : "offline") });
|
||||||
|
state.wsText = el("span", { class: "muted" }, state.wsOnline ? t("online") : t("offline"));
|
||||||
|
return el("span", { class: "ws-indicator" }, state.wsDot, state.wsText);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateWsIndicator() {
|
||||||
|
if (state.wsDot) {
|
||||||
|
state.wsDot.classList.toggle("online", state.wsOnline);
|
||||||
|
state.wsDot.classList.toggle("offline", !state.wsOnline);
|
||||||
|
}
|
||||||
|
if (state.wsText) state.wsText.textContent = state.wsOnline ? t("online") : t("offline");
|
||||||
|
}
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
clearNode(root);
|
clearNode(root);
|
||||||
const backBtn = el("button", { class: "ghost",
|
const backBtn = el("button", { class: "ghost",
|
||||||
onclick: () => { if (state.ws) { state.ws.close(); state.ws = null; } navigate("competitions"); } },
|
onclick: () => { if (state.ws) { state.ws.close(); state.ws = null; } navigate("competitions"); } },
|
||||||
"← " + t("back"));
|
"← " + t("back"));
|
||||||
root.appendChild(renderTopbar(user, { extra: backBtn }));
|
root.appendChild(renderTopbar(user, { extra: [wsIndicator(), backBtn] }));
|
||||||
|
|
||||||
const container = el("div", { class: "container" });
|
const container = el("div", { class: "container" });
|
||||||
container.appendChild(el("div", { class: "row", style: { justifyContent: "space-between", marginBottom: "0.5rem" } },
|
container.appendChild(el("div", { class: "row", style: { justifyContent: "space-between", marginBottom: "0.5rem" } },
|
||||||
@@ -1329,10 +1343,6 @@
|
|||||||
isClosed() ? " " : null,
|
isClosed() ? " " : null,
|
||||||
isClosed() ? el("span", { class: "badge warn" }, t("closed")) : null,
|
isClosed() ? el("span", { class: "badge warn" }, t("closed")) : null,
|
||||||
),
|
),
|
||||||
el("div", { class: "row" },
|
|
||||||
el("span", { class: "connection-status " + (state.wsOnline ? "online" : "offline") }),
|
|
||||||
el("span", { class: "muted" }, state.wsOnline ? t("online") : t("offline")),
|
|
||||||
),
|
|
||||||
));
|
));
|
||||||
|
|
||||||
const tabs = el("div", { class: "tabs" });
|
const tabs = el("div", { class: "tabs" });
|
||||||
@@ -1393,8 +1403,8 @@
|
|||||||
|
|
||||||
await loadAll();
|
await loadAll();
|
||||||
state.ws = openCompetitionWS(competitionId, {
|
state.ws = openCompetitionWS(competitionId, {
|
||||||
onopen: () => { state.wsOnline = true; const e = document.querySelector(".connection-status"); if (e) { e.classList.add("online"); e.classList.remove("offline"); } },
|
onopen: () => { state.wsOnline = true; updateWsIndicator(); },
|
||||||
onclose: () => { state.wsOnline = false; const e = document.querySelector(".connection-status"); if (e) { e.classList.remove("online"); e.classList.add("offline"); } },
|
onclose: () => { state.wsOnline = false; updateWsIndicator(); },
|
||||||
onmessage: handleWSMessage,
|
onmessage: handleWSMessage,
|
||||||
});
|
});
|
||||||
render();
|
render();
|
||||||
|
|||||||
+12
-1
@@ -387,11 +387,22 @@ tbody tr.transferred { background: #f3f6fb; }
|
|||||||
height: 8px;
|
height: 8px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: #d1d5db;
|
background: #d1d5db;
|
||||||
margin-right: 0.5rem;
|
flex: none;
|
||||||
}
|
}
|
||||||
.connection-status.online { background: #10b981; }
|
.connection-status.online { background: #10b981; }
|
||||||
.connection-status.offline { background: #ef4444; }
|
.connection-status.offline { background: #ef4444; }
|
||||||
|
|
||||||
|
.ws-indicator {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.4rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
/* Optically drop the dot ~1px: an all-caps label (Online/Offline) has
|
||||||
|
unused descender space, so its glyphs sit above the flex centre line. */
|
||||||
|
.ws-indicator .connection-status { position: relative; top: 1px; }
|
||||||
|
|
||||||
textarea { resize: vertical; min-height: 60px; }
|
textarea { resize: vertical; min-height: 60px; }
|
||||||
|
|
||||||
@media (max-width: 700px) {
|
@media (max-width: 700px) {
|
||||||
|
|||||||
Reference in New Issue
Block a user