Componentize messages, error messages and progress indicator.

This commit is contained in:
Dermot Duffy
2021-09-20 21:33:37 -07:00
parent e4f07ac50d
commit 63a5a93eeb
5 changed files with 105 additions and 49 deletions
+17 -47
View File
@@ -11,6 +11,8 @@ import { customElement, property, query, state } from 'lit/decorators';
import { classMap } from 'lit/directives/class-map.js';
import { until } from 'lit/directives/until.js';
import { renderMessage, renderErrorMessage, renderProgressIndicator } from './message';
import {
HomeAssistant,
LovelaceCardEditor,
@@ -50,9 +52,6 @@ import { MessageBase } from 'home-assistant-js-websocket';
import JSMpeg from '@cycjimmy/jsmpeg-player';
const URL_TROUBLESHOOTING =
'https://github.com/dermotduffy/frigate-hass-card#troubleshooting';
// Load dayjs plugin(s).
dayjs.extend(dayjs_custom_parse_format);
@@ -520,28 +519,6 @@ export class FrigateCard extends LitElement {
return this._makeWSRequest(resolvedMediaSchema, request);
}
// Render an attention grabbing icon.
protected _renderAttentionIcon(
icon: string,
message: string | TemplateResult | null = null,
): TemplateResult {
return html` <div class="attention">
<span>
<ha-icon icon="${icon}"> </ha-icon>
${message ? html`&nbsp;${message}` : ''}
</span>
</div>`;
}
// Render an embedded error situation.
protected _renderError(error: string): TemplateResult {
return this._renderAttentionIcon(
'mdi:alert-circle',
html`${error ? `${error}.` : `${localize('error.unknown_error')}.`}
<a href="${URL_TROUBLESHOOTING}">${localize('error.troubleshooting')}</a>.`,
);
}
// Render Frigate events into a card gallery.
protected async _renderEvents(): Promise<TemplateResult> {
let parent;
@@ -552,15 +529,15 @@ export class FrigateCard extends LitElement {
parent = await this._browseMediaQuery(this._view.is('clips'));
}
} catch (e: any) {
return this._renderError(e.message);
return renderErrorMessage(e.message);
}
if (this._getFirstTrueMediaChildIndex(parent) == null) {
return this._renderAttentionIcon(
this._view.is('clips') ? 'mdi:filmstrip-off' : 'mdi:camera-off',
return renderMessage(
this._view.is('clips')
? localize('common.no_clips')
: localize('common.no_snapshots'),
? localize('common.no_clips')
: localize('common.no_snapshots'),
this._view.is('clips') ? 'mdi:filmstrip-off' : 'mdi:camera-off',
);
}
@@ -625,13 +602,6 @@ export class FrigateCard extends LitElement {
</ul>`;
}
// Render a progress spinner while content loads.
protected _renderProgressIndicator(): TemplateResult {
return html` <div class="attention">
<ha-circular-progress active="true" size="large"></ha-circular-progress>
</div>`;
}
protected _menuActionHandler(name: string): void {
switch (name) {
case 'frigate':
@@ -802,15 +772,15 @@ export class FrigateCard extends LitElement {
try {
parent = await this._browseMediaQuery(this._view.is('clip'));
} catch (e: any) {
return this._renderError(e.message);
return renderErrorMessage(e.message);
}
childIndex = this._getFirstTrueMediaChildIndex(parent);
if (!parent || !parent.children || childIndex == null) {
return this._renderAttentionIcon(
this._view.is('clip') ? 'mdi:filmstrip-off' : 'mdi:camera-off',
return renderMessage(
this._view.is('clip')
? localize('common.no_clip')
: localize('common.no_snapshot'),
this._view.is('clip') ? 'mdi:filmstrip-off' : 'mdi:camera-off',
);
}
mediaToRender = parent.children[childIndex];
@@ -825,7 +795,7 @@ export class FrigateCard extends LitElement {
const resolvedMedia = await this._resolveMedia(mediaToRender);
if (!mediaToRender || !resolvedMedia) {
// Home Assistant could not resolve media item.
return this._renderError(localize('error.could_not_resolve'));
return renderErrorMessage(localize('error.could_not_resolve'));
}
const neighbors = this._getMediaNeighbors(parent, childIndex);
@@ -998,7 +968,7 @@ export class FrigateCard extends LitElement {
const jsmpeg_url = await this._getJSMPEGURL();
if (!jsmpeg_url) {
return this._renderError('Could not retrieve or sign JSMPEG websocket path');
return renderErrorMessage('Could not retrieve or sign JSMPEG websocket path');
}
// Return the html canvas node only after the JSMPEG video has loaded and
@@ -1029,9 +999,9 @@ export class FrigateCard extends LitElement {
// is always rendered (but sometimes hidden).
protected async _renderLiveViewer(): Promise<TemplateResult> {
if (!this._hass || !(this.config.camera_entity in this._hass.states)) {
return this._renderAttentionIcon(
'mdi:camera-off',
return renderMessage(
localize('error.no_live_camera'),
'mdi:camera-off',
);
}
if (this._webrtcElement) {
@@ -1091,13 +1061,13 @@ export class FrigateCard extends LitElement {
<div class="container_16_9 outer">
<div class="frigate-card-contents">
${this._view.is('clips') || this._view.is('snapshots')
? until(this._renderEvents(), this._renderProgressIndicator())
? until(this._renderEvents(), renderProgressIndicator())
: ``}
${this._view.is('clip') || this._view.is('snapshot')
? until(this._renderViewer(), this._renderProgressIndicator())
? until(this._renderViewer(), renderProgressIndicator())
: ``}
${this._view.is('live')
? until(this._renderLiveViewer(), this._renderProgressIndicator())
? until(this._renderLiveViewer(), renderProgressIndicator())
: ``}
</div>
</div>
+75
View File
@@ -0,0 +1,75 @@
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
import { customElement, property } from 'lit/decorators';
import { localize } from './localize/localize';
import frigate_card_message_style from './scss/message.scss';
const URL_TROUBLESHOOTING =
'https://github.com/dermotduffy/frigate-hass-card#troubleshooting';
@customElement('frigate-card-message')
export class FrigateCardMessage extends LitElement {
@property({ attribute: false })
protected message = '';
@property({ attribute: false })
protected icon = 'mdi:information-outline';
// Render the menu.
protected render(): TemplateResult {
return html` <div class="message">
<span>
<ha-icon icon="${this.icon}"> </ha-icon>
${this.message ? html`&nbsp;${this.message}` : ''}
</span>
</div>`;
}
static get styles(): CSSResultGroup {
return unsafeCSS(frigate_card_message_style);
}
}
@customElement('frigate-card-error-message')
export class FrigateCardErrorMessage extends LitElement {
@property({ attribute: false })
protected error = '';
protected render(): TemplateResult {
return html` <frigate-card-message
.message=${html` ${this.error}.
<a href="${URL_TROUBLESHOOTING}"> ${localize('error.troubleshooting')} </a>.`}
.icon=${'mdi:alert-circle'}
>
</frigate-card-message>`;
}
}
@customElement('frigate-card-progress-indicator')
export class FrigateCardProgressIndicator extends LitElement {
protected render(): TemplateResult {
return html` <div class="message">
<ha-circular-progress active="true" size="large"> </ha-circular-progress>
</div>`;
}
static get styles(): CSSResultGroup {
return unsafeCSS(frigate_card_message_style);
}
}
export function renderErrorMessage(error: string): TemplateResult {
return html`
<frigate-card-error-message .error=${error}></frigate-card-error-message>
`;
}
export function renderMessage(message: string, icon: string): TemplateResult {
return html`
<frigate-card-message .message=${message} .icon=${icon}></frigate-card-message>
`;
}
export function renderProgressIndicator(): TemplateResult {
return html` <frigate-card-progress-indicator> </frigate-card-progress-indicator> `;
}
+8
View File
@@ -0,0 +1,8 @@
.message {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
padding: 10%;
}