diff --git a/src/card.ts b/src/card.ts index a3dffc5d..4ffa9a4f 100644 --- a/src/card.ts +++ b/src/card.ts @@ -67,6 +67,7 @@ import { MenuButton, Message, RawFrigateCardConfig, + CardWideConfig, } from './types.js'; import { convertActionToFrigateCardCustomAction, @@ -166,6 +167,9 @@ export class FrigateCard extends LitElement { protected _config!: FrigateCardConfig; protected _rawConfig?: RawFrigateCardConfig; + @state() + protected _cardWideConfig?: CardWideConfig; + @state() protected _overriddenConfig?: FrigateCardConfig; @@ -976,6 +980,10 @@ export class FrigateCard extends LitElement { this._rawConfig = inputConfig; this._config = config; + this._cardWideConfig = { + performance: config.performance, + }; + this._overriddenConfig = undefined; this._cameras = undefined; this._view = undefined; @@ -1919,7 +1927,7 @@ export class FrigateCard extends LitElement { this._changeView({ resetMessage: false }); return this._render(); })(), - renderProgressIndicator(), + renderProgressIndicator({cardWideConfig: this._cardWideConfig}), ) : // Always want to call render even if there's a message, to // ensure live preload is always present (even if not displayed). @@ -1989,6 +1997,7 @@ export class FrigateCard extends LitElement { .cameras=${this._cameras} .galleryConfig=${this._getConfig().event_gallery} .dataManager=${this._dataManager} + .cardWideConfig=${this._cardWideConfig} > ` : ``} @@ -2000,6 +2009,7 @@ export class FrigateCard extends LitElement { .viewerConfig=${this._getConfig().media_viewer} .resolvedMediaCache=${this._resolvedMediaCache} .dataManager=${this._dataManager} + .cardWideConfig=${this._cardWideConfig} > ` : ``} @@ -2031,6 +2041,7 @@ export class FrigateCard extends LitElement { .liveOverrides=${getOverridesByKey(this._getConfig().overrides, 'live')} .cameras=${this._cameras} .dataManager=${this._dataManager} + .cardWideConfig=${this._cardWideConfig} class="${classMap(liveClasses)}" > diff --git a/src/components/gallery.ts b/src/components/gallery.ts index 32930e92..b00f613a 100644 --- a/src/components/gallery.ts +++ b/src/components/gallery.ts @@ -11,6 +11,7 @@ import { customElement, property } from 'lit/decorators.js'; import galleryStyle from '../scss/gallery.scss'; import { CameraConfig, + CardWideConfig, ExtendedHomeAssistant, frigateCardConfigDefaults, GalleryConfig, @@ -46,6 +47,9 @@ export class FrigateCardGallery extends LitElement { @property({ attribute: false }) public dataManager?: DataManager; + @property({ attribute: false }) + public cardWideConfig?: CardWideConfig; + /** * Master render method. * @returns A rendered template. @@ -96,7 +100,7 @@ export class FrigateCardGallery extends LitElement { browseMediaQueryParameters, ); } - return renderProgressIndicator(); + return renderProgressIndicator({cardWideConfig: this.cardWideConfig}); } return html` diff --git a/src/components/live.ts b/src/components/live.ts index 4f1f32d2..4a3b2f34 100644 --- a/src/components/live.ts +++ b/src/components/live.ts @@ -27,6 +27,7 @@ import liveCarouselStyle from '../scss/live-carousel.scss'; import liveProviderStyle from '../scss/live-provider.scss'; import { CameraConfig, + CardWideConfig, ExtendedHomeAssistant, frigateCardConfigDefaults, FrigateCardError, @@ -99,6 +100,9 @@ export class FrigateCardLive extends LitElement { @property({ attribute: false }) public dataManager?: DataManager; + @property({ attribute: false }) + public cardWideConfig?: CardWideConfig; + // Whether or not the live view is currently in the background (i.e. preloaded // but not visible) @state() @@ -250,6 +254,7 @@ export class FrigateCardLive extends LitElement { .inBackground=${this._inBackground} .conditionState=${this.conditionState} .liveOverrides=${this.liveOverrides} + .cardWideConfig=${this.cardWideConfig} > `, @@ -290,6 +295,9 @@ export class FrigateCardLiveCarousel extends LitElement { @property({ attribute: false }) public conditionState?: ConditionState; + @property({ attribute: false }) + public cardWideConfig?: CardWideConfig; + // Index between camera name and slide number. protected _cameraToSlide: Record = {}; protected _refMediaCarousel: Ref = createRef(); @@ -520,6 +528,7 @@ export class FrigateCardLiveCarousel extends LitElement { .label=${getCameraTitle(this.hass, cameraConfig)} .liveConfig=${config} .hass=${this.hass} + .cardWideConfig=${this.cardWideConfig} @frigate-card:media:loaded=${(ev: CustomEvent) => { wrapMediaLoadedEventForCarousel(slideIndex, ev); }} @@ -667,6 +676,9 @@ export class FrigateCardLiveProvider extends LitElement { @property({ attribute: false }) public label = ''; + @property({ attribute: false }) + public cardWideConfig?: CardWideConfig; + @state() protected _isVideoMediaLoaded = false; @@ -819,6 +831,7 @@ export class FrigateCardLiveProvider extends LitElement { .hass=${this.hass} .cameraConfig=${this.cameraConfig} .webRTCConfig=${this.liveConfig.webrtc_card} + .cardWideConfig=${this.cardWideConfig} @frigate-card:media:loaded=${this._videoMediaShowHandler.bind(this)} > ` @@ -828,6 +841,7 @@ export class FrigateCardLiveProvider extends LitElement { .hass=${this.hass} .cameraConfig=${this.cameraConfig} .jsmpegConfig=${this.liveConfig.jsmpeg} + .cardWideConfig=${this.cardWideConfig} @frigate-card:media:loaded=${this._videoMediaShowHandler.bind(this)} > `} @@ -950,6 +964,9 @@ export class FrigateCardLiveWebRTCCard extends LitElement { @property({ attribute: false }) public cameraConfig?: CameraConfig; + @property({ attribute: false }) + public cardWideConfig?: CardWideConfig; + protected hass?: HomeAssistant; // A task to await the load of the WebRTC component. @@ -1079,9 +1096,13 @@ export class FrigateCardLiveWebRTCCard extends LitElement { // Use a task to allow us to asynchronously wait for the WebRTC card to // load, but yet still have the card load be followed by the updated() // lifecycle callback (unlike just using `until`). - return renderTask(this, this._webrtcTask, render, () => - renderProgressIndicator(localize('error.webrtc_card_waiting')), - ); + return renderTask(this, this._webrtcTask, render, { + inProgressFunc: () => + renderProgressIndicator({ + message: localize('error.webrtc_card_waiting'), + cardWideConfig: this.cardWideConfig, + }), + }); } /** @@ -1121,6 +1142,9 @@ export class FrigateCardLiveJSMPEG extends LitElement { @property({ attribute: false, hasChanged: contentsChanged }) public jsmpegConfig?: JSMPEGConfig; + @property({ attribute: false }) + public cardWideConfig?: CardWideConfig; + protected hass?: ExtendedHomeAssistant; protected _jsmpegCanvasElement?: HTMLCanvasElement; @@ -1327,7 +1351,12 @@ export class FrigateCardLiveJSMPEG extends LitElement { } return html`${this._jsmpegCanvasElement}`; }; - return html`${until(_render(), renderProgressIndicator())}`; + return html`${until( + _render(), + renderProgressIndicator({ + cardWideConfig: this.cardWideConfig, + }), + )}`; } /** diff --git a/src/components/message.ts b/src/components/message.ts index 33c7469c..448057ab 100644 --- a/src/components/message.ts +++ b/src/components/message.ts @@ -4,7 +4,7 @@ import { classMap } from 'lit/directives/class-map.js'; import { TROUBLESHOOTING_URL } from '../const.js'; import { localize } from '../localize/localize.js'; import messageStyle from '../scss/message.scss'; -import { FrigateCardError, Message, MessageType } from '../types.js'; +import { CardWideConfig, FrigateCardError, Message, MessageType } from '../types.js'; import { dispatchFrigateCardEvent } from '../utils/basic.js'; @customElement('frigate-card-message') @@ -82,11 +82,14 @@ export class FrigateCardProgressIndicator extends LitElement { @property({ attribute: false }) public message: string | TemplateResult = ''; + @property({ attribute: false }) + public animated = false; + protected render(): TemplateResult { return html`
- - - + ${this.animated + ? html` ` + : html``} ${this.message ? html`${this.message}` : html``}
`; } @@ -112,9 +115,15 @@ export function renderMessage(message: Message): TemplateResult { return html``; } -export function renderProgressIndicator(message?: string): TemplateResult { +export function renderProgressIndicator(options?: { + message?: string; + cardWideConfig?: CardWideConfig; +}): TemplateResult { return html` - + `; } diff --git a/src/components/next-prev-control.ts b/src/components/next-prev-control.ts index 5d4d0cde..a4943e71 100644 --- a/src/components/next-prev-control.ts +++ b/src/components/next-prev-control.ts @@ -91,7 +91,7 @@ export class FrigateCardNextPreviousControl extends LitElement { aria-label="${this.label}" />` : html``, - () => html`
`, + { inProgressFunc: () => html`
` }, ); } diff --git a/src/components/thumbnail.ts b/src/components/thumbnail.ts index e43b30d6..626ef239 100644 --- a/src/components/thumbnail.ts +++ b/src/components/thumbnail.ts @@ -95,7 +95,7 @@ export class FrigateCardThumbnailFeatureEvent extends LitElement { this._embedThumbnailTask, (embeddedThumbnail: string | null) => embeddedThumbnail ? html`` : html``, - () => imageOff, + {inProgressFunc: () => imageOff}, ) : imageOff} `; } diff --git a/src/components/viewer.ts b/src/components/viewer.ts index 25ae366d..5e1b7ed1 100644 --- a/src/components/viewer.ts +++ b/src/components/viewer.ts @@ -20,6 +20,7 @@ import { BrowseMediaNeighbors, BrowseMediaQueryParameters, CameraConfig, + CardWideConfig, ExtendedHomeAssistant, FrigateBrowseMediaSource, frigateCardConfigDefaults, @@ -97,6 +98,9 @@ export class FrigateCardViewer extends LitElement { @property({ attribute: false }) public dataManager?: DataManager; + @property({ attribute: false }) + public cardWideConfig?: CardWideConfig; + /** * Master render method. * @returns A rendered template. @@ -151,7 +155,7 @@ export class FrigateCardViewer extends LitElement { }), ); } - return renderProgressIndicator(); + return renderProgressIndicator({ cardWideConfig: this.cardWideConfig }); } return html` `; @@ -206,6 +211,9 @@ export class FrigateCardViewerCarousel extends LitElement { @property({ attribute: false }) public resolvedMediaCache?: ResolvedMediaCache; + @property({ attribute: false }) + public cardWideConfig?: CardWideConfig; + protected _refMediaCarousel: Ref = createRef(); // Mapping of slide # to FrigateBrowseMediaSource child #. @@ -645,7 +653,9 @@ export class FrigateCardViewerCarousel extends LitElement { // If lazy loading is not enabled, wait for the media resolver task to // complete and show a progress indictator until this. if (!this.viewerConfig?.lazy_load && !this._isMediaFullyResolved()) { - return renderTask(this, this._mediaResolutionTask, this._render.bind(this)); + return renderTask(this, this._mediaResolutionTask, this._render.bind(this), { + cardWideConfig: this.cardWideConfig, + }); } return this._render(); } diff --git a/src/performance.ts b/src/performance.ts index 98b4d709..ed6eb928 100644 --- a/src/performance.ts +++ b/src/performance.ts @@ -1,7 +1,6 @@ // TODO: Change defaults themselves? -// TODO: Camera auto-dep detection // TODO: CSS -// TODO: Renderprogress +// TODO: Don't pump out conditionStates based on state? import { deepRemoveDefaults } from './utils/zod.js'; import { @@ -16,7 +15,6 @@ import { CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL, CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL, CONF_LIVE_AUTO_MUTE, - CONF_LIVE_AUTO_PLAY, CONF_LIVE_CONTROLS_THUMBNAILS_MODE, CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_DETAILS, CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL, diff --git a/src/scss/message.scss b/src/scss/message.scss index 50746fb2..953c9331 100644 --- a/src/scss/message.scss +++ b/src/scss/message.scss @@ -54,6 +54,10 @@ div.message div.icon { word-break: break-all; } +.message ha-icon, ha-circular-progress { + padding: 10px; +} + .dotdotdot:after { @keyframes dots { 0%, diff --git a/src/utils/task.ts b/src/utils/task.ts index afb91518..155958b6 100644 --- a/src/utils/task.ts +++ b/src/utils/task.ts @@ -4,6 +4,7 @@ import { dispatchFrigateCardErrorEvent, renderProgressIndicator, } from '../components/message'; +import { CardWideConfig } from '../types'; import { errorToConsole } from './basic'; /** @@ -18,11 +19,19 @@ export const renderTask = ( host: EventTarget, task: Task, completeFunc: (result: R) => TemplateResult | void, - inProgressFunc?: () => TemplateResult | void, + options?: { + cardWideConfig?: CardWideConfig; + inProgressFunc?: () => TemplateResult | void; + }, ): TemplateResult => { + const progressConfig = { + ...(options?.cardWideConfig && { cardWideConfig: options.cardWideConfig }), + }; return html` ${task.render({ - initial: () => inProgressFunc?.() ?? renderProgressIndicator(), - pending: () => inProgressFunc?.() ?? renderProgressIndicator(), + initial: () => + options?.inProgressFunc?.() ?? renderProgressIndicator(progressConfig), + pending: () => + options?.inProgressFunc?.() ?? renderProgressIndicator(progressConfig), error: (e: unknown) => { errorToConsole(e as Error); dispatchFrigateCardErrorEvent(host, e as Error);