fix(call): Wave-1 audit fixes (calls host side)
- C-H1: forceState only on FIRST join; on EC reconnect re-arm the fork handlers (resendForkState — deafen+quality only) instead of clobbering live mic/video/ deafen back to the join-time snapshot. - C-H2: AFK auto-mute reads the fork's io.lotus.call_state VAD of the LOCAL published track instead of getUserMedia on the browser DEFAULT mic (which could measure silence while the user spoke on another device → auto-mute an active speaker). Fails safe (never mutes) when call_state is null OR empty. - C-H3: control observer re-binds after EC re-renders (body subtree:true + 100ms debounce) with an early-return so unchanged state doesn't re-render. - C-M3 setQuality join-gated; C-M4 hangup 4s fallback dispose (idempotent); C-M5 PTT no longer silently un-deafens; C-M6 screenshare-audio mute resets on stop; C-L4 deafen key works in the iframe; C-L6 setState-after-unmount guards. Reviewed (C-H2 [] fail-safe + C-H3 re-render guard applied). tsc/eslint/prettier clean, build OK, 677 tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -57,6 +57,10 @@ export class CallEmbed {
|
||||
|
||||
public joined = false;
|
||||
|
||||
// C-M4: set once dispose() has run so the hangup fallback timer can tell
|
||||
// whether the embed was already torn down by the normal Close/Hangup echo.
|
||||
public disposed = false;
|
||||
|
||||
// [lotus #2] Latest per-participant state from io.lotus.call_state, or null
|
||||
// until the fork sends the first one. When non-null, the speaker/mute hooks
|
||||
// read it instead of scraping the EC iframe DOM.
|
||||
@@ -403,6 +407,8 @@ export class CallEmbed {
|
||||
* @param opts
|
||||
*/
|
||||
public dispose(): void {
|
||||
if (this.disposed) return;
|
||||
this.disposed = true;
|
||||
this.disposables.forEach((disposable) => {
|
||||
disposable();
|
||||
});
|
||||
@@ -501,9 +507,19 @@ export class CallEmbed {
|
||||
|
||||
private onCallJoined(): void {
|
||||
this.settleLoad();
|
||||
this.joined = true;
|
||||
this.applyStyles();
|
||||
this.control.startObserving();
|
||||
|
||||
// C-H1: EC fires JoinCall again on an EC reconnect (this action has no
|
||||
// once-guard). forceState() would reset live mic/video/deafen back to the
|
||||
// join-time snapshot, so only run it on the FIRST join. On a rejoin we just
|
||||
// re-apply styles/observers (above) and re-push the sticky fork state
|
||||
// (deafen/quality), leaving the user's live media state untouched.
|
||||
if (this.joined) {
|
||||
this.control.resendForkState();
|
||||
return;
|
||||
}
|
||||
this.joined = true;
|
||||
// EC ignores io.element.device_mute before join; re-apply desired state now that EC is live
|
||||
this.control.forceState(this.initialState);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user