From 915053261e3385d65fff1d266dee2e60c5f9f777 Mon Sep 17 00:00:00 2001 From: Dermot Duffy Date: Sat, 25 Dec 2021 15:58:45 -0800 Subject: [PATCH] Fix error condition rendering. --- src/card.ts | 255 ++++++++++++++++----------------- src/components/live.ts | 5 +- src/editor.ts | 9 +- src/localize/languages/en.json | 4 +- src/scss/editor.scss | 2 +- 5 files changed, 138 insertions(+), 137 deletions(-) diff --git a/src/card.ts b/src/card.ts index 323f0409..5f7a7c6a 100644 --- a/src/card.ts +++ b/src/card.ts @@ -402,6 +402,7 @@ export class FrigateCard extends LitElement { */ protected async _loadCameras(): Promise { const cameras: Map = new Map(); + let errorFree = true; const addCameraConfig = async (config: CameraConfig) => { if (!config.camera_name && config.camera_entity) { @@ -416,13 +417,11 @@ export class FrigateCard extends LitElement { if (config.camera_name) { const id = config.card_id || config.camera_entity || config.camera_name; if (cameras.has(id)) { - this._setMessageAndUpdate( - { - message: localize('error.duplicate_frigate_camera_name'), - type: 'error', - }, - true, - ); + this._setMessageAndUpdate({ + message: localize('error.duplicate_frigate_camera_name'), + type: 'error', + }); + errorFree = false; } else { cameras.set(id, config); } @@ -434,16 +433,16 @@ export class FrigateCard extends LitElement { } if (!cameras.size) { - return this._setMessageAndUpdate( - { - message: localize('error.no_cameras'), - type: 'error', - }, - true, - ); + return this._setMessageAndUpdate({ + message: localize('error.no_cameras'), + type: 'error', + }); + errorFree = false; } - this._cameras = cameras; + if (errorFree) { + this._cameras = cameras; + } } /** @@ -602,10 +601,12 @@ export class FrigateCard extends LitElement { this._changeView(); } - protected _changeView(view?: View): void { - this._message = null; + protected _changeView(args?: { view?: View; resetMessage?: boolean }): void { + if (args?.resetMessage ?? true) { + this._message = null; + } - if (view === undefined) { + if (args?.view === undefined) { let camera = this._view?.camera; if (!camera && this._cameras?.size) { camera = this._cameras.keys().next().value; @@ -616,11 +617,12 @@ export class FrigateCard extends LitElement { view: this.config.view.default, camera: camera, }); + this._generateConditionState(); } } else { - this._view = view; + this._view = args.view; + this._generateConditionState(); } - this._generateConditionState(); } /** @@ -628,7 +630,7 @@ export class FrigateCard extends LitElement { * @param e The change view event. */ protected _changeViewHandler(e: CustomEvent): void { - this._changeView(e.detail); + this._changeView({ view: e.detail }); } /** @@ -762,12 +764,12 @@ export class FrigateCard extends LitElement { case 'snapshot': case 'snapshots': if (this._view) { - this._changeView( - new View({ + this._changeView({ + view: new View({ view: action, camera: this._view.camera, }), - ); + }); } break; case 'download': @@ -787,20 +789,14 @@ export class FrigateCard extends LitElement { case 'camera_select': const camera = frigateCardAction.camera; if (this._cameras?.has(camera) && this._view) { - this._changeView( - new View({ + this._changeView({ + view: new View({ view: this._view.view, camera: camera, }), - ); + }); } break; - // case 'next_camera': - // this._changeCamera({ next: true }); - // break; - // case 'previous_camera': - // this._changeCamera({ previous: true }); - // break; default: console.warn(`Frigate card received unknown card action: ${action}`); } @@ -1058,6 +1054,11 @@ export class FrigateCard extends LitElement { absolute: padding != null, }; + const pictureElementsClasses = { + 'picture-elements': true, + gallery: !!this._view?.isGalleryView(), + }; + const actions = this._getMergedActions(); return html` this._actionHandler(ev, actions)} @ll-custom=${this._cardActionHandler.bind(this)} + @frigate-card:message=${this._messageHandler} + @frigate-card:change-view=${this._changeViewHandler} + @frigate-card:media-show=${this._mediaShowHandler} + @frigate-card:pause=${this._pauseHandler} + @frigate-card:play=${this._playHandler} > ${this.config.menu.mode == 'above' ? this._renderMenu() : ''}
- ${this._cameras === undefined - ? until( - (async () => { - await this._loadCameras(); - this._changeView(); - return this._render(); - })(), - renderProgressIndicator(), - ) - : this._render()} +
+ ${this.config.elements + ? // Always show elements to allow for custom menu items (etc.) to + // be present even if a particular view has an error. + html` { + this._addDynamicMenuButton(e.detail); + }} + @frigate-card:menu-remove=${(e) => { + this._removeDynamicMenuButton(e.detail); + }} + @frigate-card:condition-state-request=${(ev) => { + conditionStateRequestHandler(ev, this._conditionState); + }} + > + ` + : ``} + ${this._cameras === undefined + ? until( + (async () => { + await this._loadCameras(); + // Don't reset messages as errors may have been generated + // during the camera load. + this._changeView({ resetMessage: false }); + return this._render(); + })(), + renderProgressIndicator(), + ) + : // Always want to call render even if there's a message, to + // ensure live preload is always present (even if not displayed). + this._render()} + ${ + // Keep message rendering to last to show messages that may have + // been generated during the render. + this._message ? renderMessage(this._message) : '' + } +
${this.config.menu.mode != 'above' ? this._renderMenu() : ''} @@ -1097,10 +1133,6 @@ export class FrigateCard extends LitElement { return html``; } - const pictureElementsClasses = { - 'picture-elements': true, - gallery: this._view.isGalleryView(), - }; const galleryClasses = { hidden: this.config.live.preload && !this._view.isGalleryView(), }; @@ -1115,93 +1147,56 @@ export class FrigateCard extends LitElement { }; return html` -
- ${this._message ? renderMessage(this._message) : ``} - ${!this._message && this._view.is('image') - ? html` - ` - : ``} - ${!this._message && this._view.isGalleryView() - ? html` - ` - : ``} - ${!this._message && this._view.isViewerView() - ? html` - ` - : ``} - ${ - // Note the subtle difference in condition below vs the other views in order - // to always render the live view for live.preload mode. - (!this._message && this._view.is('live')) || this.config.live.preload - ? html` - - - ` - : `` - } - ${this.config.elements + ${!this._message && this._view.is('image') + ? html` + ` + : ``} + ${!this._message && this._view.isGalleryView() + ? html` + ` + : ``} + ${!this._message && this._view.isViewerView() + ? html` + ` + : ``} + ${ + // Note the subtle difference in condition below vs the other views in order + // to always render the live view for live.preload mode. + (!this._message && this._view.is('live')) || this.config.live.preload ? html` - { - this._addDynamicMenuButton(e.detail); - }} - @frigate-card:menu-remove=${(e) => { - this._removeDynamicMenuButton(e.detail); - }} - @frigate-card:condition-state-request=${(ev) => { - conditionStateRequestHandler(ev, this._conditionState); - }} + .view=${this._view} + .liveConfig=${this.config.live} + .cameras=${this._cameras} + .preload=${this.config.live.preload && !this._view.is('live')} + class="${classMap(liveClasses)}" > - + ` - : ``} -
+ : `` + } `; } diff --git a/src/components/live.ts b/src/components/live.ts index afe6a53b..67764a48 100644 --- a/src/components/live.ts +++ b/src/components/live.ts @@ -1,6 +1,9 @@ -// TODO editor // TODO editor: fill in a new camera and keep focus // TODO editor: fill in a new camera then backspace it away +// TODO editor: render correctly when name title is really long +// TODO editor: should not be able to move up a new camera before it's not new. +// TODO reconsider card_id, and using entity (which is unique) as that id +// TODO rename card_id to id? // TODO webrtc entities in camera section? // TODO conditional elements based on camera name (requires event changed to propagate upwards) // TODO change url to frigate_url? Would need to also fix upgrade logic to refer to new name. diff --git a/src/editor.ts b/src/editor.ts index 078d92b7..f4ecb527 100644 --- a/src/editor.ts +++ b/src/editor.ts @@ -255,7 +255,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor */ protected _renderCameraHeader( cameraIndex: number, - cameraConfig: RawFrigateCardConfig | undefined, + cameraConfig?: RawFrigateCardConfig, addNewCamera?: boolean, ): TemplateResult { return html` @@ -275,11 +275,10 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor // starting with the most likely to be useful and working our // ways towards the least useful. html` - Camera: ${cameraConfig?.title || - cameraConfig?.camera_entity || cameraConfig?.card_id || [ + cameraConfig?.camera_entity, cameraConfig?.client_id, cameraConfig?.camera_name, cameraConfig?.label, @@ -771,6 +770,10 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor return; } + if (getConfigValue(this._config, key) === value) { + return; + } + const newConfig = copyConfig(this._config); if (value === '' || typeof value === 'undefined') { deleteConfigValue(newConfig, key); diff --git a/src/localize/languages/en.json b/src/localize/languages/en.json index c81700ed..9661af17 100644 --- a/src/localize/languages/en.json +++ b/src/localize/languages/en.json @@ -155,8 +155,8 @@ "invalid_configuration_no_hint": "No location hint available (bad or missing type?)", "upgrade_available": "An automated card configuration upgrade is available, please visit the visual card editor", "missing_webrtc": "WebRTC component not found", - "no_cameras": "No cameras found, you must configure at least one camera configured with a camera_entity or camera_name", - "duplicate_frigate_camera_name": "Duplicate Frigate camera name, use the 'id' parameter to uniquely identify them", + "no_cameras": "No valid cameras found, you must configure at least one camera with either a camera_entity or camera_name", + "duplicate_frigate_camera_name": "Duplicate Frigate camera name, use the 'card_id' parameter to uniquely identify them", "could_not_render_elements": "Could not render picture elements", "invalid_elements_config": "Invalid picture elements configuration", "jsmpeg_no_sign": "Could not retrieve or sign JSMPEG websocket path", diff --git a/src/scss/editor.scss b/src/scss/editor.scss index a2813fa0..5ba61e1f 100644 --- a/src/scss/editor.scss +++ b/src/scss/editor.scss @@ -20,7 +20,6 @@ pointer-events: none; } .values { - margin-left: 30px; padding: 10px; background: var(--secondary-background-color); display: grid; @@ -54,6 +53,7 @@ div.upgrade span { } .camera-header .new-camera { font-style: italic; + color: var(--secondary-text-color, 'black'); } .cameras { margin: 5px 5px 10px 20px;