feat: Explicit answer/reject for inbound calls (#2504)
This commit is contained in:
committed by
dermotduffy
parent
b9f09b7c4e
commit
f397596ed1
@@ -14,6 +14,7 @@ import { ActionConfig } from '../config/schema/actions/types.js';
|
||||
import { localize } from '../localize/localize.js';
|
||||
import callControlsStyle from '../scss/call-controls.scss';
|
||||
import {
|
||||
createCallAnswerAction,
|
||||
createCallEndAction,
|
||||
createGeneralAction,
|
||||
stopEventFromActivatingCardWideActions,
|
||||
@@ -22,19 +23,27 @@ import { hasPopOutAnimationEnded } from '../utils/animation.js';
|
||||
import { fireAdvancedCameraCardEvent } from '../utils/fire-advanced-camera-card-event.js';
|
||||
|
||||
/**
|
||||
* The on-screen overlay shown during an active two-way audio call: a centered
|
||||
* pill with end-call, microphone-toggle, and mute-toggle buttons.
|
||||
* The on-screen overlay shown during a two-way audio call: a centered pill
|
||||
* whose contents depend on call state. Pre-answer (inbound ringing) shows
|
||||
* reject + answer; post-answer (or outbound) shows end-call + microphone
|
||||
* toggle + audio-out toggle.
|
||||
*
|
||||
* This is a purely presentational control showing state and emitting intents.
|
||||
* The end-call and microphone buttons dispatch actions; the audio-out button
|
||||
* fires an `advanced-camera-card:call:mute-toggle` event for the host to act on.
|
||||
* Button taps dispatch actions; the audio-out button fires an
|
||||
* `advanced-camera-card:call:mute-toggle` event for the host to act on.
|
||||
*/
|
||||
@customElement('advanced-camera-card-call-controls')
|
||||
export class AdvancedCameraCardCallControls extends LitElement {
|
||||
// Whether a call is in progress.
|
||||
// Whether a call exists on this carousel's camera, in either the unanswered
|
||||
// or answered state. Drives whether the overlay renders at all.
|
||||
@property({ attribute: false })
|
||||
public active = false;
|
||||
|
||||
// Whether that call has been answered. Selects between the pre-answer (reject
|
||||
// + answer) and post-answer (end + mic + audio) button sets.
|
||||
@property({ attribute: false })
|
||||
public answered = true;
|
||||
|
||||
@property({ attribute: false })
|
||||
public microphoneState?: MicrophoneState;
|
||||
|
||||
@@ -49,6 +58,13 @@ export class AdvancedCameraCardCallControls extends LitElement {
|
||||
@state()
|
||||
private _exiting = false;
|
||||
|
||||
// Tracks which controls are rendered. Synced from `answered` only while
|
||||
// the controls are actually showing (`active`), so it keeps its last value
|
||||
// through the exit animation (as the parent's `answered` prop may otherwise
|
||||
// change mid-exit when the call session disappears).
|
||||
@state()
|
||||
private _type: 'answered' | 'unanswered' = 'answered';
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
window.addEventListener('keydown', this._handleKeyDown);
|
||||
@@ -72,6 +88,14 @@ export class AdvancedCameraCardCallControls extends LitElement {
|
||||
// call (re)starting cancels any in-progress exit.
|
||||
this._exiting = !this.active && !!changedProps.get('active');
|
||||
}
|
||||
|
||||
// Only mirror `answered` while the pill is actually showing: this leaves
|
||||
// `_type` frozen through the exit animation, so the outgoing pill keeps the
|
||||
// same button set it had pre-exit even if the parent's `answered` prop
|
||||
// changes after the call session disappears.
|
||||
if (this.active) {
|
||||
this._type = this.answered ? 'answered' : 'unanswered';
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
@@ -79,21 +103,50 @@ export class AdvancedCameraCardCallControls extends LitElement {
|
||||
return;
|
||||
}
|
||||
|
||||
const microphoneMuted = this.microphoneState?.muted ?? true;
|
||||
const audioAvailable = this.muted !== undefined;
|
||||
const audioMuted = this.muted ?? true;
|
||||
|
||||
return html`<div class="overlay">
|
||||
<div
|
||||
class=${classMap({ panel: true, exiting: this._exiting })}
|
||||
@click=${(ev: Event) => stopEventFromActivatingCardWideActions(ev)}
|
||||
@animationend=${this._handleAnimationEnd}
|
||||
>
|
||||
${this._type === 'answered'
|
||||
? this._renderPostAnswerButtons()
|
||||
: this._renderPreAnswerButtons()}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
private _renderPreAnswerButtons(): TemplateResult {
|
||||
return html`
|
||||
<div class="buttons">
|
||||
${this._renderButton(
|
||||
'mdi:phone-hangup',
|
||||
localize('config.live.controls.call.reject'),
|
||||
{
|
||||
emphasis: 'negative',
|
||||
action: createCallEndAction(),
|
||||
},
|
||||
)}
|
||||
${this._renderButton('mdi:phone', localize('config.live.controls.call.answer'), {
|
||||
emphasis: 'positive',
|
||||
action: createCallAnswerAction(),
|
||||
})}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderPostAnswerButtons(): TemplateResult {
|
||||
const microphoneMuted = this.microphoneState?.muted ?? true;
|
||||
const audioAvailable = this.muted !== undefined;
|
||||
const audioMuted = this.muted ?? true;
|
||||
|
||||
return html`
|
||||
<div class="buttons">
|
||||
${this._renderButton(
|
||||
'mdi:phone-hangup',
|
||||
localize('config.live.controls.call.end'),
|
||||
{
|
||||
emphasis: 'critical',
|
||||
emphasis: 'negative',
|
||||
action: createCallEndAction(),
|
||||
},
|
||||
)}
|
||||
@@ -103,7 +156,7 @@ export class AdvancedCameraCardCallControls extends LitElement {
|
||||
? localize('config.live.controls.call.unmute_microphone')
|
||||
: localize('config.live.controls.call.mute_microphone'),
|
||||
{
|
||||
emphasis: microphoneMuted ? undefined : 'critical',
|
||||
emphasis: microphoneMuted ? undefined : 'negative',
|
||||
action: createGeneralAction(
|
||||
microphoneMuted ? 'microphone_unmute' : 'microphone_mute',
|
||||
),
|
||||
@@ -120,7 +173,7 @@ export class AdvancedCameraCardCallControls extends LitElement {
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
</div>`;
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleKeyDown = (ev: KeyboardEvent): void => {
|
||||
@@ -142,7 +195,7 @@ export class AdvancedCameraCardCallControls extends LitElement {
|
||||
label: string,
|
||||
options?: {
|
||||
disabled?: boolean;
|
||||
emphasis?: 'critical';
|
||||
emphasis?: 'negative' | 'positive';
|
||||
action?: ActionConfig;
|
||||
handler?: () => void;
|
||||
},
|
||||
@@ -152,7 +205,7 @@ export class AdvancedCameraCardCallControls extends LitElement {
|
||||
.label=${label}
|
||||
title=${label}
|
||||
?disabled=${!!options?.disabled}
|
||||
class=${options?.emphasis === 'critical' ? 'critical' : ''}
|
||||
class=${options?.emphasis ?? ''}
|
||||
@click=${() => {
|
||||
if (options?.handler) {
|
||||
options.handler();
|
||||
|
||||
Reference in New Issue
Block a user