From 85d7c653cd239652c6ec788a739b48788350ed74 Mon Sep 17 00:00:00 2001 From: Dermot Duffy Date: Sat, 6 Aug 2022 12:26:29 -0700 Subject: [PATCH] Reduce boilerplate associated with lit tasks --- src/components/live.ts | 12 +++++------ src/components/next-prev-control.ts | 28 ++++++++----------------- src/components/thumbnail.ts | 21 ++++++++----------- src/components/viewer.ts | 8 ++------ src/utils/task.ts | 32 +++++++++++++++++++++++++++++ src/utils/thumbnail.ts | 8 +++++--- 6 files changed, 61 insertions(+), 48 deletions(-) create mode 100644 src/utils/task.ts diff --git a/src/components/live.ts b/src/components/live.ts index 92fa484c..4b767caa 100644 --- a/src/components/live.ts +++ b/src/components/live.ts @@ -65,6 +65,7 @@ import './title-control.js'; import './surround-thumbnails'; import '../patches/ha-camera-stream'; import { EmblaCarouselPlugins } from './carousel.js'; +import { renderTask } from '../utils/task.js'; // Number of seconds a signed URL is valid for. const URL_SIGN_EXPIRY_SECONDS = 24 * 60 * 60; @@ -142,7 +143,7 @@ export class FrigateCardLive extends LitElement { * Determine whether the element should be updated. * @param _changedProps The changed properties if any. * @returns `true` if the element should be updated. - */ + */ // eslint-disable-next-line @typescript-eslint/no-unused-vars protected shouldUpdate(_changedProps: PropertyValues): boolean { // Don't process updates if it's in the background and a message was @@ -981,12 +982,9 @@ 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 html`${this._webrtcTask.render({ - initial: () => renderProgressIndicator(localize('error.webrtc_card_waiting')), - pending: () => renderProgressIndicator(localize('error.webrtc_card_waiting')), - error: (e: unknown) => dispatchFrigateCardErrorEvent(this, e as Error), - complete: () => render(), - })}`; + return renderTask(this, this._webrtcTask, render, () => + renderProgressIndicator(localize('error.webrtc_card_waiting')), + ); } /** diff --git a/src/components/next-prev-control.ts b/src/components/next-prev-control.ts index 435a2b35..5a7ccd67 100644 --- a/src/components/next-prev-control.ts +++ b/src/components/next-prev-control.ts @@ -7,8 +7,7 @@ import { NextPreviousControlConfig } from '../types.js'; import controlStyle from '../scss/next-previous-control.scss'; import { createFetchThumbnailTask } from '../utils/thumbnail.js'; import { HomeAssistant } from 'custom-card-helpers'; -import { dispatchFrigateCardErrorEvent } from './message.js'; -import { errorToConsole } from '../utils/basic.js'; +import { renderTask } from '../utils/task.js'; @customElement('frigate-card-next-previous-control') export class FrigateCardNextPreviousControl extends LitElement { @@ -80,29 +79,20 @@ export class FrigateCardNextPreviousControl extends LitElement { return html``; } - const renderControlInProgress = (): TemplateResult => { - // Just render an 'empty' thumbnail control until the thumbnail loads. - return html`
`; - }; - - return html`${this._embedThumbnailTask.render({ - initial: () => renderControlInProgress(), - pending: () => renderControlInProgress(), - error: (e: unknown) => { - errorToConsole(e as Error); - dispatchFrigateCardErrorEvent(this, e as Error); - }, - complete: (embeddedThumbnail: string | null) => { - return embeddedThumbnail + return renderTask( + this, + this._embedThumbnailTask, + (embeddedThumbnail: string | null) => + embeddedThumbnail ? html`` - : html``; - }, - })}`; + : html``, + () => html`
`, + ); } static get styles(): CSSResultGroup { diff --git a/src/components/thumbnail.ts b/src/components/thumbnail.ts index 5617e729..ca224410 100644 --- a/src/components/thumbnail.ts +++ b/src/components/thumbnail.ts @@ -17,9 +17,9 @@ import { stopEventFromActivatingCardWideActions } from '../utils/action.js'; import { errorToConsole, prettifyTitle } from '../utils/basic.js'; import { retainEvent } from '../utils/frigate.js'; import { getEventDurationString } from '../utils/ha/browse-media.js'; +import { renderTask } from '../utils/task.js'; import { createFetchThumbnailTask } from '../utils/thumbnail.js'; import { View } from '../view.js'; -import { dispatchFrigateCardErrorEvent, renderProgressIndicator } from './message.js'; // The minimum width of a thumbnail with details enabled. export const THUMBNAIL_DETAILS_WIDTH_MIN = 300; @@ -41,19 +41,14 @@ export class FrigateCardThumbnailFeatureEvent extends LitElement { protected render(): TemplateResult | void { return html` ${this.thumbnail - ? html` ${this._embedThumbnailTask.render({ - initial: () => renderProgressIndicator(), - pending: () => renderProgressIndicator(), - error: (e: unknown) => { - errorToConsole(e as Error); - dispatchFrigateCardErrorEvent(this, e as Error); - }, - complete: (embeddedThumbnail: string | null) => { - return embeddedThumbnail + ? renderTask( + this, + this._embedThumbnailTask, + (embeddedThumbnail: string | null) => + embeddedThumbnail ? html`` - : html``; - }, - })}` + : html`` + ) : html` renderProgressIndicator(), - pending: () => renderProgressIndicator(), - error: (e: unknown) => dispatchFrigateCardErrorEvent(this, e as Error), - complete: () => this._render(), - })}`; + return renderTask(this, this._mediaResolutionTask, this._render.bind(this)); } return this._render(); } diff --git a/src/utils/task.ts b/src/utils/task.ts new file mode 100644 index 00000000..afb91518 --- /dev/null +++ b/src/utils/task.ts @@ -0,0 +1,32 @@ +import { Task } from '@lit-labs/task'; +import { html, TemplateResult } from 'lit'; +import { + dispatchFrigateCardErrorEvent, + renderProgressIndicator, +} from '../components/message'; +import { errorToConsole } from './basic'; + +/** + * Render the result of a Lit task. + * @param host The host object. + * @param task The Lit task. + * @param completeFunc The function to call with the result. + * @param inProgressFunc The function to call whilst in progress. + * @returns A template. + */ +export const renderTask = ( + host: EventTarget, + task: Task, + completeFunc: (result: R) => TemplateResult | void, + inProgressFunc?: () => TemplateResult | void, +): TemplateResult => { + return html` ${task.render({ + initial: () => inProgressFunc?.() ?? renderProgressIndicator(), + pending: () => inProgressFunc?.() ?? renderProgressIndicator(), + error: (e: unknown) => { + errorToConsole(e as Error); + dispatchFrigateCardErrorEvent(host, e as Error); + }, + complete: completeFunc, + })}`; +}; diff --git a/src/utils/thumbnail.ts b/src/utils/thumbnail.ts index cb4c62b9..2a002adb 100644 --- a/src/utils/thumbnail.ts +++ b/src/utils/thumbnail.ts @@ -42,6 +42,8 @@ export const fetchThumbnail = async ( }); }; +type FetchThumbnailTaskArgs = [boolean, string | undefined]; + /** * Create a Lit task to fetch a thumbnail. * @param host The Lit Element. @@ -53,10 +55,10 @@ export const createFetchThumbnailTask = ( host: ReactiveControllerHost, getHASS: () => HomeAssistant | undefined, getThumbnailURL: () => string | undefined, -): Task => { +): Task => { return new Task( host, - async ([haveHASS, thumbnailURL]: [boolean, string | undefined]): Promise< + async ([haveHASS, thumbnailURL]: FetchThumbnailTaskArgs): Promise< string | null > => { const hass = getHASS(); @@ -66,6 +68,6 @@ export const createFetchThumbnailTask = ( return fetchThumbnail(hass, thumbnailURL); }, // Do not re-run the task if hass changes, unless it was previously undefined. - (): [boolean, string | undefined] => [!!getHASS(), getThumbnailURL()], + (): FetchThumbnailTaskArgs => [!!getHASS(), getThumbnailURL()], ); };