From 571d5a84ae9cc9bb9af4b44810e38dea31710a9b Mon Sep 17 00:00:00 2001 From: Disco DeDisco Date: Sat, 30 May 2026 01:49:35 -0400 Subject: [PATCH] =?UTF-8?q?voice=20glow:=20regression=20spec=20=E2=80=94?= =?UTF-8?q?=203-min=20mute=20auto-disconnect=20stops=20the=20priRd/.fa-ban?= =?UTF-8?q?=20path=20+=20returns=20to=20the=20available=20nudge,=20live=20?= =?UTF-8?q?=E2=80=94=20TDD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The auto-disconnect already drives this asynchronously (burger-btn `_muteAutoDisconnect` → VoiceRoom.leave → _teardown → _notify{inCall:false,muted:false} → voice-glow render), and the `voiceroom:ready` subscribe fix guarantees voice-glow receives it without a refresh. Lock the muted→not-in-call transition behind a VoiceGlowSpec case: drops `voice-muted` (priRd + .fa-ban) + restores the channel-available `voice-glow` nudge, no longer "live" (no pulse/eq). Jasmine green. Code architected by Disco DeDisco Co-Authored-By: Claude Opus 4.8 (1M context) --- src/static/tests/VoiceGlowSpec.js | 17 +++++++++++++++++ src/static_src/tests/VoiceGlowSpec.js | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/static/tests/VoiceGlowSpec.js b/src/static/tests/VoiceGlowSpec.js index 336642d..c9ece7b 100644 --- a/src/static/tests/VoiceGlowSpec.js +++ b/src/static/tests/VoiceGlowSpec.js @@ -143,6 +143,23 @@ describe("VoiceGlow", () => { expect(hasMuted(burger)).toBe(false); }); + it("auto-disconnect (muted→not-in-call) stops the priRd/.fa-ban path + returns to the available nudge", () => { + // The 3-min mute timeout leaves voice → the mesh notifies + // {inCall:false, muted:false}; the glow must drop the muted colour + // (priRd/.fa-ban) + fall back to the channel-available nudge, live — + // no refresh (user-spec 2026-05-30). + vg = bindVoiceGlow(); + burger.classList.add("active"); // fan open → cues land on the voice btn + vg.setVoiceState({ inCall: true, peerCount: 0, muted: true }); + expect(hasMuted(voice)).toBe(true); + vg.setVoiceState({ inCall: false, peerCount: 0, muted: false }); + expect(hasMuted(voice)).toBe(false); // priRd/.fa-ban path stopped + expect(hasMuted(burger)).toBe(false); + expect(hasGlow(voice)).toBe(true); // back to the available nudge + expect(hasPulse(voice)).toBe(false); // not "live" anymore + expect(hasEq(voice)).toBe(false); + }); + it("hands the pulse to whichever surface is showing (fan toggles)", () => { vg = bindVoiceGlow(); vg.setVoiceState({ inCall: true, peerCount: 0 }); diff --git a/src/static_src/tests/VoiceGlowSpec.js b/src/static_src/tests/VoiceGlowSpec.js index 336642d..c9ece7b 100644 --- a/src/static_src/tests/VoiceGlowSpec.js +++ b/src/static_src/tests/VoiceGlowSpec.js @@ -143,6 +143,23 @@ describe("VoiceGlow", () => { expect(hasMuted(burger)).toBe(false); }); + it("auto-disconnect (muted→not-in-call) stops the priRd/.fa-ban path + returns to the available nudge", () => { + // The 3-min mute timeout leaves voice → the mesh notifies + // {inCall:false, muted:false}; the glow must drop the muted colour + // (priRd/.fa-ban) + fall back to the channel-available nudge, live — + // no refresh (user-spec 2026-05-30). + vg = bindVoiceGlow(); + burger.classList.add("active"); // fan open → cues land on the voice btn + vg.setVoiceState({ inCall: true, peerCount: 0, muted: true }); + expect(hasMuted(voice)).toBe(true); + vg.setVoiceState({ inCall: false, peerCount: 0, muted: false }); + expect(hasMuted(voice)).toBe(false); // priRd/.fa-ban path stopped + expect(hasMuted(burger)).toBe(false); + expect(hasGlow(voice)).toBe(true); // back to the available nudge + expect(hasPulse(voice)).toBe(false); // not "live" anymore + expect(hasEq(voice)).toBe(false); + }); + it("hands the pulse to whichever surface is showing (fan toggles)", () => { vg = bindVoiceGlow(); vg.setVoiceState({ inCall: true, peerCount: 0 });