diff --git a/src/card-controller/view/factory.ts b/src/card-controller/view/factory.ts index 5a0f6b8e..0327a7e0 100644 --- a/src/card-controller/view/factory.ts +++ b/src/card-controller/view/factory.ts @@ -245,7 +245,6 @@ export class ViewFactory { } else { switch (view.view) { case 'live': - this._setTimelineWindowToLive(view); if (config.live.controls.thumbnails.mode !== 'none') { await executeMediaQuery( config.live.controls.thumbnails.media_type === 'recordings' @@ -282,6 +281,7 @@ export class ViewFactory { } } + this._setOrRemoveTimelineWindow(view); this._setOrRemoveSeekTime( view, options?.queryExecutorOptions?.selectResult?.time?.time, @@ -289,29 +289,37 @@ export class ViewFactory { return view; } - protected _setTimelineWindowToLive(view: View): void { - const now = new Date(); - const liveConfig = this._api.getConfigManager().getConfig()?.live; + protected _setOrRemoveTimelineWindow(view: View): void { + if (view.is('live')) { + // For live views, always force the timeline to now, regardless of + // presence or not of events. + const now = new Date(); + const liveConfig = this._api.getConfigManager().getConfig()?.live; - /* istanbul ignore if: this if branch cannot be reached as if the config is - empty this function is never called -- @preserve */ - if (!liveConfig) { - return; - } + /* istanbul ignore if: this if branch cannot be reached as if the config is + empty this function is never called -- @preserve */ + if (!liveConfig) { + return; + } - view.mergeInContext({ - // Force the window to start at the most recent time, not - // necessarily when the most recent event/recording was: - // https://github.com/dermotduffy/frigate-hass-card/issues/1301 - timeline: { - window: { - start: sub(now, { - seconds: liveConfig.controls.timeline.window_seconds, - }), - end: now, + view.mergeInContext({ + // Force the window to start at the most recent time, not + // necessarily when the most recent event/recording was: + // https://github.com/dermotduffy/frigate-hass-card/issues/1301 + timeline: { + window: { + start: sub(now, { + seconds: liveConfig.controls.timeline.window_seconds, + }), + end: now, + }, }, - }, - }); + }); + } else { + // For non-live views stick to default timeline behavior (will select and + // scroll to event). + view.removeContextProperty('timeline', 'window'); + } } protected _setOrRemoveSeekTime(view: View, time?: Date): void { diff --git a/tests/card-controller/view/factory.test.ts b/tests/card-controller/view/factory.test.ts index b837e39b..c0fcae8c 100644 --- a/tests/card-controller/view/factory.test.ts +++ b/tests/card-controller/view/factory.test.ts @@ -17,6 +17,7 @@ import { createCardAPI, createConfig, createStore, + createView, } from '../../test-utils'; import { createPopulatedAPI } from './test-utils'; @@ -340,22 +341,46 @@ describe('getViewByParametersWithNewQuery', () => { vi.useRealTimers(); }); - it('should set timeline window', async () => { - const executor = mock(); - const factory = new ViewFactory(createPopulatedAPI(), executor); - const view = await factory.getViewByParametersWithNewQuery({ - params: { - view: 'live', - }, + describe('should set timeline window', async () => { + it('should set timeline to now for live views', async () => { + const executor = mock(); + const factory = new ViewFactory(createPopulatedAPI(), executor); + const view = await factory.getViewByParametersWithNewQuery({ + params: { + view: 'live', + }, + }); + + expect(view?.context).toEqual({ + timeline: { + window: { + start: new Date('2024-07-21T12:22:06.000Z'), + end: new Date('2024-07-21T13:22:06.000Z'), + }, + }, + }); }); - expect(view?.context).toEqual({ - timeline: { - window: { - start: new Date('2024-07-21T12:22:06.000Z'), - end: new Date('2024-07-21T13:22:06.000Z'), + it('should unset timeline for non-live views', async () => { + const executor = mock(); + const factory = new ViewFactory(createPopulatedAPI(), executor); + const view = await factory.getViewByParametersWithNewQuery({ + baseView: createView({ + context: { + timeline: { + window: { + start: new Date('2024-07-21T12:22:06.000Z'), + end: new Date('2024-07-21T13:22:06.000Z'), + }, + }, + }, + }), + params: { + view: 'clip', }, - }, + }); + + expect(view?.context).toEqual({ timeline: {} }); }); });