diff --git a/README.md b/README.md index 4a99ea72..6ffc2d6f 100644 --- a/README.md +++ b/README.md @@ -542,7 +542,6 @@ See the [fully expanded timeline configuration example](#config-expanded-timelin | `media` | `all` | :heavy_multiplication_x: | Whether to show only events with `clips`, events with `snapshots` or `all` events. When `all` is used, `clips` are favored for events that have both a clip and a snapshot.| | `show_recordings` | `true` | :heavy_multiplication_x: | Whether to show recordings on the timeline (specifically: which hours have any recorded content).| | `controls` | | :heavy_multiplication_x: | Configuration for the timeline controls. See below.| -| `actions` | | :heavy_multiplication_x: | Actions to use for the `timeline` views. See [actions](#actions) below.| #### Timeline Controls: Thumbnails @@ -933,6 +932,8 @@ next/previous controls, thumbnails, etc), but in some cases this is not possible (e.g. embedded WebRTC card controls) -- in these cases duplicate actions may occur with certain configurations (e.g. `tap`). +**Note:** Card-wide actions are not supported on the timeline view. + ## Menu Styles This card supports several menu styles. @@ -1716,18 +1717,6 @@ timeline: size: 100 show_details: true show_controls: true - actions: - entity: light.office_main_lights - tap_action: - action: none - hold_action: - action: none - double_tap_action: - action: none - start_tap_action: - action: none - end_tap_action: - action: none ``` diff --git a/src/action-handler-directive.ts b/src/action-handler-directive.ts index e7309ddf..b1e52948 100644 --- a/src/action-handler-directive.ts +++ b/src/action-handler-directive.ts @@ -147,9 +147,6 @@ class ActionHandler extends HTMLElement implements ActionHandler { element.addEventListener('mousedown', start, { passive: true }); element.addEventListener('click', end); - // For the timeline which generates native pointer events. - element.addEventListener('pointerdown', start, { passive: true }); - element.addEventListener('keyup', handleEnter); } } diff --git a/src/card.ts b/src/card.ts index 52438f12..87422e03 100644 --- a/src/card.ts +++ b/src/card.ts @@ -128,7 +128,10 @@ import pkg from '../package.json'; /* eslint no-console: 0 */ console.info( - `%c FRIGATE-HASS-CARD \n%c ${localize('common.version')} ${pkg.version} `, + `%c FRIGATE-HASS-CARD \n` + + `%c ${localize('common.version')} ` + + `${pkg.version} ` + + `${process.env.NODE_ENV === 'development' ? `(${pkg['buildDate']})` : ''}`, 'color: pink; font-weight: bold; background: black', 'color: white; font-weight: bold; background: dimgray', ); @@ -1692,8 +1695,17 @@ export class FrigateCard extends LitElement { * @returns A combined set of action. */ protected _getMergedActions(): Actions { - let specificActions: Actions | undefined = undefined; + if (this._view?.is('timeline')) { + // Timeline does not support actions as it is not possible to prevent + // touch actions on the timeline surface from triggering card-wide actions + // inappropriately, whilst also maintaining touch interaction with the + // timeline itself (and almost the entire timeline surface can be + // interacted with). This causes duplicate/inappropriate card-wide actions + // on touch surfaces. + return {}; + } + let specificActions: Actions | undefined = undefined; if (this._view?.is('live')) { specificActions = this._getConfig().live.actions; } else if (this._view?.isGalleryView()) { @@ -1702,8 +1714,6 @@ export class FrigateCard extends LitElement { specificActions = this._getConfig().media_viewer.actions; } else if (this._view?.is('image')) { specificActions = this._getConfig().image?.actions; - } else if (this._view?.is('timeline')) { - specificActions = this._getConfig().timeline?.actions; } return { ...this._getConfig().view.actions, ...specificActions }; } diff --git a/src/components/timeline.ts b/src/components/timeline.ts index 17271e04..b75a37a4 100644 --- a/src/components/timeline.ts +++ b/src/components/timeline.ts @@ -490,9 +490,9 @@ export class FrigateCardTimelineCore extends LitElement { protected _timeline?: Timeline; // Need a way to separate when a user clicks (to pan the timeline) vs when a - // user clicks (to choose a recording (non-event) to play). On pan, - // _wasDragged will be set to true, and the click subsequently ignored. - protected _wasDragged = false; + // user clicks (to choose a recording (non-event) to play). + protected _pointerHeld = false; + protected _ignoreClick = false; /** * Get a tooltip for a given timeline event. @@ -733,9 +733,9 @@ export class FrigateCardTimelineCore extends LitElement { protected _timelineRangeChangeHandler( properties: TimelineEventPropertiesResult, ): void { - if (properties.event) { - // When a human changes the range, an event will be set. - this._wasDragged = true; + if (properties.event && this._pointerHeld) { + // An event will have been set when it's a human changes the range, + this._ignoreClick = true; } } @@ -744,11 +744,14 @@ export class FrigateCardTimelineCore extends LitElement { * @param properties The properties of the timeline click event. */ protected _timelineClickHandler(properties: TimelineEventPropertiesResult): void { - if (properties.what === 'item' || this._wasDragged) { + // Calls to stopEventFromActivatingCardWideActions() are included for + // completeness. Timeline does not support card-wide events and they are + // disabled in card.ts in `_getMergedActions`. + if (properties.what === 'item' || this._ignoreClick) { stopEventFromActivatingCardWideActions(properties.event); } - if (!this._wasDragged && properties.what && this.timelineConfig?.show_recordings) { + if (!this._ignoreClick && properties.what && this.timelineConfig?.show_recordings) { if (['background', 'group-label'].includes(properties.what)) { stopEventFromActivatingCardWideActions(properties.event); this._changeViewToRecording(properties.time, String(properties.group)); @@ -758,7 +761,7 @@ export class FrigateCardTimelineCore extends LitElement { } } - this._wasDragged = false; + this._ignoreClick = false; } /** @@ -1245,6 +1248,17 @@ export class FrigateCardTimelineCore extends LitElement { this._timeline.on('rangechanged', this._timelineRangeHandler.bind(this)); this._timeline.on('click', this._timelineClickHandler.bind(this)); this._timeline.on('rangechange', this._timelineRangeChangeHandler.bind(this)); + + // This complexity exists to ensure we can tell between a click that + // causes the timeline zoom/range to change, and a 'static' click on the + // // timeline (which may need to trigger a card wide event). + this._timeline.on('mouseDown', () => { + this._pointerHeld = true; + this._ignoreClick = false; + }) + this._timeline.on('mouseUp', () => { + this._pointerHeld = false; + }) } } diff --git a/src/types.ts b/src/types.ts index 29302125..e370679f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1052,7 +1052,6 @@ const timelineConfigSchema = z }) .default(timelineConfigDefault.controls), }) - .merge(actionsSchema) .default(timelineConfigDefault); export type TimelineConfig = z.infer;