diff --git a/package.json b/package.json index d7731871..6ed65656 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "custom-card-helpers": "^1.9.0", "date-fns": "^2.29.2", "date-fns-tz": "^1.3.7", - "embla-carousel": "^7.0.2", + "embla-carousel": "^7.0.9", "embla-carousel-wheel-gestures": "^3.0.0", "home-assistant-js-websocket": "^8.0.0", "keycharm": "^0.4.0", @@ -37,13 +37,12 @@ "quick-lru": "^6.1.0", "screenfull": "^6.0.2", "side-drawer": "^3.1.0", - "ts-toolbelt": "^9.6.0", "uuid": "^8.3.2", "vis-data": "^7.1.4", "vis-timeline": "^7.7.0", "vis-util": "^5.0.2", "xss": "^1.0.14", - "zod": "^3.20.2" + "zod": "^3.20.6" }, "devDependencies": { "@babel/core": "^7.19.0", @@ -73,8 +72,9 @@ "rollup-plugin-visualizer": "^5.8.2", "sass": "^1.54.9", "ts-prune": "^0.10.3", - "typescript": "^4.8.3" + "typescript": "^4.9.5" }, + "packageManager": "yarn@3.4.1", "scripts": { "start": "rollup -c --watch", "build": "yarn run lint && yarn run rollup", diff --git a/src/camera-manager/frigate/media.ts b/src/camera-manager/frigate/media.ts index 0b5f5a3e..aad8acbe 100644 --- a/src/camera-manager/frigate/media.ts +++ b/src/camera-manager/frigate/media.ts @@ -1,4 +1,3 @@ -import { HomeAssistant } from 'custom-card-helpers'; import fromUnixTime from 'date-fns/fromUnixTime'; import isEqual from 'lodash-es/isEqual'; import { CameraConfig } from '../../types'; diff --git a/src/camera-manager/manager.ts b/src/camera-manager/manager.ts index c3e0a11c..318f8fc0 100644 --- a/src/camera-manager/manager.ts +++ b/src/camera-manager/manager.ts @@ -525,9 +525,10 @@ export class CameraManager { // are assumed to be unique. uniqBy(mediaArray, (media) => media.getID() ?? media), - // Sort all items leading with the most recent. + // Sort all items leading oldest -> youngest (so media is loaded in this + // order in the viewer which matches the left-to-right timeline order). (media) => media.getStartTime(), - 'desc', + 'asc', ); } diff --git a/src/components/gallery.ts b/src/components/gallery.ts index 2959e4ca..f786938d 100644 --- a/src/components/gallery.ts +++ b/src/components/gallery.ts @@ -35,6 +35,7 @@ import { MediaQueriesResults } from '../view/media-queries-results'; import { errorToConsole } from '../utils/basic'; import './media-filter'; import "./surround-basic"; +import { ViewMedia } from '../view/media'; const GALLERY_MEDIA_CHUNK_SIZE = 100; @@ -91,8 +92,8 @@ export class FrigateCardGallery extends LitElement { const mediaType = this.view.is('snapshots') ? 'snapshots' : this.view.is('clips') - ? 'clips' - : null; + ? 'clips' + : null; changeViewToRecentEventsForCameraAndDependents( this, this.hass, @@ -110,14 +111,14 @@ export class FrigateCardGallery extends LitElement { return html` ${this.galleryConfig && this.galleryConfig.controls.filter.mode !== 'none' - ? html` ` - : ''} + : ''} - html` + html` { - if (this.view) { - this.view - .evolve({ - view: 'media', - queryResults: this.view.queryResults?.clone().selectResult(index), - }) - .dispatchChangeEvent(this); - } - stopEventFromActivatingCardWideActions(ev); - }} + if (this.view && this._media) { + this.view + .evolve({ + view: 'media', + queryResults: this.view.queryResults?.clone().selectResult( + // Media in the gallery is reversed vs the queryResults (see + // note above). + this._media.length - index - 1 + ), + }) + .dispatchChangeEvent(this); + } + stopEventFromActivatingCardWideActions(ev); + }} > `, - )} + )} ${this._showExtensionLoader ? html`${renderProgressIndicator({ - cardWideConfig: this.cardWideConfig, - componentRef: this._refLoader, - })}` + cardWideConfig: this.cardWideConfig, + componentRef: this._refLoader, + })}` : ''} `; } diff --git a/src/components/viewer.ts b/src/components/viewer.ts index d68f0dea..68e7930a 100644 --- a/src/components/viewer.ts +++ b/src/components/viewer.ts @@ -402,18 +402,23 @@ export class FrigateCardViewerCarousel extends LitElement { .dispatchChangeEvent(this); } - /** - * Handle the user selecting a new slide in the carousel. - */ protected _setViewHandler(ev: CustomEvent): void { - // The slide may already be selected on load, so don't dispatch a new view - // unless necessary. - if (ev.detail.index !== this.view?.queryResults?.getSelectedIndex()) { - this._setViewSelectedIndex(ev.detail.index); - } + this._setViewSelectedIndex(ev.detail.index); } protected _setViewSelectedIndex(index: number): void { + if (!this.view?.queryResults) { + return; + } + + const selectedIndex = this.view.queryResults.getSelectedIndex(); + if (selectedIndex === null || selectedIndex === index) { + // 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 + // index). + return; + } + const newResults = this.view?.queryResults?.clone().selectResult(index); if (!newResults) { return; @@ -555,7 +560,7 @@ export class FrigateCardViewerCarousel extends LitElement { } const media = this.view?.queryResults?.getSelectedResult(); - if (!media || !this.cameras) { + if (!media || !this.cameras || !this.view || !this.view.queryResults) { return; } @@ -578,7 +583,7 @@ export class FrigateCardViewerCarousel extends LitElement { draggable: this.viewerConfig?.draggable ?? true, }))} .carouselPlugins=${guard( - [this.viewerConfig, this.view?.queryResults?.getResults()], + [this.viewerConfig, this.view.queryResults.getResults()], this._getPlugins.bind(this), )} .label=${media.getTitle() ?? undefined} diff --git a/src/utils/media-to-view.ts b/src/utils/media-to-view.ts index 01b59eba..9a82106f 100644 --- a/src/utils/media-to-view.ts +++ b/src/utils/media-to-view.ts @@ -198,8 +198,9 @@ const executeMediaQueryForView = async ( if (!mediaArray) { return null; } - - const queryResults = new MediaQueriesResults(mediaArray); + // Select the last item by default (which is the most recent). + const selectedIndex = mediaArray.length ? mediaArray.length - 1 : undefined; + const queryResults = new MediaQueriesResults(mediaArray, selectedIndex); let viewerContext: ViewContext | undefined = {}; if (options?.targetTime && options.cameraIDs) { diff --git a/src/utils/timeline-source.ts b/src/utils/timeline-source.ts index ab84cf00..79886099 100644 --- a/src/utils/timeline-source.ts +++ b/src/utils/timeline-source.ts @@ -132,12 +132,13 @@ export class TimelineDataSource { } const mediaArray = await this._cameraManager.executeMediaQueries(hass, eventQueries); + const data: FrigateCardTimelineItem[] = [] for (const media of mediaArray ?? []) { const endTime = media.getEndTime(); const startTime = media.getStartTime(); const id = media.getID(); if (id && startTime) { - this._dataset.update({ + data.push({ id: id, group: media.getCameraID(), content: '', @@ -148,6 +149,7 @@ export class TimelineDataSource { }); } } + this._dataset.update(data); this._eventRanges.add({ ...cacheFriendlyWindow, diff --git a/yarn.lock b/yarn.lock index 790427da..612741d1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1858,7 +1858,7 @@ __metadata: languageName: node linkType: hard -"embla-carousel@npm:^7.0.2": +"embla-carousel@npm:^7.0.9": version: 7.0.9 resolution: "embla-carousel@npm:7.0.9" checksum: dabb371a44c2d67f21aa7a0ff3f452a5c2678b79e80cb251e0a83b707c4b684fbdbe010927142cdcae3d1c9711a269e05655bd05c39608337fd1b6f5d2c20371 @@ -2424,7 +2424,7 @@ __metadata: custom-card-helpers: ^1.9.0 date-fns: ^2.29.2 date-fns-tz: ^1.3.7 - embla-carousel: ^7.0.2 + embla-carousel: ^7.0.9 embla-carousel-wheel-gestures: ^3.0.0 eslint: ^8.23.0 eslint-config-airbnb-base: ^15.0.0 @@ -2450,14 +2450,13 @@ __metadata: screenfull: ^6.0.2 side-drawer: ^3.1.0 ts-prune: ^0.10.3 - ts-toolbelt: ^9.6.0 - typescript: ^4.8.3 + typescript: ^4.9.5 uuid: ^8.3.2 vis-data: ^7.1.4 vis-timeline: ^7.7.0 vis-util: ^5.0.2 xss: ^1.0.14 - zod: ^3.20.2 + zod: ^3.20.6 languageName: unknown linkType: soft @@ -5122,13 +5121,6 @@ __metadata: languageName: node linkType: hard -"ts-toolbelt@npm:^9.6.0": - version: 9.6.0 - resolution: "ts-toolbelt@npm:9.6.0" - checksum: 9f35fd95d895a5d32ea9fd2e532a695b0bae6cbff6832b77292efa188a0ed1ed6e54f63f74a8920390f3d909a7a3adb20a144686372a8e78b420246a9bd3d58a - languageName: node - linkType: hard - "tsconfig-paths@npm:^3.14.1": version: 3.14.1 resolution: "tsconfig-paths@npm:3.14.1" @@ -5193,7 +5185,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^4.5.4, typescript@npm:^4.8.3": +"typescript@npm:^4.5.4, typescript@npm:^4.9.5": version: 4.9.5 resolution: "typescript@npm:4.9.5" bin: @@ -5203,7 +5195,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@^4.5.4#~builtin, typescript@patch:typescript@^4.8.3#~builtin": +"typescript@patch:typescript@^4.5.4#~builtin, typescript@patch:typescript@^4.9.5#~builtin": version: 4.9.5 resolution: "typescript@patch:typescript@npm%3A4.9.5#~builtin::version=4.9.5&hash=23ec76" bin: @@ -5474,9 +5466,9 @@ __metadata: languageName: node linkType: hard -"zod@npm:^3.20.2": - version: 3.20.2 - resolution: "zod@npm:3.20.2" - checksum: 04172f7e9350372684ccd298d4716908edc9113751295b6c4e1b3ea84e2af8997e504b33ba36f4741417bb2a5dc90bfd40501f6b0e7389df10e42a63d6d8366c +"zod@npm:^3.20.6": + version: 3.20.6 + resolution: "zod@npm:3.20.6" + checksum: 804b1934b8b5e2fa3750bec90043e8118b201f330b9957b8b768389a971acadf812d2060cf62921086512dab4af691d10490acb03333da58fc485c0791893c89 languageName: node linkType: hard