mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-05 20:32:27 +00:00
Combine relay list with relay health check
- Remove separate relay health panel from profile modal. - Merge health indicators (latency, errors) directly into the relay list items. - Consolidate refresh logic to update both list configuration and health status. - Move telemetry toggle to the bottom of the relay section. Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
d018e21ad4
commit
5df489e504
4 files changed
+133
-168
No files matched your search
+57
-139
@@ -1253,13 +1253,7 @@ export class ProfileModalController {
|
||||
this.relayInput = null;
|
||||
this.addRelayButton = null;
|
||||
this.restoreRelaysButton = null;
|
||||
this.relayHealthPanel = null;
|
||||
this.relayHealthSummary = null;
|
||||
this.relayHealthCounts = null;
|
||||
this.relayHealthMessage = null;
|
||||
this.relayHealthList = null;
|
||||
this.relayHealthStatus = null;
|
||||
this.relayHealthRefreshButton = null;
|
||||
this.relayHealthTelemetryToggle = null;
|
||||
this.relayHealthRefreshPromise = null;
|
||||
this.profileRelayList = null;
|
||||
@@ -1599,20 +1593,8 @@ export class ProfileModalController {
|
||||
this.addRelayButton = document.getElementById("addRelayBtn") || null;
|
||||
this.restoreRelaysButton =
|
||||
document.getElementById("restoreRelaysBtn") || null;
|
||||
this.relayHealthPanel =
|
||||
document.getElementById("relayHealthPanel") || null;
|
||||
this.relayHealthSummary =
|
||||
document.getElementById("relayHealthSummary") || null;
|
||||
this.relayHealthCounts =
|
||||
document.getElementById("relayHealthCounts") || null;
|
||||
this.relayHealthMessage =
|
||||
document.getElementById("relayHealthMessage") || null;
|
||||
this.relayHealthList =
|
||||
document.getElementById("relayHealthList") || null;
|
||||
this.relayHealthStatus =
|
||||
document.getElementById("relayHealthStatus") || null;
|
||||
this.relayHealthRefreshButton =
|
||||
document.getElementById("relayHealthRefreshBtn") || null;
|
||||
this.relayHealthTelemetryToggle =
|
||||
document.getElementById("relayHealthTelemetryOptIn") || null;
|
||||
this.profileRelayRefreshBtn =
|
||||
@@ -5423,6 +5405,10 @@ export class ProfileModalController {
|
||||
.loadRelayList(activeHex)
|
||||
.then(() => {
|
||||
this.populateProfileRelays();
|
||||
void this.refreshRelayHealthPanel({
|
||||
forceRefresh: true,
|
||||
reason: "relay-update",
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
devLogger.warn("[profileModal] Failed to refresh relay list:", error);
|
||||
@@ -5430,12 +5416,6 @@ export class ProfileModalController {
|
||||
});
|
||||
}
|
||||
|
||||
if (this.relayHealthRefreshButton instanceof HTMLElement) {
|
||||
this.relayHealthRefreshButton.addEventListener("click", () => {
|
||||
void this.refreshRelayHealthPanel({ forceRefresh: true, reason: "manual" });
|
||||
});
|
||||
}
|
||||
|
||||
if (this.relayHealthTelemetryToggle instanceof HTMLInputElement) {
|
||||
this.relayHealthTelemetryToggle.addEventListener("change", () => {
|
||||
this.handleRelayHealthTelemetryToggle();
|
||||
@@ -7455,8 +7435,8 @@ export class ProfileModalController {
|
||||
|
||||
relays.forEach((entry) => {
|
||||
const item = document.createElement("li");
|
||||
item.className =
|
||||
"card flex items-start justify-between gap-4 p-4";
|
||||
item.className = "card flex items-start justify-between gap-4 p-4";
|
||||
item.dataset.relayUrl = entry.url;
|
||||
|
||||
const info = document.createElement("div");
|
||||
info.className = "flex-1 min-w-0";
|
||||
@@ -7475,8 +7455,13 @@ export class ProfileModalController {
|
||||
}
|
||||
statusEl.textContent = modeLabel;
|
||||
|
||||
const health = document.createElement("div");
|
||||
health.className = "flex flex-wrap items-center gap-x-3 gap-y-1 mt-1 text-2xs text-muted empty:hidden";
|
||||
health.dataset.role = "relay-health";
|
||||
|
||||
info.appendChild(urlEl);
|
||||
info.appendChild(statusEl);
|
||||
info.appendChild(health);
|
||||
|
||||
const actions = document.createElement("div");
|
||||
actions.className = "flex items-center gap-2";
|
||||
@@ -7518,126 +7503,58 @@ export class ProfileModalController {
|
||||
this.relayHealthStatus.textContent = text;
|
||||
}
|
||||
|
||||
updateRelayHealthSummary(snapshot = []) {
|
||||
if (!this.relayHealthCounts || !this.relayHealthMessage) {
|
||||
updateRelayHealthIndicators(snapshot = []) {
|
||||
if (!this.relayList) {
|
||||
return;
|
||||
}
|
||||
|
||||
const entries = Array.isArray(snapshot) ? snapshot : [];
|
||||
const total = entries.length;
|
||||
const healthy = entries.filter((entry) => entry.connected).length;
|
||||
const failed = Math.max(0, total - healthy);
|
||||
|
||||
this.relayHealthCounts.textContent = `${healthy} healthy • ${failed} failed`;
|
||||
|
||||
let message = "";
|
||||
if (!total) {
|
||||
message = "No relays configured.";
|
||||
} else if (failed > 0) {
|
||||
message =
|
||||
failed === total
|
||||
? "All relays are unreachable. Check your connection or update your relay list."
|
||||
: `${failed} relay${failed === 1 ? "" : "s"} unreachable. Playback and publishing may be delayed.`;
|
||||
} else {
|
||||
message = "All relays reachable.";
|
||||
}
|
||||
|
||||
this.relayHealthMessage.className = "mt-1 text-xs text-muted";
|
||||
if (failed > 0) {
|
||||
this.relayHealthMessage.classList.add("text-status-warning");
|
||||
} else if (total > 0) {
|
||||
this.relayHealthMessage.classList.add("text-status-success");
|
||||
}
|
||||
this.relayHealthMessage.textContent = message;
|
||||
}
|
||||
|
||||
renderRelayHealthSnapshot(snapshot = []) {
|
||||
if (!this.relayHealthList) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateRelayHealthSummary(snapshot);
|
||||
this.relayHealthList.innerHTML = "";
|
||||
|
||||
if (!snapshot.length) {
|
||||
const emptyState = document.createElement("li");
|
||||
emptyState.className =
|
||||
"card border border-dashed border-surface-strong p-4 text-center text-sm text-muted";
|
||||
emptyState.textContent = "No relays configured.";
|
||||
this.relayHealthList.appendChild(emptyState);
|
||||
if (!Array.isArray(snapshot)) {
|
||||
return;
|
||||
}
|
||||
|
||||
snapshot.forEach((entry) => {
|
||||
const item = document.createElement("li");
|
||||
item.className = "card flex flex-col gap-2 p-4";
|
||||
|
||||
const header = document.createElement("div");
|
||||
header.className = "flex items-center justify-between gap-3";
|
||||
|
||||
const urlEl = document.createElement("p");
|
||||
urlEl.className = "text-sm font-medium text-primary break-all";
|
||||
urlEl.textContent = entry.url;
|
||||
|
||||
const connection = document.createElement("span");
|
||||
const connected = Boolean(entry.connected);
|
||||
connection.className = `text-xs font-semibold ${
|
||||
connected ? "text-status-success" : "text-status-danger"
|
||||
}`;
|
||||
connection.textContent = connected ? "Connected" : "Unavailable";
|
||||
|
||||
header.appendChild(urlEl);
|
||||
header.appendChild(connection);
|
||||
|
||||
const details = document.createElement("div");
|
||||
details.className = "grid grid-cols-2 gap-3 text-xs text-muted sm:grid-cols-3";
|
||||
|
||||
const latency = document.createElement("div");
|
||||
latency.className = "flex items-center justify-between gap-2";
|
||||
const latencyLabel = document.createElement("span");
|
||||
latencyLabel.textContent = "Latency";
|
||||
const latencyValue = document.createElement("span");
|
||||
latencyValue.className = "text-text";
|
||||
latencyValue.textContent = Number.isFinite(entry.lastLatencyMs)
|
||||
? `${entry.lastLatencyMs} ms`
|
||||
: "—";
|
||||
latency.appendChild(latencyLabel);
|
||||
latency.appendChild(latencyValue);
|
||||
|
||||
const errors = document.createElement("div");
|
||||
errors.className = "flex items-center justify-between gap-2";
|
||||
const errorsLabel = document.createElement("span");
|
||||
errorsLabel.textContent = "Errors";
|
||||
const errorsValue = document.createElement("span");
|
||||
errorsValue.className = "text-text";
|
||||
errorsValue.textContent = Number.isFinite(entry.errorCount)
|
||||
? `${entry.errorCount}`
|
||||
: "0";
|
||||
errors.appendChild(errorsLabel);
|
||||
errors.appendChild(errorsValue);
|
||||
|
||||
const checks = document.createElement("div");
|
||||
checks.className = "flex items-center justify-between gap-2";
|
||||
const checksLabel = document.createElement("span");
|
||||
checksLabel.textContent = "Checked";
|
||||
const checksValue = document.createElement("span");
|
||||
checksValue.className = "text-text";
|
||||
if (Number.isFinite(entry.lastCheckedAt) && entry.lastCheckedAt > 0) {
|
||||
checksValue.textContent = new Date(entry.lastCheckedAt).toLocaleTimeString();
|
||||
} else {
|
||||
checksValue.textContent = "—";
|
||||
const url = entry.url;
|
||||
const item = this.relayList.querySelector(
|
||||
`li[data-relay-url="${CSS.escape(url)}"]`,
|
||||
);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
checks.appendChild(checksLabel);
|
||||
checks.appendChild(checksValue);
|
||||
|
||||
details.appendChild(latency);
|
||||
details.appendChild(errors);
|
||||
details.appendChild(checks);
|
||||
const healthContainer = item.querySelector('[data-role="relay-health"]');
|
||||
if (!healthContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
item.appendChild(header);
|
||||
item.appendChild(details);
|
||||
healthContainer.innerHTML = "";
|
||||
|
||||
this.relayHealthList.appendChild(item);
|
||||
const createBadge = (label, value, colorClass) => {
|
||||
const span = document.createElement("span");
|
||||
span.className = "inline-flex items-center gap-1 bg-surface-strong/30 px-1.5 py-0.5 rounded";
|
||||
const l = document.createElement("span");
|
||||
l.textContent = label;
|
||||
const v = document.createElement("span");
|
||||
v.className = colorClass || "text-text";
|
||||
v.textContent = value;
|
||||
span.appendChild(l);
|
||||
span.appendChild(v);
|
||||
return span;
|
||||
};
|
||||
|
||||
if (Number.isFinite(entry.lastLatencyMs)) {
|
||||
let color = "text-status-success";
|
||||
if (entry.lastLatencyMs > 1000) color = "text-status-danger";
|
||||
else if (entry.lastLatencyMs > 300) color = "text-status-warning";
|
||||
healthContainer.appendChild(
|
||||
createBadge("Ping:", `${entry.lastLatencyMs}ms`, color),
|
||||
);
|
||||
}
|
||||
|
||||
if (entry.errorCount > 0) {
|
||||
healthContainer.appendChild(
|
||||
createBadge("Errors:", `${entry.errorCount}`, "text-status-danger"),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7658,7 +7575,7 @@ export class ProfileModalController {
|
||||
|
||||
async refreshRelayHealthPanel({ forceRefresh = false, reason = "" } = {}) {
|
||||
const service = this.services?.relayHealthService;
|
||||
if (!service || !this.relayHealthList) {
|
||||
if (!service) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -7667,7 +7584,7 @@ export class ProfileModalController {
|
||||
}
|
||||
|
||||
const snapshot = service.getSnapshot();
|
||||
this.renderRelayHealthSnapshot(snapshot);
|
||||
this.updateRelayHealthIndicators(snapshot);
|
||||
|
||||
if (!forceRefresh) {
|
||||
return snapshot;
|
||||
@@ -7677,13 +7594,14 @@ export class ProfileModalController {
|
||||
return this.relayHealthRefreshPromise;
|
||||
}
|
||||
|
||||
const statusMessage = reason === "manual" ? "Refreshing relay health…" : "Checking relays…";
|
||||
const statusMessage =
|
||||
reason === "manual" ? "Refreshing relay health…" : "Checking relays…";
|
||||
this.updateRelayHealthStatus(statusMessage);
|
||||
|
||||
const refreshPromise = service
|
||||
.refresh()
|
||||
.then((latest) => {
|
||||
this.renderRelayHealthSnapshot(latest);
|
||||
this.updateRelayHealthIndicators(latest);
|
||||
this.updateRelayHealthStatus("Relay health updated.");
|
||||
return latest;
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user