diff --git a/src/card.ts b/src/card.ts index 3587535d..55d3881c 100644 --- a/src/card.ts +++ b/src/card.ts @@ -43,6 +43,7 @@ import { FrigateCardElements } from './components/elements.js'; import { FRIGATE_BUTTON_MENU_ICON, FrigateCardMenu } from './components/menu.js'; import { View } from './view.js'; import { + contentsChanged, convertActionToFrigateCardCustomAction, createFrigateCardCustomAction, getActionConfigGivenAction, @@ -231,11 +232,17 @@ export class FrigateCard extends LitElement { camera: this._view?.camera, }; - this._overriddenConfig = getOverriddenConfig( + const overriddenConfig = getOverriddenConfig( this._baseConfig, this._baseConfig.overrides, this._conditionState, ) as FrigateCardConfig; + + // Save on Lit re-rendering costs by only updating the configuration if it + // actually changes. + if (contentsChanged(overriddenConfig, this._overriddenConfig)) { + this._overriddenConfig = overriddenConfig; + } } /** diff --git a/src/common.ts b/src/common.ts index ed09ecb4..46327533 100644 --- a/src/common.ts +++ b/src/common.ts @@ -2,6 +2,7 @@ import { HassEntity, MessageBase } from 'home-assistant-js-websocket'; import { HomeAssistant, stateIcon } from 'custom-card-helpers'; import { StyleInfo } from 'lit/directives/style-map'; import { ZodSchema, z } from 'zod'; +import { isEqual } from 'lodash-es'; import { localize } from './localize/localize.js'; import { @@ -483,3 +484,15 @@ export function arrayMove(target: unknown[], from: number, to: number): void { target.splice(from, 1); target.splice(to, 0, element); } + +/** + * Determine if the contents of the n(ew) and o(ld) values have changed. For use + * in lit web components that may have a value that changes address but not + * contents -- and for which a re-render is expensive/jarring. + * @param n The new value. + * @param o The old value. + * @returns `true` is the contents have changed. + */ +export function contentsChanged(n: unknown, o: unknown): boolean { + return !isEqual(n, o); +} \ No newline at end of file diff --git a/src/components/live.ts b/src/components/live.ts index cfe29598..9689e499 100644 --- a/src/components/live.ts +++ b/src/components/live.ts @@ -1,4 +1,3 @@ -// TODO use the schema default remover code to simplify config schema // TODO in fullscreen mode on live view with mixed cameras, center video // TODO verify README links worked correctly (e.g. basic cameras configuration) @@ -26,7 +25,6 @@ import { import { EmblaOptionsType } from 'embla-carousel'; import { HomeAssistant } from 'custom-card-helpers'; import { customElement, property, state } from 'lit/decorators.js'; -import { isEqual } from 'lodash-es'; import { ref } from 'lit/directives/ref'; import { until } from 'lit/directives/until.js'; @@ -38,6 +36,7 @@ import { ThumbnailCarouselTap } from './thumbnail-carousel.js'; import { View } from '../view.js'; import { localize } from '../localize/localize.js'; import { + contentsChanged, dispatchErrorMessageEvent, dispatchExistingMediaShowInfoAsEvent, dispatchMediaShowEvent, @@ -518,7 +517,7 @@ export class FrigateCardLiveProvider extends LitElement { // Label that is used for ARIA support and as tooltip. @property({ attribute: false }) - public label = ""; + public label = ''; protected _getResolvedProvider(): LiveProvider { if (this.cameraConfig?.live_provider === 'auto') { @@ -531,7 +530,9 @@ export class FrigateCardLiveProvider extends LitElement { } return frigateCardConfigDefaults.cameras.live_provider; } - return this.cameraConfig?.live_provider || frigateCardConfigDefaults.cameras.live_provider; + return ( + this.cameraConfig?.live_provider || frigateCardConfigDefaults.cameras.live_provider + ); } /** @@ -618,18 +619,7 @@ export class FrigateCardLiveFrigate extends LitElement { // - https://github.com/AlexxIT/WebRTC @customElement('frigate-card-live-webrtc') export class FrigateCardLiveWebRTC extends LitElement { - @property({ - attribute: false, - - // Resetting the WebRTC/JSMPEG configuration is expensive as the connections - // need to be re-established. These configurations may be overridden which - // creates semantically equal configurations at different addresses -- - // ensure LIT only considers the property as having changed if it's actually - // different. - hasChanged(n: WebRTCConfig, o: WebRTCConfig): boolean { - return !isEqual(n, o); - }, - }) + @property({ attribute: false, hasChanged: contentsChanged }) protected webRTCConfig?: WebRTCConfig; @property({ attribute: false }) @@ -734,13 +724,7 @@ export class FrigateCardLiveJSMPEG extends LitElement { @property({ attribute: false }) protected cameraConfig?: CameraConfig; - @property({ - attribute: false, - // See note under FrigateCardLiveWebRTC. - hasChanged(n: JSMPEGConfig, o: JSMPEGConfig): boolean { - return !isEqual(n, o); - }, - }) + @property({ attribute: false, hasChanged: contentsChanged }) protected jsmpegConfig?: JSMPEGConfig; protected hass?: HomeAssistant & ExtendedHomeAssistant; diff --git a/src/components/viewer.ts b/src/components/viewer.ts index b883824b..3c43bfca 100644 --- a/src/components/viewer.ts +++ b/src/components/viewer.ts @@ -31,7 +31,7 @@ import { } from './thumbnail-carousel.js'; import { ResolvedMediaCache, ResolvedMediaUtil } from '../resolved-media.js'; import { View } from '../view.js'; -import { createMediaShowInfo, dispatchErrorMessageEvent } from '../common.js'; +import { contentsChanged, createMediaShowInfo, dispatchErrorMessageEvent } from '../common.js'; import { renderProgressIndicator } from '../components/message.js'; import './next-prev-control.js'; @@ -111,7 +111,8 @@ export class FrigateCardViewerCore extends LitElement { @property({ attribute: false }) protected view?: Readonly; - @property({ attribute: false }) + // See note on viewerConfig in . + @property({ attribute: false, hasChanged: contentsChanged }) protected viewerConfig?: ViewerConfig; @property({ attribute: false }) @@ -186,7 +187,12 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel { @property({ attribute: false }) protected view?: Readonly; - @property({ attribute: false }) + // Resetting the viewer configuration causes a full reset so ensure the config + // has actually changed with a full comparison (dynamic configuration + // overrides may causes changes elsewhere in the full card configuration that + // could lead to the address of the viewerConfig changing without it being + // semantically different). + @property({ attribute: false, hasChanged: contentsChanged }) protected viewerConfig?: ViewerConfig; @property({ attribute: false }) diff --git a/src/scss/carousel.scss b/src/scss/carousel.scss index 66ea1971..68a2132e 100644 --- a/src/scss/carousel.scss +++ b/src/scss/carousel.scss @@ -33,6 +33,11 @@ img,video { width: 100%; height: 100%; overflow: hidden; + + // Center vertically in the viewport. + display: flex; + flex-direction: column; + justify-content: center; } .embla__viewport.is-draggable { cursor: move;