Fix card-wide actions.

This commit is contained in:
Dermot Duffy
2024-04-07 21:18:55 -07:00
parent 43406b21e6
commit 9c9548afc8
3 changed files with 52 additions and 10 deletions
+20 -4
View File
@@ -1,3 +1,4 @@
import { z } from 'zod';
import { import {
Actions, Actions,
ActionsConfig, ActionsConfig,
@@ -13,6 +14,15 @@ import {
import { getStreamCameraID } from '../utils/substream.js'; import { getStreamCameraID } from '../utils/substream.js';
import { CardActionsManagerAPI } from './types.js'; import { CardActionsManagerAPI } from './types.js';
const interactionSchema = z.object({
action: z.enum(['tap', 'double_tap', 'hold', 'start_tap', 'end_tap']),
});
export type Interaction = z.infer<typeof interactionSchema>;
const interactionEventSchema = z.object({
detail: interactionSchema,
});
export class ActionsManager { export class ActionsManager {
protected _api: CardActionsManagerAPI; protected _api: CardActionsManagerAPI;
@@ -49,7 +59,14 @@ export class ActionsManager {
/** /**
* Handle an human interaction called on an element (e.g. 'tap'). * Handle an human interaction called on an element (e.g. 'tap').
*/ */
public handleInteraction(interaction: string): void { public handleInteractionEvent = (ev: Event): void => {
const result = interactionEventSchema.safeParse(ev);
if (!result.success) {
// The event may not be a CustomEvent object, see:
// https://github.com/custom-cards/custom-card-helpers/blob/master/src/fire-event.ts#L70
return;
}
const interaction = result.data.detail.action;
const hass = this._api.getHASSManager().getHASS(); const hass = this._api.getHASSManager().getHASS();
const config = this.getMergedActions(); const config = this.getMergedActions();
const actionConfig = getActionConfigGivenAction(interaction, config); const actionConfig = getActionConfigGivenAction(interaction, config);
@@ -70,12 +87,11 @@ export class ActionsManager {
actionConfig, actionConfig,
); );
} }
} };
public handleActionEvent = (ev: Event): void => { public handleActionEvent = (ev: Event): void => {
if (!('detail' in ev)) { if (!('detail' in ev)) {
// The event may not actually be a CustomEvent object, but may still have a // The event may not be a CustomEvent object, see:
// detail field. See:
// https://github.com/custom-cards/custom-card-helpers/blob/master/src/fire-event.ts#L70 // https://github.com/custom-cards/custom-card-helpers/blob/master/src/fire-event.ts#L70
return; return;
} }
+10 -2
View File
@@ -77,7 +77,11 @@ export class CardElementManager {
this._api.getActionsManager().handleActionEvent, this._api.getActionsManager().handleActionEvent,
); );
this._element.addEventListener( this._element.addEventListener(
'@action', 'action',
this._api.getActionsManager().handleInteractionEvent,
);
this._element.addEventListener(
'action',
this._api.getInteractionManager().reportInteraction, this._api.getInteractionManager().reportInteraction,
); );
@@ -123,7 +127,11 @@ export class CardElementManager {
this._api.getActionsManager().handleActionEvent, this._api.getActionsManager().handleActionEvent,
); );
this._element.removeEventListener( this._element.removeEventListener(
'@action', 'action',
this._api.getActionsManager().handleInteractionEvent,
);
this._element.removeEventListener(
'action',
this._api.getInteractionManager().reportInteraction, this._api.getInteractionManager().reportInteraction,
); );
+22 -4
View File
@@ -14,7 +14,7 @@ import {
frigateCardHandleActionConfig, frigateCardHandleActionConfig,
getActionConfigGivenAction, getActionConfigGivenAction,
} from '../../src/utils/action.js'; } from '../../src/utils/action.js';
import { ActionsManager } from '../../src/card-controller/actions-manager'; import { ActionsManager, Interaction } from '../../src/card-controller/actions-manager';
import { import {
createCardAPI, createCardAPI,
createConfig, createConfig,
@@ -165,7 +165,9 @@ describe('ActionsManager.handleInteraction', () => {
}; };
vi.mocked(getActionConfigGivenAction).mockReturnValue(actionForThisInteraction); vi.mocked(getActionConfigGivenAction).mockReturnValue(actionForThisInteraction);
manager.handleInteraction('tap'); manager.handleInteractionEvent(
new CustomEvent<Interaction>('event', { detail: { action: 'tap' } }),
);
expect(frigateCardHandleActionConfig).toBeCalledWith( expect(frigateCardHandleActionConfig).toBeCalledWith(
element, element,
@@ -176,13 +178,29 @@ describe('ActionsManager.handleInteraction', () => {
); );
}); });
it('should not handle interaction', () => { it('should not handle interaction without hass', () => {
const api = createCardAPI(); const api = createCardAPI();
const manager = new ActionsManager(api); const manager = new ActionsManager(api);
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(null); vi.mocked(api.getHASSManager().getHASS).mockReturnValue(null);
// No values of hass. // No values of hass.
manager.handleInteraction('tap'); manager.handleInteractionEvent(
new CustomEvent<Interaction>('event', { detail: { action: 'tap' } }),
);
expect(frigateCardHandleActionConfig).not.toBeCalledWith();
});
it('should not handle malformed interaction', () => {
const api = createCardAPI();
const manager = new ActionsManager(api);
manager.handleInteractionEvent(
new CustomEvent<Interaction>('event', {
// Malformed interaction type.
detail: { action: 'double_finger_snap' } as unknown as Interaction,
}),
);
expect(frigateCardHandleActionConfig).not.toBeCalledWith(); expect(frigateCardHandleActionConfig).not.toBeCalledWith();
}); });
}); });