Save sidebar in user data (#25555)

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Wendelin 2025-05-26 15:25:11 +02:00 committed by GitHub
parent 9f5f100e98
commit 208e863327
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 203 additions and 174 deletions

View File

@ -14,7 +14,6 @@ import {
mdiTooltipAccount, mdiTooltipAccount,
mdiViewDashboard, mdiViewDashboard,
} from "@mdi/js"; } from "@mdi/js";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import type { CSSResultGroup, PropertyValues } from "lit"; import type { CSSResultGroup, PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit"; import { LitElement, css, html, nothing } from "lit";
import { import {
@ -30,6 +29,7 @@ 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";
import { throttle } from "../common/util/throttle"; import { throttle } from "../common/util/throttle";
import { subscribeFrontendUserData } from "../data/frontend";
import type { ActionHandlerDetail } from "../data/lovelace/action_handler"; import type { ActionHandlerDetail } from "../data/lovelace/action_handler";
import type { PersistentNotification } from "../data/persistent_notification"; import type { PersistentNotification } from "../data/persistent_notification";
import { subscribeNotifications } from "../data/persistent_notification"; import { subscribeNotifications } from "../data/persistent_notification";
@ -41,11 +41,13 @@ 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 { 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-fade-in";
import "./ha-icon"; import "./ha-icon";
import "./ha-icon-button"; import "./ha-icon-button";
import "./ha-md-list"; import "./ha-md-list";
import "./ha-md-list-item"; import "./ha-md-list-item";
import type { HaMdListItem } from "./ha-md-list-item"; import type { HaMdListItem } from "./ha-md-list-item";
import "./ha-spinner";
import "./ha-svg-icon"; import "./ha-svg-icon";
import "./user/ha-user-badge"; import "./user/ha-user-badge";
@ -187,38 +189,57 @@ class HaSidebar extends SubscribeMixin(LitElement) {
@property({ attribute: "always-expand", type: Boolean }) @property({ attribute: "always-expand", type: Boolean })
public alwaysExpand = false; public alwaysExpand = false;
@property({ attribute: false })
public panelOrder!: string[];
@property({ attribute: false })
public hiddenPanels!: string[];
@state() private _notifications?: PersistentNotification[]; @state() private _notifications?: PersistentNotification[];
@state() private _updatesCount = 0; @state() private _updatesCount = 0;
@state() private _issuesCount = 0; @state() private _issuesCount = 0;
@state() private _panelOrder?: string[];
@state() private _hiddenPanels?: string[];
private _mouseLeaveTimeout?: number; private _mouseLeaveTimeout?: number;
private _tooltipHideTimeout?: number; private _tooltipHideTimeout?: number;
private _recentKeydownActiveUntil = 0; private _recentKeydownActiveUntil = 0;
private _unsubPersistentNotifications: UnsubscribeFunc | undefined;
@query(".tooltip") private _tooltip!: HTMLDivElement; @query(".tooltip") private _tooltip!: HTMLDivElement;
public hassSubscribe(): UnsubscribeFunc[] { public hassSubscribe() {
return this.hass.user?.is_admin return [
? [ subscribeFrontendUserData(
subscribeRepairsIssueRegistry(this.hass.connection!, (repairs) => { this.hass.connection,
this._issuesCount = repairs.issues.filter( "sidebar",
(issue) => !issue.ignored ({ value }) => {
).length; this._panelOrder = value?.panelOrder;
}), this._hiddenPanels = value?.hiddenPanels;
]
: []; // fallback to old localStorage values
if (!this._panelOrder) {
const storedOrder = localStorage.getItem("sidebarPanelOrder");
this._panelOrder = storedOrder ? JSON.parse(storedOrder) : [];
}
if (!this._hiddenPanels) {
const storedHidden = localStorage.getItem("sidebarHiddenPanels");
this._hiddenPanels = storedHidden ? JSON.parse(storedHidden) : [];
}
}
),
subscribeNotifications(this.hass.connection, (notifications) => {
this._notifications = notifications;
}),
...(this.hass.user?.is_admin
? [
subscribeRepairsIssueRegistry(this.hass.connection!, (repairs) => {
this._issuesCount = repairs.issues.filter(
(issue) => !issue.ignored
).length;
}),
]
: []),
];
} }
protected render() { protected render() {
@ -254,8 +275,8 @@ class HaSidebar extends SubscribeMixin(LitElement) {
changedProps.has("_updatesCount") || changedProps.has("_updatesCount") ||
changedProps.has("_issuesCount") || changedProps.has("_issuesCount") ||
changedProps.has("_notifications") || changedProps.has("_notifications") ||
changedProps.has("hiddenPanels") || changedProps.has("_hiddenPanels") ||
changedProps.has("panelOrder") changedProps.has("_panelOrder")
) { ) {
return true; return true;
} }
@ -279,23 +300,6 @@ class HaSidebar extends SubscribeMixin(LitElement) {
); );
} }
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
this._subscribePersistentNotifications();
}
private _subscribePersistentNotifications(): void {
if (this._unsubPersistentNotifications) {
this._unsubPersistentNotifications();
}
this._unsubPersistentNotifications = subscribeNotifications(
this.hass.connection,
(notifications) => {
this._notifications = notifications;
}
);
}
protected updated(changedProps) { protected updated(changedProps) {
super.updated(changedProps); super.updated(changedProps);
if (changedProps.has("alwaysExpand")) { if (changedProps.has("alwaysExpand")) {
@ -307,14 +311,6 @@ class HaSidebar extends SubscribeMixin(LitElement) {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined; const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (
this.hass &&
oldHass?.connected === false &&
this.hass.connected === true
) {
this._subscribePersistentNotifications();
}
this._calculateCounts(); this._calculateCounts();
if (!SUPPORT_SCROLL_IF_NEEDED) { if (!SUPPORT_SCROLL_IF_NEEDED) {
@ -369,11 +365,19 @@ class HaSidebar extends SubscribeMixin(LitElement) {
} }
private _renderAllPanels(selectedPanel: string) { private _renderAllPanels(selectedPanel: string) {
if (!this._panelOrder || !this._hiddenPanels) {
return html`
<ha-fade-in .delay=${500}
><ha-spinner size="large"></ha-spinner
></ha-fade-in>
`;
}
const [beforeSpacer, afterSpacer] = computePanels( const [beforeSpacer, afterSpacer] = computePanels(
this.hass.panels, this.hass.panels,
this.hass.defaultPanel, this.hass.defaultPanel,
this.panelOrder, this._panelOrder,
this.hiddenPanels, this._hiddenPanels,
this.hass.locale this.hass.locale
); );
@ -559,18 +563,9 @@ class HaSidebar extends SubscribeMixin(LitElement) {
return; return;
} }
showEditSidebarDialog(this, { showEditSidebarDialog(this);
saveCallback: this._saveSidebar,
});
} }
private _saveSidebar = (order: string[], hidden: string[]) => {
fireEvent(this, "hass-edit-sidebar", {
order,
hidden,
});
};
private _itemMouseEnter(ev: MouseEvent) { private _itemMouseEnter(ev: MouseEvent) {
// On keypresses on the listbox, we're going to ignore mouse enter events // On keypresses on the listbox, we're going to ignore mouse enter events
// for 100ms so that we ignore it when pressing down arrow scrolls the // for 100ms so that we ignore it when pressing down arrow scrolls the
@ -730,13 +725,22 @@ class HaSidebar extends SubscribeMixin(LitElement) {
display: none; display: none;
} }
ha-fade-in,
ha-md-list { ha-md-list {
padding: 4px 0;
box-sizing: border-box;
height: calc(100% - var(--header-height) - 132px);
height: calc( height: calc(
100% - var(--header-height) - 132px - var(--safe-area-inset-bottom) 100% - var(--header-height) - 132px - var(--safe-area-inset-bottom)
); );
}
ha-fade-in {
display: flex;
justify-content: center;
align-items: center;
}
ha-md-list {
padding: 4px 0;
box-sizing: border-box;
overflow-x: hidden; overflow-x: hidden;
background: none; background: none;
margin-left: var(--safe-area-inset-left); margin-left: var(--safe-area-inset-left);

View File

@ -5,9 +5,15 @@ export interface CoreFrontendUserData {
showEntityIdPicker?: boolean; showEntityIdPicker?: boolean;
} }
export interface SidebarFrontendUserData {
panelOrder: string[];
hiddenPanels: string[];
}
declare global { declare global {
interface FrontendUserData { interface FrontendUserData {
core: CoreFrontendUserData; core: CoreFrontendUserData;
sidebar: SidebarFrontendUserData;
} }
} }

View File

@ -1,9 +1,5 @@
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import { import { saveFrontendUserData, subscribeFrontendUserData } from "./frontend";
fetchFrontendUserData,
saveFrontendUserData,
subscribeFrontendUserData,
} from "./frontend";
export enum NumberFormat { export enum NumberFormat {
language = "language", language = "language",
@ -78,9 +74,6 @@ export type TranslationCategory =
| "selector" | "selector"
| "services"; | "services";
export const fetchTranslationPreferences = (hass: HomeAssistant) =>
fetchFrontendUserData(hass.connection, "language");
export const subscribeTranslationPreferences = ( export const subscribeTranslationPreferences = (
hass: HomeAssistant, hass: HomeAssistant,
callback: (data: { value: FrontendLocaleData | null }) => void callback: (data: { value: FrontendLocaleData | null }) => void

View File

@ -1,18 +1,25 @@
import "@material/mwc-linear-progress/mwc-linear-progress"; import "@material/mwc-linear-progress/mwc-linear-progress";
import { mdiClose } from "@mdi/js"; import { mdiClose } from "@mdi/js";
import { css, html, LitElement, nothing } from "lit"; import { css, html, LitElement, nothing, type TemplateResult } from "lit";
import { customElement, property, query, state } from "lit/decorators"; import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import "../../components/ha-alert";
import "../../components/ha-dialog-header"; import "../../components/ha-dialog-header";
import "../../components/ha-fade-in";
import "../../components/ha-icon-button"; import "../../components/ha-icon-button";
import "../../components/ha-items-display-editor"; import "../../components/ha-items-display-editor";
import type { DisplayValue } from "../../components/ha-items-display-editor"; import type { DisplayValue } from "../../components/ha-items-display-editor";
import "../../components/ha-md-dialog"; import "../../components/ha-md-dialog";
import type { HaMdDialog } from "../../components/ha-md-dialog"; import type { HaMdDialog } from "../../components/ha-md-dialog";
import { computePanels, PANEL_ICONS } from "../../components/ha-sidebar"; import { computePanels, PANEL_ICONS } from "../../components/ha-sidebar";
import "../../components/ha-spinner";
import {
fetchFrontendUserData,
saveFrontendUserData,
} from "../../data/frontend";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import type { EditSidebarDialogParams } from "./show-dialog-edit-sidebar"; import { showConfirmationDialog } from "../generic/show-dialog-box";
@customElement("dialog-edit-sidebar") @customElement("dialog-edit-sidebar")
class DialogEditSidebar extends LitElement { class DialogEditSidebar extends LitElement {
@ -22,21 +29,43 @@ class DialogEditSidebar extends LitElement {
@query("ha-md-dialog") private _dialog?: HaMdDialog; @query("ha-md-dialog") private _dialog?: HaMdDialog;
@state() private _order: string[] = []; @state() private _order?: string[];
@state() private _hidden: string[] = []; @state() private _hidden?: string[];
private _saveCallback?: (order: string[], hidden: string[]) => void; @state() private _error?: string;
public async showDialog(params: EditSidebarDialogParams): Promise<void> { /**
* If user has old localStorage values, show a confirmation dialog
*/
@state() private _migrateToUserData = false;
public async showDialog(): Promise<void> {
this._open = true; this._open = true;
const storedOrder = localStorage.getItem("sidebarPanelOrder"); this._getData();
const storedHidden = localStorage.getItem("sidebarHiddenPanels"); }
this._order = storedOrder ? JSON.parse(storedOrder) : this._order; private async _getData() {
this._hidden = storedHidden ? JSON.parse(storedHidden) : this._hidden; try {
this._saveCallback = params.saveCallback; const data = await fetchFrontendUserData(this.hass.connection, "sidebar");
this._order = data?.panelOrder;
this._hidden = data?.hiddenPanels;
// fallback to old localStorage values
if (!this._order) {
const storedOrder = localStorage.getItem("sidebarPanelOrder");
this._migrateToUserData = !!storedOrder;
this._order = storedOrder ? JSON.parse(storedOrder) : [];
}
if (!this._hidden) {
const storedHidden = localStorage.getItem("sidebarHiddenPanels");
this._migrateToUserData = this._migrateToUserData || !!storedHidden;
this._hidden = storedHidden ? JSON.parse(storedHidden) : [];
}
} catch (err: any) {
this._error = err.message || err;
}
} }
private _dialogClosed(): void { private _dialogClosed(): void {
@ -52,12 +81,16 @@ class DialogEditSidebar extends LitElement {
panels ? Object.values(panels) : [] panels ? Object.values(panels) : []
); );
protected render() { private _renderContent(): TemplateResult {
if (!this._open) { if (!this._order || !this._hidden) {
return nothing; return html`<ha-fade-in .delay=${500}
><ha-spinner size="large"></ha-spinner
></ha-fade-in>`;
} }
const dialogTitle = this.hass.localize("ui.sidebar.edit_sidebar"); if (this._error) {
return html`<ha-alert alert-type="error">${this._error}</ha-alert>`;
}
const panels = this._panels(this.hass.panels); const panels = this._panels(this.hass.panels);
@ -71,7 +104,7 @@ class DialogEditSidebar extends LitElement {
const items = [ const items = [
...beforeSpacer, ...beforeSpacer,
...panels.filter((panel) => this._hidden.includes(panel.url_path)), ...panels.filter((panel) => this._hidden!.includes(panel.url_path)),
...afterSpacer.filter((panel) => panel.url_path !== "config"), ...afterSpacer.filter((panel) => panel.url_path !== "config"),
].map((panel) => ({ ].map((panel) => ({
value: panel.url_path, value: panel.url_path,
@ -89,6 +122,26 @@ class DialogEditSidebar extends LitElement {
disableSorting: panel.url_path === "developer-tools", disableSorting: panel.url_path === "developer-tools",
})); }));
return html`<ha-items-display-editor
.hass=${this.hass}
.value=${{
order: this._order,
hidden: this._hidden,
}}
.items=${items}
@value-changed=${this._changed}
dont-sort-visible
>
</ha-items-display-editor>`;
}
protected render() {
if (!this._open) {
return nothing;
}
const dialogTitle = this.hass.localize("ui.sidebar.edit_sidebar");
return html` return html`
<ha-md-dialog open @closed=${this._dialogClosed}> <ha-md-dialog open @closed=${this._dialogClosed}>
<ha-dialog-header slot="headline"> <ha-dialog-header slot="headline">
@ -98,26 +151,22 @@ class DialogEditSidebar extends LitElement {
.path=${mdiClose} .path=${mdiClose}
@click=${this.closeDialog} @click=${this.closeDialog}
></ha-icon-button> ></ha-icon-button>
<span slot="title" .title=${dialogTitle}> ${dialogTitle} </span> <span slot="title" .title=${dialogTitle}>${dialogTitle}</span>
${!this._migrateToUserData
? html`<span slot="subtitle"
>${this.hass.localize("ui.sidebar.edit_subtitle")}</span
>`
: nothing}
</ha-dialog-header> </ha-dialog-header>
<div slot="content" class="content"> <div slot="content" class="content">${this._renderContent()}</div>
<ha-items-display-editor
.hass=${this.hass}
.value=${{
order: this._order,
hidden: this._hidden,
}}
.items=${items}
@value-changed=${this._changed}
dont-sort-visible
>
</ha-items-display-editor>
</div>
<div slot="actions"> <div slot="actions">
<ha-button @click=${this.closeDialog}> <ha-button @click=${this.closeDialog}>
${this.hass.localize("ui.common.cancel")} ${this.hass.localize("ui.common.cancel")}
</ha-button> </ha-button>
<ha-button @click=${this._save}> <ha-button
.disabled=${!this._order || !this._hidden}
@click=${this._save}
>
${this.hass.localize("ui.common.save")} ${this.hass.localize("ui.common.save")}
</ha-button> </ha-button>
</div> </div>
@ -131,8 +180,27 @@ class DialogEditSidebar extends LitElement {
this._hidden = [...hidden]; this._hidden = [...hidden];
} }
private _save(): void { private async _save() {
this._saveCallback?.(this._order ?? [], this._hidden ?? []); if (this._migrateToUserData) {
const confirmation = await showConfirmationDialog(this, {
destructive: true,
text: this.hass.localize("ui.sidebar.migrate_to_user_data"),
});
if (!confirmation) {
return;
}
}
try {
await saveFrontendUserData(this.hass.connection, "sidebar", {
panelOrder: this._order!,
hiddenPanels: this._hidden!,
});
} catch (err: any) {
this._error = err.message || err;
return;
}
this.closeDialog(); this.closeDialog();
} }
@ -149,6 +217,12 @@ class DialogEditSidebar extends LitElement {
min-height: 100%; min-height: 100%;
} }
} }
ha-fade-in {
display: flex;
justify-content: center;
align-items: center;
}
`; `;
} }

View File

@ -1,18 +1,11 @@
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
export interface EditSidebarDialogParams {
saveCallback: (order: string[], hidden: string[]) => void;
}
export const loadEditSidebarDialog = () => import("./dialog-edit-sidebar"); export const loadEditSidebarDialog = () => import("./dialog-edit-sidebar");
export const showEditSidebarDialog = ( export const showEditSidebarDialog = (element: HTMLElement): void => {
element: HTMLElement,
dialogParams: EditSidebarDialogParams
): void => {
fireEvent(element, "show-dialog", { fireEvent(element, "show-dialog", {
dialogTag: "dialog-edit-sidebar", dialogTag: "dialog-edit-sidebar",
dialogImport: loadEditSidebarDialog, dialogImport: loadEditSidebarDialog,
dialogParams, dialogParams: {},
}); });
}; };

View File

@ -5,31 +5,23 @@ import type { HASSDomEvent } from "../common/dom/fire_event";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { listenMediaQuery } from "../common/dom/media_query"; import { listenMediaQuery } from "../common/dom/media_query";
import { toggleAttribute } from "../common/dom/toggle_attribute"; import { toggleAttribute } from "../common/dom/toggle_attribute";
import { computeRTLDirection } from "../common/util/compute_rtl";
import "../components/ha-drawer"; import "../components/ha-drawer";
import { showNotificationDrawer } from "../dialogs/notifications/show-notification-drawer"; import { showNotificationDrawer } from "../dialogs/notifications/show-notification-drawer";
import type { HomeAssistant, Route } from "../types"; import type { HomeAssistant, Route } from "../types";
import "./partial-panel-resolver"; import "./partial-panel-resolver";
import { computeRTLDirection } from "../common/util/compute_rtl";
import { storage } from "../common/decorators/storage";
declare global { declare global {
// for fire event // for fire event
interface HASSDomEvents { interface HASSDomEvents {
"hass-toggle-menu": undefined | { open?: boolean }; "hass-toggle-menu": undefined | { open?: boolean };
"hass-edit-sidebar": EditSideBarEvent;
"hass-show-notifications": undefined; "hass-show-notifications": undefined;
} }
interface HTMLElementEventMap { interface HTMLElementEventMap {
"hass-edit-sidebar": HASSDomEvent<EditSideBarEvent>;
"hass-toggle-menu": HASSDomEvent<HASSDomEvents["hass-toggle-menu"]>; "hass-toggle-menu": HASSDomEvent<HASSDomEvents["hass-toggle-menu"]>;
} }
} }
interface EditSideBarEvent {
order: string[];
hidden: string[];
}
@customElement("home-assistant-main") @customElement("home-assistant-main")
export class HomeAssistantMain extends LitElement { export class HomeAssistantMain extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@ -44,22 +36,6 @@ export class HomeAssistantMain extends LitElement {
@state() private _drawerOpen = false; @state() private _drawerOpen = false;
@state()
@storage({
key: "sidebarPanelOrder",
state: true,
subscribe: true,
})
private _panelOrder: string[] = [];
@state()
@storage({
key: "sidebarHiddenPanels",
state: true,
subscribe: true,
})
private _hiddenPanels: string[] = [];
constructor() { constructor() {
super(); super();
listenMediaQuery("(max-width: 870px)", (matches) => { listenMediaQuery("(max-width: 870px)", (matches) => {
@ -81,8 +57,6 @@ export class HomeAssistantMain extends LitElement {
.hass=${this.hass} .hass=${this.hass}
.narrow=${sidebarNarrow} .narrow=${sidebarNarrow}
.route=${this.route} .route=${this.route}
.panelOrder=${this._panelOrder}
.hiddenPanels=${this._hiddenPanels}
.alwaysExpand=${sidebarNarrow || this.hass.dockedSidebar === "docked"} .alwaysExpand=${sidebarNarrow || this.hass.dockedSidebar === "docked"}
></ha-sidebar> ></ha-sidebar>
<partial-panel-resolver <partial-panel-resolver
@ -106,14 +80,6 @@ export class HomeAssistantMain extends LitElement {
); );
} }
this.addEventListener(
"hass-edit-sidebar",
(ev: HASSDomEvent<EditSideBarEvent>) => {
this._panelOrder = ev.detail.order;
this._hiddenPanels = ev.detail.hidden;
}
);
this.addEventListener("hass-toggle-menu", (ev) => { this.addEventListener("hass-toggle-menu", (ev) => {
if (this._sidebarEditMode) { if (this._sidebarEditMode) {
return; return;

View File

@ -150,6 +150,23 @@ class HaProfileSectionGeneral extends LitElement {
.narrow=${this.narrow} .narrow=${this.narrow}
.hass=${this.hass} .hass=${this.hass}
></ha-pick-first-weekday-row> ></ha-pick-first-weekday-row>
<ha-settings-row .narrow=${this.narrow}>
<span slot="heading">
${this.hass.localize(
"ui.panel.profile.customize_sidebar.header"
)}
</span>
<span slot="description">
${this.hass.localize(
"ui.panel.profile.customize_sidebar.description"
)}
</span>
<mwc-button @click=${this._customizeSidebar}>
${this.hass.localize(
"ui.panel.profile.customize_sidebar.button"
)}
</mwc-button>
</ha-settings-row>
${this.hass.user!.is_admin ${this.hass.user!.is_admin
? html` ? html`
<ha-advanced-mode-row <ha-advanced-mode-row
@ -187,23 +204,6 @@ class HaProfileSectionGeneral extends LitElement {
.narrow=${this.narrow} .narrow=${this.narrow}
.hass=${this.hass} .hass=${this.hass}
></ha-pick-dashboard-row> ></ha-pick-dashboard-row>
<ha-settings-row .narrow=${this.narrow}>
<span slot="heading">
${this.hass.localize(
"ui.panel.profile.customize_sidebar.header"
)}
</span>
<span slot="description">
${this.hass.localize(
"ui.panel.profile.customize_sidebar.description"
)}
</span>
<mwc-button @click=${this._customizeSidebar}>
${this.hass.localize(
"ui.panel.profile.customize_sidebar.button"
)}
</mwc-button>
</ha-settings-row>
${this.hass.dockedSidebar !== "auto" || !this.narrow ${this.hass.dockedSidebar !== "auto" || !this.narrow
? html` ? html`
<ha-force-narrow-row <ha-force-narrow-row
@ -248,18 +248,9 @@ class HaProfileSectionGeneral extends LitElement {
} }
private _customizeSidebar() { private _customizeSidebar() {
showEditSidebarDialog(this, { showEditSidebarDialog(this);
saveCallback: this._saveSidebar,
});
} }
private _saveSidebar = (order: string[], hidden: string[]) => {
fireEvent(this, "hass-edit-sidebar", {
order,
hidden,
});
};
private _handleLogOut() { private _handleLogOut() {
showConfirmationDialog(this, { showConfirmationDialog(this, {
title: this.hass.localize("ui.panel.profile.logout_title"), title: this.hass.localize("ui.panel.profile.logout_title"),

View File

@ -2019,7 +2019,9 @@
"sidebar": { "sidebar": {
"external_app_configuration": "App settings", "external_app_configuration": "App settings",
"sidebar_toggle": "Sidebar toggle", "sidebar_toggle": "Sidebar toggle",
"edit_sidebar": "Edit sidebar" "edit_sidebar": "Edit sidebar",
"edit_subtitle": "Synced on all devices",
"migrate_to_user_data": "This will change the sidebar on all the devices you are logged into. To create a sidebar per device, you should use a different user for that device."
}, },
"panel": { "panel": {
"my": { "my": {