Use subscribe for todo items (#18776)

This commit is contained in:
Bram Kragten 2023-11-28 08:28:13 +01:00 committed by GitHub
parent db68c7faa9
commit ffb19b31a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 47 additions and 56 deletions

View File

@ -47,15 +47,25 @@ export interface TodoItems {
export const fetchItems = async ( export const fetchItems = async (
hass: HomeAssistant, hass: HomeAssistant,
entityId: string entity_id: string
): Promise<TodoItem[]> => { ): Promise<TodoItem[]> => {
const result = await hass.callWS<TodoItems>({ const result = await hass.callWS<TodoItems>({
type: "todo/item/list", type: "todo/item/list",
entity_id: entityId, entity_id,
}); });
return result.items; return result.items;
}; };
export const subscribeItems = (
hass: HomeAssistant,
entity_id: string,
callback: (item) => void
) =>
hass.connection.subscribeMessage<any>(callback, {
type: "todo/item/subscribe",
entity_id,
});
export const updateItem = ( export const updateItem = (
hass: HomeAssistant, hass: HomeAssistant,
entity_id: string, entity_id: string,

View File

@ -24,36 +24,32 @@ import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_elemen
import { supportsFeature } from "../../../common/entity/supports-feature"; import { supportsFeature } from "../../../common/entity/supports-feature";
import "../../../components/ha-card"; import "../../../components/ha-card";
import "../../../components/ha-checkbox"; import "../../../components/ha-checkbox";
import "../../../components/ha-icon-button";
import "../../../components/ha-list-item"; import "../../../components/ha-list-item";
import "../../../components/ha-select"; import "../../../components/ha-select";
import "../../../components/ha-svg-icon"; import "../../../components/ha-svg-icon";
import "../../../components/ha-icon-button";
import "../../../components/ha-textfield"; import "../../../components/ha-textfield";
import type { HaTextField } from "../../../components/ha-textfield"; import type { HaTextField } from "../../../components/ha-textfield";
import { isUnavailableState } from "../../../data/entity";
import { import {
TodoItem, TodoItem,
TodoItemStatus, TodoItemStatus,
TodoListEntityFeature, TodoListEntityFeature,
createItem, createItem,
deleteItems, deleteItems,
fetchItems,
moveItem, moveItem,
subscribeItems,
updateItem, updateItem,
} from "../../../data/todo"; } from "../../../data/todo";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import type { SortableInstance } from "../../../resources/sortable"; import type { SortableInstance } from "../../../resources/sortable";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
import { createEntityNotFoundWarning } from "../components/hui-warning"; import { createEntityNotFoundWarning } from "../components/hui-warning";
import { LovelaceCard, LovelaceCardEditor } from "../types"; import { LovelaceCard, LovelaceCardEditor } from "../types";
import { TodoListCardConfig } from "./types"; import { TodoListCardConfig } from "./types";
import { isUnavailableState } from "../../../data/entity";
@customElement("hui-todo-list-card") @customElement("hui-todo-list-card")
export class HuiTodoListCard export class HuiTodoListCard extends LitElement implements LovelaceCard {
extends SubscribeMixin(LitElement)
implements LovelaceCard
{
public static async getConfigElement(): Promise<LovelaceCardEditor> { public static async getConfigElement(): Promise<LovelaceCardEditor> {
await import("../editor/config-elements/hui-todo-list-editor"); await import("../editor/config-elements/hui-todo-list-editor");
return document.createElement("hui-todo-list-card-editor"); return document.createElement("hui-todo-list-card-editor");
@ -87,10 +83,24 @@ export class HuiTodoListCard
@state() private _reordering = false; @state() private _reordering = false;
private _unsubItems?: Promise<UnsubscribeFunc>;
private _sortable?: SortableInstance; private _sortable?: SortableInstance;
@query("#unchecked") private _uncheckedContainer?: HTMLElement; @query("#unchecked") private _uncheckedContainer?: HTMLElement;
connectedCallback(): void {
super.connectedCallback();
if (this.hasUpdated) {
this._subscribeItems();
}
}
disconnectedCallback(): void {
super.disconnectedCallback();
this._unsubItems?.then((unsub) => unsub());
}
public getCardSize(): number { public getCardSize(): number {
return (this._config ? (this._config.title ? 2 : 0) : 0) + 3; return (this._config ? (this._config.title ? 2 : 0) : 0) + 3;
} }
@ -132,26 +142,13 @@ export class HuiTodoListCard
if (!this._entityId) { if (!this._entityId) {
this._entityId = this.getEntityId(); this._entityId = this.getEntityId();
} }
this._fetchData(); this._subscribeItems();
} else if (changedProperties.has("_entityId") || !this._items) { } else if (changedProperties.has("_entityId") || !this._items) {
this._items = undefined; this._items = undefined;
this._fetchData(); this._subscribeItems();
} }
} }
public hassSubscribe(): Promise<UnsubscribeFunc>[] {
return [
this.hass!.connection.subscribeEvents(() => {
if (
this._entityId &&
this.hass!.entities[this._entityId]?.platform === "shopping_list"
) {
this._fetchData();
}
}, "shopping_list_updated"),
];
}
protected updated(changedProps: PropertyValues): void { protected updated(changedProps: PropertyValues): void {
super.updated(changedProps); super.updated(changedProps);
if (!this._config || !this.hass) { if (!this._config || !this.hass) {
@ -169,15 +166,6 @@ export class HuiTodoListCard
) { ) {
applyThemesOnElement(this, this.hass.themes, this._config.theme); applyThemesOnElement(this, this.hass.themes, this._config.theme);
} }
if (
this._entityId &&
oldHass &&
oldHass.states[this._entityId] !== this.hass.states[this._entityId] &&
this.hass.entities[this._entityId]?.platform !== "shopping_list"
) {
this._fetchData();
}
} }
protected render() { protected render() {
@ -392,14 +380,20 @@ export class HuiTodoListCard
return entityStateObj && supportsFeature(entityStateObj, feature); return entityStateObj && supportsFeature(entityStateObj, feature);
} }
private async _fetchData(): Promise<void> { private async _subscribeItems(): Promise<void> {
if (this._unsubItems) {
this._unsubItems.then((unsub) => unsub());
this._unsubItems = undefined;
}
if (!this.hass || !this._entityId) { if (!this.hass || !this._entityId) {
return; return;
} }
if (!(this._entityId in this.hass.states)) { if (!(this._entityId in this.hass.states)) {
return; return;
} }
this._items = await fetchItems(this.hass!, this._entityId!); this._unsubItems = subscribeItems(this.hass!, this._entityId, (update) => {
this._items = update.items;
});
} }
private _getItem(itemId: string) { private _getItem(itemId: string) {
@ -416,7 +410,7 @@ export class HuiTodoListCard
status: ev.target.checked status: ev.target.checked
? TodoItemStatus.Completed ? TodoItemStatus.Completed
: TodoItemStatus.NeedsAction, : TodoItemStatus.NeedsAction,
}).finally(() => this._fetchData()); });
} }
private _saveEdit(ev): void { private _saveEdit(ev): void {
@ -429,13 +423,11 @@ export class HuiTodoListCard
updateItem(this.hass!, this._entityId!, { updateItem(this.hass!, this._entityId!, {
...item, ...item,
summary: ev.target.value, summary: ev.target.value,
}).finally(() => this._fetchData()); });
} else if ( } else if (
this.todoListSupportsFeature(TodoListEntityFeature.DELETE_TODO_ITEM) this.todoListSupportsFeature(TodoListEntityFeature.DELETE_TODO_ITEM)
) { ) {
deleteItems(this.hass!, this._entityId!, [ev.target.itemId]).finally(() => deleteItems(this.hass!, this._entityId!, [ev.target.itemId]);
this._fetchData()
);
} }
ev.target.blur(); ev.target.blur();
@ -447,9 +439,7 @@ export class HuiTodoListCard
} }
const checkedItems = this._getCheckedItems(this._items); const checkedItems = this._getCheckedItems(this._items);
const uids = checkedItems.map((item: TodoItem) => item.uid); const uids = checkedItems.map((item: TodoItem) => item.uid);
deleteItems(this.hass!, this._entityId!, uids).finally(() => deleteItems(this.hass!, this._entityId!, uids);
this._fetchData()
);
} }
private get _newItem(): HaTextField { private get _newItem(): HaTextField {
@ -459,9 +449,7 @@ export class HuiTodoListCard
private _addItem(ev): void { private _addItem(ev): void {
const newItem = this._newItem; const newItem = this._newItem;
if (newItem.value!.length > 0) { if (newItem.value!.length > 0) {
createItem(this.hass!, this._entityId!, newItem.value!).finally(() => createItem(this.hass!, this._entityId!, newItem.value!);
this._fetchData()
);
} }
newItem.value = ""; newItem.value = "";
@ -475,9 +463,7 @@ export class HuiTodoListCard
if (!item) { if (!item) {
return; return;
} }
deleteItems(this.hass!, this._entityId!, [item.uid]).finally(() => deleteItems(this.hass!, this._entityId!, [item.uid]);
this._fetchData()
);
} }
private _addKeyPress(ev): void { private _addKeyPress(ev): void {
@ -552,12 +538,7 @@ export class HuiTodoListCard
} }
this._items = [...this._items!]; this._items = [...this._items!];
await moveItem( await moveItem(this.hass!, this._entityId!, item.uid, prevItem?.uid);
this.hass!,
this._entityId!,
item.uid,
prevItem?.uid
).finally(() => this._fetchData());
} }
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {