feat: mute screenshare audio independently + fix CI lint/prettier
- Add screenshareAudioMuted state to CallControlState and CallControl - setSound() now preserves screenshare audio mute when un-deafening - Add toggleScreenshareAudio() targeting audio[data-lk-source="screen_share_audio"] - Add ScreenshareAudioButton (volume icon, warns when muted) to controls bar - Fix unused prevScreenshare variable (ESLint error from prior commit) - Run Prettier on Controls.tsx and CallControl.ts (CI formatting failures) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@ import {
|
||||
FullscreenButton,
|
||||
MicrophoneButton,
|
||||
ScreenShareButton,
|
||||
ScreenshareAudioButton,
|
||||
SoundButton,
|
||||
VideoButton,
|
||||
} from './Controls';
|
||||
@@ -67,9 +68,8 @@ export function CallControls({ callEmbed }: CallControlsProps) {
|
||||
}
|
||||
}, [callEmbedRef]);
|
||||
|
||||
const { microphone, video, sound, screenshare, spotlight } = useCallControlState(
|
||||
callEmbed.control,
|
||||
);
|
||||
const { microphone, video, sound, screenshare, spotlight, screenshareAudioMuted } =
|
||||
useCallControlState(callEmbed.control);
|
||||
|
||||
const [cords, setCords] = useState<RectCords>();
|
||||
const [shareConfirm, setShareConfirm] = useState(false);
|
||||
@@ -346,6 +346,10 @@ export function CallControls({ callEmbed }: CallControlsProps) {
|
||||
onToggle={() => callEmbed.control.toggleMicrophone()}
|
||||
/>
|
||||
<SoundButton enabled={sound} onToggle={() => callEmbed.control.toggleSound()} />
|
||||
<ScreenshareAudioButton
|
||||
muted={screenshareAudioMuted}
|
||||
onToggle={() => callEmbed.control.toggleScreenshareAudio()}
|
||||
/>
|
||||
</Box>
|
||||
{!compact && <ControlDivider />}
|
||||
<Box shrink="No" alignItems="Inherit" justifyContent="Inherit" gap="200">
|
||||
|
||||
@@ -197,11 +197,41 @@ export function FullscreenButton({ isFullscreen, onToggle }: FullscreenButtonPro
|
||||
aria-pressed={isFullscreen}
|
||||
outlined
|
||||
>
|
||||
{isFullscreen ? (
|
||||
<ExitFullscreenIcon />
|
||||
) : (
|
||||
<FullscreenIcon />
|
||||
)}
|
||||
{isFullscreen ? <ExitFullscreenIcon /> : <FullscreenIcon />}
|
||||
</IconButton>
|
||||
)}
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
|
||||
type ScreenshareAudioButtonProps = {
|
||||
muted: boolean;
|
||||
onToggle: () => void;
|
||||
};
|
||||
export function ScreenshareAudioButton({ muted, onToggle }: ScreenshareAudioButtonProps) {
|
||||
return (
|
||||
<TooltipProvider
|
||||
position="Top"
|
||||
delay={500}
|
||||
tooltip={
|
||||
<Tooltip>
|
||||
<Text size="T200">{muted ? 'Unmute Screenshare Audio' : 'Mute Screenshare Audio'}</Text>
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
{(anchorRef) => (
|
||||
<IconButton
|
||||
ref={anchorRef}
|
||||
variant={muted ? 'Warning' : 'Surface'}
|
||||
fill="Soft"
|
||||
radii="400"
|
||||
size="400"
|
||||
onClick={onToggle}
|
||||
aria-label={muted ? 'Unmute Screenshare Audio' : 'Mute Screenshare Audio'}
|
||||
aria-pressed={muted}
|
||||
outlined
|
||||
>
|
||||
<Icon size="400" src={muted ? Icons.VolumeMute : Icons.VolumeHigh} filled={muted} />
|
||||
</IconButton>
|
||||
)}
|
||||
</TooltipProvider>
|
||||
|
||||
@@ -93,6 +93,10 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
return this.state.spotlight;
|
||||
}
|
||||
|
||||
public get screenshareAudioMuted(): boolean {
|
||||
return this.state.screenshareAudioMuted;
|
||||
}
|
||||
|
||||
public async applyState() {
|
||||
await this.setMediaState({
|
||||
audio_enabled: this.microphone,
|
||||
@@ -145,11 +149,24 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
const callDocument = this.iframe.contentDocument ?? this.iframe.contentWindow?.document;
|
||||
if (callDocument) {
|
||||
callDocument.querySelectorAll('audio').forEach((el) => {
|
||||
el.muted = !sound;
|
||||
const isScreenshareAudio = el.getAttribute('data-lk-source') === 'screen_share_audio';
|
||||
el.muted = !sound || (isScreenshareAudio && this.screenshareAudioMuted);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private applyScreenshareAudioMuted(): void {
|
||||
if (!this.sound) return;
|
||||
const callDocument = this.iframe.contentDocument ?? this.iframe.contentWindow?.document;
|
||||
if (callDocument) {
|
||||
callDocument
|
||||
.querySelectorAll<HTMLAudioElement>('audio[data-lk-source="screen_share_audio"]')
|
||||
.forEach((el) => {
|
||||
el.muted = this.screenshareAudioMuted;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public onMediaState(evt: CustomEvent<ElementMediaStateDetail>) {
|
||||
const { data } = evt.detail;
|
||||
if (!data) return;
|
||||
@@ -160,6 +177,7 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
this.sound,
|
||||
this.screenshare,
|
||||
this.spotlight,
|
||||
this.screenshareAudioMuted,
|
||||
);
|
||||
|
||||
this.state = state;
|
||||
@@ -171,8 +189,6 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
}
|
||||
|
||||
public onControlMutation() {
|
||||
const prevScreenshare = this.screenshare;
|
||||
|
||||
const screenshare: boolean = this.screenshareButton?.getAttribute('data-kind') === 'primary';
|
||||
const spotlight: boolean = this.spotlightButton?.checked ?? false;
|
||||
|
||||
@@ -182,9 +198,9 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
this.sound,
|
||||
screenshare,
|
||||
spotlight,
|
||||
this.screenshareAudioMuted,
|
||||
);
|
||||
this.emitStateUpdate();
|
||||
|
||||
}
|
||||
|
||||
public setMicrophone(enabled: boolean) {
|
||||
@@ -215,6 +231,8 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
const sound = !this.sound;
|
||||
|
||||
this.setSound(sound);
|
||||
// After un-deafening, re-apply screenshare audio mute if active
|
||||
if (sound) this.applyScreenshareAudioMuted();
|
||||
|
||||
const state = new CallControlState(
|
||||
this.microphone,
|
||||
@@ -222,6 +240,7 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
sound,
|
||||
this.screenshare,
|
||||
this.spotlight,
|
||||
this.screenshareAudioMuted,
|
||||
);
|
||||
this.state = state;
|
||||
this.emitStateUpdate();
|
||||
@@ -231,6 +250,20 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
}
|
||||
}
|
||||
|
||||
public toggleScreenshareAudio() {
|
||||
const screenshareAudioMuted = !this.screenshareAudioMuted;
|
||||
this.state = new CallControlState(
|
||||
this.microphone,
|
||||
this.video,
|
||||
this.sound,
|
||||
this.screenshare,
|
||||
this.spotlight,
|
||||
screenshareAudioMuted,
|
||||
);
|
||||
this.emitStateUpdate();
|
||||
this.applyScreenshareAudioMuted();
|
||||
}
|
||||
|
||||
public toggleScreenshare() {
|
||||
this.screenshareButton?.click();
|
||||
}
|
||||
|
||||
@@ -9,17 +9,21 @@ export class CallControlState {
|
||||
|
||||
public readonly spotlight: boolean;
|
||||
|
||||
public readonly screenshareAudioMuted: boolean;
|
||||
|
||||
constructor(
|
||||
microphone: boolean,
|
||||
video: boolean,
|
||||
sound: boolean,
|
||||
screenshare = false,
|
||||
spotlight = false,
|
||||
screenshareAudioMuted = false,
|
||||
) {
|
||||
this.microphone = microphone;
|
||||
this.video = video;
|
||||
this.sound = sound;
|
||||
this.screenshare = screenshare;
|
||||
this.spotlight = spotlight;
|
||||
this.screenshareAudioMuted = screenshareAudioMuted;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user