diff --git a/src/app/features/call/CallControls.tsx b/src/app/features/call/CallControls.tsx index 199858c37..e66749340 100644 --- a/src/app/features/call/CallControls.tsx +++ b/src/app/features/call/CallControls.tsx @@ -58,6 +58,15 @@ export function CallControls({ callEmbed }: CallControlsProps) { const [pttKey] = useSetting(settingsAtom, 'pttKey'); const [pttActive, setPttActive] = useState(false); + // Mute mic immediately when PTT is enabled mid-call so the key handler can activate + const pttModeRef = useRef(pttMode); + useEffect(() => { + if (pttMode && !pttModeRef.current && microphone) { + callEmbed.control.setMicrophone(false); + } + pttModeRef.current = pttMode; + }, [pttMode, callEmbed, microphone]); + const handleOpenMenu: MouseEventHandler = (evt) => { setCords(evt.currentTarget.getBoundingClientRect()); }; diff --git a/src/app/plugins/call/CallControl.ts b/src/app/plugins/call/CallControl.ts index e5da3f193..145283898 100644 --- a/src/app/plugins/call/CallControl.ts +++ b/src/app/plugins/call/CallControl.ts @@ -101,6 +101,11 @@ export class CallControl extends EventEmitter implements CallControlState { this.emitStateUpdate(); } + public async forceState(desired: CallControlState) { + this.state = new CallControlState(desired.microphone, desired.video, desired.sound, this.screenshare, this.spotlight); + await this.applyState(); + } + public startObserving() { this.controlMutationObserver.disconnect(); diff --git a/src/app/plugins/call/CallEmbed.ts b/src/app/plugins/call/CallEmbed.ts index 2a42e90f8..efc833e62 100644 --- a/src/app/plugins/call/CallEmbed.ts +++ b/src/app/plugins/call/CallEmbed.ts @@ -47,6 +47,8 @@ export class CallEmbed { private readonly disposables: Array<() => void> = []; + private readonly initialState: CallControlState; + static getIntent(dm: boolean, ongoing: boolean): ElementCallIntent { if (ongoing) { return dm ? ElementCallIntent.JoinExistingDM : ElementCallIntent.JoinExisting; @@ -149,6 +151,7 @@ export class CallEmbed { const controlState = initialControlState ?? new CallControlState(true, false, true); this.control = new CallControl(controlState, call, iframe); + this.initialState = controlState; let initialMediaEvent = true; this.disposables.push( @@ -251,6 +254,8 @@ export class CallEmbed { this.joined = true; this.applyStyles(); this.control.startObserving(); + // EC ignores io.element.device_mute before join; re-apply desired state now that EC is live + this.control.forceState(this.initialState); } private applyStyles(): void {