diff --git a/src/components/call-controls.ts b/src/components/call-controls.ts index d2a18d38..137d569b 100644 --- a/src/components/call-controls.ts +++ b/src/components/call-controls.ts @@ -49,6 +49,16 @@ export class AdvancedCameraCardCallControls extends LitElement { @state() private _exiting = false; + public connectedCallback(): void { + super.connectedCallback(); + window.addEventListener('keydown', this._handleKeyDown); + } + + public disconnectedCallback(): void { + window.removeEventListener('keydown', this._handleKeyDown); + super.disconnectedCallback(); + } + protected willUpdate(changedProps: PropertyValues): void { if (changedProps.has('buttonSize') && this.buttonSize) { this.style.setProperty( @@ -113,6 +123,14 @@ export class AdvancedCameraCardCallControls extends LitElement { `; } + private _handleKeyDown = (ev: KeyboardEvent): void => { + if (this.active && ev.key === 'Escape') { + dispatchActionExecutionRequest(this, { actions: createCallEndAction() }); + ev.stopPropagation(); + ev.preventDefault(); + } + }; + private _handleAnimationEnd = (ev: AnimationEvent): void => { if (hasPopOutAnimationEnded(ev)) { this._exiting = false; diff --git a/src/components/notification/popup.ts b/src/components/notification/popup.ts index 7aa2a089..b6a925e1 100644 --- a/src/components/notification/popup.ts +++ b/src/components/notification/popup.ts @@ -24,13 +24,17 @@ export class AdvancedCameraCardNotification extends LitElement { super.connectedCallback(); window.addEventListener('click', this._handleOutsideInteraction); window.addEventListener('focusin', this._handleOutsideInteraction); - window.addEventListener('keydown', this._handleKeyDown); + + // Escape is claimed in the capture phase: the popup is a modal surface and + // must consume Escape before non-modal background controls (e.g. the call + // controls) that also listen on `window`. + window.addEventListener('keydown', this._handleKeyDown, { capture: true }); } public disconnectedCallback(): void { window.removeEventListener('click', this._handleOutsideInteraction); window.removeEventListener('focusin', this._handleOutsideInteraction); - window.removeEventListener('keydown', this._handleKeyDown); + window.removeEventListener('keydown', this._handleKeyDown, { capture: true }); super.disconnectedCallback(); } @@ -95,7 +99,11 @@ export class AdvancedCameraCardNotification extends LitElement { private _handleKeyDown = (ev: KeyboardEvent): void => { if (ev.key === 'Escape') { this._dismiss(); - ev.stopPropagation(); + + // `stopImmediatePropagation()` (not `stopPropagation()`) is required to + // block sibling `window` listeners -- `stopPropagation()` only stops + // propagation to other targets, not other listeners on `window` itself. + ev.stopImmediatePropagation(); ev.preventDefault(); } };