mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-23 09:16:38 +00:00
Add advanced mode (#3298)
* Add advanced mode * Move advanced mode to profile * Add promo for advanced mode
This commit is contained in:
parent
2c3cc1fbc7
commit
e804e62e66
74
src/data/collection.ts
Normal file
74
src/data/collection.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import {
|
||||
Collection,
|
||||
Connection,
|
||||
getCollection,
|
||||
UnsubscribeFunc,
|
||||
} from "home-assistant-js-websocket";
|
||||
import { Store } from "home-assistant-js-websocket/dist/store";
|
||||
|
||||
interface OptimisticCollection<T> extends Collection<T> {
|
||||
save(data: T): Promise<unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an optimistic collection that includes a save function.
|
||||
* When the collection is saved, the collection is optimistically updated.
|
||||
* The update is reversed when the update failed.
|
||||
*/
|
||||
|
||||
export const getOptimisticCollection = <StateType>(
|
||||
saveCollection: (conn: Connection, data: StateType) => Promise<unknown>,
|
||||
conn: Connection,
|
||||
key: string,
|
||||
fetchCollection: (conn: Connection) => Promise<StateType>,
|
||||
subscribeUpdates?: (
|
||||
conn: Connection,
|
||||
store: Store<StateType>
|
||||
) => Promise<UnsubscribeFunc>
|
||||
): OptimisticCollection<StateType> => {
|
||||
const updateKey = `${key}-optimistic`;
|
||||
|
||||
const collection = getCollection<StateType>(
|
||||
conn,
|
||||
key,
|
||||
fetchCollection,
|
||||
async (_conn, store) => {
|
||||
// Subscribe to original updates
|
||||
const subUpResult = subscribeUpdates
|
||||
? subscribeUpdates(conn, store)
|
||||
: undefined;
|
||||
// Store the store
|
||||
conn[updateKey] = store;
|
||||
|
||||
// Unsub function to undo both
|
||||
return () => {
|
||||
if (subUpResult) {
|
||||
subUpResult.then((unsub) => unsub());
|
||||
}
|
||||
conn[updateKey] = undefined;
|
||||
};
|
||||
}
|
||||
);
|
||||
return {
|
||||
...collection,
|
||||
async save(data: StateType) {
|
||||
const store: Store<StateType> | undefined = conn[updateKey];
|
||||
let current;
|
||||
|
||||
// Can be undefined if currently no subscribers
|
||||
if (store) {
|
||||
current = store.state;
|
||||
store.setState(data, true);
|
||||
}
|
||||
|
||||
try {
|
||||
return await saveCollection(conn, data);
|
||||
} catch (err) {
|
||||
if (store) {
|
||||
store.setState(current as any, true);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
@ -1,8 +1,14 @@
|
||||
import { HomeAssistant } from "../types";
|
||||
import { Connection } from "home-assistant-js-websocket";
|
||||
import { getOptimisticCollection } from "./collection";
|
||||
|
||||
export interface CoreFrontendUserData {
|
||||
showAdvanced?: boolean;
|
||||
}
|
||||
|
||||
declare global {
|
||||
// tslint:disable-next-line
|
||||
interface FrontendUserData {}
|
||||
interface FrontendUserData {
|
||||
core: CoreFrontendUserData;
|
||||
}
|
||||
}
|
||||
|
||||
export type ValidUserDataKey = keyof FrontendUserData;
|
||||
@ -10,10 +16,10 @@ export type ValidUserDataKey = keyof FrontendUserData;
|
||||
export const fetchFrontendUserData = async <
|
||||
UserDataKey extends ValidUserDataKey
|
||||
>(
|
||||
hass: HomeAssistant,
|
||||
conn: Connection,
|
||||
key: UserDataKey
|
||||
): Promise<FrontendUserData[UserDataKey] | null> => {
|
||||
const result = await hass.callWS<{
|
||||
const result = await conn.sendMessagePromise<{
|
||||
value: FrontendUserData[UserDataKey] | null;
|
||||
}>({
|
||||
type: "frontend/get_user_data",
|
||||
@ -25,12 +31,31 @@ export const fetchFrontendUserData = async <
|
||||
export const saveFrontendUserData = async <
|
||||
UserDataKey extends ValidUserDataKey
|
||||
>(
|
||||
hass: HomeAssistant,
|
||||
conn: Connection,
|
||||
key: UserDataKey,
|
||||
value: FrontendUserData[UserDataKey]
|
||||
): Promise<void> =>
|
||||
hass.callWS<void>({
|
||||
conn.sendMessagePromise<void>({
|
||||
type: "frontend/set_user_data",
|
||||
key,
|
||||
value,
|
||||
});
|
||||
|
||||
export const getOptimisticFrontendUserDataCollection = <
|
||||
UserDataKey extends ValidUserDataKey
|
||||
>(
|
||||
conn: Connection,
|
||||
userDataKey: UserDataKey
|
||||
) =>
|
||||
getOptimisticCollection(
|
||||
(_conn, data) =>
|
||||
saveFrontendUserData(
|
||||
conn,
|
||||
userDataKey,
|
||||
// @ts-ignore
|
||||
data
|
||||
),
|
||||
conn,
|
||||
`_frontendUserData-${userDataKey}`,
|
||||
() => fetchFrontendUserData(conn, userDataKey)
|
||||
);
|
||||
|
@ -12,12 +12,12 @@ declare global {
|
||||
}
|
||||
|
||||
export const fetchTranslationPreferences = (hass: HomeAssistant) =>
|
||||
fetchFrontendUserData(hass, "language");
|
||||
fetchFrontendUserData(hass.connection, "language");
|
||||
|
||||
export const saveTranslationPreferences = (
|
||||
hass: HomeAssistant,
|
||||
data: FrontendTranslationData
|
||||
) => saveFrontendUserData(hass, "language", data);
|
||||
) => saveFrontendUserData(hass.connection, "language", data);
|
||||
|
||||
export const getHassTranslations = async (
|
||||
hass: HomeAssistant,
|
||||
|
@ -37,6 +37,7 @@ class HaConfigCore extends LocalizeMixin(PolymerElement) {
|
||||
<div class$="[[computeClasses(isWide)]]">
|
||||
<ha-config-section-core
|
||||
is-wide="[[isWide]]"
|
||||
show-advanced="[[showAdvanced]]"
|
||||
hass="[[hass]]"
|
||||
></ha-config-section-core>
|
||||
</div>
|
||||
@ -48,6 +49,7 @@ class HaConfigCore extends LocalizeMixin(PolymerElement) {
|
||||
return {
|
||||
hass: Object,
|
||||
isWide: Boolean,
|
||||
showAdvanced: Boolean,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -63,79 +63,80 @@ class HaConfigSectionCore extends LocalizeMixin(PolymerElement) {
|
||||
<ha-config-name-form hass="[[hass]]"></ha-config-name-form>
|
||||
<ha-config-core-form hass="[[hass]]"></ha-config-core-form>
|
||||
|
||||
<ha-card
|
||||
header="[[localize('ui.panel.config.core.section.core.validation.heading')]]"
|
||||
>
|
||||
<div class="card-content">
|
||||
[[localize('ui.panel.config.core.section.core.validation.introduction')]]
|
||||
<template is="dom-if" if="[[!validateLog]]">
|
||||
<div class="validate-container">
|
||||
<template is="dom-if" if="[[!validating]]">
|
||||
<template is="dom-if" if="[[isValid]]">
|
||||
<div class="validate-result" id="result">
|
||||
[[localize('ui.panel.config.core.section.core.validation.valid')]]
|
||||
</div>
|
||||
<template is="dom-if" if="[[showAdvanced]]">
|
||||
<ha-card
|
||||
header="[[localize('ui.panel.config.core.section.core.validation.heading')]]"
|
||||
>
|
||||
<div class="card-content">
|
||||
[[localize('ui.panel.config.core.section.core.validation.introduction')]]
|
||||
<template is="dom-if" if="[[!validateLog]]">
|
||||
<div class="validate-container">
|
||||
<template is="dom-if" if="[[!validating]]">
|
||||
<template is="dom-if" if="[[isValid]]">
|
||||
<div class="validate-result" id="result">
|
||||
[[localize('ui.panel.config.core.section.core.validation.valid')]]
|
||||
</div>
|
||||
</template>
|
||||
<mwc-button raised="" on-click="validateConfig">
|
||||
[[localize('ui.panel.config.core.section.core.validation.check_config')]]
|
||||
</mwc-button>
|
||||
</template>
|
||||
<template is="dom-if" if="[[validating]]">
|
||||
<paper-spinner active=""></paper-spinner>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template is="dom-if" if="[[validateLog]]">
|
||||
<div class="config-invalid">
|
||||
<span class="text">
|
||||
[[localize('ui.panel.config.core.section.core.validation.invalid')]]
|
||||
</span>
|
||||
<mwc-button raised="" on-click="validateConfig">
|
||||
[[localize('ui.panel.config.core.section.core.validation.check_config')]]
|
||||
</mwc-button>
|
||||
</template>
|
||||
<template is="dom-if" if="[[validating]]">
|
||||
<paper-spinner active=""></paper-spinner>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template is="dom-if" if="[[validateLog]]">
|
||||
<div class="config-invalid">
|
||||
<span class="text">
|
||||
[[localize('ui.panel.config.core.section.core.validation.invalid')]]
|
||||
</span>
|
||||
<mwc-button raised="" on-click="validateConfig">
|
||||
[[localize('ui.panel.config.core.section.core.validation.check_config')]]
|
||||
</mwc-button>
|
||||
</div>
|
||||
<div id="configLog" class="validate-log">[[validateLog]]</div>
|
||||
</template>
|
||||
</div>
|
||||
</ha-card>
|
||||
|
||||
<ha-card
|
||||
header="[[localize('ui.panel.config.core.section.core.reloading.heading')]]"
|
||||
>
|
||||
<div class="card-content">
|
||||
[[localize('ui.panel.config.core.section.core.reloading.introduction')]]
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<ha-call-service-button
|
||||
hass="[[hass]]"
|
||||
domain="homeassistant"
|
||||
service="reload_core_config"
|
||||
>[[localize('ui.panel.config.core.section.core.reloading.core')]]
|
||||
</ha-call-service-button>
|
||||
<ha-call-service-button
|
||||
hass="[[hass]]"
|
||||
domain="group"
|
||||
service="reload"
|
||||
hidden$="[[!groupLoaded(hass)]]"
|
||||
>[[localize('ui.panel.config.core.section.core.reloading.group')]]
|
||||
</ha-call-service-button>
|
||||
<ha-call-service-button
|
||||
hass="[[hass]]"
|
||||
domain="automation"
|
||||
service="reload"
|
||||
hidden$="[[!automationLoaded(hass)]]"
|
||||
>[[localize('ui.panel.config.core.section.core.reloading.automation')]]
|
||||
</ha-call-service-button>
|
||||
<ha-call-service-button
|
||||
hass="[[hass]]"
|
||||
domain="script"
|
||||
service="reload"
|
||||
hidden$="[[!scriptLoaded(hass)]]"
|
||||
>[[localize('ui.panel.config.core.section.core.reloading.script')]]
|
||||
</ha-call-service-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
</div>
|
||||
<div id="configLog" class="validate-log">[[validateLog]]</div>
|
||||
</template>
|
||||
</div>
|
||||
</ha-card>
|
||||
|
||||
<ha-card
|
||||
header="[[localize('ui.panel.config.core.section.core.reloading.heading')]]"
|
||||
>
|
||||
<div class="card-content">
|
||||
[[localize('ui.panel.config.core.section.core.reloading.introduction')]]
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<ha-call-service-button
|
||||
hass="[[hass]]"
|
||||
domain="homeassistant"
|
||||
service="reload_core_config"
|
||||
>[[localize('ui.panel.config.core.section.core.reloading.core')]]
|
||||
</ha-call-service-button>
|
||||
<ha-call-service-button
|
||||
hass="[[hass]]"
|
||||
domain="group"
|
||||
service="reload"
|
||||
hidden$="[[!groupLoaded(hass)]]"
|
||||
>[[localize('ui.panel.config.core.section.core.reloading.group')]]
|
||||
</ha-call-service-button>
|
||||
<ha-call-service-button
|
||||
hass="[[hass]]"
|
||||
domain="automation"
|
||||
service="reload"
|
||||
hidden$="[[!automationLoaded(hass)]]"
|
||||
>[[localize('ui.panel.config.core.section.core.reloading.automation')]]
|
||||
</ha-call-service-button>
|
||||
<ha-call-service-button
|
||||
hass="[[hass]]"
|
||||
domain="script"
|
||||
service="reload"
|
||||
hidden$="[[!scriptLoaded(hass)]]"
|
||||
>[[localize('ui.panel.config.core.section.core.reloading.script')]]
|
||||
</ha-call-service-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
</template>
|
||||
<ha-card
|
||||
header="[[localize('ui.panel.config.core.section.core.server_management.heading')]]"
|
||||
>
|
||||
@ -188,6 +189,8 @@ class HaConfigSectionCore extends LocalizeMixin(PolymerElement) {
|
||||
type: String,
|
||||
value: "",
|
||||
},
|
||||
|
||||
showAdvanced: Boolean,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -31,10 +31,17 @@ class HaConfigDashboard extends NavigateMixin(LocalizeMixin(PolymerElement)) {
|
||||
.content {
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
a {
|
||||
ha-card a {
|
||||
text-decoration: none;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
.promo-advanced {
|
||||
text-align: center;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.promo-advanced a {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
<app-header-layout has-scrolling-region="">
|
||||
@ -99,7 +106,16 @@ class HaConfigDashboard extends NavigateMixin(LocalizeMixin(PolymerElement)) {
|
||||
</a>
|
||||
</ha-card>
|
||||
|
||||
<ha-config-navigation hass="[[hass]]"></ha-config-navigation>
|
||||
<ha-config-navigation
|
||||
hass="[[hass]]"
|
||||
show-advanced="[[showAdvanced]]"
|
||||
></ha-config-navigation>
|
||||
|
||||
<template is='dom-if' if='[[!showAdvanced]]'>
|
||||
<div class='promo-advanced'>
|
||||
Missing config options? Enable advanced mode on <a href="/profile">your profile page.</a>
|
||||
</div>
|
||||
</template>
|
||||
</ha-config-section>
|
||||
</div>
|
||||
</app-header-layout>
|
||||
@ -111,6 +127,7 @@ class HaConfigDashboard extends NavigateMixin(LocalizeMixin(PolymerElement)) {
|
||||
hass: Object,
|
||||
isWide: Boolean,
|
||||
cloudStatus: Object,
|
||||
showAdvanced: Boolean,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,87 +0,0 @@
|
||||
import "@polymer/iron-icon/iron-icon";
|
||||
import "@polymer/paper-item/paper-item-body";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import NavigateMixin from "../../../mixins/navigate-mixin";
|
||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||
|
||||
import isComponentLoaded from "../../../common/config/is_component_loaded";
|
||||
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon-next";
|
||||
|
||||
const CORE_PAGES = ["core", "customize", "entity_registry", "area_registry"];
|
||||
/*
|
||||
* @appliesMixin LocalizeMixin
|
||||
* @appliesMixin NavigateMixin
|
||||
*/
|
||||
class HaConfigNavigation extends LocalizeMixin(NavigateMixin(PolymerElement)) {
|
||||
static get template() {
|
||||
return html`
|
||||
<style include="iron-flex">
|
||||
ha-card {
|
||||
overflow: hidden;
|
||||
}
|
||||
paper-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
<ha-card>
|
||||
<template is="dom-repeat" items="[[pages]]">
|
||||
<template is="dom-if" if="[[_computeLoaded(hass, item)]]">
|
||||
<paper-item on-click="_navigate">
|
||||
<paper-item-body two-line="">
|
||||
[[_computeCaption(item, localize)]]
|
||||
<div secondary="">[[_computeDescription(item, localize)]]</div>
|
||||
</paper-item-body>
|
||||
<ha-icon-next></ha-icon-next>
|
||||
</paper-item>
|
||||
</template>
|
||||
</template>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
pages: {
|
||||
type: Array,
|
||||
value: [
|
||||
"core",
|
||||
"person",
|
||||
"entity_registry",
|
||||
"area_registry",
|
||||
"automation",
|
||||
"script",
|
||||
"zha",
|
||||
"zwave",
|
||||
"customize",
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
_computeLoaded(hass, page) {
|
||||
return CORE_PAGES.includes(page) || isComponentLoaded(hass, page);
|
||||
}
|
||||
|
||||
_computeCaption(page, localize) {
|
||||
return localize(`ui.panel.config.${page}.caption`);
|
||||
}
|
||||
|
||||
_computeDescription(page, localize) {
|
||||
return localize(`ui.panel.config.${page}.description`);
|
||||
}
|
||||
|
||||
_navigate(ev) {
|
||||
this.navigate("/config/" + ev.model.item);
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-config-navigation", HaConfigNavigation);
|
82
src/panels/config/dashboard/ha-config-navigation.ts
Normal file
82
src/panels/config/dashboard/ha-config-navigation.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import "@polymer/iron-icon/iron-icon";
|
||||
import "@polymer/paper-item/paper-item-body";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
|
||||
import isComponentLoaded from "../../../common/config/is_component_loaded";
|
||||
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon-next";
|
||||
import {
|
||||
LitElement,
|
||||
html,
|
||||
TemplateResult,
|
||||
property,
|
||||
customElement,
|
||||
CSSResult,
|
||||
css,
|
||||
} from "lit-element";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
|
||||
const PAGES: Array<{
|
||||
page: string;
|
||||
core?: boolean;
|
||||
advanced?: boolean;
|
||||
}> = [
|
||||
{ page: "core", core: true },
|
||||
{ page: "person" },
|
||||
{ page: "entity_registry", core: true },
|
||||
{ page: "area_registry", core: true },
|
||||
{ page: "automation" },
|
||||
{ page: "script" },
|
||||
{ page: "zha" },
|
||||
{ page: "zwave" },
|
||||
{ page: "customize", core: true, advanced: true },
|
||||
];
|
||||
|
||||
@customElement("ha-config-navigation")
|
||||
class HaConfigNavigation extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
@property() public showAdvanced!: boolean;
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
return html`
|
||||
<ha-card>
|
||||
${PAGES.map(({ page, core, advanced }) =>
|
||||
(core || isComponentLoaded(this.hass, page)) &&
|
||||
(!advanced || this.showAdvanced)
|
||||
? html`
|
||||
<a href=${`/config/${page}`}>
|
||||
<paper-item>
|
||||
<paper-item-body two-line="">
|
||||
${this.hass.localize(`ui.panel.config.${page}.caption`)}
|
||||
<div secondary>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.${page}.description`
|
||||
)}
|
||||
</div>
|
||||
</paper-item-body>
|
||||
<ha-icon-next></ha-icon-next>
|
||||
</paper-item>
|
||||
</a>
|
||||
`
|
||||
: ""
|
||||
)}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-config-navigation": HaConfigNavigation;
|
||||
}
|
||||
}
|
@ -5,6 +5,10 @@ import { HomeAssistant } from "../../types";
|
||||
import { CloudStatus, fetchCloudStatus } from "../../data/cloud";
|
||||
import { listenMediaQuery } from "../../common/dom/media_query";
|
||||
import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page";
|
||||
import {
|
||||
CoreFrontendUserData,
|
||||
getOptimisticFrontendUserDataCollection,
|
||||
} from "../../data/frontend";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
@ -17,8 +21,6 @@ declare global {
|
||||
class HaPanelConfig extends HassRouterPage {
|
||||
@property() public hass!: HomeAssistant;
|
||||
@property() public narrow!: boolean;
|
||||
@property() public _wideSidebar: boolean = false;
|
||||
@property() public _wide: boolean = false;
|
||||
|
||||
protected routerOptions: RouterOptions = {
|
||||
defaultPage: "dashboard",
|
||||
@ -93,6 +95,9 @@ class HaPanelConfig extends HassRouterPage {
|
||||
},
|
||||
};
|
||||
|
||||
@property() private _wideSidebar: boolean = false;
|
||||
@property() private _wide: boolean = false;
|
||||
@property() private _coreUserData?: CoreFrontendUserData;
|
||||
@property() private _cloudStatus?: CloudStatus;
|
||||
|
||||
private _listeners: Array<() => void> = [];
|
||||
@ -109,6 +114,14 @@ class HaPanelConfig extends HassRouterPage {
|
||||
this._wideSidebar = matches;
|
||||
})
|
||||
);
|
||||
this._listeners.push(
|
||||
getOptimisticFrontendUserDataCollection(
|
||||
this.hass.connection,
|
||||
"core"
|
||||
).subscribe((coreUserData) => {
|
||||
this._coreUserData = coreUserData || {};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public disconnectedCallback() {
|
||||
@ -131,6 +144,7 @@ class HaPanelConfig extends HassRouterPage {
|
||||
protected updatePageEl(el) {
|
||||
el.route = this.routeTail;
|
||||
el.hass = this.hass;
|
||||
el.showAdvanced = !!(this._coreUserData && this._coreUserData.showAdvanced);
|
||||
el.isWide = this.hass.dockedSidebar ? this._wideSidebar : this._wide;
|
||||
el.narrow = this.narrow;
|
||||
el.cloudStatus = this._cloudStatus;
|
||||
|
65
src/panels/profile/ha-advanced-mode-card.ts
Normal file
65
src/panels/profile/ha-advanced-mode-card.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import {
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
html,
|
||||
customElement,
|
||||
CSSResult,
|
||||
css,
|
||||
} from "lit-element";
|
||||
import "../../components/ha-card";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import {
|
||||
CoreFrontendUserData,
|
||||
getOptimisticFrontendUserDataCollection,
|
||||
} from "../../data/frontend";
|
||||
|
||||
@customElement("ha-advanced-mode-card")
|
||||
class AdvancedModeCard extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
@property() public coreUserData?: CoreFrontendUserData;
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
return html`
|
||||
<ha-card>
|
||||
<div class="card-header">
|
||||
<div class="title">Advanced mode</div>
|
||||
<paper-toggle-button
|
||||
.checked=${this.coreUserData && this.coreUserData.showAdvanced}
|
||||
.disabled=${this.coreUserData === undefined}
|
||||
@change=${this._advancedToggled}
|
||||
></paper-toggle-button>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
Home Assistant hides advanced features and options by default. You can
|
||||
make these features accessible by checking this toggle. This is a
|
||||
user-specific setting and does not impact other users using Home
|
||||
Assistant.
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _advancedToggled(ev) {
|
||||
getOptimisticFrontendUserDataCollection(this.hass.connection, "core").save({
|
||||
showAdvanced: ev.currentTarget.checked,
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
.card-header {
|
||||
display: flex;
|
||||
}
|
||||
.title {
|
||||
flex: 1;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-advanced-mode-card": AdvancedModeCard;
|
||||
}
|
||||
}
|
@ -11,11 +11,14 @@ import "../../components/ha-card";
|
||||
import "../../components/ha-menu-button";
|
||||
import "../../resources/ha-style";
|
||||
|
||||
import { getOptimisticFrontendUserDataCollection } from "../../data/frontend";
|
||||
|
||||
import { EventsMixin } from "../../mixins/events-mixin";
|
||||
import LocalizeMixin from "../../mixins/localize-mixin";
|
||||
|
||||
import "./ha-change-password-card";
|
||||
import "./ha-mfa-modules-card";
|
||||
import "./ha-advanced-mode-card";
|
||||
import "./ha-refresh-tokens-card";
|
||||
import "./ha-long-lived-access-tokens-card";
|
||||
|
||||
@ -98,6 +101,11 @@ class HaPanelProfile extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
mfa-modules="[[hass.user.mfa_modules]]"
|
||||
></ha-mfa-modules-card>
|
||||
|
||||
<ha-advanced-mode-card
|
||||
hass="[[hass]]"
|
||||
core-user-data="[[_coreUserData]]"
|
||||
></ha-mfa-modules-card>
|
||||
|
||||
<ha-refresh-tokens-card
|
||||
hass="[[hass]]"
|
||||
refresh-tokens="[[_refreshTokens]]"
|
||||
@ -119,12 +127,27 @@ class HaPanelProfile extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
hass: Object,
|
||||
narrow: Boolean,
|
||||
_refreshTokens: Array,
|
||||
_coreUserData: Object,
|
||||
};
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._refreshRefreshTokens();
|
||||
this._unsubCoreData = getOptimisticFrontendUserDataCollection(
|
||||
this.hass.connection,
|
||||
"core"
|
||||
).subscribe((coreUserData) => {
|
||||
this._coreUserData = coreUserData;
|
||||
});
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
if (this._unsubCoreData) {
|
||||
this._unsubCoreData();
|
||||
this._unsubCoreData = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async _refreshRefreshTokens() {
|
||||
|
@ -9,9 +9,11 @@ import { HassBaseEl } from "./hass-base-mixin";
|
||||
import { computeLocalize } from "../common/translations/localize";
|
||||
import { computeRTL } from "../common/util/compute_rtl";
|
||||
import { HomeAssistant } from "../types";
|
||||
import { saveFrontendUserData } from "../data/frontend";
|
||||
import { storeState } from "../util/ha-pref-storage";
|
||||
import { getHassTranslations } from "../data/translation";
|
||||
import {
|
||||
getHassTranslations,
|
||||
saveTranslationPreferences,
|
||||
} from "../data/translation";
|
||||
|
||||
/*
|
||||
* superClass needs to contain `this.hass` and `this._updateHass`.
|
||||
@ -65,7 +67,7 @@ export default (superClass: Constructor<LitElement & HassBaseEl>) =>
|
||||
this._updateHass({ language, selectedLanguage: language });
|
||||
storeState(this.hass);
|
||||
if (saveToBackend) {
|
||||
saveFrontendUserData(this.hass, "language", { language });
|
||||
saveTranslationPreferences(this.hass, { language });
|
||||
}
|
||||
|
||||
this._applyTranslations(this.hass);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { translationMetadata } from "../resources/translations-metadata";
|
||||
import { fetchFrontendUserData } from "../data/frontend";
|
||||
import { HomeAssistant } from "../types";
|
||||
import { fetchTranslationPreferences } from "../data/translation";
|
||||
|
||||
const STORAGE = window.localStorage || {};
|
||||
|
||||
@ -43,7 +43,7 @@ function findAvailableLanguage(language: string) {
|
||||
* Get user selected language from backend
|
||||
*/
|
||||
export async function getUserLanguage(hass: HomeAssistant) {
|
||||
const result = await fetchFrontendUserData(hass, "language");
|
||||
const result = await fetchTranslationPreferences(hass);
|
||||
const language = result ? result.language : null;
|
||||
if (language) {
|
||||
const availableLanguage = findAvailableLanguage(language);
|
||||
|
Loading…
x
Reference in New Issue
Block a user