Fix init/select out of sync issues.

This commit is contained in:
Dermot Duffy
2023-09-05 20:39:54 -07:00
parent 25150be36b
commit 1a4d38e609
4 changed files with 27 additions and 22 deletions
+1 -3
View File
@@ -102,9 +102,7 @@ export class FrigateCardCarousel extends LitElement {
plugins: this.plugins, plugins: this.plugins,
}, },
); );
} } else if (changedProps.has('selected')) {
if (changedProps.has('selected')) {
this._carousel?.selectSlide(this.selected); this._carousel?.selectSlide(this.selected);
} }
} }
+3 -1
View File
@@ -434,7 +434,9 @@ export class FrigateCardLiveCarousel extends LitElement {
protected _getSelectedCameraIndex(): number { protected _getSelectedCameraIndex(): number {
const cameraIDs = this.cameraManager?.getStore().getVisibleCameraIDs(); const cameraIDs = this.cameraManager?.getStore().getVisibleCameraIDs();
if (!cameraIDs || !this.view) { if (!cameraIDs || !this.view || this.viewFilterCameraID) {
// If the carousel is limited to a single cameraID, the first (only)
// element is always the selected one.
return 0; return 0;
} }
return Math.max(0, Array.from(cameraIDs).indexOf(this.view.camera)); return Math.max(0, Array.from(cameraIDs).indexOf(this.view.camera));
+18 -13
View File
@@ -6,7 +6,7 @@ import {
TemplateResult, TemplateResult,
unsafeCSS, unsafeCSS,
} from 'lit'; } from 'lit';
import { customElement, property } from 'lit/decorators.js'; import { customElement, property, state } from 'lit/decorators.js';
import { guard } from 'lit/directives/guard.js'; import { guard } from 'lit/directives/guard.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';
@@ -223,8 +223,8 @@ export class FrigateCardViewerCarousel extends LitElement {
@property({ attribute: false }) @property({ attribute: false })
public cameraManager?: CameraManager; public cameraManager?: CameraManager;
@property({ attribute: false }) @state()
public selected = 0; protected _selected = 0;
protected _media: ViewMedia[] | null = null; protected _media: ViewMedia[] | null = null;
protected _titleTimer = new Timer(); protected _titleTimer = new Timer();
@@ -298,12 +298,12 @@ export class FrigateCardViewerCarousel extends LitElement {
*/ */
protected _getMediaNeighbors(): MediaNeighbors | null { protected _getMediaNeighbors(): MediaNeighbors | null {
const mediaCount = this._media?.length ?? 0; const mediaCount = this._media?.length ?? 0;
if (!this._media || this.selected === null) { if (!this._media) {
return null; return null;
} }
const prevIndex = this.selected > 0 ? this.selected - 1 : null; const prevIndex = this._selected > 0 ? this._selected - 1 : null;
const nextIndex = this.selected + 1 < mediaCount ? this.selected + 1 : null; const nextIndex = this._selected + 1 < mediaCount ? this._selected + 1 : null;
return { return {
...(prevIndex !== null && { ...(prevIndex !== null && {
previous: { previous: {
@@ -325,7 +325,7 @@ export class FrigateCardViewerCarousel extends LitElement {
return; return;
} }
if (this.selected === null || this.selected === index) { if (this._selected === index) {
// The slide may already be selected on load, so don't dispatch a new view // The slide may already be selected on load, so don't dispatch a new view
// unless necessary (i.e. the new index is different from the current // unless necessary (i.e. the new index is different from the current
// index). // index).
@@ -408,10 +408,10 @@ export class FrigateCardViewerCarousel extends LitElement {
this.view?.queryResults?.getSelectedIndex(this.viewFilterCameraID) ?? 0; this.view?.queryResults?.getSelectedIndex(this.viewFilterCameraID) ?? 0;
const newSeek = this.view?.context?.mediaViewer?.seek; const newSeek = this.view?.context?.mediaViewer?.seek;
if (newMedia !== this._media || newSelected !== this.selected || !newSeek) { if (newMedia !== this._media || newSelected !== this._selected || !newSeek) {
setOrRemoveAttribute(this, false, 'unseekable'); setOrRemoveAttribute(this, false, 'unseekable');
this._media = newMedia; this._media = newMedia;
this.selected = newSelected; this._selected = newSelected;
} }
} }
} }
@@ -427,7 +427,7 @@ export class FrigateCardViewerCarousel extends LitElement {
// If there's no selected media, just choose the last (most recent one) to // If there's no selected media, just choose the last (most recent one) to
// avoid rendering a blank. This situation should not occur in practice, as // avoid rendering a blank. This situation should not occur in practice, as
// this view should not be called without a selected media. // this view should not be called without a selected media.
const selectedMedia = this._media[this.selected] ?? this._media[mediaCount - 1]; const selectedMedia = this._media[this._selected] ?? this._media[mediaCount - 1];
if (!this.hass || !this.cameraManager || !selectedMedia) { if (!this.hass || !this.cameraManager || !selectedMedia) {
return; return;
@@ -460,7 +460,7 @@ export class FrigateCardViewerCarousel extends LitElement {
<frigate-card-carousel <frigate-card-carousel
.dragEnabled=${this.viewerConfig?.draggable ?? true} .dragEnabled=${this.viewerConfig?.draggable ?? true}
.plugins=${guard([this.viewerConfig, this._media], this._getPlugins.bind(this))} .plugins=${guard([this.viewerConfig, this._media], this._getPlugins.bind(this))}
.selected=${this.selected ?? 0} .selected=${this._selected}
transitionEffect=${this._getTransitionEffect()} transitionEffect=${this._getTransitionEffect()}
@frigate-card:carousel:select=${(ev: CustomEvent<CarouselSelected>) => { @frigate-card:carousel:select=${(ev: CustomEvent<CarouselSelected>) => {
this._setViewSelectedIndex(ev.detail.index); this._setViewSelectedIndex(ev.detail.index);
@@ -526,10 +526,15 @@ export class FrigateCardViewerCarousel extends LitElement {
*/ */
protected async _seekHandler(): Promise<void> { protected async _seekHandler(): Promise<void> {
const seek = this.view?.context?.mediaViewer?.seek; const seek = this.view?.context?.mediaViewer?.seek;
if (!this.hass || !seek || !this._media || this.selected === null || !this._player) { if (
!this.hass ||
!seek ||
!this._media ||
!this._player
) {
return; return;
} }
const selectedMedia = this._media[this.selected]; const selectedMedia = this._media[this._selected];
if (!selectedMedia) { if (!selectedMedia) {
return; return;
} }
+5 -5
View File
@@ -137,10 +137,11 @@ export class CarouselController {
); );
const getCarouselSelectedObject = (): CarouselSelected | null => { const getCarouselSelectedObject = (): CarouselSelected | null => {
const selectedIndex = this.getSelectedIndex(); // Caution: Must use methods/accessors of the new carousel, not the public
const slide = this.getSlide(selectedIndex); // API of this controller which may use a different carousel.
const selectedIndex = carousel.selectedScrollSnap();
if (selectedIndex !== null && slide) { const slide = carousel.slideNodes()[selectedIndex] ?? null;
if (slide) {
return { return {
index: selectedIndex, index: selectedIndex,
element: slide, element: slide,
@@ -160,7 +161,6 @@ export class CarouselController {
} }
}; };
carousel.on('init', () => selectSlide());
carousel.on('select', () => selectSlide()); carousel.on('select', () => selectSlide());
carousel.on('settle', () => { carousel.on('settle', () => {
const carouselSelected = getCarouselSelectedObject(); const carouselSelected = getCarouselSelectedObject();