diff --git a/src/components/timeline-core.ts b/src/components/timeline-core.ts index 28be5db1..aa4f1692 100644 --- a/src/components/timeline-core.ts +++ b/src/components/timeline-core.ts @@ -435,7 +435,7 @@ export class FrigateCardTimelineCore extends LitElement { * Called whenever the timeline is clicked. * @param properties The properties of the timeline click event. */ - protected _timelineClickHandler(properties: TimelineEventPropertiesResult): void { + protected async _timelineClickHandler(properties: TimelineEventPropertiesResult): Promise { // Calls to stopEventFromActivatingCardWideActions() are included for // completeness. Timeline does not support card-wide events and they are // disabled in card.ts in `_getMergedActions`. @@ -459,13 +459,13 @@ export class FrigateCardTimelineCore extends LitElement { return; } - let viewPromise: Promise | null = null; + let view: View | null = null; if ( this.timelineConfig?.show_recordings && ['background', 'group-label'].includes(properties.what) ) { - viewPromise = createViewForRecordings( + view = await createViewForRecordings( this.hass, this.cameraManager, this.cameras, @@ -481,7 +481,7 @@ export class FrigateCardTimelineCore extends LitElement { }, ); } else if (this.timelineConfig?.show_recordings && properties.what === 'axis') { - viewPromise = createViewForRecordings( + view = await createViewForRecordings( this.hass, this.cameraManager, this.cameras, @@ -498,24 +498,18 @@ export class FrigateCardTimelineCore extends LitElement { properties.what === 'item' && this.view.query?.areRecordingQueries() ) { - viewPromise = (async (): Promise => { - if (!properties.item || !this.cameraManager || !this.hass) { - return null; - } - const view = await this._createViewWithEventMediaQuery( - this._createEventMediaQuerys(), - { - selectedItem: properties.item, - targetView: 'media', - }, - ); - const results = view?.queryResults?.getResults(); - // Specifically ensure there are _some_ results before dispatching the - // view change. - if (!view || !results || !results.length) { - return null; - } - view.mergeInContext( + const eventView = await this._createViewWithEventMediaQuery( + this._createEventMediaQuerys(), + { + selectedItem: properties.item, + targetView: 'media', + }, + ); + const results = eventView?.queryResults?.getResults(); + // Specifically ensure there are _some_ results before dispatching the + // view change. + if (eventView && results && results.length) { + eventView.mergeInContext( await generateMediaViewerContext( this.hass, this.cameraManager, @@ -523,18 +517,15 @@ export class FrigateCardTimelineCore extends LitElement { properties.time, ), ); - return view; - })(); + view = eventView; + } } else if ( properties.item && properties.what === 'item' && - this.view.queryResults?.hasResults() + this.view.queryResults?.hasResults() && + this.view.query ) { - viewPromise = (async (): Promise => { - if (!this.view?.query) { - return null; - } - return this.view.evolve({ + view = this.view.evolve({ queryResults: this.view.queryResults ?.clone() .resetSelectedResult() @@ -544,24 +535,18 @@ export class FrigateCardTimelineCore extends LitElement { media.getID(this.cameras.get(media.getCameraID())) === properties.item, ), }); - })(); } - if (viewPromise) { - viewPromise.then((view: View | null) => { - if (view) { - view - // If the user is clicking something in the timeline, don't - // subsequently shift the window (it's pretty jarring). - .mergeInContext(this._generateTimelineContext({ noSetWindow: true })) - .dispatchChangeEvent(this); - if (this.view?.is('timeline')) { - dispatchFrigateCardEvent(this, 'thumbnails:open'); - } - this._ignoreClick = false; - return; - } - }); + if (view) { + view + // If the user is clicking something in the timeline, don't + // subsequently shift the window (it's pretty jarring). + .mergeInContext(this._generateTimelineContext({ noSetWindow: true })) + .dispatchChangeEvent(this); + + if (this.view?.is('timeline')) { + dispatchFrigateCardEvent(this, 'thumbnails:open'); + } } else if (this.view?.is('timeline')) { dispatchFrigateCardEvent(this, 'thumbnails:close'); } @@ -586,39 +571,37 @@ export class FrigateCardTimelineCore extends LitElement { * Handle a range change in the timeline. * @param properties vis.js provided range information. */ - protected _timelineRangeChangedHandler(properties: { + protected async _timelineRangeChangedHandler(properties: { start: Date; end: Date; byUser: boolean; event: Event & { additionalEvent: string }; - }): void { + }): Promise { if (!properties.byUser) { return; } this._removeTargetBar(); - (async (): Promise => { - if (!this.hass || !this.cameras) { - return; - } + if (!this.hass || !this.cameras) { + return; + } - const prefetchedWindow = this._getPrefetchWindow(properties); - await this._timelineSource?.refresh(this.hass, this.cameras, prefetchedWindow); + const prefetchedWindow = this._getPrefetchWindow(properties); + await this._timelineSource?.refresh(this.hass, this.cameras, prefetchedWindow); - // Don't show event thumbnails if the user is looking at recordings, - // as the recording "hours" are the media, not the event - // clips/snapshots. - if (this._timeline && this.view && !this.view.query?.areRecordingQueries()) { - ( - await this._createViewWithEventMediaQuery( - this._createEventMediaQuerys({ window: prefetchedWindow }), - { - noSetWindow: true, - }, - ) - )?.dispatchChangeEvent(this); - } - })(); + // Don't show event thumbnails if the user is looking at recordings, + // as the recording "hours" are the media, not the event + // clips/snapshots. + if (this._timeline && this.view && !this.view.query?.areRecordingQueries()) { + ( + await this._createViewWithEventMediaQuery( + this._createEventMediaQuerys({ window: prefetchedWindow }), + { + noSetWindow: true, + }, + ) + )?.dispatchChangeEvent(this); + } } protected _createEventMediaQuerys(options?: { diff --git a/src/view.ts b/src/view.ts index 97779e4a..e930692d 100644 --- a/src/view.ts +++ b/src/view.ts @@ -1,5 +1,4 @@ // Easy: -// - TODO: Search for references to frigate.js and see where it's being called outside of the cameraManager. Can I collapse some of those functions in? // - TODO: Refactor thumbnailsControlSchema to all use the shortform for other thumbnail users beyond live. // - TODO: limit param in recordings should do something // - TODO: Should be able to set live media to 'all' and have it work. @@ -7,15 +6,15 @@ // - 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: 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 _timelineRangeChangedHandler be an async method in timeline-core to improve cleanliness? // Medium: -// - TODO: Callers of all async methods of data-engine need to catch errors. -// - TODO: Add garbage collecting of segments not present in the recording summaries anymore. -// - TODO: Do I need to dedup recordings? (i.e. multiple zones on same camera may need to be dedup'd somewhere before returning the view). The media getID() call may be useful for this. -// - TODO: Do a fresh media query in the viewer on snapshot click, since the first query may (e.g.) only have requested events with snapshots (which would miss an event with just a clip). -// - TODO: ts-prune https://camchenry.com/blog/deleting-dead-code-in-typescript +// - TODO: Callers of all async methods of data-engine need to catch errors. +// - TODO: Add garbage collecting of segments not present in the recording summaries anymore. +// - TODO: Do I need to dedup recordings? (i.e. multiple zones on same camera may need to be dedup'd somewhere before returning the view). The media getID() call may be useful for this. +// - TODO: Do a fresh media query in the viewer on snapshot click, since the first query may (e.g.) only have requested events with snapshots (which would miss an event with just a clip). +// - TODO: Move view/ stuff into a view directory. +// - TODO: Move frigate specific view-media under the camera manager. +// - TODO: ts-prune https://camchenry.com/blog/deleting-dead-code-in-typescript // Hard: // - TODO: Implement dragging the timeline seeking forward in both Frigate recordings & events.