From 02ca3e3581b69e68cb4cb6b9ad8b3af34ca1e45f Mon Sep 17 00:00:00 2001 From: byGalax Date: Wed, 22 Apr 2026 20:14:57 +0200 Subject: [PATCH] refactor(call): move noise-suppression toggle out of the call bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Default flipped to off so voice doesn't get coloured by browser NS on first run. Users turn it on explicitly under Settings → Sprache, where the toggle already lived before the in-call button shipped. The in-call SparklesIcon button and all the associated wiring (onToggleNoiseSuppression, noiseSuppression prop, local subscription in InCallPanel) is removed. The hot-swap UX is preserved: a new subscribeAudioSettings watcher in CallContext detects noiseSuppression flips during an active call and re-runs setupMicPipeline so the change takes effect without rejoining. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/desktop/src/components/CallControls.tsx | 25 -------------------- apps/desktop/src/components/InCallPanel.tsx | 19 --------------- apps/desktop/src/context/CallContext.tsx | 17 +++++++++++++ apps/desktop/src/lib/audioSettings.ts | 5 +++- 4 files changed, 21 insertions(+), 45 deletions(-) diff --git a/apps/desktop/src/components/CallControls.tsx b/apps/desktop/src/components/CallControls.tsx index fd932c0..0250fa9 100644 --- a/apps/desktop/src/components/CallControls.tsx +++ b/apps/desktop/src/components/CallControls.tsx @@ -9,7 +9,6 @@ import { MonitorStopIcon, MusicIcon, PhoneOffIcon, - SparklesIcon, UsersIcon, VideoIcon, } from './icons'; @@ -19,7 +18,6 @@ interface Props { sharing: boolean; video: boolean; deafened: boolean; - noiseSuppression?: boolean; onToggleMute: () => void; onToggleShare: () => void; /** Right-click on the share button opens the quality picker dialog while @@ -28,7 +26,6 @@ interface Props { onShareContextMenu?: (e: React.MouseEvent) => void; onToggleVideo?: () => void; onToggleDeafen: () => void; - onToggleNoiseSuppression?: () => void; onHangup: () => void; onOpenParticipants?: () => void; /** Toggle the in-call soundboard popover. Active = panel currently open. */ @@ -48,13 +45,11 @@ export function CallControls({ sharing, video, deafened, - noiseSuppression, onToggleMute, onToggleShare, onShareContextMenu, onToggleVideo, onToggleDeafen, - onToggleNoiseSuppression, onHangup, onOpenParticipants, onToggleSoundboard, @@ -134,26 +129,6 @@ export function CallControls({ )} - {onToggleNoiseSuppression && ( - - - - )} {onToggleSoundboard && ( (null); - const [noiseSuppression, setNoiseSuppression] = useState( - () => getAudioSettings().noiseSuppression, - ); - useEffect(() => subscribeAudioSettings((s) => setNoiseSuppression(s.noiseSuppression)), []); // Active-speaker auto-focus uses "who most recently started speaking" // rather than "exactly one speaker" — matches Discord more closely and // handles the case where two people talk briefly without the focus @@ -180,7 +170,6 @@ export function InCallPanel({ conversation }: Props) { sharing={isScreenSharing} video={isCameraEnabled} deafened={isDeafened} - noiseSuppression={noiseSuppression} onToggleMute={toggleMute} // 1-click share uses last-saved preset + displaySurface. Right-click // opens the quality picker for users who want to change settings @@ -198,14 +187,6 @@ export function InCallPanel({ conversation }: Props) { }} onToggleVideo={() => void toggleCamera()} onToggleDeafen={toggleDeafen} - // Hot-swap the mic track with new constraints by replaying the - // input-device switch with the same id. setAudioInputDevice re-reads - // audioSettings, so flipping noiseSuppression first is enough. - onToggleNoiseSuppression={() => { - const prev = getAudioSettings().noiseSuppression; - updateAudioSettings({ noiseSuppression: !prev }); - void setAudioInputDevice(getAudioSettings().inputDeviceId); - }} onOpenParticipants={() => setParticipantsOpen((v) => !v)} participantsOpen={participantsOpen} onToggleSoundboard={() => setSoundboardOpen((v) => !v)} diff --git a/apps/desktop/src/context/CallContext.tsx b/apps/desktop/src/context/CallContext.tsx index 7a79f2f..e2c7012 100644 --- a/apps/desktop/src/context/CallContext.tsx +++ b/apps/desktop/src/context/CallContext.tsx @@ -1235,6 +1235,23 @@ export function CallProvider({ children }: { children: ReactNode }) { }); }, []); + // Hot-swap the mic track when the user flips noiseSuppression in Settings + // so the change takes effect without needing to rejoin the call. Tracks + // the previous value in a ref so we only re-acquire getUserMedia on + // actual transitions (avoid a rebuild on every unrelated settings save). + useEffect(() => { + let lastNs = getAudioSettings().noiseSuppression; + return subscribeAudioSettings((s) => { + if (s.noiseSuppression === lastNs) return; + lastNs = s.noiseSuppression; + const r = roomRef.current; + if (!r) return; + // setupMicPipeline re-reads audioSettings so the new NS constraint + // gets picked up. Same helper as the initial join + retry paths. + void setupMicPipeline(r); + }); + }, [setupMicPipeline]); + // --- Push-to-talk ------------------------------------------------------ // While PTT is active + we're in a connected call, the mic is held off // except while the configured key is pressed. Under Tauri we also register diff --git a/apps/desktop/src/lib/audioSettings.ts b/apps/desktop/src/lib/audioSettings.ts index 618ca00..a71b452 100644 --- a/apps/desktop/src/lib/audioSettings.ts +++ b/apps/desktop/src/lib/audioSettings.ts @@ -41,7 +41,10 @@ const DEFAULTS: AudioSettings = { inputDeviceId: null, outputDeviceId: null, voiceThreshold: 0.03, - noiseSuppression: true, + // Off by default — browser-native NS colours voice audibly on some + // mics and is a frequent "why does my voice sound weird" report. + // Users who want it enable it explicitly in Settings → Sprache. + noiseSuppression: false, videoBackgroundBlur: false, ringtoneVolume: 0.9, };