fix(ui): timestamps in logs, proper call debounce, no cross-calling

- Copy/Share log now includes HH:MM:SS timestamps
- callInProgress stays true until call resolves (setup or hangup),
  preventing multiple taps from firing multiple place_call offers
- Block place_call when there's a pending incoming call
- leaveVoice clears all call state (callInProgress, pendingCallId)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-04-14 19:16:20 +04:00
parent 98ed981805
commit 217567383d

View File

@@ -284,20 +284,25 @@ ctxMenu.addEventListener("click", (e) => { if (e.target === ctxMenu) ctxMenu.cla
let callInProgress = false; let callInProgress = false;
ctxCallBtn.addEventListener("click", async () => { ctxCallBtn.addEventListener("click", async () => {
if (!contextUser || callInProgress) return; if (!contextUser || callInProgress) return;
// Don't allow calling self
if (contextUser.fingerprint === myFingerprint) { if (contextUser.fingerprint === myFingerprint) {
ctxMenu.classList.add("hidden"); ctxMenu.classList.add("hidden");
return; return;
} }
// Don't place a call if there's already a pending incoming call
if (pendingCallId) {
ctxMenu.classList.add("hidden");
return;
}
callInProgress = true; 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 {
await invoke("place_call", { targetFp: contextUser.fingerprint }); await invoke("place_call", { targetFp: contextUser.fingerprint });
// Keep callInProgress true until the call resolves (setup/hangup)
// — it's cleared in leaveVoice() or when the call connects
} 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; callInProgress = false;
} }
}); });
@@ -350,7 +355,9 @@ function enterVoice(isDirect: boolean) {
function leaveVoice() { function leaveVoice() {
inVoice = false; inVoice = false;
callInProgress = false;
directCallPeer = null; directCallPeer = null;
pendingCallId = null;
voiceDrawer.classList.add("hidden"); voiceDrawer.classList.add("hidden");
joinVoiceBtn.classList.remove("hidden"); joinVoiceBtn.classList.remove("hidden");
vdLevelBar.style.width = "0%"; vdLevelBar.style.width = "0%";
@@ -674,11 +681,17 @@ sCallDebugClearBtn?.addEventListener("click", () => {
sCallDebugLogEl.textContent = ""; sCallDebugLogEl.textContent = "";
}); });
sCallDebugCopyBtn?.addEventListener("click", () => { sCallDebugCopyBtn?.addEventListener("click", () => {
const text = callDebugBuffer.map((e) => `${e.step} ${JSON.stringify(e.details)}`).join("\n"); const text = callDebugBuffer.map((e) => {
const t = new Date(e.ts_ms).toLocaleTimeString("en-GB", { hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit" });
return `${t} ${e.step} ${JSON.stringify(e.details)}`;
}).join("\n");
navigator.clipboard?.writeText(text).catch(() => {}); navigator.clipboard?.writeText(text).catch(() => {});
}); });
sCallDebugShareBtn?.addEventListener("click", async () => { sCallDebugShareBtn?.addEventListener("click", async () => {
const text = callDebugBuffer.map((e) => `${e.step} ${JSON.stringify(e.details)}`).join("\n"); const text = callDebugBuffer.map((e) => {
const t = new Date(e.ts_ms).toLocaleTimeString("en-GB", { hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit" });
return `${t} ${e.step} ${JSON.stringify(e.details)}`;
}).join("\n");
try { await (navigator as any).share({ text }); } catch {} try { await (navigator as any).share({ text }); } catch {}
}); });