diff --git a/src/common/decorators/storage.ts b/src/common/decorators/storage.ts index a35f0b414a..261a43aa49 100644 --- a/src/common/decorators/storage.ts +++ b/src/common/decorators/storage.ts @@ -108,6 +108,8 @@ export const storage = subscribe?: boolean; state?: boolean; stateOptions?: InternalPropertyDeclaration; + serializer?: (value: any) => any; + deserializer?: (value: any) => any; }): any => (clsElement: ClassElement) => { const storageName = options.storage || "localStorage"; @@ -141,7 +143,9 @@ export const storage = const getValue = (): any => storageInstance.hasKey(storageKey!) - ? storageInstance.getValue(storageKey!) + ? options.deserializer + ? options.deserializer(storageInstance.getValue(storageKey!)) + : storageInstance.getValue(storageKey!) : initVal; const setValue = (el: ReactiveElement, value: any) => { @@ -149,7 +153,10 @@ export const storage = if (options.state) { oldValue = getValue(); } - storageInstance.setValue(storageKey!, value); + storageInstance.setValue( + storageKey!, + options.serializer ? options.serializer(value) : value + ); if (options.state) { el.requestUpdate(clsElement.key, oldValue); } diff --git a/src/common/navigate.ts b/src/common/navigate.ts index a9d41ab04d..86c61173fb 100644 --- a/src/common/navigate.ts +++ b/src/common/navigate.ts @@ -11,6 +11,7 @@ declare global { export interface NavigateOptions { replace?: boolean; + data?: any; } export const navigate = (path: string, options?: NavigateOptions) => { @@ -24,7 +25,7 @@ export const navigate = (path: string, options?: NavigateOptions) => { if (__DEMO__) { if (replace) { mainWindow.history.replaceState( - mainWindow.history.state?.root ? { root: true } : null, + mainWindow.history.state?.root ? { root: true } : options?.data ?? null, "", `${mainWindow.location.pathname}#${path}` ); @@ -33,12 +34,12 @@ export const navigate = (path: string, options?: NavigateOptions) => { } } else if (replace) { mainWindow.history.replaceState( - mainWindow.history.state?.root ? { root: true } : null, + mainWindow.history.state?.root ? { root: true } : options?.data ?? null, "", path ); } else { - mainWindow.history.pushState(null, "", path); + mainWindow.history.pushState(options?.data ?? null, "", path); } fireEvent(mainWindow, "location-changed", { replace, diff --git a/src/components/ha-filter-blueprints.ts b/src/components/ha-filter-blueprints.ts index 2b48e27e54..7d438dc76a 100644 --- a/src/components/ha-filter-blueprints.ts +++ b/src/components/ha-filter-blueprints.ts @@ -1,7 +1,14 @@ import { SelectedDetail } from "@material/mwc-list"; import "@material/mwc-menu/mwc-menu-surface"; import { mdiFilterVariantRemove } from "@mdi/js"; -import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; +import { + css, + CSSResultGroup, + html, + LitElement, + nothing, + PropertyValues, +} from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../common/dom/fire_event"; import { Blueprints, fetchBlueprints } from "../data/blueprint"; @@ -25,6 +32,16 @@ export class HaFilterBlueprints extends LitElement { @state() private _blueprints?: Blueprints; + public willUpdate(properties: PropertyValues) { + super.willUpdate(properties); + + if (!this.hasUpdated) { + if (this.value?.length) { + this._findRelated(); + } + } + } + protected render() { return html` >> ) { const blueprints = this._blueprints!; - const relatedPromises: Promise[] = []; if (!ev.detail.index.size) { fireEvent(this, "data-table-filter-changed", { @@ -112,13 +128,33 @@ export class HaFilterBlueprints extends LitElement { for (const index of ev.detail.index) { const blueprintId = Object.keys(blueprints)[index]; value.push(blueprintId); + } + + this.value = value; + + this._findRelated(); + } + + private async _findRelated() { + if (!this.value?.length) { + fireEvent(this, "data-table-filter-changed", { + value: [], + items: undefined, + }); + this.value = []; + return; + } + + const relatedPromises: Promise[] = []; + + for (const blueprintId of this.value) { if (this.type) { relatedPromises.push( findRelated(this.hass, `${this.type}_blueprint`, blueprintId) ); } } - this.value = value; + const results = await Promise.all(relatedPromises); const items: Set = new Set(); for (const result of results) { @@ -128,7 +164,7 @@ export class HaFilterBlueprints extends LitElement { } fireEvent(this, "data-table-filter-changed", { - value, + value: this.value, items: this.type ? items : undefined, }); } diff --git a/src/components/ha-filter-devices.ts b/src/components/ha-filter-devices.ts index 66da797b1e..8cde8b1f04 100644 --- a/src/components/ha-filter-devices.ts +++ b/src/components/ha-filter-devices.ts @@ -41,6 +41,9 @@ export class HaFilterDevices extends LitElement { if (!this.hasUpdated) { loadVirtualizer(); + if (this.value?.length) { + this._findRelated(); + } } } diff --git a/src/components/ha-filter-entities.ts b/src/components/ha-filter-entities.ts index 2f3f1f7b45..31eaccde25 100644 --- a/src/components/ha-filter-entities.ts +++ b/src/components/ha-filter-entities.ts @@ -42,6 +42,9 @@ export class HaFilterEntities extends LitElement { if (!this.hasUpdated) { loadVirtualizer(); + if (this.value?.length) { + this._findRelated(); + } } } @@ -186,15 +189,12 @@ export class HaFilterEntities extends LitElement { return; } - const value: string[] = []; - for (const entityId of this.value) { - value.push(entityId); if (this.type) { relatedPromises.push(findRelated(this.hass, "entity", entityId)); } } - this.value = value; + const results = await Promise.all(relatedPromises); const items: Set = new Set(); for (const result of results) { @@ -204,7 +204,7 @@ export class HaFilterEntities extends LitElement { } fireEvent(this, "data-table-filter-changed", { - value, + value: this.value, items: this.type ? items : undefined, }); } diff --git a/src/components/ha-filter-floor-areas.ts b/src/components/ha-filter-floor-areas.ts index 8810c9d6d2..921e5c49fe 100644 --- a/src/components/ha-filter-floor-areas.ts +++ b/src/components/ha-filter-floor-areas.ts @@ -1,7 +1,14 @@ import "@material/mwc-menu/mwc-menu-surface"; import { mdiFilterVariantRemove, mdiTextureBox } from "@mdi/js"; import { UnsubscribeFunc } from "home-assistant-js-websocket"; -import { CSSResultGroup, LitElement, css, html, nothing } from "lit"; +import { + CSSResultGroup, + LitElement, + PropertyValues, + css, + html, + nothing, +} from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { repeat } from "lit/directives/repeat"; @@ -42,6 +49,16 @@ export class HaFilterFloorAreas extends SubscribeMixin(LitElement) { @state() private _floors?: FloorRegistryEntry[]; + public willUpdate(properties: PropertyValues) { + super.willUpdate(properties); + + if (!this.hasUpdated) { + if (this.value?.floors?.length || this.value?.areas?.length) { + this._findRelated(); + } + } + } + protected render() { const areas = this._areas(this.hass.areas, this._floors); @@ -190,6 +207,10 @@ export class HaFilterFloorAreas extends SubscribeMixin(LitElement) { } } + protected firstUpdated() { + this._findRelated(); + } + private _expandedWillChange(ev) { this._shouldRender = ev.detail.expanded; } diff --git a/src/data/data_table_filters.ts b/src/data/data_table_filters.ts new file mode 100644 index 0000000000..d10f6f8840 --- /dev/null +++ b/src/data/data_table_filters.ts @@ -0,0 +1,28 @@ +export interface DataTableFilters { + [key: string]: { + value: string[] | { key: string[] } | undefined; + items: Set | undefined; + }; +} + +export const serializeFilters = (value: DataTableFilters) => { + const serializedValue = {}; + Object.entries(value).forEach(([key, val]) => { + serializedValue[key] = { + value: val.value, + items: val.items instanceof Set ? Array.from(val.items) : val.items, + }; + }); + return serializedValue; +}; + +export const deserializeFilters = (value: DataTableFilters) => { + const deserializedValue = {}; + Object.entries(value).forEach(([key, val]) => { + deserializedValue[key] = { + value: val.value, + items: Array.isArray(val.items) ? new Set(val.items) : val.items, + }; + }); + return deserializedValue; +}; diff --git a/src/panels/config/automation/ha-automation-picker.ts b/src/panels/config/automation/ha-automation-picker.ts index c7c8837ae1..40f7185592 100644 --- a/src/panels/config/automation/ha-automation-picker.ts +++ b/src/panels/config/automation/ha-automation-picker.ts @@ -114,6 +114,11 @@ import { showCategoryRegistryDetailDialog } from "../category/show-dialog-catego import { configSections } from "../ha-panel-config"; import { showLabelDetailDialog } from "../labels/show-dialog-label-detail"; import { showNewAutomationDialog } from "./show-dialog-new-automation"; +import { + DataTableFilters, + deserializeFilters, + serializeFilters, +} from "../../../data/data_table_filters"; type AutomationItem = AutomationEntity & { name: string; @@ -140,10 +145,15 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { @state() private _filteredAutomations?: string[] | null; - @state() private _filters: Record< - string, - { value: string[] | undefined; items: Set | undefined } - > = {}; + @storage({ + storage: "sessionStorage", + key: "automation-table-filters-full", + state: true, + subscribe: false, + serializer: serializeFilters, + deserializer: deserializeFilters, + }) + private _filters: DataTableFilters = {}; @state() private _expandedFilter?: string; @@ -916,7 +926,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { private _filterChanged(ev) { const type = ev.target.localName; - this._filters[type] = ev.detail; + this._filters = { ...this._filters, [type]: ev.detail }; this._applyFilters(); } @@ -935,7 +945,11 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { items.intersection(filter.items) : new Set([...items].filter((x) => filter.items!.has(x))); } - if (key === "ha-filter-categories" && filter.value?.length) { + if ( + key === "ha-filter-categories" && + Array.isArray(filter.value) && + filter.value.length + ) { const categoryItems: Set = new Set(); this.automations .filter( @@ -956,13 +970,17 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { items.intersection(categoryItems) : new Set([...items].filter((x) => categoryItems!.has(x))); } - if (key === "ha-filter-labels" && filter.value?.length) { + if ( + key === "ha-filter-labels" && + Array.isArray(filter.value) && + filter.value.length + ) { const labelItems: Set = new Set(); this.automations .filter((automation) => this._entityReg .find((reg) => reg.entity_id === automation.entity_id) - ?.labels.some((lbl) => filter.value!.includes(lbl)) + ?.labels.some((lbl) => (filter.value as string[]).includes(lbl)) ) .forEach((automation) => labelItems.add(automation.entity_id)); if (!items) { diff --git a/src/panels/config/devices/ha-config-devices-dashboard.ts b/src/panels/config/devices/ha-config-devices-dashboard.ts index 5d714d763b..580775f805 100644 --- a/src/panels/config/devices/ha-config-devices-dashboard.ts +++ b/src/panels/config/devices/ha-config-devices-dashboard.ts @@ -10,6 +10,7 @@ import { import { CSSResultGroup, LitElement, + PropertyValues, TemplateResult, css, html, @@ -85,6 +86,11 @@ import "../integrations/ha-integration-overflow-menu"; import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog"; import { showLabelDetailDialog } from "../labels/show-dialog-label-detail"; import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box"; +import { + serializeFilters, + deserializeFilters, + DataTableFilters, +} from "../../../data/data_table_filters"; interface DeviceRowData extends DeviceRegistryEntry { device?: DeviceRowData; @@ -118,10 +124,15 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) { @state() private _filter: string = history.state?.filter || ""; - @state() private _filters: Record< - string, - { value: string[] | undefined; items: Set | undefined } - > = {}; + @storage({ + storage: "sessionStorage", + key: "devices-table-filters-full", + state: true, + subscribe: false, + serializer: serializeFilters, + deserializer: deserializeFilters, + }) + private _filters: DataTableFilters = {}; @state() private _expandedFilter?: string; @@ -180,14 +191,11 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) { }, ]); - firstUpdated() { - this._filters = { - "ha-filter-states": { - value: [], - items: undefined, - }, - }; - this._setFiltersFromUrl(); + willUpdate(changedProps: PropertyValues) { + super.willUpdate(changedProps); + if (!this.hasUpdated) { + this._setFiltersFromUrl(); + } } private _setFiltersFromUrl() { @@ -196,7 +204,7 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) { ...this._filters, "ha-filter-states": { value: [ - ...(this._filters["ha-filter-states"]?.value || []), + ...((this._filters["ha-filter-states"]?.value as string[]) || []), "disabled", ], items: undefined, @@ -212,7 +220,7 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) { ...this._filters, "ha-filter-states": { value: [ - ...(this._filters["ha-filter-states"]?.value || []), + ...((this._filters["ha-filter-states"]?.value as string[]) || []), "disabled", ], items: undefined, @@ -253,10 +261,7 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) { entities: EntityRegistryEntry[], areas: HomeAssistant["areas"], manifests: IntegrationManifest[], - filters: Record< - string, - { value: string[] | undefined; items: Set | undefined } - >, + filters: DataTableFilters, localize: LocalizeFunc, labelReg?: LabelRegistryEntry[] ) => { @@ -295,15 +300,21 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) { const filteredDomains = new Set(); Object.entries(filters).forEach(([key, filter]) => { - if (key === "config_entry" && filter.value?.length) { + if ( + key === "config_entry" && + Array.isArray(filter.value) && + filter.value.length + ) { outputDevices = outputDevices.filter((device) => device.config_entries.some((entryId) => - filter.value?.includes(entryId) + (filter.value as string[]).includes(entryId) ) ); const configEntries = entries.filter( - (entry) => entry.entry_id && filter.value?.includes(entry.entry_id) + (entry) => + entry.entry_id && + (filter.value as string[]).includes(entry.entry_id) ); configEntries.forEach((configEntry) => { @@ -312,17 +323,31 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) { if (configEntries.length === 1) { filteredConfigEntry = configEntries[0]; } - } else if (key === "ha-filter-integrations" && filter.value?.length) { + } else if ( + key === "ha-filter-integrations" && + Array.isArray(filter.value) && + filter.value.length + ) { const entryIds = entries - .filter((entry) => filter.value!.includes(entry.domain)) + .filter((entry) => + (filter.value as string[]).includes(entry.domain) + ) .map((entry) => entry.entry_id); outputDevices = outputDevices.filter((device) => device.config_entries.some((entryId) => entryIds.includes(entryId)) ); - filter.value!.forEach((domain) => filteredDomains.add(domain)); - } else if (key === "ha-filter-labels" && filter.value?.length) { + (filter.value as string[]).forEach((domain) => + filteredDomains.add(domain) + ); + } else if ( + key === "ha-filter-labels" && + Array.isArray(filter.value) && + filter.value.length + ) { outputDevices = outputDevices.filter((device) => - device.labels.some((lbl) => filter.value!.includes(lbl)) + device.labels.some((lbl) => + (filter.value as string[]).includes(lbl) + ) ); } else if (filter.items) { outputDevices = outputDevices.filter((device) => @@ -331,7 +356,9 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) { } }); - const stateFilters = filters["ha-filter-states"]?.value; + const stateFilters = filters["ha-filter-states"]?.value as + | string[] + | undefined; const showDisabled = stateFilters?.length && stateFilters.includes("disabled"); @@ -698,7 +725,8 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) { > - ${this._filters.config_entry?.value?.length + ${Array.isArray(this._filters.config_entry?.value) && + this._filters.config_entry?.value.length ? html` Filtering by config entry ${this.entries?.find( diff --git a/src/panels/config/entities/ha-config-entities.ts b/src/panels/config/entities/ha-config-entities.ts index d4f96fb04f..6bc6881a46 100644 --- a/src/panels/config/entities/ha-config-entities.ts +++ b/src/panels/config/entities/ha-config-entities.ts @@ -98,6 +98,11 @@ import { configSections } from "../ha-panel-config"; import "../integrations/ha-integration-overflow-menu"; import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog"; import { showLabelDetailDialog } from "../labels/show-dialog-label-detail"; +import { + serializeFilters, + deserializeFilters, + DataTableFilters, +} from "../../../data/data_table_filters"; export interface StateEntity extends Omit { @@ -143,10 +148,15 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) { @state() private _searchParms = new URLSearchParams(window.location.search); - @state() private _filters: Record< - string, - { value: string[] | undefined; items: Set | undefined } - > = {}; + @storage({ + storage: "sessionStorage", + key: "entities-table-filters-full", + state: true, + subscribe: false, + serializer: serializeFilters, + deserializer: deserializeFilters, + }) + private _filters: DataTableFilters = {}; @state() private _selected: string[] = []; @@ -414,16 +424,13 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) { devices: HomeAssistant["devices"], areas: HomeAssistant["areas"], stateEntities: StateEntity[], - filters: Record< - string, - { value: string[] | undefined; items: Set | undefined } - >, + filters: DataTableFilters, entries?: ConfigEntry[], labelReg?: LabelRegistryEntry[] ) => { const result: EntityRow[] = []; - const stateFilters = filters["ha-filter-states"]?.value; + const stateFilters = filters["ha-filter-states"]?.value as string[]; const showEnabled = !stateFilters?.length || stateFilters.includes("enabled"); @@ -448,11 +455,15 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) { const filteredDomains = new Set(); Object.entries(filters).forEach(([key, filter]) => { - if (key === "config_entry" && filter.value?.length) { + if ( + key === "config_entry" && + Array.isArray(filter.value) && + filter.value.length + ) { filteredEntities = filteredEntities.filter( (entity) => entity.config_entry_id && - filter.value?.includes(entity.config_entry_id) + (filter.value as string[]).includes(entity.config_entry_id) ); if (!entries) { @@ -461,7 +472,9 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) { } const configEntries = entries.filter( - (entry) => entry.entry_id && filter.value?.includes(entry.entry_id) + (entry) => + entry.entry_id && + (filter.value as string[]).includes(entry.entry_id) ); configEntries.forEach((configEntry) => { @@ -470,29 +483,45 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) { if (configEntries.length === 1) { filteredConfigEntry = configEntries[0]; } - } else if (key === "ha-filter-integrations" && filter.value?.length) { + } else if ( + key === "ha-filter-integrations" && + Array.isArray(filter.value) && + filter.value.length + ) { if (!entries) { this._loadConfigEntries(); return; } const entryIds = entries - .filter((entry) => filter.value!.includes(entry.domain)) + .filter((entry) => + (filter.value as string[]).includes(entry.domain) + ) .map((entry) => entry.entry_id); filteredEntities = filteredEntities.filter( (entity) => - filter.value?.includes(entity.platform) || + (filter.value as string[]).includes(entity.platform) || (entity.config_entry_id && entryIds.includes(entity.config_entry_id)) ); filter.value!.forEach((domain) => filteredDomains.add(domain)); - } else if (key === "ha-filter-domains" && filter.value?.length) { + } else if ( + key === "ha-filter-domains" && + Array.isArray(filter.value) && + filter.value.length + ) { filteredEntities = filteredEntities.filter((entity) => - filter.value?.includes(computeDomain(entity.entity_id)) + (filter.value as string[]).includes(computeDomain(entity.entity_id)) ); - } else if (key === "ha-filter-labels" && filter.value?.length) { + } else if ( + key === "ha-filter-labels" && + Array.isArray(filter.value) && + filter.value.length + ) { filteredEntities = filteredEntities.filter((entity) => - entity.labels.some((lbl) => filter.value!.includes(lbl)) + entity.labels.some((lbl) => + (filter.value as string[]).includes(lbl) + ) ); } else if (filter.items) { filteredEntities = filteredEntities.filter((entity) => @@ -811,7 +840,8 @@ ${ ${ - this._filters.config_entry?.value?.length + Array.isArray(this._filters.config_entry?.value) && + this._filters.config_entry?.value.length ? html` Filtering by config entry ${this._entries?.find( @@ -913,6 +943,9 @@ ${ } protected firstUpdated() { + if (Object.keys(this._filters).length) { + return; + } this._filters = { "ha-filter-states": { value: ["enabled"], diff --git a/src/panels/config/helpers/ha-config-helpers.ts b/src/panels/config/helpers/ha-config-helpers.ts index b90c12b9d8..a080b9041d 100644 --- a/src/panels/config/helpers/ha-config-helpers.ts +++ b/src/panels/config/helpers/ha-config-helpers.ts @@ -96,6 +96,11 @@ import "../integrations/ha-integration-overflow-menu"; import { showLabelDetailDialog } from "../labels/show-dialog-label-detail"; import { isHelperDomain } from "./const"; import { showHelperDetailDialog } from "./show-dialog-helper-detail"; +import { + serializeFilters, + deserializeFilters, + DataTableFilters, +} from "../../../data/data_table_filters"; type HelperItem = { id: string; @@ -164,10 +169,15 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { @state() private _activeFilters?: string[]; - @state() private _filters: Record< - string, - { value: string[] | undefined; items: Set | undefined } - > = {}; + @storage({ + storage: "sessionStorage", + key: "helpers-table-filters-full", + state: true, + subscribe: false, + serializer: serializeFilters, + deserializer: deserializeFilters, + }) + private _filters: DataTableFilters = {}; @state() private _expandedFilter?: string; @@ -722,7 +732,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { private _filterChanged(ev) { const type = ev.target.localName; - this._filters[type] = ev.detail; + this._filters = { ...this._filters, [type]: ev.detail }; this._applyFilters(); } @@ -741,13 +751,17 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { items.intersection(filter.items) : new Set([...items].filter((x) => filter.items!.has(x))); } - if (key === "ha-filter-labels" && filter.value?.length) { + if ( + key === "ha-filter-labels" && + Array.isArray(filter.value) && + filter.value.length + ) { const labelItems: Set = new Set(); this._stateItems .filter((stateItem) => this._entityReg .find((reg) => reg.entity_id === stateItem.entity_id) - ?.labels.some((lbl) => filter.value!.includes(lbl)) + ?.labels.some((lbl) => (filter.value as string[]).includes(lbl)) ) .forEach((stateItem) => labelItems.add(stateItem.entity_id)); if (!items) { @@ -760,7 +774,11 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { items.intersection(labelItems) : new Set([...items].filter((x) => labelItems!.has(x))); } - if (key === "ha-filter-categories" && filter.value?.length) { + if ( + key === "ha-filter-categories" && + Array.isArray(filter.value) && + filter.value.length + ) { const categoryItems: Set = new Set(); this._stateItems .filter( diff --git a/src/panels/config/scene/ha-scene-dashboard.ts b/src/panels/config/scene/ha-scene-dashboard.ts index 2a56d90f1c..e479c89a11 100644 --- a/src/panels/config/scene/ha-scene-dashboard.ts +++ b/src/panels/config/scene/ha-scene-dashboard.ts @@ -104,6 +104,11 @@ import { showAssignCategoryDialog } from "../category/show-dialog-assign-categor import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail"; import { configSections } from "../ha-panel-config"; import { showLabelDetailDialog } from "../labels/show-dialog-label-detail"; +import { + serializeFilters, + deserializeFilters, + DataTableFilters, +} from "../../../data/data_table_filters"; type SceneItem = SceneEntity & { name: string; @@ -132,10 +137,15 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { @state() private _filteredScenes?: string[] | null; - @state() private _filters: Record< - string, - { value: string[] | undefined; items: Set | undefined } - > = {}; + @storage({ + storage: "sessionStorage", + key: "scene-table-filters-full", + state: true, + subscribe: false, + serializer: serializeFilters, + deserializer: deserializeFilters, + }) + private _filters: DataTableFilters = {}; @state() private _expandedFilter?: string; @@ -783,7 +793,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { private _filterChanged(ev) { const type = ev.target.localName; - this._filters[type] = ev.detail; + this._filters = { ...this._filters, [type]: ev.detail }; this._applyFilters(); } @@ -802,7 +812,11 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { items.intersection(filter.items) : new Set([...items].filter((x) => filter.items!.has(x))); } - if (key === "ha-filter-categories" && filter.value?.length) { + if ( + key === "ha-filter-categories" && + Array.isArray(filter.value) && + filter.value.length + ) { const categoryItems: Set = new Set(); this.scenes .filter( @@ -822,13 +836,17 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { items.intersection(categoryItems) : new Set([...items].filter((x) => categoryItems!.has(x))); } - if (key === "ha-filter-labels" && filter.value?.length) { + if ( + key === "ha-filter-labels" && + Array.isArray(filter.value) && + filter.value.length + ) { const labelItems: Set = new Set(); this.scenes .filter((scene) => this._entityReg .find((reg) => reg.entity_id === scene.entity_id) - ?.labels.some((lbl) => filter.value!.includes(lbl)) + ?.labels.some((lbl) => (filter.value as string[]).includes(lbl)) ) .forEach((scene) => labelItems.add(scene.entity_id)); if (!items) { diff --git a/src/panels/config/script/ha-script-picker.ts b/src/panels/config/script/ha-script-picker.ts index c2435b6ac9..551d5030f4 100644 --- a/src/panels/config/script/ha-script-picker.ts +++ b/src/panels/config/script/ha-script-picker.ts @@ -106,6 +106,11 @@ import { showAssignCategoryDialog } from "../category/show-dialog-assign-categor import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail"; import { configSections } from "../ha-panel-config"; import { showLabelDetailDialog } from "../labels/show-dialog-label-detail"; +import { + serializeFilters, + deserializeFilters, + DataTableFilters, +} from "../../../data/data_table_filters"; type ScriptItem = ScriptEntity & { name: string; @@ -136,10 +141,15 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { @state() private _filteredScripts?: string[] | null; - @state() private _filters: Record< - string, - { value: string[] | undefined; items: Set | undefined } - > = {}; + @storage({ + storage: "sessionStorage", + key: "script-table-filters-full", + state: true, + subscribe: false, + serializer: serializeFilters, + deserializer: deserializeFilters, + }) + private _filters: DataTableFilters = {}; @state() private _expandedFilter?: string; @@ -812,7 +822,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { private _filterChanged(ev) { const type = ev.target.localName; - this._filters[type] = ev.detail; + this._filters = { ...this._filters, [type]: ev.detail }; this._applyFilters(); } @@ -836,7 +846,11 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { items.intersection(filter.items) : new Set([...items].filter((x) => filter.items!.has(x))); } - if (key === "ha-filter-categories" && filter.value?.length) { + if ( + key === "ha-filter-categories" && + Array.isArray(filter.value) && + filter.value.length + ) { const categoryItems: Set = new Set(); this.scripts .filter( @@ -856,13 +870,17 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { items.intersection(categoryItems) : new Set([...items].filter((x) => categoryItems!.has(x))); } - if (key === "ha-filter-labels" && filter.value?.length) { + if ( + key === "ha-filter-labels" && + Array.isArray(filter.value) && + filter.value.length + ) { const labelItems: Set = new Set(); this.scripts .filter((script) => this._entityReg .find((reg) => reg.entity_id === script.entity_id) - ?.labels.some((lbl) => filter.value!.includes(lbl)) + ?.labels.some((lbl) => (filter.value as string[]).includes(lbl)) ) .forEach((script) => labelItems.add(script.entity_id)); if (!items) {