Change view to null for unspecified attributes.
This commit is contained in:
+6
-2
@@ -381,7 +381,11 @@ export class FrigateCard extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
if (this._getConfig().menu.buttons.download && this._view?.isViewerView()) {
|
||||
if (
|
||||
this._getConfig().menu.buttons.download &&
|
||||
(this._view?.isViewerView() || this._view?.is('timeline') &&
|
||||
!!this._view?.media)
|
||||
) {
|
||||
buttons.push({
|
||||
type: 'custom:frigate-card-menu-icon',
|
||||
title: localize('config.menu.buttons.download'),
|
||||
@@ -743,7 +747,7 @@ export class FrigateCard extends LitElement {
|
||||
* Download media being displayed in the viewer.
|
||||
*/
|
||||
protected async _downloadViewerMedia(): Promise<void> {
|
||||
if (!this._hass || !this._view?.isViewerView()) {
|
||||
if (!this._hass || !(this._view?.isViewerView() || this._view?.is('timeline'))) {
|
||||
// Should not occur.
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -214,7 +214,6 @@ export class FrigateCardGalleryCore extends LitElement {
|
||||
.evolve({
|
||||
view: this.view.is('clips') ? 'clip' : 'snapshot',
|
||||
childIndex: index,
|
||||
previous: this.view,
|
||||
})
|
||||
.dispatchChangeEvent(this);
|
||||
}
|
||||
|
||||
@@ -357,7 +357,10 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
|
||||
this.view
|
||||
.evolve({
|
||||
camera: Array.from(this.cameras.keys())[selectedSnap],
|
||||
previous: this.view,
|
||||
|
||||
// Reset the target so thumbnails will be re-fetched.
|
||||
target: null,
|
||||
childIndex: null,
|
||||
})
|
||||
.dispatchChangeEvent(this);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ export class FrigateCardSurround extends LitElement {
|
||||
view = view as Readonly<View>;
|
||||
browseMediaParams = browseMediaParams as BrowseMediaQueryParameters;
|
||||
|
||||
if (!hass || !view || !browseMediaParams) {
|
||||
if (!hass || !view || view.target || !browseMediaParams) {
|
||||
return;
|
||||
}
|
||||
let parent: FrigateBrowseMediaSource | null;
|
||||
@@ -74,12 +74,20 @@ export class FrigateCardSurround extends LitElement {
|
||||
?.evolve({
|
||||
...(this.targetView && { view: this.targetView }),
|
||||
target: parent,
|
||||
childIndex: undefined,
|
||||
childIndex: null,
|
||||
})
|
||||
.dispatchChangeEvent(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a drawer is being used.
|
||||
* @returns `true` if a drawer is used, `false` otherwise.
|
||||
*/
|
||||
protected _hasDrawer(): boolean {
|
||||
return !!this.config && ['left', 'right'].includes(this.config.mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Master render method.
|
||||
* @returns A rendered template.
|
||||
@@ -89,19 +97,22 @@ export class FrigateCardSurround extends LitElement {
|
||||
return;
|
||||
}
|
||||
|
||||
return html` <frigate-card-surround
|
||||
@frigate-card:thumbnails:open=${(ev: CustomEvent) => {
|
||||
if (this.config && ['left', 'right'].includes(this.config.mode)) {
|
||||
// Protects encapsulation: Catches the request to view thumbnails and
|
||||
// re-dispatches a request to open the drawer (if the thumbnails are
|
||||
// in a drawer). The new event needs to be dispatched from the origin
|
||||
// of the inbound event, so it can be handled by
|
||||
const changeDrawer = (ev: CustomEvent, action: 'open' | 'close') => {
|
||||
// The event catch/re-dispatch below protect encapsulation: Catches the
|
||||
// request to view thumbnails and re-dispatches a request to open the drawer
|
||||
// (if the thumbnails are in a drawer). The new event needs to be dispatched
|
||||
// from the origin of the inbound event, so it can be handled by
|
||||
// <frigate-card-surround> .
|
||||
dispatchFrigateCardEvent(ev.composedPath()[0], 'drawer:open', {
|
||||
if (this.config && this._hasDrawer()) {
|
||||
dispatchFrigateCardEvent(ev.composedPath()[0], 'drawer:' + action, {
|
||||
drawer: this.config.mode,
|
||||
});
|
||||
}
|
||||
}}
|
||||
};
|
||||
|
||||
return html` <frigate-card-surround
|
||||
@frigate-card:thumbnails:open=${(ev: CustomEvent) => changeDrawer(ev, 'open')}
|
||||
@frigate-card:thumbnails:close=${(ev: CustomEvent) => changeDrawer(ev, 'close')}
|
||||
>
|
||||
${this.config?.mode !== 'none'
|
||||
? html` <frigate-card-thumbnail-carousel
|
||||
@@ -109,16 +120,8 @@ export class FrigateCardSurround extends LitElement {
|
||||
.config=${this.config}
|
||||
.view=${this.view}
|
||||
.target=${this.view.target}
|
||||
.selected=${this.view.childIndex ?? null}
|
||||
@frigate-card:change-view=${(ev) => {
|
||||
// Close the drawer if the carousel or thumbnail requests a view change
|
||||
// (e.g. playing the clip, or viewing something on the timeline).
|
||||
if (this.config && ['left', 'right'].includes(this.config.mode)) {
|
||||
dispatchFrigateCardEvent(ev.composedPath()[0], 'drawer:close', {
|
||||
drawer: this.config.mode,
|
||||
});
|
||||
}
|
||||
}}
|
||||
.selected=${this.view.childIndex}
|
||||
@frigate-card:change-view=${(ev: CustomEvent) => changeDrawer(ev, 'close')}
|
||||
@frigate-card:carousel:tap=${(ev: CustomEvent<ThumbnailCarouselTap>) => {
|
||||
this.view
|
||||
?.evolve({
|
||||
|
||||
@@ -36,7 +36,7 @@ export class FrigateCardThumbnailCarousel extends FrigateCardCarousel {
|
||||
// Use contentsChanged here to avoid the carousel rebuilding and resetting in
|
||||
// front of the user, unless the contents have actually changed.
|
||||
@property({ attribute: false, hasChanged: contentsChanged })
|
||||
public target?: FrigateBrowseMediaSource;
|
||||
public target?: FrigateBrowseMediaSource | null;
|
||||
|
||||
// Thumbnail carousels can expand (e.g. drawer-based carousels after the main
|
||||
// media loads). The carousel must be re-initialized in these cases, or the
|
||||
|
||||
@@ -90,7 +90,7 @@ export class FrigateCardThumbnail extends LitElement {
|
||||
?.evolve({
|
||||
view: 'timeline',
|
||||
target: this.target,
|
||||
childIndex: this.childIndex,
|
||||
childIndex: this.childIndex ?? null,
|
||||
context: {},
|
||||
})
|
||||
.dispatchChangeEvent(this);
|
||||
|
||||
+17
-15
@@ -1,8 +1,6 @@
|
||||
// TODO: Clips vs snapshots: Should be able to navigate from snapshots view and it should just work.
|
||||
// TODO: Hover over an event should show something useful.
|
||||
// TODO: Periodically refetch events.
|
||||
// TODO: Search for TODOs and logging statements.
|
||||
// TODO: Allow download of selected event in timeline.
|
||||
|
||||
import {
|
||||
CSSResultGroup,
|
||||
@@ -211,9 +209,8 @@ class TimelineEventManager {
|
||||
if (!cameraConfig || !this._dateStart || !this._dateEnd) {
|
||||
return;
|
||||
}
|
||||
const browseMediaQueryParametersBase = BrowseMediaUtil.getBrowseMediaQueryParametersBase(
|
||||
cameraConfig,
|
||||
);
|
||||
const browseMediaQueryParametersBase =
|
||||
BrowseMediaUtil.getBrowseMediaQueryParametersBase(cameraConfig);
|
||||
if (!browseMediaQueryParametersBase) {
|
||||
return;
|
||||
}
|
||||
@@ -384,20 +381,27 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
protected _timelineSelectHandler(data: { items: string[]; event: Event }): void {
|
||||
if (!this._thumbnails || !this._thumbnails.children || data.items.length <= 0) {
|
||||
if (!this._thumbnails || !this._thumbnails.children) {
|
||||
return;
|
||||
}
|
||||
const childIndex = this._thumbnails.children.findIndex(
|
||||
|
||||
const childIndex = data.items.length
|
||||
? this._thumbnails.children.findIndex(
|
||||
(child) => child.frigate?.event.id === data.items[0],
|
||||
);
|
||||
if (childIndex >= 0) {
|
||||
)
|
||||
: null;
|
||||
|
||||
this.view
|
||||
?.evolve({
|
||||
target: this._thumbnails,
|
||||
childIndex: childIndex,
|
||||
})
|
||||
.dispatchChangeEvent(this);
|
||||
|
||||
if (childIndex !== null && childIndex >= 0) {
|
||||
dispatchFrigateCardEvent(this, 'thumbnails:open');
|
||||
} else {
|
||||
dispatchFrigateCardEvent(this, 'thumbnails:close');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,12 +432,12 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
['all', 'snapshots'].includes(this.timelineConfig.media) &&
|
||||
BrowseMediaUtil.isTrueMedia(item.snapshot)
|
||||
) {
|
||||
added = true
|
||||
added = true;
|
||||
children.push(item.snapshot);
|
||||
}
|
||||
|
||||
if (added && selected.includes(item.event.id)) {
|
||||
childIndex = children.length-1;
|
||||
childIndex = children.length - 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -459,7 +463,7 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
this.view
|
||||
?.evolve({
|
||||
target: this._thumbnails,
|
||||
childIndex: childIndex < 0 ? undefined : childIndex,
|
||||
childIndex: childIndex < 0 ? null : childIndex,
|
||||
})
|
||||
.dispatchChangeEvent(this);
|
||||
}
|
||||
@@ -642,9 +646,7 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
});
|
||||
|
||||
const timelineWindow = this._timeline.getWindow();
|
||||
const context = this.view.context
|
||||
? (this.view.context as TimelineViewContext)
|
||||
: undefined;
|
||||
const context = this.view.context as TimelineViewContext | null;
|
||||
|
||||
if (context?.window) {
|
||||
console.info(
|
||||
|
||||
+19
-17
@@ -145,11 +145,11 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
||||
|
||||
// A task to resolve target media if lazy loading is disabled.
|
||||
protected _mediaResolutionTask = new Task<
|
||||
[FrigateBrowseMediaSource | undefined],
|
||||
[FrigateBrowseMediaSource | null | undefined],
|
||||
void
|
||||
>(
|
||||
this,
|
||||
async ([target]: (FrigateBrowseMediaSource | undefined)[]): Promise<void> => {
|
||||
async ([target]: (FrigateBrowseMediaSource | null | undefined)[]): Promise<void> => {
|
||||
for (
|
||||
let i = 0;
|
||||
!this.viewerConfig?.lazy_load &&
|
||||
@@ -183,12 +183,12 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
||||
if (this._carousel && changedProperties.has('view')) {
|
||||
const oldView = changedProperties.get('view') as View | undefined;
|
||||
if (oldView) {
|
||||
if (oldView.target != this.view?.target) {
|
||||
if (oldView.target !== this.view?.target) {
|
||||
// If the media target is different entirely, reset the carousel.
|
||||
this._destroyCarousel();
|
||||
} else if (this.view?.childIndex != oldView.childIndex) {
|
||||
const slide = this._getSlideForChild(this.view?.childIndex);
|
||||
if (slide !== undefined && slide !== this.carouselSelected()) {
|
||||
} else if (this.view.childIndex != oldView.childIndex) {
|
||||
const slide = this._getSlideForChild(this.view.childIndex);
|
||||
if (slide !== null && slide !== this.carouselSelected()) {
|
||||
// If the media target is the same as already loaded, but isn't of
|
||||
// the selected slide, scroll to that slide.
|
||||
this.carouselScrollTo(slide);
|
||||
@@ -226,14 +226,19 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
||||
// need to be destroyed here.
|
||||
}
|
||||
|
||||
protected _getSlideForChild(childIndex: number | undefined): number | undefined {
|
||||
if (childIndex === undefined) {
|
||||
return undefined;
|
||||
/**
|
||||
* Get the slide number given a media child number.
|
||||
* @param childIndex The child index (relative to `view.target`)
|
||||
* @returns A number or null if the child is not found.
|
||||
*/
|
||||
protected _getSlideForChild(childIndex: number | null | undefined): number | null {
|
||||
if (childIndex === undefined || childIndex === null) {
|
||||
return null;
|
||||
}
|
||||
const slideIndex = Object.keys(this._slideToChild).find(
|
||||
(key) => this._slideToChild[key] === childIndex,
|
||||
);
|
||||
return slideIndex !== undefined ? Number(slideIndex) : undefined;
|
||||
return slideIndex !== undefined ? Number(slideIndex) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,7 +256,7 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
||||
protected _getOptions(): EmblaOptionsType {
|
||||
return {
|
||||
// Start the carousel on the selected child number.
|
||||
startIndex: this._getSlideForChild(this.view?.childIndex),
|
||||
startIndex: this._getSlideForChild(this.view?.childIndex) ?? undefined,
|
||||
draggable: this.viewerConfig?.draggable,
|
||||
};
|
||||
}
|
||||
@@ -286,7 +291,7 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
||||
!this.view ||
|
||||
!this.view.target ||
|
||||
!this.view.target.children ||
|
||||
this.view.childIndex === undefined
|
||||
this.view.childIndex === null
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
@@ -398,12 +403,10 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
||||
}
|
||||
const clipStartTime = BrowseMediaUtil.getEventStartTime(child);
|
||||
if (clipStartTime && clipStartTime === snapshotStartTime) {
|
||||
return new View({
|
||||
return this.view.evolve({
|
||||
view: 'clip',
|
||||
camera: this.view.camera,
|
||||
target: clips,
|
||||
childIndex: i,
|
||||
previous: this.view,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -426,7 +429,6 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
||||
this.view
|
||||
.evolve({
|
||||
childIndex: childIndex,
|
||||
previous: this.view,
|
||||
})
|
||||
.dispatchChangeEvent(this);
|
||||
}
|
||||
@@ -443,7 +445,7 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
||||
const childIndex: number | undefined = this._slideToChild[index];
|
||||
|
||||
if (
|
||||
childIndex == undefined ||
|
||||
childIndex === undefined ||
|
||||
!this.hass ||
|
||||
!this.view ||
|
||||
!this.view.target ||
|
||||
|
||||
+27
-25
@@ -7,10 +7,10 @@ export interface ViewContext {}
|
||||
export interface ViewEvolveParameters {
|
||||
view?: FrigateCardView;
|
||||
camera?: string;
|
||||
target?: FrigateBrowseMediaSource;
|
||||
childIndex?: number;
|
||||
previous?: View;
|
||||
context?: ViewContext;
|
||||
target?: FrigateBrowseMediaSource | null;
|
||||
childIndex?: number | null;
|
||||
previous?: View | null;
|
||||
context?: ViewContext | null;
|
||||
}
|
||||
|
||||
export interface ViewParameters extends ViewEvolveParameters {
|
||||
@@ -21,18 +21,18 @@ export interface ViewParameters extends ViewEvolveParameters {
|
||||
export class View {
|
||||
view: FrigateCardView;
|
||||
camera: string;
|
||||
target?: FrigateBrowseMediaSource;
|
||||
childIndex?: number;
|
||||
previous?: View;
|
||||
context?: ViewContext;
|
||||
target: FrigateBrowseMediaSource | null;
|
||||
childIndex: number | null;
|
||||
previous: View | null;
|
||||
context: ViewContext | null;
|
||||
|
||||
constructor(params: ViewParameters) {
|
||||
this.view = params?.view;
|
||||
this.camera = params?.camera;
|
||||
this.target = params?.target;
|
||||
this.childIndex = params?.childIndex;
|
||||
this.previous = params?.previous;
|
||||
this.context = params?.context;
|
||||
this.view = params.view;
|
||||
this.camera = params.camera;
|
||||
this.target = params.target ?? null;
|
||||
this.childIndex = params.childIndex ?? null;
|
||||
this.previous = params.previous ?? null;
|
||||
this.context = params.context ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,12 +56,15 @@ export class View {
|
||||
*/
|
||||
public evolve(params: ViewEvolveParameters): View {
|
||||
return new View({
|
||||
view: params.view ?? this.view,
|
||||
camera: params.camera ?? this.camera,
|
||||
target: params.target ?? this.target,
|
||||
childIndex: params.childIndex ?? this.childIndex,
|
||||
previous: params.previous ?? this.previous,
|
||||
context: params.context ?? this.context,
|
||||
view: params.view !== undefined ? params.view : this.view,
|
||||
camera: params.camera !== undefined ? params.camera : this.camera,
|
||||
target: params.target !== undefined ? params.target : this.target,
|
||||
childIndex: params.childIndex !== undefined ? params.childIndex : this.childIndex,
|
||||
context: params.context !== undefined ? params.context : this.context,
|
||||
|
||||
// Special case: Set the previous to this of the evolved view (rather than
|
||||
// the previous of this).
|
||||
previous: params.previous !== undefined ? params.previous : this,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -110,14 +113,13 @@ export class View {
|
||||
/**
|
||||
* Get the media item that should be played.
|
||||
**/
|
||||
get media(): FrigateBrowseMediaSource | undefined {
|
||||
get media(): FrigateBrowseMediaSource | null {
|
||||
if (this.target) {
|
||||
if (this.target.children && this.childIndex !== undefined) {
|
||||
return this.target.children[this.childIndex];
|
||||
if (this.target.children && this.childIndex !== null) {
|
||||
return this.target.children[this.childIndex] ?? null;
|
||||
}
|
||||
return this.target;
|
||||
}
|
||||
return undefined;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user