fix(ui): self-call prevention, debounce, codec in stats
- Filter self from lobby list (double-check in renderLobbyUsers) - Disable "Direct Call" button when tapping own user - Debounce call button (callInProgress flag prevents double-tap) - Block calling own fingerprint - Stats line shows codec names + fps + audio level The direct call to the other phone failing is likely because both phones share the same reflexive addr:port on the same NAT, making determine_role return None (equal addrs). This is an existing edge case in reflect.rs — not a UI bug. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -216,7 +216,9 @@ sQuality?.addEventListener("input", () => updateQualityUI(parseInt(sQuality.valu
|
|||||||
// ── Lobby rendering ───────────────────────────────────────────────
|
// ── Lobby rendering ───────────────────────────────────────────────
|
||||||
function renderLobbyUsers() {
|
function renderLobbyUsers() {
|
||||||
lobbyUserList.innerHTML = "";
|
lobbyUserList.innerHTML = "";
|
||||||
const users = Array.from(lobbyUsers.values()).sort((a, b) => {
|
const users = Array.from(lobbyUsers.values())
|
||||||
|
.filter((u) => u.fingerprint !== myFingerprint) // always exclude self
|
||||||
|
.sort((a, b) => {
|
||||||
// Voice users first, then alphabetical
|
// Voice users first, then alphabetical
|
||||||
if (a.inVoice !== b.inVoice) return a.inVoice ? -1 : 1;
|
if (a.inVoice !== b.inVoice) return a.inVoice ? -1 : 1;
|
||||||
return (a.alias || a.fingerprint).localeCompare(b.alias || b.fingerprint);
|
return (a.alias || a.fingerprint).localeCompare(b.alias || b.fingerprint);
|
||||||
@@ -269,14 +271,25 @@ function openContextMenu(user: LobbyUser) {
|
|||||||
ctxIdenticon.appendChild(createIdenticonEl(user.fingerprint, 40, true));
|
ctxIdenticon.appendChild(createIdenticonEl(user.fingerprint, 40, true));
|
||||||
ctxName.textContent = user.alias || user.fingerprint.substring(0, 16);
|
ctxName.textContent = user.alias || user.fingerprint.substring(0, 16);
|
||||||
ctxFp.textContent = user.fingerprint;
|
ctxFp.textContent = user.fingerprint;
|
||||||
|
// Hide call button for self
|
||||||
|
const isSelf = user.fingerprint === myFingerprint;
|
||||||
|
(ctxCallBtn as HTMLButtonElement).disabled = isSelf;
|
||||||
|
(ctxCallBtn as HTMLElement).style.opacity = isSelf ? "0.3" : "1";
|
||||||
ctxMenu.classList.remove("hidden");
|
ctxMenu.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
ctxCloseBtn.addEventListener("click", () => ctxMenu.classList.add("hidden"));
|
ctxCloseBtn.addEventListener("click", () => ctxMenu.classList.add("hidden"));
|
||||||
ctxMenu.addEventListener("click", (e) => { if (e.target === ctxMenu) ctxMenu.classList.add("hidden"); });
|
ctxMenu.addEventListener("click", (e) => { if (e.target === ctxMenu) ctxMenu.classList.add("hidden"); });
|
||||||
|
|
||||||
|
let callInProgress = false;
|
||||||
ctxCallBtn.addEventListener("click", async () => {
|
ctxCallBtn.addEventListener("click", async () => {
|
||||||
if (!contextUser) return;
|
if (!contextUser || callInProgress) return;
|
||||||
|
// Don't allow calling self
|
||||||
|
if (contextUser.fingerprint === myFingerprint) {
|
||||||
|
ctxMenu.classList.add("hidden");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callInProgress = true;
|
||||||
ctxMenu.classList.add("hidden");
|
ctxMenu.classList.add("hidden");
|
||||||
directCallPeer = { fingerprint: contextUser.fingerprint, alias: contextUser.alias };
|
directCallPeer = { fingerprint: contextUser.fingerprint, alias: contextUser.alias };
|
||||||
try {
|
try {
|
||||||
@@ -284,6 +297,8 @@ ctxCallBtn.addEventListener("click", async () => {
|
|||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error("place_call failed:", e);
|
console.error("place_call failed:", e);
|
||||||
directCallPeer = null;
|
directCallPeer = null;
|
||||||
|
} finally {
|
||||||
|
callInProgress = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -415,8 +430,8 @@ async function pollStatus() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stats
|
// Stats with codec
|
||||||
vdStats.textContent = `TX: ${st.tx_codec} ${st.encode_fps}fps | RX: ${st.rx_codec} ${st.recv_fps}fps`;
|
vdStats.textContent = `TX: ${st.tx_codec || "?"} ${st.encode_fps || 0}fps | RX: ${st.rx_codec || "?"} ${st.recv_fps || 0}fps | Level: ${st.audio_level || 0}`;
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user