move default dashboard setting to user settings

This commit is contained in:
Bram Kragten 2025-06-05 17:37:47 +01:00
parent 8eb7fe8b0a
commit 365db51526
15 changed files with 84 additions and 66 deletions

View File

@ -44,7 +44,7 @@ const createPanelNavigationItem = (hass: HomeAssistant, panel: PanelInfo) => ({
path: `/${panel.url_path}`, path: `/${panel.url_path}`,
icon: panel.icon ?? "mdi:view-dashboard", icon: panel.icon ?? "mdi:view-dashboard",
title: title:
panel.url_path === hass.defaultPanel panel.url_path === hass.sidebar.defaultPanel
? hass.localize("panel.states") ? hass.localize("panel.states")
: hass.localize(`panel.${panel.title}`) || : hass.localize(`panel.${panel.title}`) ||
panel.title || panel.title ||

View File

@ -50,6 +50,7 @@ import type { HaMdListItem } from "./ha-md-list-item";
import "./ha-spinner"; import "./ha-spinner";
import "./ha-svg-icon"; import "./ha-svg-icon";
import "./user/ha-user-badge"; import "./user/ha-user-badge";
import { DEFAULT_PANEL } from "../data/panel";
const SHOW_AFTER_SPACER = ["config", "developer-tools"]; const SHOW_AFTER_SPACER = ["config", "developer-tools"];
@ -140,9 +141,9 @@ const defaultPanelSorter = (
export const computePanels = memoizeOne( export const computePanels = memoizeOne(
( (
panels: HomeAssistant["panels"], panels: HomeAssistant["panels"],
defaultPanel: HomeAssistant["defaultPanel"], defaultPanel: HomeAssistant["sidebar"]["defaultPanel"],
panelsOrder: string[], panelsOrder: HomeAssistant["sidebar"]["panelOrder"],
hiddenPanels: string[], hiddenPanels: HomeAssistant["sidebar"]["hiddenPanels"],
locale: HomeAssistant["locale"] locale: HomeAssistant["locale"]
): [PanelInfo[], PanelInfo[]] => { ): [PanelInfo[], PanelInfo[]] => {
if (!panels) { if (!panels) {
@ -195,10 +196,6 @@ class HaSidebar extends SubscribeMixin(LitElement) {
@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;
@ -213,18 +210,32 @@ class HaSidebar extends SubscribeMixin(LitElement) {
this.hass.connection, this.hass.connection,
"sidebar", "sidebar",
({ value }) => { ({ value }) => {
this._panelOrder = value?.panelOrder; let panelOrder = value?.panelOrder;
this._hiddenPanels = value?.hiddenPanels; let hiddenPanels = value?.hiddenPanels;
let defaultPanel = value?.defaultPanel;
// fallback to old localStorage values // fallback to old localStorage values
if (!this._panelOrder) { if (!panelOrder) {
const storedOrder = localStorage.getItem("sidebarPanelOrder"); const storedOrder = localStorage.getItem("sidebarPanelOrder");
this._panelOrder = storedOrder ? JSON.parse(storedOrder) : []; panelOrder = storedOrder ? JSON.parse(storedOrder) : [];
} }
if (!this._hiddenPanels) { if (!hiddenPanels) {
const storedHidden = localStorage.getItem("sidebarHiddenPanels"); const storedHidden = localStorage.getItem("sidebarHiddenPanels");
this._hiddenPanels = storedHidden ? JSON.parse(storedHidden) : []; hiddenPanels = storedHidden ? JSON.parse(storedHidden) : [];
} }
if (!defaultPanel) {
const storedDefault = localStorage.getItem("defaultPanel");
defaultPanel = storedDefault
? JSON.parse(storedDefault)
: DEFAULT_PANEL;
}
fireEvent(this, "hass-set-sidebar-data", {
...value,
defaultPanel: defaultPanel as string,
panelOrder: panelOrder as string[],
hiddenPanels: hiddenPanels as string[],
});
} }
), ),
subscribeNotifications(this.hass.connection, (notifications) => { subscribeNotifications(this.hass.connection, (notifications) => {
@ -275,8 +286,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("hass") &&
changedProps.has("_panelOrder") changedProps.get("hass")?.sidebar !== this.hass.sidebar)
) { ) {
return true; return true;
} }
@ -295,7 +306,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
hass.localize !== oldHass.localize || hass.localize !== oldHass.localize ||
hass.locale !== oldHass.locale || hass.locale !== oldHass.locale ||
hass.states !== oldHass.states || hass.states !== oldHass.states ||
hass.defaultPanel !== oldHass.defaultPanel || hass.sidebar !== oldHass.sidebar ||
hass.connected !== oldHass.connected hass.connected !== oldHass.connected
); );
} }
@ -365,7 +376,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
} }
private _renderAllPanels(selectedPanel: string) { private _renderAllPanels(selectedPanel: string) {
if (!this._panelOrder || !this._hiddenPanels) { if (!this.hass.sidebar.panelOrder || !this.hass.sidebar.hiddenPanels) {
return html` return html`
<ha-fade-in .delay=${500} <ha-fade-in .delay=${500}
><ha-spinner size="small"></ha-spinner ><ha-spinner size="small"></ha-spinner
@ -375,9 +386,9 @@ class HaSidebar extends SubscribeMixin(LitElement) {
const [beforeSpacer, afterSpacer] = computePanels( const [beforeSpacer, afterSpacer] = computePanels(
this.hass.panels, this.hass.panels,
this.hass.defaultPanel, this.hass.sidebar.defaultPanel,
this._panelOrder, this.hass.sidebar.panelOrder,
this._hiddenPanels, this.hass.sidebar.hiddenPanels,
this.hass.locale this.hass.locale
); );
@ -402,11 +413,11 @@ class HaSidebar extends SubscribeMixin(LitElement) {
return panels.map((panel) => return panels.map((panel) =>
this._renderPanel( this._renderPanel(
panel.url_path, panel.url_path,
panel.url_path === this.hass.defaultPanel panel.url_path === this.hass.sidebar.defaultPanel
? panel.title || this.hass.localize("panel.states") ? panel.title || this.hass.localize("panel.states")
: this.hass.localize(`panel.${panel.title}`) || panel.title, : this.hass.localize(`panel.${panel.title}`) || panel.title,
panel.icon, panel.icon,
panel.url_path === this.hass.defaultPanel && !panel.icon panel.url_path === this.hass.sidebar.defaultPanel && !panel.icon
? PANEL_ICONS.lovelace ? PANEL_ICONS.lovelace
: panel.url_path in PANEL_ICONS : panel.url_path in PANEL_ICONS
? PANEL_ICONS[panel.url_path] ? PANEL_ICONS[panel.url_path]

View File

@ -8,6 +8,7 @@ export interface CoreFrontendUserData {
export interface SidebarFrontendUserData { export interface SidebarFrontendUserData {
panelOrder: string[]; panelOrder: string[];
hiddenPanels: string[]; hiddenPanels: string[];
defaultPanel?: string;
} }
declare global { declare global {

View File

@ -1,4 +1,3 @@
import { fireEvent } from "../common/dom/fire_event";
import type { HomeAssistant, PanelInfo } from "../types"; import type { HomeAssistant, PanelInfo } from "../types";
/** Panel to show when no panel is picked. */ /** Panel to show when no panel is picked. */
@ -10,16 +9,9 @@ export const getStorageDefaultPanelUrlPath = (): string => {
return defaultPanel ? JSON.parse(defaultPanel) : DEFAULT_PANEL; return defaultPanel ? JSON.parse(defaultPanel) : DEFAULT_PANEL;
}; };
export const setDefaultPanel = (
element: HTMLElement,
urlPath: string
): void => {
fireEvent(element, "hass-default-panel", { defaultPanel: urlPath });
};
export const getDefaultPanel = (hass: HomeAssistant): PanelInfo => export const getDefaultPanel = (hass: HomeAssistant): PanelInfo =>
hass.panels[hass.defaultPanel] hass.panels[hass.sidebar.defaultPanel]
? hass.panels[hass.defaultPanel] ? hass.panels[hass.sidebar.defaultPanel]
: hass.panels[DEFAULT_PANEL]; : hass.panels[DEFAULT_PANEL];
export const getPanelNameTranslationKey = (panel: PanelInfo) => { export const getPanelNameTranslationKey = (panel: PanelInfo) => {

View File

@ -96,7 +96,7 @@ class DialogEditSidebar extends LitElement {
const [beforeSpacer, afterSpacer] = computePanels( const [beforeSpacer, afterSpacer] = computePanels(
this.hass.panels, this.hass.panels,
this.hass.defaultPanel, this.hass.sidebar.defaultPanel,
this._order, this._order,
this._hidden, this._hidden,
this.hass.locale this.hass.locale
@ -109,12 +109,12 @@ class DialogEditSidebar extends LitElement {
].map((panel) => ({ ].map((panel) => ({
value: panel.url_path, value: panel.url_path,
label: label:
panel.url_path === this.hass.defaultPanel panel.url_path === this.hass.sidebar.defaultPanel
? panel.title || this.hass.localize("panel.states") ? panel.title || this.hass.localize("panel.states")
: this.hass.localize(`panel.${panel.title}`) || panel.title || "?", : this.hass.localize(`panel.${panel.title}`) || panel.title || "?",
icon: panel.icon || undefined, icon: panel.icon || undefined,
iconPath: iconPath:
panel.url_path === this.hass.defaultPanel && !panel.icon panel.url_path === this.hass.sidebar.defaultPanel && !panel.icon
? PANEL_ICONS.lovelace ? PANEL_ICONS.lovelace
: panel.url_path in PANEL_ICONS : panel.url_path in PANEL_ICONS
? PANEL_ICONS[panel.url_path] ? PANEL_ICONS[panel.url_path]
@ -195,6 +195,7 @@ class DialogEditSidebar extends LitElement {
await saveFrontendUserData(this.hass.connection, "sidebar", { await saveFrontendUserData(this.hass.connection, "sidebar", {
panelOrder: this._order!, panelOrder: this._order!,
hiddenPanels: this._hidden!, hiddenPanels: this._hidden!,
defaultPanel: this.hass.sidebar.defaultPanel,
}); });
} catch (err: any) { } catch (err: any) {
this._error = err.message || err; this._error = err.message || err;

View File

@ -13,10 +13,11 @@ import type {
LovelaceDashboardCreateParams, LovelaceDashboardCreateParams,
LovelaceDashboardMutableParams, LovelaceDashboardMutableParams,
} from "../../../../data/lovelace/dashboard"; } from "../../../../data/lovelace/dashboard";
import { DEFAULT_PANEL, setDefaultPanel } from "../../../../data/panel"; import { DEFAULT_PANEL } from "../../../../data/panel";
import { haStyleDialog } from "../../../../resources/styles"; import { haStyleDialog } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types"; import type { HomeAssistant } from "../../../../types";
import type { LovelaceDashboardDetailsDialogParams } from "./show-dialog-lovelace-dashboard-detail"; import type { LovelaceDashboardDetailsDialogParams } from "./show-dialog-lovelace-dashboard-detail";
import { saveFrontendUserData } from "../../../../data/frontend";
@customElement("dialog-lovelace-dashboard-detail") @customElement("dialog-lovelace-dashboard-detail")
export class DialogLovelaceDashboardDetail extends LitElement { export class DialogLovelaceDashboardDetail extends LitElement {
@ -59,7 +60,7 @@ export class DialogLovelaceDashboardDetail extends LitElement {
if (!this._params || !this._data) { if (!this._params || !this._data) {
return nothing; return nothing;
} }
const defaultPanelUrlPath = this.hass.defaultPanel; const defaultPanelUrlPath = this.hass.sidebar.defaultPanel;
const titleInvalid = !this._data.title || !this._data.title.trim(); const titleInvalid = !this._data.title || !this._data.title.trim();
return html` return html`
@ -249,15 +250,17 @@ export class DialogLovelaceDashboardDetail extends LitElement {
}; };
} }
private _toggleDefault() { private async _toggleDefault() {
const urlPath = this._params?.urlPath; const urlPath = this._params?.urlPath;
if (!urlPath) { if (!urlPath) {
return; return;
} }
setDefaultPanel( await saveFrontendUserData(this.hass!.connection, "sidebar", {
this, panelOrder: this.hass!.sidebar.panelOrder,
urlPath === this.hass.defaultPanel ? DEFAULT_PANEL : urlPath hiddenPanels: this.hass!.sidebar.hiddenPanels,
); defaultPanel:
urlPath === this.hass.sidebar.defaultPanel ? DEFAULT_PANEL : urlPath,
});
} }
private async _updateDashboard() { private async _updateDashboard() {

View File

@ -255,7 +255,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
const defaultMode = ( const defaultMode = (
this.hass.panels?.lovelace?.config as LovelacePanelConfig this.hass.panels?.lovelace?.config as LovelacePanelConfig
).mode; ).mode;
const defaultUrlPath = this.hass.defaultPanel; const defaultUrlPath = this.hass.sidebar.defaultPanel;
const isDefault = defaultUrlPath === "lovelace"; const isDefault = defaultUrlPath === "lovelace";
const result: DataTableItem[] = [ const result: DataTableItem[] = [
{ {

View File

@ -139,7 +139,7 @@ export class HuiDialogSelectDashboard extends LitElement {
...(this._params!.dashboards || (await fetchDashboards(this.hass))), ...(this._params!.dashboards || (await fetchDashboards(this.hass))),
]; ];
const currentPath = this._fromUrlPath || this.hass.defaultPanel; const currentPath = this._fromUrlPath || this.hass.sidebar.defaultPanel;
for (const dashboard of this._dashboards!) { for (const dashboard of this._dashboards!) {
if (dashboard.url_path !== currentPath) { if (dashboard.url_path !== currentPath) {
this._toUrlPath = dashboard.url_path; this._toUrlPath = dashboard.url_path;

View File

@ -77,7 +77,7 @@ export class HuiDialogSelectView extends LitElement {
"ui.panel.lovelace.editor.select_view.dashboard_label" "ui.panel.lovelace.editor.select_view.dashboard_label"
)} )}
.disabled=${!this._dashboards.length} .disabled=${!this._dashboards.length}
.value=${this._urlPath || this.hass.defaultPanel} .value=${this._urlPath || this.hass.sidebar.defaultPanel}
@selected=${this._dashboardChanged} @selected=${this._dashboardChanged}
@closed=${stopPropagation} @closed=${stopPropagation}
fixedMenuPosition fixedMenuPosition

View File

@ -6,8 +6,8 @@ import "../../components/ha-select";
import "../../components/ha-settings-row"; import "../../components/ha-settings-row";
import type { LovelaceDashboard } from "../../data/lovelace/dashboard"; import type { LovelaceDashboard } from "../../data/lovelace/dashboard";
import { fetchDashboards } from "../../data/lovelace/dashboard"; import { fetchDashboards } from "../../data/lovelace/dashboard";
import { setDefaultPanel } from "../../data/panel";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import { saveFrontendUserData } from "../../data/frontend";
@customElement("ha-pick-dashboard-row") @customElement("ha-pick-dashboard-row")
class HaPickDashboardRow extends LitElement { class HaPickDashboardRow extends LitElement {
@ -37,7 +37,7 @@ class HaPickDashboardRow extends LitElement {
"ui.panel.profile.dashboard.dropdown_label" "ui.panel.profile.dashboard.dropdown_label"
)} )}
.disabled=${!this._dashboards?.length} .disabled=${!this._dashboards?.length}
.value=${this.hass.defaultPanel} .value=${this.hass.sidebar.defaultPanel}
@selected=${this._dashboardChanged} @selected=${this._dashboardChanged}
naturalMenuWidth naturalMenuWidth
> >
@ -71,12 +71,16 @@ class HaPickDashboardRow extends LitElement {
this._dashboards = await fetchDashboards(this.hass); this._dashboards = await fetchDashboards(this.hass);
} }
private _dashboardChanged(ev) { private async _dashboardChanged(ev) {
const urlPath = ev.target.value; const urlPath = ev.target.value;
if (!urlPath || urlPath === this.hass.defaultPanel) { if (!urlPath || urlPath === this.hass.sidebar.defaultPanel) {
return; return;
} }
setDefaultPanel(this, urlPath); await saveFrontendUserData(this.hass!.connection, "sidebar", {
panelOrder: this.hass!.sidebar.panelOrder,
hiddenPanels: this.hass!.sidebar.hiddenPanels,
defaultPanel: urlPath,
});
} }
} }

View File

@ -167,6 +167,10 @@ class HaProfileSectionGeneral extends LitElement {
)} )}
</mwc-button> </mwc-button>
</ha-settings-row> </ha-settings-row>
<ha-pick-dashboard-row
.narrow=${this.narrow}
.hass=${this.hass}
></ha-pick-dashboard-row>
${this.hass.user!.is_admin ${this.hass.user!.is_admin
? html` ? html`
<ha-advanced-mode-row <ha-advanced-mode-row
@ -200,10 +204,6 @@ class HaProfileSectionGeneral extends LitElement {
.narrow=${this.narrow} .narrow=${this.narrow}
.hass=${this.hass} .hass=${this.hass}
></ha-pick-theme-row> ></ha-pick-theme-row>
<ha-pick-dashboard-row
.narrow=${this.narrow}
.hass=${this.hass}
></ha-pick-dashboard-row>
${this.hass.dockedSidebar !== "auto" || !this.narrow ${this.hass.dockedSidebar !== "auto" || !this.narrow
? html` ? html`
<ha-force-narrow-row <ha-force-narrow-row

View File

@ -59,7 +59,11 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
services: null as any, services: null as any,
user: null as any, user: null as any,
panelUrl: (this as any)._panelUrl, panelUrl: (this as any)._panelUrl,
defaultPanel: DEFAULT_PANEL, sidebar: {
defaultPanel: DEFAULT_PANEL,
hiddenPanels: [],
panelOrder: [],
},
language, language,
selectedLanguage: null, selectedLanguage: null,
locale: { locale: {

View File

@ -7,20 +7,16 @@ interface DockSidebarParams {
dock: HomeAssistant["dockedSidebar"]; dock: HomeAssistant["dockedSidebar"];
} }
interface DefaultPanelParams {
defaultPanel: HomeAssistant["defaultPanel"];
}
declare global { declare global {
// for fire event // for fire event
interface HASSDomEvents { interface HASSDomEvents {
"hass-dock-sidebar": DockSidebarParams; "hass-dock-sidebar": DockSidebarParams;
"hass-default-panel": DefaultPanelParams; "hass-set-sidebar-data": HomeAssistant["sidebar"];
} }
// for add event listener // for add event listener
interface HTMLElementEventMap { interface HTMLElementEventMap {
"hass-dock-sidebar": HASSDomEvent<DockSidebarParams>; "hass-dock-sidebar": HASSDomEvent<DockSidebarParams>;
"hass-default-panel": HASSDomEvent<DefaultPanelParams>; "hass-set-sidebar-data": HASSDomEvent<HomeAssistant["sidebar"]>;
} }
} }
@ -32,8 +28,10 @@ export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
this._updateHass({ dockedSidebar: ev.detail.dock }); this._updateHass({ dockedSidebar: ev.detail.dock });
storeState(this.hass!); storeState(this.hass!);
}); });
this.addEventListener("hass-default-panel", (ev) => { this.addEventListener("hass-set-sidebar-data", async (ev) => {
this._updateHass({ defaultPanel: ev.detail.defaultPanel }); this._updateHass({
sidebar: ev.detail,
});
storeState(this.hass!); storeState(this.hass!);
}); });
} }

View File

@ -243,7 +243,11 @@ export interface HomeAssistant {
vibrate: boolean; vibrate: boolean;
debugConnection: boolean; debugConnection: boolean;
dockedSidebar: "docked" | "always_hidden" | "auto"; dockedSidebar: "docked" | "always_hidden" | "auto";
defaultPanel: string; sidebar: {
defaultPanel: string;
panelOrder: string[];
hiddenPanels: string[];
};
moreInfoEntityId: string | null; moreInfoEntityId: string | null;
user?: CurrentUser; user?: CurrentUser;
userData?: CoreFrontendUserData | null; userData?: CoreFrontendUserData | null;

View File

@ -8,7 +8,7 @@ const STORED_STATE = [
"debugConnection", "debugConnection",
"suspendWhenHidden", "suspendWhenHidden",
"enableShortcuts", "enableShortcuts",
"defaultPanel", "sidebar",
]; ];
export function storeState(hass: HomeAssistant) { export function storeState(hass: HomeAssistant) {