Reset the carousel of the plugins/options change.

This commit is contained in:
Dermot Duffy
2022-07-10 20:05:18 -07:00
parent 898a46fda2
commit 214a553aa7
5 changed files with 91 additions and 74 deletions
+19
View File
@@ -65,6 +65,21 @@ export class FrigateCardCarousel extends LitElement {
super.disconnectedCallback(); super.disconnectedCallback();
} }
/**
* Destroy the carousel if certain properties change.
* @param changedProps The changed properties
*/
protected willUpdate(changedProps: PropertyValues): void {
const destroyProperties = [
'direction',
'carouselOptions',
'carouselOptions',
] as const;
if (destroyProperties.some((prop) => changedProps.has(prop))) {
this._destroyCarousel();
}
}
/** /**
* Scroll to a particular slide. * Scroll to a particular slide.
* @param index Slide number. * @param index Slide number.
@@ -193,6 +208,10 @@ export class FrigateCardCarousel extends LitElement {
index: selected, index: selected,
}); });
} }
// Make sure every select causes a refresh to allow for re-paint of the
// next/previous controls.
this.requestUpdate();
}); });
} }
} }
-1
View File
@@ -86,7 +86,6 @@ export class FrigateCardImage extends LitElement {
* Ensure there is a cached value before an update. * Ensure there is a cached value before an update.
* @param _changedProps The changed properties * @param _changedProps The changed properties
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected willUpdate(changedProps: PropertyValues): void { protected willUpdate(changedProps: PropertyValues): void {
if (changedProps.has('imageConfig')) { if (changedProps.has('imageConfig')) {
if (this._cachedValueController) { if (this._cachedValueController) {
+30 -33
View File
@@ -1,7 +1,7 @@
import JSMpeg from '@cycjimmy/jsmpeg-player'; import JSMpeg from '@cycjimmy/jsmpeg-player';
import { Task } from '@lit-labs/task'; import { Task } from '@lit-labs/task';
import { HomeAssistant } from 'custom-card-helpers'; import { HomeAssistant } from 'custom-card-helpers';
import { EmblaOptionsType, EmblaPluginType } from 'embla-carousel'; import { EmblaOptionsType } from 'embla-carousel';
import { WheelGesturesPlugin } from 'embla-carousel-wheel-gestures'; import { WheelGesturesPlugin } from 'embla-carousel-wheel-gestures';
import { import {
CSSResultGroup, CSSResultGroup,
@@ -13,6 +13,7 @@ import {
} from 'lit'; } from 'lit';
import { customElement, property, state } from 'lit/decorators.js'; 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 { guard } from 'lit/directives/guard.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 { import {
@@ -51,12 +52,16 @@ import {
import { View } from '../view.js'; import { View } from '../view.js';
import { AutoMediaPlugin } from './embla-plugins/automedia.js'; import { AutoMediaPlugin } from './embla-plugins/automedia.js';
import { Lazyload } from './embla-plugins/lazyload.js'; import { Lazyload } from './embla-plugins/lazyload.js';
import { FrigateCardMediaCarousel, wrapMediaShowEventForCarousel } from './media-carousel.js'; import {
FrigateCardMediaCarousel,
wrapMediaShowEventForCarousel,
} from './media-carousel.js';
import { dispatchErrorMessageEvent } from './message.js'; import { dispatchErrorMessageEvent } from './message.js';
import './next-prev-control.js'; import './next-prev-control.js';
import './title-control.js'; import './title-control.js';
import './surround-thumbnails'; import './surround-thumbnails';
import '../patches/ha-camera-stream'; import '../patches/ha-camera-stream';
import { EmblaCarouselPlugins } from './carousel.js';
// Number of seconds a signed URL is valid for. // Number of seconds a signed URL is valid for.
const URL_SIGN_EXPIRY_SECONDS = 24 * 60 * 60; const URL_SIGN_EXPIRY_SECONDS = 24 * 60 * 60;
@@ -241,27 +246,14 @@ export class FrigateCardLiveCarousel extends LitElement {
frigateCardCarousel && frigateCardCarousel &&
changedProperties.has('preloaded') changedProperties.has('preloaded')
) { ) {
const automedia = frigateCardCarousel.getCarouselPlugins()?.autoMedia; // If this has changed to preloaded (i.e. is now loaded but in the
if (automedia) { // background) take the appropriate play/pause/mute/unmute actions.
// If this has changed to preloaded (i.e. is now loaded but in the if (this.preloaded) {
// background) take the appropriate play/pause/mute/unmute actions. frigateCardMediaCarousel.autoPause();
if (this.preloaded) { frigateCardMediaCarousel.autoMute();
if ( } else {
this.liveConfig?.auto_pause && frigateCardMediaCarousel.autoPlay();
['all', 'unselected'].includes(this.liveConfig.auto_pause) frigateCardMediaCarousel.autoUnmute();
) {
automedia.pause();
}
if (
this.liveConfig?.auto_mute &&
['all', 'unselected'].includes(this.liveConfig.auto_mute)
) {
automedia.mute();
}
} else {
frigateCardMediaCarousel.autoPlay();
frigateCardMediaCarousel.autoUnmute();
}
} }
} }
} }
@@ -296,7 +288,7 @@ export class FrigateCardLiveCarousel extends LitElement {
* Get the Embla plugins to use. * Get the Embla plugins to use.
* @returns A list of EmblaOptionsTypes. * @returns A list of EmblaOptionsTypes.
*/ */
protected _getPlugins(): EmblaPluginType[] { protected _getPlugins(): EmblaCarouselPlugins {
return [ return [
// Only enable wheel plugin if there is more than one camera. // Only enable wheel plugin if there is more than one camera.
...(this.cameras && this.cameras.size > 1 ...(this.cameras && this.cameras.size > 1
@@ -406,7 +398,7 @@ export class FrigateCardLiveCarousel extends LitElement {
slide: Element, slide: Element,
): void { ): void {
if (slide instanceof HTMLSlotElement) { if (slide instanceof HTMLSlotElement) {
slide = slide.assignedElements({flatten: true})[0]; slide = slide.assignedElements({ flatten: true })[0];
} }
const liveProvider = slide?.querySelector( const liveProvider = slide?.querySelector(
@@ -448,7 +440,7 @@ export class FrigateCardLiveCarousel extends LitElement {
.liveConfig=${config} .liveConfig=${config}
.hass=${this.hass} .hass=${this.hass}
@frigate-card:media-show=${(e: CustomEvent<MediaShowInfo>) => { @frigate-card:media-show=${(e: CustomEvent<MediaShowInfo>) => {
wrapMediaShowEventForCarousel(slideIndex, e) wrapMediaShowEventForCarousel(slideIndex, e);
}} }}
> >
</frigate-card-live-provider> </frigate-card-live-provider>
@@ -498,15 +490,20 @@ export class FrigateCardLiveCarousel extends LitElement {
const [prev, next] = this._getCameraNeighbors(); const [prev, next] = this._getCameraNeighbors();
const title = getCameraTitle(this.hass, this.cameras.get(this.view.camera)); const title = getCameraTitle(this.hass, this.cameras.get(this.view.camera));
// guard() is used below to avoid reseting the carousel unless the
// options/plugins actually change.
return html` return html`
<frigate-card-media-carousel <frigate-card-media-carousel
${ref(this._refMediaCarousel)} ${ref(this._refMediaCarousel)}
.carouselOptions=${this._getOptions()} .carouselOptions=${guard(
.carouselPlugins=${this._getPlugins()} [this.cameras, this.liveConfig],
.autoPlayCondition=${this.liveConfig.auto_play} this._getOptions.bind(this),
.autoPauseCondition=${this.liveConfig.auto_pause} )}
.autoMuteCondition=${this.liveConfig.auto_mute} .carouselPlugins=${guard(
.autoUnmuteCondition=${this.liveConfig.auto_unmute} [this.cameras, this.liveConfig],
this._getPlugins.bind(this),
) as EmblaCarouselPlugins}
.label="${title ? `${localize('common.live')}: ${title}` : ''}" .label="${title ? `${localize('common.live')}: ${title}` : ''}"
.titlePopupConfig=${config.controls.title} .titlePopupConfig=${config.controls.title}
transitionEffect=${this._getTransitionEffect()} transitionEffect=${this._getTransitionEffect()}
@@ -548,7 +545,7 @@ export class FrigateCardLiveCarousel extends LitElement {
/** /**
* Get styles. * Get styles.
*/ */
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return unsafeCSS(liveCarouselStyle); return unsafeCSS(liveCarouselStyle);
} }
} }
+32 -34
View File
@@ -1,17 +1,12 @@
// TODO: Use the auto-height plugin instead of adaptive height // TODO: Use the auto-height plugin instead of adaptive height
import { EmblaOptionsType } from 'embla-carousel'; import { EmblaOptionsType } from 'embla-carousel';
import { EmblaPluginsType } from 'embla-carousel/components/Plugins';
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 { ifDefined } from 'lit/directives/if-defined.js'; import { ifDefined } from 'lit/directives/if-defined.js';
import { createRef, ref, Ref } from 'lit/directives/ref.js'; import { createRef, ref, Ref } from 'lit/directives/ref.js';
import mediaCarouselStyle from '../scss/media-carousel.scss'; import mediaCarouselStyle from '../scss/media-carousel.scss';
import type { import type {
AutoMuteCondition,
AutoPauseCondition,
AutoPlayCondition,
AutoUnmuteCondition,
MediaShowInfo, MediaShowInfo,
NextPreviousControlConfig, NextPreviousControlConfig,
TitleControlConfig, TitleControlConfig,
@@ -106,18 +101,6 @@ export class FrigateCardMediaCarousel extends LitElement {
@property({ attribute: false }) @property({ attribute: false })
public titlePopupConfig?: TitleControlConfig; public titlePopupConfig?: TitleControlConfig;
@property({ attribute: false })
public autoPlayCondition?: AutoPlayCondition;
@property({ attribute: false })
public autoUnmuteCondition?: AutoUnmuteCondition;
@property({ attribute: false })
public autoPauseCondition?: AutoPauseCondition;
@property({ attribute: false })
public autoMuteCondition?: AutoMuteCondition;
// A "map" from slide number to MediaShowInfo object. // A "map" from slide number to MediaShowInfo object.
protected _mediaShowInfo: Record<number, MediaShowInfo> = {}; protected _mediaShowInfo: Record<number, MediaShowInfo> = {};
protected _nextControlRef: Ref<FrigateCardNextPreviousControl> = createRef(); protected _nextControlRef: Ref<FrigateCardNextPreviousControl> = createRef();
@@ -126,9 +109,9 @@ export class FrigateCardMediaCarousel extends LitElement {
protected _titleTimerID: number | null = null; protected _titleTimerID: number | null = null;
protected _boundAutoPlayHandler = this.autoPlay.bind(this); protected _boundAutoPlayHandler = this.autoPlay.bind(this);
protected _boundAutoPauseHandler = this.autoPause.bind(this);
protected _boundAutoMuteHandler = this.autoMute.bind(this);
protected _boundAutoUnmuteHandler = this.autoUnmute.bind(this); protected _boundAutoUnmuteHandler = this.autoUnmute.bind(this);
protected _boundAdaptiveHeightHandler = this._adaptiveHeightHandler.bind(this);
protected _boundTitleHandler = this._titleHandler.bind(this);
// This carousel may be resized by Lovelace resizes, window resizes, // This carousel may be resized by Lovelace resizes, window resizes,
// fullscreen, etc. Always call the adaptive height handler when the size // fullscreen, etc. Always call the adaptive height handler when the size
@@ -165,7 +148,11 @@ export class FrigateCardMediaCarousel extends LitElement {
* Play the media on the selected slide. * Play the media on the selected slide.
*/ */
public autoPlay(): void { public autoPlay(): void {
if (this.autoPlayCondition && ['all', 'selected'].includes(this.autoPlayCondition)) { const automediaOptions = this._getAutoMediaPlugin()?.options;
if (
automediaOptions?.autoPlayCondition &&
['all', 'selected'].includes(automediaOptions?.autoPlayCondition)
) {
this._getAutoMediaPlugin()?.play(); this._getAutoMediaPlugin()?.play();
} }
} }
@@ -174,9 +161,10 @@ export class FrigateCardMediaCarousel extends LitElement {
* Pause the media on the selected slide. * Pause the media on the selected slide.
*/ */
public autoPause(): void { public autoPause(): void {
const automediaOptions = this._getAutoMediaPlugin()?.options;
if ( if (
this.autoPauseCondition && automediaOptions?.autoPauseCondition &&
['all', 'selected'].includes(this.autoPauseCondition) ['all', 'selected'].includes(automediaOptions.autoPauseCondition)
) { ) {
this._getAutoMediaPlugin()?.pause(); this._getAutoMediaPlugin()?.pause();
} }
@@ -186,9 +174,10 @@ export class FrigateCardMediaCarousel extends LitElement {
* Unmute the media on the selected slide. * Unmute the media on the selected slide.
*/ */
public autoUnmute(): void { public autoUnmute(): void {
const automediaOptions = this._getAutoMediaPlugin()?.options;
if ( if (
this.autoUnmuteCondition && automediaOptions?.autoUnmuteCondition &&
['all', 'selected'].includes(this.autoUnmuteCondition) ['all', 'selected'].includes(automediaOptions?.autoUnmuteCondition)
) { ) {
this._getAutoMediaPlugin()?.unmute(); this._getAutoMediaPlugin()?.unmute();
} }
@@ -198,7 +187,11 @@ export class FrigateCardMediaCarousel extends LitElement {
* Mute the media on the selected slide. * Mute the media on the selected slide.
*/ */
public autoMute(): void { public autoMute(): void {
if (this.autoMuteCondition && ['all', 'selected'].includes(this.autoMuteCondition)) { const automediaOptions = this._getAutoMediaPlugin()?.options;
if (
automediaOptions?.autoMuteCondition &&
['all', 'selected'].includes(automediaOptions?.autoMuteCondition)
) {
this._getAutoMediaPlugin()?.mute(); this._getAutoMediaPlugin()?.mute();
} }
} }
@@ -233,10 +226,11 @@ export class FrigateCardMediaCarousel extends LitElement {
*/ */
connectedCallback(): void { connectedCallback(): void {
super.connectedCallback(); super.connectedCallback();
this.addEventListener('frigate-card:media-show', this.autoPlay);
this.addEventListener('frigate-card:media-show', this.autoUnmute); this.addEventListener('frigate-card:media-show', this._boundAutoPlayHandler);
this.addEventListener('frigate-card:media-show', this._adaptiveHeightHandler); this.addEventListener('frigate-card:media-show', this._boundAutoUnmuteHandler);
this.addEventListener('frigate-card:media-show', this._titleHandler); this.addEventListener('frigate-card:media-show', this._boundAdaptiveHeightHandler);
this.addEventListener('frigate-card:media-show', this._boundTitleHandler);
this._resizeObserver.observe(this); this._resizeObserver.observe(this);
this._intersectionObserver.observe(this); this._intersectionObserver.observe(this);
} }
@@ -245,13 +239,17 @@ export class FrigateCardMediaCarousel extends LitElement {
* Component disconnected callback. * Component disconnected callback.
*/ */
disconnectedCallback(): void { disconnectedCallback(): void {
super.disconnectedCallback(); this.removeEventListener('frigate-card:media-show', this._boundAutoPlayHandler);
this.removeEventListener('frigate-card:media-show', this.autoPlay); this.removeEventListener('frigate-card:media-show', this._boundAutoUnmuteHandler);
this.removeEventListener('frigate-card:media-show', this.autoUnmute); this.removeEventListener(
this.removeEventListener('frigate-card:media-show', this._adaptiveHeightHandler); 'frigate-card:media-show',
this.removeEventListener('frigate-card:media-show', this._titleHandler); this._boundAdaptiveHeightHandler,
);
this.removeEventListener('frigate-card:media-show', this._boundTitleHandler);
this._resizeObserver.disconnect(); this._resizeObserver.disconnect();
this._intersectionObserver.disconnect(); this._intersectionObserver.disconnect();
super.disconnectedCallback();
} }
/** /**
+10 -6
View File
@@ -9,6 +9,7 @@ import {
TemplateResult, TemplateResult,
unsafeCSS, unsafeCSS,
} from 'lit'; } from 'lit';
import { guard } from 'lit/directives/guard.js';
import { customElement, property } from 'lit/decorators.js'; import { customElement, property } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js'; import { ifDefined } from 'lit/directives/if-defined.js';
import { createRef, Ref, ref } from 'lit/directives/ref.js'; import { createRef, Ref, ref } from 'lit/directives/ref.js';
@@ -54,6 +55,7 @@ import './next-prev-control.js';
import './title-control.js'; import './title-control.js';
import '../patches/ha-hls-player'; import '../patches/ha-hls-player';
import './surround-thumbnails'; import './surround-thumbnails';
import { EmblaCarouselPlugins } from './carousel.js';
@customElement('frigate-card-viewer') @customElement('frigate-card-viewer')
export class FrigateCardViewer extends LitElement { export class FrigateCardViewer extends LitElement {
@@ -603,14 +605,16 @@ export class FrigateCardViewerCarousel extends LitElement {
const neighbors = this._getMediaNeighbors(); const neighbors = this._getMediaNeighbors();
const [prev, next] = [neighbors?.previous, neighbors?.next]; const [prev, next] = [neighbors?.previous, neighbors?.next];
// guard() is used below to avoid reseting the carousel unless the
// options/plugins actually change.
return html` <frigate-card-media-carousel return html` <frigate-card-media-carousel
${ref(this._refMediaCarousel)} ${ref(this._refMediaCarousel)}
.carouselOptions=${this._getOptions()} .carouselOptions=${guard([this.viewerConfig], this._getOptions.bind(this))}
.carouselPlugins=${this._getPlugins()} .carouselPlugins=${guard(
.autoPlayCondition=${this.viewerConfig?.auto_play} [this.viewerConfig, this.view?.target?.children?.length],
.autoPauseCondition=${this.viewerConfig?.auto_pause} this._getPlugins.bind(this),
.autoMuteCondition=${this.viewerConfig?.auto_mute} ) as EmblaCarouselPlugins}
.autoUnmuteCondition=${this.viewerConfig?.auto_unmute}
.label="${this.view.media.title}" .label="${this.view.media.title}"
.titlePopupConfig=${this.viewerConfig?.controls.title} .titlePopupConfig=${this.viewerConfig?.controls.title}
transitionEffect=${this._getTransitionEffect()} transitionEffect=${this._getTransitionEffect()}