Add message priorities.

This commit is contained in:
Dermot Duffy
2022-06-24 19:42:25 -07:00
parent b86f44627f
commit 5a3703457d
9 changed files with 73 additions and 49 deletions
+10 -7
View File
@@ -63,6 +63,7 @@ import {
MediaShowInfo, MediaShowInfo,
MEDIA_TYPE_IMAGE, MEDIA_TYPE_IMAGE,
MEDIA_TYPE_VIDEO, MEDIA_TYPE_VIDEO,
MESSAGE_TYPE_PRIORITIES,
MenuButton, MenuButton,
Message, Message,
RawFrigateCardConfig, RawFrigateCardConfig,
@@ -1115,7 +1116,7 @@ export class FrigateCard extends LitElement {
{ {
message: localize('error.reconnecting'), message: localize('error.reconnecting'),
icon: 'mdi:lan-disconnect', icon: 'mdi:lan-disconnect',
type: 'info', type: 'connection',
dotdotdot: true, dotdotdot: true,
}, },
true, true,
@@ -1403,7 +1404,7 @@ export class FrigateCard extends LitElement {
this._setMessageAndUpdate({ this._setMessageAndUpdate({
message: localize('error.diagnostics'), message: localize('error.diagnostics'),
type: 'info', type: 'diagnostics',
icon: 'mdi:information', icon: 'mdi:information',
context: { context: {
ha_version: this._hass.config.version, ha_version: this._hass.config.version,
@@ -1562,8 +1563,12 @@ export class FrigateCard extends LitElement {
* @param skipUpdate If true an update request is skipped. * @param skipUpdate If true an update request is skipped.
*/ */
protected _setMessageAndUpdate(message: Message, skipUpdate?: boolean): void { protected _setMessageAndUpdate(message: Message, skipUpdate?: boolean): void {
// Register the first message, or prioritize errors if there's pre-render competition. const currentPriority = this._message
if (!this._message || (message.type == 'error' && this._message.type != 'error')) { ? MESSAGE_TYPE_PRIORITIES[this._message.type] ?? 0
: 0;
const newPriority = MESSAGE_TYPE_PRIORITIES[message.type] ?? 0;
if (!this._message || newPriority >= currentPriority) {
this._message = message; this._message = message;
if (!skipUpdate) { if (!skipUpdate) {
this.requestUpdate(); this.requestUpdate();
@@ -1650,14 +1655,12 @@ export class FrigateCard extends LitElement {
// - Aspect ratio enforcement is disabled. // - Aspect ratio enforcement is disabled.
// - Aspect ratio enforcement is dynamic and it's a media view (i.e. not the // - Aspect ratio enforcement is dynamic and it's a media view (i.e. not the
// gallery) or timeline. // gallery) or timeline.
// - There is a message to display to the user.
return !( return !(
(screenfull.isEnabled && screenfull.isFullscreen) || (screenfull.isEnabled && screenfull.isFullscreen) ||
aspectRatioMode == 'unconstrained' || aspectRatioMode == 'unconstrained' ||
(aspectRatioMode == 'dynamic' && (aspectRatioMode == 'dynamic' &&
(this._view?.isAnyMediaView() || this._view?.is('timeline'))) || (this._view?.isAnyMediaView() || this._view?.is('timeline')))
this._message != null
); );
} }
+1 -1
View File
@@ -237,7 +237,7 @@ export class FrigateCardImage extends LitElement {
dispatchErrorMessageEvent( dispatchErrorMessageEvent(
this, this,
localize('error.image_load_error'), localize('error.image_load_error'),
this.imageConfig, { context: this.imageConfig },
); );
} }
}} }}
+6 -6
View File
@@ -15,7 +15,7 @@ import { customElement, property, state } from 'lit/decorators.js';
import { createRef, Ref, ref } from 'lit/directives/ref.js'; import { createRef, Ref, ref } from 'lit/directives/ref.js';
import { until } from 'lit/directives/until.js'; import { until } from 'lit/directives/until.js';
import { ConditionState, getOverriddenConfig } from '../card-condition.js'; import { ConditionState, getOverriddenConfig } from '../card-condition.js';
import { renderProgressIndicator } from '../components/message.js'; import { dispatchFrigateCardErrorEvent, renderProgressIndicator } from '../components/message.js';
import { localize } from '../localize/localize.js'; import { localize } from '../localize/localize.js';
import liveFrigateStyle from '../scss/live-frigate.scss'; import liveFrigateStyle from '../scss/live-frigate.scss';
import liveJSMPEGStyle from '../scss/live-jsmpeg.scss'; import liveJSMPEGStyle from '../scss/live-jsmpeg.scss';
@@ -751,7 +751,7 @@ export class FrigateCardLiveFrigate extends LitElement {
return dispatchErrorMessageEvent( return dispatchErrorMessageEvent(
this, this,
localize('error.no_live_camera'), localize('error.no_live_camera'),
this.cameraConfig, { context: this.cameraConfig },
); );
} }
@@ -760,7 +760,7 @@ export class FrigateCardLiveFrigate extends LitElement {
return dispatchErrorMessageEvent( return dispatchErrorMessageEvent(
this, this,
localize('error.live_camera_unavailable'), localize('error.live_camera_unavailable'),
this.cameraConfig, { context: this.cameraConfig },
); );
} }
@@ -906,7 +906,7 @@ export class FrigateCardLiveWebRTCCard extends LitElement {
e instanceof FrigateCardError e instanceof FrigateCardError
? e.message ? e.message
: localize('error.webrtc_card_reported_error') + ': ' + (e as Error).message, : localize('error.webrtc_card_reported_error') + ': ' + (e as Error).message,
(e as FrigateCardError).context, { context: (e as FrigateCardError).context },
); );
} }
if (webrtcElement) { if (webrtcElement) {
@@ -924,7 +924,7 @@ export class FrigateCardLiveWebRTCCard extends LitElement {
return html`${this._webrtcTask.render({ return html`${this._webrtcTask.render({
initial: () => renderProgressIndicator(localize('error.webrtc_card_waiting')), initial: () => renderProgressIndicator(localize('error.webrtc_card_waiting')),
pending: () => renderProgressIndicator(localize('error.webrtc_card_waiting')), pending: () => renderProgressIndicator(localize('error.webrtc_card_waiting')),
error: (e: unknown) => dispatchErrorMessageEvent(this, (e as Error).message), error: (e: unknown) => dispatchFrigateCardErrorEvent(this, e as Error),
complete: () => render(), complete: () => render(),
})}`; })}`;
} }
@@ -1147,7 +1147,7 @@ export class FrigateCardLiveJSMPEG extends LitElement {
return dispatchErrorMessageEvent( return dispatchErrorMessageEvent(
this, this,
localize('error.no_camera_name'), localize('error.no_camera_name'),
this.cameraConfig, { context: this.cameraConfig },
); );
} }
+25 -20
View File
@@ -1,9 +1,10 @@
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit'; import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
import { customElement, property } from 'lit/decorators.js'; import { customElement, property } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { TROUBLESHOOTING_URL } from '../const.js'; import { TROUBLESHOOTING_URL } from '../const.js';
import { localize } from '../localize/localize.js'; import { localize } from '../localize/localize.js';
import messageStyle from '../scss/message.scss'; import messageStyle from '../scss/message.scss';
import { FrigateCardError, Message } from '../types.js'; import { FrigateCardError, Message, MessageType } from '../types.js';
import { dispatchFrigateCardEvent } from '../utils/basic.js'; import { dispatchFrigateCardEvent } from '../utils/basic.js';
@customElement('frigate-card-message') @customElement('frigate-card-message')
@@ -23,14 +24,18 @@ export class FrigateCardMessage extends LitElement {
// Render the menu. // Render the menu.
protected render(): TemplateResult { protected render(): TemplateResult {
const icon = this.icon ? this.icon : 'mdi:information-outline'; const icon = this.icon ? this.icon : 'mdi:information-outline';
const classes = {
dotdotdot: !!this.dotdotdot,
};
return html` <div class="wrapper"> return html` <div class="wrapper">
<div class="message"> <div class="message">
<div class="icon"> <div class="icon">
<ha-icon icon="${icon}"> </ha-icon> <ha-icon icon="${icon}"> </ha-icon>
</div> </div>
<div class="contents"> <div class="contents">
<span> ${this.message ? html`${this.message}` : ''} </span> <span class="${classMap(classes)}">
${this.dotdotdot ? html`<span class="dotdotdot"></span>` : ``} ${this.message ? html`${this.message}` : ''}
</span>
${this.context ${this.context
? html`<pre>${JSON.stringify(this.context, null, 2)}</pre>` ? html`<pre>${JSON.stringify(this.context, null, 2)}</pre>`
: ''} : ''}
@@ -92,7 +97,7 @@ export function renderMessage(message: Message): TemplateResult {
return html` <frigate-card-error-message return html` <frigate-card-error-message
.message=${message} .message=${message}
></frigate-card-error-message>`; ></frigate-card-error-message>`;
} else if (message.type === 'info') { } else {
return html` <frigate-card-message return html` <frigate-card-message
.message=${message.message} .message=${message.message}
.icon=${message.icon} .icon=${message.icon}
@@ -114,19 +119,22 @@ export function renderProgressIndicator(message?: string): TemplateResult {
* Dispatch an event with a message to show to the user. * Dispatch an event with a message to show to the user.
* @param element The element to send the event. * @param element The element to send the event.
* @param message The message to show. * @param message The message to show.
* @param icon An optional icon to attach to the message. * @param options Optional icon and context to include.
*/ */
export function dispatchMessageEvent( export function dispatchMessageEvent(
element: EventTarget, element: EventTarget,
message: string, message: string,
icon?: string, type: MessageType,
context?: unknown, options?: {
icon?: string;
context?: unknown;
},
): void { ): void {
dispatchFrigateCardEvent<Message>(element, 'message', { dispatchFrigateCardEvent<Message>(element, 'message', {
message: message, message: message,
type: 'info', type: type,
icon: icon, icon: options?.icon,
context: context, context: options?.context,
}); });
} }
@@ -134,16 +142,17 @@ export function dispatchMessageEvent(
* Dispatch an event with an error message to show to the user. * Dispatch an event with an error message to show to the user.
* @param element The element to send the event. * @param element The element to send the event.
* @param message The message to show. * @param message The message to show.
* @param options Optional context to include.
*/ */
export function dispatchErrorMessageEvent( export function dispatchErrorMessageEvent(
element: EventTarget, element: EventTarget,
message: string, message: string,
context?: unknown, options?: {
context?: unknown;
},
): void { ): void {
dispatchFrigateCardEvent<Message>(element, 'message', { dispatchMessageEvent(element, message, 'error', {
message: message, context: options?.context,
type: 'error',
context: context,
}); });
} }
@@ -156,11 +165,7 @@ export function dispatchFrigateCardErrorEvent(
element: EventTarget, element: EventTarget,
error: FrigateCardError, error: FrigateCardError,
): void { ): void {
dispatchFrigateCardEvent<Message>(element, 'message', { dispatchErrorMessageEvent(element, error.message, { context: error.context });
message: error.message,
type: 'error',
context: error.context || '',
});
} }
declare global { declare global {
+4 -1
View File
@@ -1227,7 +1227,10 @@ export class FrigateCardTimelineCore extends LitElement {
dispatchMessageEvent( dispatchMessageEvent(
this, this,
localize('error.timeline_no_cameras'), localize('error.timeline_no_cameras'),
'mdi:chart-gantt', 'info',
{
icon: 'mdi:chart-gantt',
}
); );
return; return;
} }
+2 -2
View File
@@ -13,7 +13,7 @@ import { customElement, property } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js'; import { ifDefined } from 'lit/directives/if-defined.js';
import { ref } from 'lit/directives/ref.js'; import { ref } from 'lit/directives/ref.js';
import { import {
dispatchErrorMessageEvent, dispatchFrigateCardErrorEvent,
renderProgressIndicator renderProgressIndicator
} from '../components/message.js'; } from '../components/message.js';
import viewerStyle from '../scss/viewer.scss'; import viewerStyle from '../scss/viewer.scss';
@@ -632,7 +632,7 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
return html`${this._mediaResolutionTask.render({ return html`${this._mediaResolutionTask.render({
initial: () => renderProgressIndicator(), initial: () => renderProgressIndicator(),
pending: () => renderProgressIndicator(), pending: () => renderProgressIndicator(),
error: (e: unknown) => dispatchErrorMessageEvent(this, (e as Error).message), error: (e: unknown) => dispatchFrigateCardErrorEvent(this, e as Error),
complete: () => this._render(), complete: () => this._render(),
})}`; })}`;
} }
+5 -2
View File
@@ -25,8 +25,11 @@ div.message {
} }
div.message div.contents { div.message div.contents {
display: flex;
flex-direction: column;
padding: 10px; padding: 10px;
height: 100%; margin-top: auto;
margin-bottom: auto;
max-width: 100%; max-width: 100%;
} }
@@ -49,7 +52,7 @@ div.message div.icon {
word-break: break-all; word-break: break-all;
} }
.dotdotdot:before { .dotdotdot:after {
@keyframes dots { @keyframes dots {
0%, 0%,
20% { 20% {
+10 -1
View File
@@ -1188,9 +1188,18 @@ export interface MediaShowInfo {
height: number; height: number;
} }
export const MESSAGE_TYPE_PRIORITIES = {
info: 10,
error: 20,
connection: 30,
diagnostics: 40,
}
export type MessageType = 'info' | 'error' | 'connection' | 'diagnostics';
export interface Message { export interface Message {
message: string; message: string;
type: 'error' | 'info'; type: MessageType,
icon?: string; icon?: string;
context?: unknown; context?: unknown;
dotdotdot?: boolean; dotdotdot?: boolean;
+10 -9
View File
@@ -3,13 +3,13 @@ import {
differenceInHours, differenceInHours,
differenceInMinutes, differenceInMinutes,
differenceInSeconds, differenceInSeconds,
fromUnixTime fromUnixTime,
} from 'date-fns'; } from 'date-fns';
import { homeAssistantWSRequest } from '.'; import { homeAssistantWSRequest } from '.';
import { import {
dispatchErrorMessageEvent, dispatchErrorMessageEvent,
dispatchFrigateCardErrorEvent, dispatchFrigateCardErrorEvent,
dispatchMessageEvent dispatchMessageEvent,
} from '../../components/message.js'; } from '../../components/message.js';
import { localize } from '../../localize/localize.js'; import { localize } from '../../localize/localize.js';
import { import {
@@ -24,7 +24,7 @@ import {
MEDIA_CLASS_PLAYLIST, MEDIA_CLASS_PLAYLIST,
MEDIA_CLASS_VIDEO, MEDIA_CLASS_VIDEO,
MEDIA_TYPE_PLAYLIST, MEDIA_TYPE_PLAYLIST,
MEDIA_TYPE_VIDEO MEDIA_TYPE_VIDEO,
} from '../../types.js'; } from '../../types.js';
import { View } from '../../view.js'; import { View } from '../../view.js';
import { getCameraTitle } from '../camera.js'; import { getCameraTitle } from '../camera.js';
@@ -318,11 +318,9 @@ export const getFullDependentBrowseMediaQueryParametersOrDispatchError = (
mediaType, mediaType,
); );
if (!params) { if (!params) {
dispatchErrorMessageEvent( dispatchErrorMessageEvent(element, localize('error.no_camera_name'), {
element, context: cameras.get(camera),
localize('error.no_camera_name'), });
cameras.get(camera),
);
return null; return null;
} }
return params; return params;
@@ -357,7 +355,10 @@ export const fetchLatestMediaAndDispatchViewChange = async (
view.isClipRelatedView() view.isClipRelatedView()
? localize('common.no_clip') ? localize('common.no_clip')
: localize('common.no_snapshot'), : localize('common.no_snapshot'),
view.isClipRelatedView() ? 'mdi:filmstrip-off' : 'mdi:camera-off', 'info',
{
icon: view.isClipRelatedView() ? 'mdi:filmstrip-off' : 'mdi:camera-off',
},
); );
} }