feat: Add live.microphone.auto_mute / auto_unmute (#2482)
This commit is contained in:
committed by
dermotduffy
parent
9eb503deff
commit
a68b665f03
@@ -0,0 +1,97 @@
|
||||
import { MicrophoneManager } from '../../card-controller/microphone-manager.js';
|
||||
import {
|
||||
MicrophoneAutoMuteCondition,
|
||||
MicrophoneAutoUnmuteCondition,
|
||||
} from '../../config/schema/common/media-actions.js';
|
||||
import { VisibilityObserver } from '../visibility-observer.js';
|
||||
|
||||
interface MicrophoneActionsControllerOptions {
|
||||
microphoneManager?: MicrophoneManager;
|
||||
autoMuteConditions?: readonly MicrophoneAutoMuteCondition[];
|
||||
autoUnmuteConditions?: readonly MicrophoneAutoUnmuteCondition[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Owns microphone auto-mute/unmute rules at the live-view level.
|
||||
*
|
||||
* The microphone is a global singleton so cannot be controlled by
|
||||
* MediaActionsController without clashes between different controllers for
|
||||
* different cameras. This gives:
|
||||
* - deterministic ordering on selection change (this single owner sequences
|
||||
* 'unselected' for the leaver and 'selected' for the arriver),
|
||||
* - a single intersection observer scoped to the live root (correct
|
||||
* 'visible'/'hidden' semantics for the whole live view, not per-cell), and
|
||||
* - a single document.visibilitychange listener.
|
||||
*/
|
||||
export class MicrophoneActionsController {
|
||||
private _options: MicrophoneActionsControllerOptions | null = null;
|
||||
private _selectedCamera: string | null = null;
|
||||
private _visibilityObserver: VisibilityObserver;
|
||||
|
||||
constructor() {
|
||||
this._visibilityObserver = new VisibilityObserver((visible) =>
|
||||
this._changeVisibility(visible),
|
||||
);
|
||||
}
|
||||
|
||||
public setOptions(options: MicrophoneActionsControllerOptions): void {
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
public setRoot(root: HTMLElement): void {
|
||||
this._visibilityObserver.setRoot(root);
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
this._selectedCamera = null;
|
||||
this._visibilityObserver.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the controller of the currently selected camera. Called from the
|
||||
* live root whenever view.camera changes. Fires 'unselected' for the previous
|
||||
* camera (if any) and 'selected' for the new camera (if any), in that order.
|
||||
*/
|
||||
public async setSelectedCamera(camera: string | null): Promise<void> {
|
||||
if (this._selectedCamera === camera) {
|
||||
return;
|
||||
}
|
||||
const previous = this._selectedCamera;
|
||||
this._selectedCamera = camera;
|
||||
|
||||
if (previous !== null) {
|
||||
this._muteIfConfigured('unselected');
|
||||
}
|
||||
if (camera !== null) {
|
||||
await this._unmuteIfConfigured('selected');
|
||||
}
|
||||
}
|
||||
|
||||
private _changeVisibility = async (visible: boolean): Promise<void> => {
|
||||
if (visible) {
|
||||
await this._unmuteIfConfigured('visible');
|
||||
} else {
|
||||
this._muteIfConfigured('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
private async _unmuteIfConfigured(
|
||||
condition: MicrophoneAutoUnmuteCondition,
|
||||
): Promise<void> {
|
||||
if (
|
||||
this._options?.microphoneManager &&
|
||||
this._options.autoUnmuteConditions?.includes(condition)
|
||||
) {
|
||||
await this._options.microphoneManager.unmute();
|
||||
}
|
||||
}
|
||||
|
||||
private _muteIfConfigured(condition: MicrophoneAutoMuteCondition): void {
|
||||
if (
|
||||
this._options?.microphoneManager &&
|
||||
this._options.autoMuteConditions?.includes(condition)
|
||||
) {
|
||||
this._options.microphoneManager.mute();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user