refactor(call): move noise-suppression toggle out of the call bar

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) <noreply@anthropic.com>
This commit is contained in:
byGalax
2026-04-22 20:14:57 +02:00
parent eb8f702576
commit 02ca3e3581
4 changed files with 21 additions and 45 deletions
@@ -6,11 +6,6 @@ import { useTranslation } from 'react-i18next';
import { useAuth } from '../context/AuthContext';
import { type CallMode, useCall } from '../context/CallContext';
import {
getAudioSettings,
subscribeAudioSettings,
updateAudioSettings,
} from '../lib/audioSettings';
import {
getPttSettings,
type PttSettings,
@@ -80,7 +75,6 @@ export function InCallPanel({ conversation }: Props) {
hangup,
setCallMode,
setFocusedId,
setAudioInputDevice,
micError,
clearMicError,
retryMic,
@@ -94,10 +88,6 @@ export function InCallPanel({ conversation }: Props) {
const [volumeMenu, setVolumeMenu] = useState<
{ userId: string; displayName: string; x: number; y: number } | null
>(null);
const [noiseSuppression, setNoiseSuppression] = useState<boolean>(
() => 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)}