Refactor Storage decorator (#16987)

This commit is contained in:
Bram Kragten 2023-06-21 17:22:10 +02:00 committed by GitHub
parent 752bc192cd
commit 7faa165558
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 187 additions and 79 deletions

View File

@ -1,13 +1,15 @@
import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { PropertyDeclaration, ReactiveElement } from "lit"; import { ReactiveElement } from "lit";
import { InternalPropertyDeclaration } from "lit/decorators";
import type { ClassElement } from "../../types"; import type { ClassElement } from "../../types";
type Callback = (oldValue: any, newValue: any) => void; type Callback = (oldValue: any, newValue: any) => void;
class Storage { class StorageClass {
constructor(subscribe = true, storage = window.localStorage) { constructor(storage = window.localStorage) {
this.storage = storage; this.storage = storage;
if (!subscribe) { if (storage !== window.localStorage) {
// storage events only work for localStorage
return; return;
} }
window.addEventListener("storage", (ev: StorageEvent) => { window.addEventListener("storage", (ev: StorageEvent) => {
@ -77,6 +79,7 @@ class Storage {
} }
public setValue(storageKey: string, value: any): any { public setValue(storageKey: string, value: any): any {
const oldValue = this._storage[storageKey];
this._storage[storageKey] = value; this._storage[storageKey] = value;
try { try {
if (value === undefined) { if (value === undefined) {
@ -86,49 +89,68 @@ class Storage {
} }
} catch (err: any) { } catch (err: any) {
// Safari in private mode doesn't allow localstorage // Safari in private mode doesn't allow localstorage
} finally {
if (this._listeners[storageKey]) {
this._listeners[storageKey].forEach((listener) =>
listener(oldValue, value)
);
}
} }
} }
} }
const subscribeStorage = new Storage(); const storages: Record<string, StorageClass> = {};
export const LocalStorage = export const storage =
( (options: {
storageKey?: string, key?: string;
property?: boolean, storage?: "localStorage" | "sessionStorage";
subscribe = true, subscribe?: boolean;
storageType?: globalThis.Storage, state?: boolean;
propertyOptions?: PropertyDeclaration stateOptions?: InternalPropertyDeclaration;
): any => }): any =>
(clsElement: ClassElement) => { (clsElement: ClassElement) => {
const storage = const storageName = options.storage || "localStorage";
subscribe && !storageType
? subscribeStorage let storageInstance: StorageClass;
: new Storage(subscribe, storageType); if (storageName && storageName in storages) {
storageInstance = storages[storageName];
} else {
storageInstance = new StorageClass(window[storageName]);
storages[storageName] = storageInstance;
}
const key = String(clsElement.key); const key = String(clsElement.key);
storageKey = storageKey || String(clsElement.key); const storageKey = options.key || String(clsElement.key);
const initVal = clsElement.initializer const initVal = clsElement.initializer
? clsElement.initializer() ? clsElement.initializer()
: undefined; : undefined;
storage.addFromStorage(storageKey); storageInstance.addFromStorage(storageKey);
const subscribeChanges = (el: ReactiveElement): UnsubscribeFunc => const subscribeChanges =
storage.subscribeChanges(storageKey!, (oldValue) => { options.subscribe !== false
el.requestUpdate(clsElement.key, oldValue); ? (el: ReactiveElement): UnsubscribeFunc =>
}); storageInstance.subscribeChanges(
storageKey!,
(oldValue, _newValue) => {
el.requestUpdate(clsElement.key, oldValue);
}
)
: undefined;
const getValue = (): any => const getValue = (): any =>
storage.hasKey(storageKey!) ? storage.getValue(storageKey!) : initVal; storageInstance.hasKey(storageKey!)
? storageInstance.getValue(storageKey!)
: initVal;
const setValue = (el: ReactiveElement, value: any) => { const setValue = (el: ReactiveElement, value: any) => {
let oldValue: unknown | undefined; let oldValue: unknown | undefined;
if (property) { if (options.state) {
oldValue = getValue(); oldValue = getValue();
} }
storage.setValue(storageKey!, value); storageInstance.setValue(storageKey!, value);
if (property) { if (options.state) {
el.requestUpdate(clsElement.key, oldValue); el.requestUpdate(clsElement.key, oldValue);
} }
}; };
@ -148,22 +170,23 @@ export const LocalStorage =
configurable: true, configurable: true,
}, },
finisher(cls: typeof ReactiveElement) { finisher(cls: typeof ReactiveElement) {
if (property && subscribe) { if (options.state && options.subscribe) {
const connectedCallback = cls.prototype.connectedCallback; const connectedCallback = cls.prototype.connectedCallback;
const disconnectedCallback = cls.prototype.disconnectedCallback; const disconnectedCallback = cls.prototype.disconnectedCallback;
cls.prototype.connectedCallback = function () { cls.prototype.connectedCallback = function () {
connectedCallback.call(this); connectedCallback.call(this);
this[`__unbsubLocalStorage${key}`] = subscribeChanges(this); this[`__unbsubLocalStorage${key}`] = subscribeChanges?.(this);
}; };
cls.prototype.disconnectedCallback = function () { cls.prototype.disconnectedCallback = function () {
disconnectedCallback.call(this); disconnectedCallback.call(this);
this[`__unbsubLocalStorage${key}`](); this[`__unbsubLocalStorage${key}`]?.();
this[`__unbsubLocalStorage${key}`] = undefined;
}; };
} }
if (property) { if (options.state) {
cls.createProperty(clsElement.key, { cls.createProperty(clsElement.key, {
noAccessor: true, noAccessor: true,
...propertyOptions, ...options.stateOptions,
}); });
} }
}, },

View File

@ -23,19 +23,19 @@ import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox"; import "@polymer/paper-listbox/paper-listbox";
import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { import {
css,
CSSResult, CSSResult,
CSSResultGroup, CSSResultGroup,
html,
LitElement, LitElement,
nothing,
PropertyValues, PropertyValues,
css,
html,
nothing,
} from "lit"; } from "lit";
import { customElement, eventOptions, property, state } from "lit/decorators"; import { customElement, eventOptions, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
import { guard } from "lit/directives/guard"; import { guard } from "lit/directives/guard";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { LocalStorage } from "../common/decorators/local-storage"; import { storage } from "../common/decorators/storage";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { toggleAttribute } from "../common/dom/toggle_attribute"; import { toggleAttribute } from "../common/dom/toggle_attribute";
import { stringCompare } from "../common/string/compare"; import { stringCompare } from "../common/string/compare";
@ -47,10 +47,10 @@ import {
subscribeNotifications, subscribeNotifications,
} from "../data/persistent_notification"; } from "../data/persistent_notification";
import { subscribeRepairsIssueRegistry } from "../data/repairs"; import { subscribeRepairsIssueRegistry } from "../data/repairs";
import { updateCanInstall, UpdateEntity } from "../data/update"; import { UpdateEntity, updateCanInstall } from "../data/update";
import { SubscribeMixin } from "../mixins/subscribe-mixin"; import { SubscribeMixin } from "../mixins/subscribe-mixin";
import { actionHandler } from "../panels/lovelace/common/directives/action-handler-directive"; import { actionHandler } from "../panels/lovelace/common/directives/action-handler-directive";
import { loadSortable, SortableInstance } from "../resources/sortable.ondemand"; import { SortableInstance, loadSortable } from "../resources/sortable.ondemand";
import { haStyleScrollbar } from "../resources/styles"; import { haStyleScrollbar } from "../resources/styles";
import type { HomeAssistant, PanelInfo, Route } from "../types"; import type { HomeAssistant, PanelInfo, Route } from "../types";
import "./ha-icon"; import "./ha-icon";
@ -214,15 +214,17 @@ class HaSidebar extends SubscribeMixin(LitElement) {
private sortableStyleLoaded = false; private sortableStyleLoaded = false;
// @ts-ignore @storage({
@LocalStorage("sidebarPanelOrder", true, { key: "sidebarPanelOrder",
attribute: false, state: true,
subscribe: true,
}) })
private _panelOrder: string[] = []; private _panelOrder: string[] = [];
// @ts-ignore @storage({
@LocalStorage("sidebarHiddenPanels", true, { key: "sidebarHiddenPanels",
attribute: false, state: true,
subscribe: true,
}) })
private _hiddenPanels: string[] = []; private _hiddenPanels: string[] = [];

View File

@ -1,7 +1,7 @@
import "@material/mwc-list/mwc-list-item"; import "@material/mwc-list/mwc-list-item";
import { css, html, LitElement, nothing, PropertyValues } from "lit"; import { css, html, LitElement, nothing, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { LocalStorage } from "../../common/decorators/local-storage"; import { storage } from "../../common/decorators/storage";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { import {
MediaPlayerBrowseAction, MediaPlayerBrowseAction,
@ -43,7 +43,12 @@ class BrowseMediaTTS extends LitElement {
@state() private _provider?: TTSEngine; @state() private _provider?: TTSEngine;
@LocalStorage("TtsMessage", true, false) private _message!: string; @storage({
key: "TtsMessage",
state: true,
subscribe: false,
})
private _message!: string;
protected render() { protected render() {
return html`<ha-card> return html`<ha-card>

View File

@ -1,7 +1,7 @@
import { mdiPlayCircleOutline } from "@mdi/js"; import { mdiPlayCircleOutline } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators"; import { customElement, property, query, state } from "lit/decorators";
import { LocalStorage } from "../../common/decorators/local-storage"; import { storage } from "../../common/decorators/storage";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import "../../components/ha-button"; import "../../components/ha-button";
import { createCloseHeading } from "../../components/ha-dialog"; import { createCloseHeading } from "../../components/ha-dialog";
@ -25,10 +25,12 @@ export class TTSTryDialog extends LitElement {
@query("#message") private _messageInput?: HaTextArea; @query("#message") private _messageInput?: HaTextArea;
@LocalStorage("ttsTryMessages", false, false) private _messages?: Record< @storage({
string, key: "ttsTryMessages",
string state: false,
>; subscribe: false,
})
private _messages?: Record<string, string>;
public showDialog(params: TTSTryDialogParams) { public showDialog(params: TTSTryDialogParams) {
this._params = params; this._params = params;

View File

@ -18,7 +18,7 @@ import {
TemplateResult, TemplateResult,
} from "lit"; } from "lit";
import { customElement, property, query, state } from "lit/decorators"; import { customElement, property, query, state } from "lit/decorators";
import { LocalStorage } from "../../common/decorators/local-storage"; import { storage } from "../../common/decorators/storage";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { stopPropagation } from "../../common/dom/stop_propagation"; import { stopPropagation } from "../../common/dom/stop_propagation";
import "../../components/ha-button"; import "../../components/ha-button";
@ -57,7 +57,12 @@ export class HaVoiceCommandDialog extends LitElement {
@state() private _opened = false; @state() private _opened = false;
@LocalStorage("AssistPipelineId", true, false) private _pipelineId?: string; @storage({
key: "AssistPipelineId",
state: true,
subscribe: false,
})
private _pipelineId?: string;
@state() private _pipeline?: AssistPipeline; @state() private _pipeline?: AssistPipeline;

View File

@ -11,7 +11,7 @@ import {
} from "lit"; } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { styleMap } from "lit/directives/style-map"; import { styleMap } from "lit/directives/style-map";
import { LocalStorage } from "../../common/decorators/local-storage"; import { storage } from "../../common/decorators/storage";
import { HASSDomEvent } from "../../common/dom/fire_event"; import { HASSDomEvent } from "../../common/dom/fire_event";
import { computeStateName } from "../../common/entity/compute_state_name"; import { computeStateName } from "../../common/entity/compute_state_name";
import "../../components/ha-card"; import "../../components/ha-card";
@ -41,7 +41,10 @@ class PanelCalendar extends LitElement {
@state() private _error?: string = undefined; @state() private _error?: string = undefined;
@LocalStorage("deSelectedCalendars", true) @storage({
key: "deSelectedCalendars",
state: true,
})
private _deSelectedCalendars: string[] = []; private _deSelectedCalendars: string[] = [];
private _start?: Date; private _start?: Date;

View File

@ -20,7 +20,7 @@ import { documentationUrl } from "../../../util/documentation-url";
import "./action/ha-automation-action"; import "./action/ha-automation-action";
import "./condition/ha-automation-condition"; import "./condition/ha-automation-condition";
import "./trigger/ha-automation-trigger"; import "./trigger/ha-automation-trigger";
import { LocalStorage } from "../../../common/decorators/local-storage"; import { storage } from "../../../common/decorators/storage";
@customElement("manual-automation-editor") @customElement("manual-automation-editor")
export class HaManualAutomationEditor extends LitElement { export class HaManualAutomationEditor extends LitElement {
@ -36,7 +36,12 @@ export class HaManualAutomationEditor extends LitElement {
@property({ attribute: false }) public stateObj?: HassEntity; @property({ attribute: false }) public stateObj?: HassEntity;
@LocalStorage("automationClipboard", true, false, window.sessionStorage) @storage({
key: "automationClipboard",
state: true,
subscribe: false,
storage: "sessionStorage",
})
private _clipboard: Clipboard = {}; private _clipboard: Clipboard = {};
protected render() { protected render() {

View File

@ -3,7 +3,7 @@ import "@material/mwc-list/mwc-list-item";
import { mdiPlayCircleOutline, mdiRobot } from "@mdi/js"; import { mdiPlayCircleOutline, mdiRobot } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators"; import { customElement, property, query, state } from "lit/decorators";
import { LocalStorage } from "../../../../common/decorators/local-storage"; import { storage } from "../../../../common/decorators/storage";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import { stopPropagation } from "../../../../common/dom/stop_propagation"; import { stopPropagation } from "../../../../common/dom/stop_propagation";
import { computeStateDomain } from "../../../../common/entity/compute_state_domain"; import { computeStateDomain } from "../../../../common/entity/compute_state_domain";
@ -31,9 +31,19 @@ export class DialogTryTts extends LitElement {
@query("#message") private _messageInput?: HaTextArea; @query("#message") private _messageInput?: HaTextArea;
@LocalStorage("cloudTtsTryMessage", false, false) private _message!: string; @storage({
key: "cloudTtsTryMessage",
state: false,
subscribe: false,
})
private _message!: string;
@LocalStorage("cloudTtsTryTarget", false, false) private _target!: string; @storage({
key: "cloudTtsTryTarget",
state: false,
subscribe: false,
})
private _target!: string;
public showDialog(params: TryTtsDialogParams) { public showDialog(params: TryTtsDialogParams) {
this._params = params; this._params = params;

View File

@ -1,7 +1,7 @@
import "@material/mwc-button"; import "@material/mwc-button";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { LocalStorage } from "../../../../../common/decorators/local-storage"; import { storage } from "../../../../../common/decorators/storage";
import "../../../../../components/ha-card"; import "../../../../../components/ha-card";
import "../../../../../components/ha-code-editor"; import "../../../../../components/ha-code-editor";
import "../../../../../components/ha-formfield"; import "../../../../../components/ha-formfield";
@ -21,19 +21,39 @@ class HaPanelDevMqtt extends LitElement {
@property({ type: Boolean }) public narrow!: boolean; @property({ type: Boolean }) public narrow!: boolean;
@LocalStorage("panel-dev-mqtt-topic-ls", true, false) @storage({
key: "panel-dev-mqtt-topic-ls",
state: true,
subscribe: false,
})
private _topic = ""; private _topic = "";
@LocalStorage("panel-dev-mqtt-payload-ls", true, false) @storage({
key: "panel-dev-mqtt-payload-ls",
state: true,
subscribe: false,
})
private _payload = ""; private _payload = "";
@LocalStorage("panel-dev-mqtt-qos-ls", true, false) @storage({
key: "panel-dev-mqtt-qos-ls",
state: true,
subscribe: false,
})
private _qos = "0"; private _qos = "0";
@LocalStorage("panel-dev-mqtt-retain-ls", true, false) @storage({
key: "panel-dev-mqtt-retain-ls",
state: true,
subscribe: false,
})
private _retain = false; private _retain = false;
@LocalStorage("panel-dev-mqtt-allow-template-ls", true, false) @storage({
key: "panel-dev-mqtt-allow-template-ls",
state: true,
subscribe: false,
})
private _allowTemplate = false; private _allowTemplate = false;
protected render(): TemplateResult { protected render(): TemplateResult {

View File

@ -8,7 +8,7 @@ import { formatTime } from "../../../../../common/datetime/format_time";
import { MQTTMessage, subscribeMQTTTopic } from "../../../../../data/mqtt"; import { MQTTMessage, subscribeMQTTTopic } from "../../../../../data/mqtt";
import { HomeAssistant } from "../../../../../types"; import { HomeAssistant } from "../../../../../types";
import "@material/mwc-list/mwc-list-item"; import "@material/mwc-list/mwc-list-item";
import { LocalStorage } from "../../../../../common/decorators/local-storage"; import { storage } from "../../../../../common/decorators/storage";
import "../../../../../components/ha-formfield"; import "../../../../../components/ha-formfield";
import "../../../../../components/ha-switch"; import "../../../../../components/ha-switch";
@ -18,13 +18,25 @@ const qosLevel = ["0", "1", "2"];
class MqttSubscribeCard extends LitElement { class MqttSubscribeCard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@LocalStorage("panel-dev-mqtt-topic-subscribe", true, false) @storage({
key: "panel-dev-mqtt-topic-subscribe",
state: true,
subscribe: false,
})
private _topic = ""; private _topic = "";
@LocalStorage("panel-dev-mqtt-qos-subscribe", true, false) @storage({
key: "panel-dev-mqtt-qos-subscribe",
state: true,
subscribe: false,
})
private _qos = "0"; private _qos = "0";
@LocalStorage("panel-dev-mqtt-json-format", true, false) @storage({
key: "panel-dev-mqtt-json-format",
state: true,
subscribe: false,
})
private _json_format = false; private _json_format = false;
@state() private _subscribed?: () => void; @state() private _subscribed?: () => void;

View File

@ -3,7 +3,7 @@ import { mdiHelpCircle } from "@mdi/js";
import deepClone from "deep-clone-simple"; import deepClone from "deep-clone-simple";
import { css, CSSResultGroup, html, LitElement } from "lit"; import { css, CSSResultGroup, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { LocalStorage } from "../../../common/decorators/local-storage"; import { storage } from "../../../common/decorators/storage";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-card"; import "../../../components/ha-card";
import "../../../components/ha-icon-button"; import "../../../components/ha-icon-button";
@ -26,7 +26,12 @@ export class HaManualScriptEditor extends LitElement {
@property({ attribute: false }) public config!: ScriptConfig; @property({ attribute: false }) public config!: ScriptConfig;
@LocalStorage("automationClipboard", true, false, window.sessionStorage) @storage({
key: "automationClipboard",
state: true,
subscribe: false,
storage: "sessionStorage",
})
private _clipboard: Clipboard = {}; private _clipboard: Clipboard = {};
protected render() { protected render() {

View File

@ -4,7 +4,7 @@ import { load } from "js-yaml";
import { css, CSSResultGroup, html, LitElement } from "lit"; import { css, CSSResultGroup, html, LitElement } from "lit";
import { property, query, state } from "lit/decorators"; import { property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { LocalStorage } from "../../../common/decorators/local-storage"; import { storage } from "../../../common/decorators/storage";
import { computeDomain } from "../../../common/entity/compute_domain"; import { computeDomain } from "../../../common/entity/compute_domain";
import { computeObjectId } from "../../../common/entity/compute_object_id"; import { computeObjectId } from "../../../common/entity/compute_object_id";
import { hasTemplate } from "../../../common/string/has-template"; import { hasTemplate } from "../../../common/string/has-template";
@ -38,10 +38,18 @@ class HaPanelDevService extends LitElement {
@state() private _uiAvailable = true; @state() private _uiAvailable = true;
@LocalStorage("panel-dev-service-state-service-data", true, false) @storage({
key: "panel-dev-service-state-service-data",
state: true,
subscribe: false,
})
private _serviceData?: ServiceAction = { service: "", target: {}, data: {} }; private _serviceData?: ServiceAction = { service: "", target: {}, data: {} };
@LocalStorage("panel-dev-service-state-yaml-mode", true, false) @storage({
key: "panel-dev-service-state-yaml-mode",
state: true,
subscribe: false,
})
private _yamlMode = false; private _yamlMode = false;
@query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor; @query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor;

View File

@ -7,7 +7,7 @@ import {
import { css, html, LitElement, PropertyValues } from "lit"; import { css, html, LitElement, PropertyValues } from "lit";
import { property, query, state } from "lit/decorators"; import { property, query, state } from "lit/decorators";
import { ensureArray } from "../../common/array/ensure-array"; import { ensureArray } from "../../common/array/ensure-array";
import { LocalStorage } from "../../common/decorators/local-storage"; import { storage } from "../../common/decorators/storage";
import { navigate } from "../../common/navigate"; import { navigate } from "../../common/navigate";
import { constructUrlCurrentPath } from "../../common/url/construct-url"; import { constructUrlCurrentPath } from "../../common/url/construct-url";
import { import {
@ -58,7 +58,11 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
@state() private _endDate: Date; @state() private _endDate: Date;
@LocalStorage("historyPickedValue", true, false) @storage({
key: "historyPickedValue",
state: true,
subscribe: false,
})
private _targetPickerValue?: HassServiceTarget; private _targetPickerValue?: HassServiceTarget;
@state() private _isLoading = false; @state() private _isLoading = false;

View File

@ -9,7 +9,7 @@ import {
TemplateResult, TemplateResult,
} from "lit"; } from "lit";
import { customElement, property, query, state } from "lit/decorators"; import { customElement, property, query, state } from "lit/decorators";
import { LocalStorage } from "../../common/decorators/local-storage"; import { storage } from "../../common/decorators/storage";
import { fireEvent, HASSDomEvent } from "../../common/dom/fire_event"; import { fireEvent, HASSDomEvent } from "../../common/dom/fire_event";
import { navigate } from "../../common/navigate"; import { navigate } from "../../common/navigate";
import "../../components/ha-menu-button"; import "../../components/ha-menu-button";
@ -71,7 +71,11 @@ class PanelMediaBrowser extends LitElement {
}, },
]; ];
@LocalStorage("mediaBrowseEntityId", true, false) @storage({
key: "mediaBrowseEntityId",
state: true,
subscribe: false,
})
private _entityId = BROWSER_PLAYER; private _entityId = BROWSER_PLAYER;
@query("ha-media-player-browse") private _browser!: HaMediaPlayerBrowse; @query("ha-media-player-browse") private _browser!: HaMediaPlayerBrowse;