Merge pull request #1067 from dermotduffy/click-always-plays

Improve behavior when timeline items are clicked
This commit is contained in:
Dermot Duffy
2023-04-07 14:45:32 -07:00
committed by GitHub
5 changed files with 88 additions and 64 deletions
+5 -4
View File
@@ -144,10 +144,7 @@ export class FrigateCardSurround extends LitElement {
return null; return null;
} }
if (this.view?.is('live')) { if (this.view?.is('live')) {
return getAllDependentCameras( return getAllDependentCameras(this.cameraManager, this.view.camera);
this.cameraManager,
this.view.camera,
);
} }
if (this.view.isViewerView()) { if (this.view.isViewerView()) {
return new Set( return new Set(
@@ -225,6 +222,10 @@ export class FrigateCardSurround extends LitElement {
slot=${this.timelineConfig.mode} slot=${this.timelineConfig.mode}
.hass=${this.hass} .hass=${this.hass}
.view=${this.view} .view=${this.view}
.itemClickAction=${this.view.isViewerView() ||
this.thumbnailConfig.mode === 'none'
? 'play'
: 'select'}
.cameraIDs=${this._cameraIDsForTimeline} .cameraIDs=${this._cameraIDsForTimeline}
.mini=${true} .mini=${true}
.timelineConfig=${this.timelineConfig} .timelineConfig=${this.timelineConfig}
+55 -48
View File
@@ -34,7 +34,7 @@ import {
ExtendedHomeAssistant, ExtendedHomeAssistant,
frigateCardConfigDefaults, frigateCardConfigDefaults,
FrigateCardView, FrigateCardView,
ThumbnailsControlConfig, ThumbnailsControlBaseConfig,
TimelineCoreConfig, TimelineCoreConfig,
} from '../types'; } from '../types';
import { stopEventFromActivatingCardWideActions } from '../utils/action'; import { stopEventFromActivatingCardWideActions } from '../utils/action';
@@ -78,6 +78,8 @@ interface TimelineViewContext {
window?: TimelineWindow; window?: TimelineWindow;
} }
type TimelineItemClickAction = 'play' | 'select';
declare module 'view' { declare module 'view' {
interface ViewContext { interface ViewContext {
timeline?: TimelineViewContext; timeline?: TimelineViewContext;
@@ -173,7 +175,7 @@ export class FrigateCardTimelineCore extends LitElement {
public timelineConfig?: TimelineCoreConfig; public timelineConfig?: TimelineCoreConfig;
@property({ attribute: true, type: Boolean }) @property({ attribute: true, type: Boolean })
public thumbnailConfig?: ThumbnailsControlConfig; public thumbnailConfig?: ThumbnailsControlBaseConfig;
// Whether or not this is a mini-timeline (in mini-mode the component takes a // Whether or not this is a mini-timeline (in mini-mode the component takes a
// supportive role for other views). // supportive role for other views).
@@ -191,6 +193,9 @@ export class FrigateCardTimelineCore extends LitElement {
@property({ attribute: false }) @property({ attribute: false })
public cardWideConfig?: CardWideConfig; public cardWideConfig?: CardWideConfig;
@property({ attribute: false })
public itemClickAction?: TimelineItemClickAction;
@state() @state()
protected _locked = false; protected _locked = false;
@@ -507,6 +512,7 @@ export class FrigateCardTimelineCore extends LitElement {
} }
let view: View | null = null; let view: View | null = null;
let drawerAction: 'open' | 'close' = 'close';
if ( if (
this.timelineConfig?.show_recordings && this.timelineConfig?.show_recordings &&
@@ -585,23 +591,23 @@ export class FrigateCardTimelineCore extends LitElement {
} else { } else {
view = this.view.evolve({ view = this.view.evolve({
queryResults: newResults, queryResults: newResults,
view: this.itemClickAction === 'play' ? 'media' : this.view.view,
}); });
} }
if (view?.queryResults?.hasResults()) { if (view?.queryResults?.hasResults()) {
view.mergeInContext({ mediaViewer: { seek: properties.time } }); view.mergeInContext({ mediaViewer: { seek: properties.time } });
} }
if (this.itemClickAction === 'select' && view) {
drawerAction = 'open';
}
} }
if (view) { if (view) {
view.dispatchChangeEvent(this); view.dispatchChangeEvent(this);
if (this.view?.is('timeline')) {
dispatchFrigateCardEvent(this, 'thumbnails:open');
}
} else if (this.view?.is('timeline')) {
dispatchFrigateCardEvent(this, 'thumbnails:close');
} }
dispatchFrigateCardEvent(this, `thumbnails:${drawerAction}`);
this._ignoreClick = false; this._ignoreClick = false;
} }
@@ -1114,13 +1120,11 @@ export class FrigateCardTimelineCore extends LitElement {
this._destroy(); this._destroy();
} }
const options = this._getOptions();
let createdTimeline = false; let createdTimeline = false;
if ( if (
this._timelineSource && this._timelineSource &&
this._refTimeline.value && this._refTimeline.value &&
options &&
this.timelineConfig && this.timelineConfig &&
(changedProperties.has('timelineConfig') || changedProperties.has('cameraIDs')) (changedProperties.has('timelineConfig') || changedProperties.has('cameraIDs'))
) { ) {
@@ -1139,45 +1143,48 @@ export class FrigateCardTimelineCore extends LitElement {
return; return;
} }
createdTimeline = true; const options = this._getOptions();
if (this.mini && groups.length === 1) { if (options) {
// In a mini timeline, if there's only one group don't bother grouping createdTimeline = true;
// at all. if (this.mini && groups.length === 1) {
this._timeline = new Timeline( // In a mini timeline, if there's only one group don't bother grouping
this._refTimeline.value, // at all.
this._timelineSource.dataset, this._timeline = new Timeline(
options, this._refTimeline.value,
) as Timeline; this._timelineSource.dataset,
this.removeAttribute('groups'); options,
} else { ) as Timeline;
this._timeline = new Timeline( this.removeAttribute('groups');
this._refTimeline.value, } else {
this._timelineSource.dataset, this._timeline = new Timeline(
groups, this._refTimeline.value,
options, this._timelineSource.dataset,
) as Timeline; groups,
this.setAttribute('groups', ''); options,
) as Timeline;
this.setAttribute('groups', '');
}
this._timeline.on('rangechanged', this._timelineRangeChangedHandler.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', (ev: TimelineEventPropertiesResult) => {
const window = this._timeline?.getWindow();
this._pointerHeld = {
...ev,
...(window && { window: window }),
};
this._ignoreClick = false;
});
this._timeline.on('mouseUp', () => {
this._pointerHeld = null;
this._removeTargetBar();
});
} }
this._timeline.on('rangechanged', this._timelineRangeChangedHandler.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', (ev: TimelineEventPropertiesResult) => {
const window = this._timeline?.getWindow();
this._pointerHeld = {
...ev,
...(window && { window: window }),
};
this._ignoreClick = false;
});
this._timeline.on('mouseUp', () => {
this._pointerHeld = null;
this._removeTargetBar();
});
} }
if (changedProperties.has('view')) { if (changedProperties.has('view')) {
+3
View File
@@ -51,6 +51,9 @@ export class FrigateCardTimeline extends LitElement {
.thumbnailConfig=${this.timelineConfig.controls.thumbnails} .thumbnailConfig=${this.timelineConfig.controls.thumbnails}
.cameraManager=${this.cameraManager} .cameraManager=${this.cameraManager}
.cardWideConfig=${this.cardWideConfig} .cardWideConfig=${this.cardWideConfig}
.itemClickAction=${this.timelineConfig.controls.thumbnails.mode === 'none'
? 'play'
: 'select'}
> >
</frigate-card-timeline-core> </frigate-card-timeline-core>
</frigate-card-surround>`; </frigate-card-surround>`;
+1 -1
View File
@@ -165,7 +165,7 @@ export const CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL =
export const CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL = export const CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL =
`${CONF_LIVE}.controls.thumbnails.show_timeline_control` as const; `${CONF_LIVE}.controls.thumbnails.show_timeline_control` as const;
export const CONF_LIVE_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD = export const CONF_LIVE_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD =
`${CONF_LIVE}.control s.timeline.clustering_threshold` as const; `${CONF_LIVE}.controls.timeline.clustering_threshold` as const;
export const CONF_LIVE_CONTROLS_TIMELINE_MEDIA = export const CONF_LIVE_CONTROLS_TIMELINE_MEDIA =
`${CONF_LIVE}.controls.timeline.media` as const; `${CONF_LIVE}.controls.timeline.media` as const;
export const CONF_LIVE_CONTROLS_TIMELINE_MODE = export const CONF_LIVE_CONTROLS_TIMELINE_MODE =
+24 -11
View File
@@ -779,8 +779,8 @@ export type ImageViewConfig = z.infer<typeof imageConfigSchema>;
/** /**
* Thumbnail controls configuration section. * Thumbnail controls configuration section.
*/ */
const thumbnailControlsDefaults = {
mode: 'right' as const, const thumbnailControlsBaseDefaults = {
size: 100, size: 100,
show_details: true, show_details: true,
show_favorite_control: true, show_favorite_control: true,
@@ -788,26 +788,39 @@ const thumbnailControlsDefaults = {
show_download_control: true, show_download_control: true,
}; };
const thumbnailsControlSchema = z.object({ // Configuration for the actual rendered thumbnail.
mode: z const thumbnailsControlBaseSchema = z.object({
.enum(['none', 'above', 'below', 'left', 'right'])
.default(thumbnailControlsDefaults.mode),
size: z size: z
.number() .number()
.min(THUMBNAIL_WIDTH_MIN) .min(THUMBNAIL_WIDTH_MIN)
.max(THUMBNAIL_WIDTH_MAX) .max(THUMBNAIL_WIDTH_MAX)
.default(thumbnailControlsDefaults.size), .default(thumbnailControlsBaseDefaults.size),
show_details: z.boolean().default(thumbnailControlsDefaults.show_details), show_details: z.boolean().default(thumbnailControlsBaseDefaults.show_details),
show_favorite_control: z show_favorite_control: z
.boolean() .boolean()
.default(thumbnailControlsDefaults.show_favorite_control), .default(thumbnailControlsBaseDefaults.show_favorite_control),
show_timeline_control: z show_timeline_control: z
.boolean() .boolean()
.default(thumbnailControlsDefaults.show_timeline_control), .default(thumbnailControlsBaseDefaults.show_timeline_control),
show_download_control: z show_download_control: z
.boolean() .boolean()
.default(thumbnailControlsDefaults.show_download_control), .default(thumbnailControlsBaseDefaults.show_download_control),
}); });
export type ThumbnailsControlBaseConfig = z.infer<typeof thumbnailsControlBaseSchema>;
// Configuration that may control the placement of the thumbnail.
const thumbnailControlsDefaults = {
...thumbnailControlsBaseDefaults,
mode: 'right' as const,
};
const thumbnailsControlSchema = thumbnailsControlBaseSchema.extend({
mode: z
.enum(['none', 'above', 'below', 'left', 'right'])
.default(thumbnailControlsDefaults.mode),
});
export type ThumbnailsControlConfig = z.infer<typeof thumbnailsControlSchema>; export type ThumbnailsControlConfig = z.infer<typeof thumbnailsControlSchema>;
/** /**