Dispatch message if the viewer has no media to show.

This commit is contained in:
Dermot Duffy
2023-01-24 19:36:54 -08:00
parent 5be6ee86e0
commit e0d2dfaa60
8 changed files with 26 additions and 30 deletions
+4 -2
View File
@@ -509,8 +509,10 @@ export class FrigateCardTimelineCore extends LitElement {
targetView: 'media', targetView: 'media',
}, },
); );
const results = view?.queryResults?.getResults() ?? null; const results = view?.queryResults?.getResults();
if (!results || !view) { // Specifically ensure there are _some_ results before dispatching the
// view change.
if (!view || !results || !results.length) {
return null; return null;
} }
view.mergeInContext( view.mergeInContext(
+9 -2
View File
@@ -12,7 +12,7 @@ import {
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 { renderProgressIndicator } from '../components/message.js'; import { dispatchMessageEvent, renderProgressIndicator } from '../components/message.js';
import viewerStyle from '../scss/viewer.scss'; import viewerStyle from '../scss/viewer.scss';
import viewerCarouselStyle from '../scss/viewer-carousel.scss'; import viewerCarouselStyle from '../scss/viewer-carousel.scss';
import { import {
@@ -53,6 +53,7 @@ import {
} from '../utils/media-to-view.js'; } from '../utils/media-to-view.js';
import { ViewMedia, ViewMediaClassifier } from '../view-media.js'; import { ViewMedia, ViewMediaClassifier } from '../view-media.js';
import { guard } from 'lit/directives/guard.js'; import { guard } from 'lit/directives/guard.js';
import { localize } from '../localize/localize.js';
export interface MediaSeek { export interface MediaSeek {
// Specifies the point at which this recording should be played, the // Specifies the point at which this recording should be played, the
@@ -566,6 +567,12 @@ export class FrigateCardViewerCarousel extends LitElement {
* @returns A template to display to the user. * @returns A template to display to the user.
*/ */
protected _render(): TemplateResult | void { protected _render(): TemplateResult | void {
if ((this.view?.queryResults?.getResultsCount() ?? 0) === 0) {
return dispatchMessageEvent(this, localize('common.no_media'), 'info', {
icon: 'mdi:multimedia',
});
}
const media = this.view?.queryResults?.getSelectedResult(); const media = this.view?.queryResults?.getSelectedResult();
if (!media || !this.cameras) { if (!media || !this.cameras) {
return; return;
@@ -622,7 +629,7 @@ export class FrigateCardViewerCarousel extends LitElement {
protected _recordingSeekHandler(): void { protected _recordingSeekHandler(): void {
const selectedIndex = this.view?.queryResults?.getSelectedIndex() ?? null; const selectedIndex = this.view?.queryResults?.getSelectedIndex() ?? null;
const seek = const seek =
selectedIndex !== null selectedIndex !== null
? this.view?.context?.mediaViewer?.seek.get(selectedIndex) ? this.view?.context?.mediaViewer?.seek.get(selectedIndex)
: null; : null;
const player = this._getPlayer(); const player = this._getPlayer();
+1 -5
View File
@@ -3,11 +3,7 @@
"frigate_card": "Frigate card", "frigate_card": "Frigate card",
"frigate_card_description": "A Lovelace card for use with Frigate", "frigate_card_description": "A Lovelace card for use with Frigate",
"live": "Live", "live": "Live",
"no_clip": "No recent clip", "no_media": "No media to display",
"no_clips": "No clips",
"no_snapshot": "No recent snapshot",
"no_snapshots": "No snapshots",
"no_recording": "No recent recording",
"recordings": "Recordings", "recordings": "Recordings",
"version": "Version" "version": "Version"
}, },
+1 -4
View File
@@ -3,10 +3,7 @@
"frigate_card": "Frigate card", "frigate_card": "Frigate card",
"frigate_card_description": "Una scheda Lovelace per l'uso con Frigate", "frigate_card_description": "Una scheda Lovelace per l'uso con Frigate",
"live": "Live", "live": "Live",
"no_clip": "Nessuna clip recente", "no_media": "",
"no_clips": "Nessun clip",
"no_snapshot": "Nessuna istantanea recente",
"no_snapshots": "Nessuna istantanea",
"recordings": "Registrazioni", "recordings": "Registrazioni",
"version": "Versione" "version": "Versione"
}, },
+1 -4
View File
@@ -3,10 +3,7 @@
"frigate_card": "Cartão Frigate", "frigate_card": "Cartão Frigate",
"frigate_card_description": "Um cartão da Lovelace para usar com Frigate", "frigate_card_description": "Um cartão da Lovelace para usar com Frigate",
"live": "Ao Vivo", "live": "Ao Vivo",
"no_clip": "Sem clip recente", "no_media": "",
"no_clips": "Sem clips",
"no_snapshot": "Sem snapshot recente",
"no_snapshots": "Sem snapshots",
"recordings": "Gravações", "recordings": "Gravações",
"version": "Versão" "version": "Versão"
}, },
+5 -7
View File
@@ -202,13 +202,11 @@ export class DataManager {
} }
} }
return mediaArray.length return new MediaQueriesResults(
? new MediaQueriesResults( orderBy(mediaArray, (media) => media.getStartTime(), 'desc'),
orderBy(mediaArray, (media) => media.getStartTime(), 'desc'), // Select the first (most-recent) item.
// Select the first (most-recent) item. mediaArray.length ? 0 : null,
0, );
)
: null;
} }
public getMediaDownloadPath(media: ViewMedia): string | null { public getMediaDownloadPath(media: ViewMedia): string | null {
+2 -2
View File
@@ -54,9 +54,9 @@ export const createViewForEvents = async (
const queries = dataManager.generateDefaultEventQueries(cameraIDs, { const queries = dataManager.generateDefaultEventQueries(cameraIDs, {
...(options?.limit && { limit: options.limit }), ...(options?.limit && { limit: options.limit }),
...((!options?.mediaType || ['clips', 'all'].includes(options.mediaType)) && { ...((!options?.mediaType || ['clips', 'all'].includes(options.mediaType)) && {
has_clip: true, hasClip: true,
}), }),
...(options?.mediaType === 'snapshots' && { has_snapshot: true }), ...(options?.mediaType === 'snapshots' && { hasSnapshot: true }),
}); });
query = new EventMediaQueries(queries); query = new EventMediaQueries(queries);
} }
+3 -4
View File
@@ -6,7 +6,6 @@
// - TODO: Are there elements of ViewMedia (e.g. getEventCount) that should be moved into subclasses (e.g. a recording subclass). // - TODO: Are there elements of ViewMedia (e.g. getEventCount) that should be moved into subclasses (e.g. a recording subclass).
// - TODO: In MediaQueriesBase, do we need to generic? Just have T be a MediaQuery? // - TODO: In MediaQueriesBase, do we need to generic? Just have T be a MediaQuery?
// - TODO: Are areEventQueries and areRecordingQueries should be in a classifier to keep with the pattern used elsewhere. // - TODO: Are areEventQueries and areRecordingQueries should be in a classifier to keep with the pattern used elsewhere.
// - TODO: Callers to the creation of new views for events/recordings need to dispatch events themselves when none are found.
// - TODO: In the viewer @click handlers should I use this.selected instead of calling carouselScrollPrevious() // - TODO: In the viewer @click handlers should I use this.selected instead of calling carouselScrollPrevious()
// - TODO: Can _timelineClickHandler be an async method in timeline-core to improve cleanliness? // - TODO: Can _timelineClickHandler be an async method in timeline-core to improve cleanliness?
// - TODO: Can _timelineRangeChangedHandler be an async method in timeline-core to improve cleanliness? // - TODO: Can _timelineRangeChangedHandler be an async method in timeline-core to improve cleanliness?
@@ -124,7 +123,7 @@ export class MediaQueriesResults {
protected _resultsTimestamp: Date | null = null; protected _resultsTimestamp: Date | null = null;
protected _selectedIndex: number | null = null; protected _selectedIndex: number | null = null;
constructor(results?: ViewMedia[], selectedIndex?: number) { constructor(results?: ViewMedia[], selectedIndex?: number | null) {
if (results) { if (results) {
this.setResults(results); this.setResults(results);
} }
@@ -177,8 +176,8 @@ export class MediaQueriesResults {
return this._resultsTimestamp; return this._resultsTimestamp;
} }
public selectResult(index: number): MediaQueriesResults { public selectResult(index: number | null): MediaQueriesResults {
if (this._results && index >= 0 && index < this._results.length) { if (index === null || (this._results && index >= 0 && index < this._results.length)) {
this._selectedIndex = index; this._selectedIndex = index;
} }
return this; return this;