Move user settings to profile page (#1560)

This commit is contained in:
Paulus Schoutsen 2018-08-11 08:46:16 +02:00 committed by GitHub
parent 1b2b62f04c
commit c39417c93d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 289 additions and 226 deletions

View File

@ -4,29 +4,31 @@ import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import EventsMixin from '../mixins/events-mixin.js'; import EventsMixin from '../mixins/events-mixin.js';
export const pushSupported = (
'serviceWorker' in navigator && 'PushManager' in window &&
(document.location.protocol === 'https:' ||
document.location.hostname === 'localhost' ||
document.location.hostname === '127.0.0.1'));
/* /*
* @appliesMixin EventsMixin * @appliesMixin EventsMixin
*/ */
class HaPushNotificationsToggle extends EventsMixin(PolymerElement) { class HaPushNotificationsToggle extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<paper-toggle-button hidden$="[[!pushSupported]]" disabled="[[loading]]" checked="{{pushChecked}}"></paper-toggle-button> <paper-toggle-button
disabled="[[_compDisabled(disabled, loading)]]"
checked="{{pushChecked}}"
></paper-toggle-button>
`; `;
} }
static get properties() { static get properties() {
return { return {
hass: { type: Object, value: null }, hass: { type: Object, value: null },
pushSupported: { disabled: {
type: Boolean, type: Boolean,
readOnly: true, value: false,
notify: true,
value: (
'serviceWorker' in navigator && 'PushManager' in window &&
(document.location.protocol === 'https:' ||
document.location.hostname === 'localhost' ||
document.location.hostname === '127.0.0.1')
)
}, },
pushChecked: { pushChecked: {
type: Boolean, type: Boolean,
@ -40,22 +42,20 @@ class HaPushNotificationsToggle extends EventsMixin(PolymerElement) {
}; };
} }
connectedCallback() { async connectedCallback() {
super.connectedCallback(); super.connectedCallback();
if (!this.pushSupported) return;
navigator.serviceWorker.ready.then( if (!('serviceWorker' in navigator)) return;
(reg) => {
reg.pushManager.getSubscription().then((subscription) => { try {
this.loading = false; const reg = await navigator.serviceWorker.ready;
this.pushChecked = !!subscription; reg.pushManager.getSubscription().then((subscription) => {
}); this.loading = false;
}, this.pushChecked = !!subscription;
() => { });
// no service worker. } catch (err) {
this._setPushSupported(false); // We don't set loading to `false` so we remain disabled
} }
);
} }
handlePushChange(pushChecked) { handlePushChange(pushChecked) {
if (!this.pushSupported) return; if (!this.pushSupported) return;
@ -121,6 +121,10 @@ class HaPushNotificationsToggle extends EventsMixin(PolymerElement) {
}); });
}); });
} }
_compDisabled(disabled, loading) {
return disabled || loading;
}
} }
customElements.define('ha-push-notifications-toggle', HaPushNotificationsToggle); customElements.define('ha-push-notifications-toggle', HaPushNotificationsToggle);

View File

@ -4,15 +4,11 @@ import '@polymer/paper-icon-button/paper-icon-button.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js'; import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js'; import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import '../../../layouts/ha-app-layout.js'; import '../../../layouts/hass-subpage.js';
import '../../../resources/ha-style.js'; import '../../../resources/ha-style.js';
import './ha-config-section-core.js'; import './ha-config-section-core.js';
import './ha-config-section-push-notifications.js';
import './ha-config-section-themes.js';
import './ha-config-section-translation.js';
import isComponentLoaded from '../../../common/config/is_component_loaded.js';
import LocalizeMixin from '../../../mixins/localize-mixin.js'; import LocalizeMixin from '../../../mixins/localize-mixin.js';
/* /*
@ -37,33 +33,11 @@ class HaConfigCore extends LocalizeMixin(PolymerElement) {
} }
</style> </style>
<ha-app-layout has-scrolling-region=""> <hass-subpage header="[[localize('ui.panel.config.core.caption')]]">
<app-header slot="header" fixed="">
<app-toolbar>
<paper-icon-button icon="hass:arrow-left" on-click="_backTapped"></paper-icon-button>
<div main-title="">[[localize('ui.panel.config.core.caption')]]</div>
</app-toolbar>
</app-header>
<div class$="[[computeClasses(isWide)]]"> <div class$="[[computeClasses(isWide)]]">
<ha-config-section-core is-wide="[[isWide]]" hass="[[hass]]"></ha-config-section-core> <ha-config-section-core is-wide="[[isWide]]" hass="[[hass]]"></ha-config-section-core>
<template is="dom-if" if="[[pushSupported]]">
<div class="border"></div>
<ha-config-section-push-notifications is-wide="[[isWide]]" hass="[[hass]]" push-supported="{{pushSupported}}"></ha-config-section-push-notifications>
</template>
<template is="dom-if" if="[[computeIsTranslationLoaded(hass)]]">
<div class="border"></div>
<ha-config-section-translation is-wide="[[isWide]]" hass="[[hass]]"></ha-config-section-translation>
</template>
<template is="dom-if" if="[[computeIsThemesLoaded(hass)]]">
<div class="border"></div>
<ha-config-section-themes is-wide="[[isWide]]" hass="[[hass]]"></ha-config-section-themes>
</template>
</div> </div>
</ha-app-layout> </hass-subpage>
`; `;
} }
@ -71,34 +45,12 @@ class HaConfigCore extends LocalizeMixin(PolymerElement) {
return { return {
hass: Object, hass: Object,
isWide: Boolean, isWide: Boolean,
pushSupported: {
type: Boolean,
value: true,
},
}; };
} }
computeClasses(isWide) { computeClasses(isWide) {
return isWide ? 'content' : 'content narrow'; return isWide ? 'content' : 'content narrow';
} }
computeIsZwaveLoaded(hass) {
return isComponentLoaded(hass, 'config.zwave');
}
computeIsTranslationLoaded(hass) {
return hass.translationMetadata &&
Object.keys(hass.translationMetadata.translations).length;
}
computeIsThemesLoaded(hass) {
return hass.themes && hass.themes.themes &&
Object.keys(hass.themes.themes).length;
}
_backTapped() {
history.back();
}
} }
customElements.define('ha-config-core', HaConfigCore); customElements.define('ha-config-core', HaConfigCore);

View File

@ -1,53 +0,0 @@
import '@polymer/iron-flex-layout/iron-flex-layout-classes.js';
import '@polymer/iron-label/iron-label.js';
import '@polymer/paper-card/paper-card.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import '../../../components/ha-push-notifications-toggle.js';
import '../ha-config-section.js';
import LocalizeMixin from '../../../mixins/localize-mixin.js';
/*
* @appliesMixin LocalizeMixin
*/
class HaConfigSectionPushNotifications extends LocalizeMixin(PolymerElement) {
static get template() {
return html`
<style include="iron-flex iron-flex-alignment iron-positioning">
ha-push-notifications-toggle {
margin-left: 16px;
}
</style>
<ha-config-section is-wide="[[isWide]]">
<span slot="header">[[localize('ui.panel.config.core.section.push_notifications.header')]]</span>
<span slot="introduction">
[[localize('ui.panel.config.core.section.push_notifications.introduction')]]
</span>
<paper-card>
<div class="card-content">
<iron-label class="horizontal layout">
[[localize('ui.panel.config.core.section.push_notifications.push_notifications')]]
<ha-push-notifications-toggle hass="[[hass]]" push-supported="{{pushSupported}}"></ha-push-notifications-toggle>
</iron-label>
</div>
</paper-card>
</ha-config-section>
`;
}
static get properties() {
return {
hass: Object,
isWide: Boolean,
pushSupported: {
type: Boolean,
notify: true,
},
};
}
}
customElements.define('ha-config-section-push-notifications', HaConfigSectionPushNotifications);

View File

@ -23,8 +23,6 @@ class HaChangePasswordCard extends PolymerElement {
} }
paper-card { paper-card {
display: block; display: block;
max-width: 600px;
margin: 16px auto;
} }
.currentPassword { .currentPassword {
margin-top: -4px; margin-top: -4px;
@ -48,22 +46,24 @@ class HaChangePasswordCard extends PolymerElement {
auto-validate auto-validate
error-message='Required' error-message='Required'
></paper-input> ></paper-input>
<paper-input <template is='dom-if' if='[[_currentPassword]]'>
label='New Password' <paper-input
type='password' label='New Password'
value='{{_password1}}' type='password'
required value='{{_password1}}'
auto-validate required
error-message='Required' auto-validate
></paper-input> error-message='Required'
<paper-input ></paper-input>
label='Confirm New Password' <paper-input
type='password' label='Confirm New Password'
value='{{_password2}}' type='password'
required value='{{_password2}}'
auto-validate required
error-message='Required' auto-validate
></paper-input> error-message='Required'
></paper-input>
</template>
</div> </div>
<div class="card-actions"> <div class="card-actions">
<template is="dom-if" if="[[_loading]]"> <template is="dom-if" if="[[_loading]]">

View File

@ -1,6 +1,8 @@
import '@polymer/app-layout/app-header-layout/app-header-layout.js'; import '@polymer/app-layout/app-header-layout/app-header-layout.js';
import '@polymer/app-layout/app-header/app-header.js'; import '@polymer/app-layout/app-header/app-header.js';
import '@polymer/paper-card/paper-card.js'; import '@polymer/paper-card/paper-card.js';
import '@polymer/paper-item/paper-item-body.js';
import '@polymer/paper-item/paper-item.js';
import '@polymer/paper-button/paper-button.js'; import '@polymer/paper-button/paper-button.js';
import '@polymer/app-layout/app-toolbar/app-toolbar.js'; import '@polymer/app-layout/app-toolbar/app-toolbar.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js'; import { html } from '@polymer/polymer/lib/utils/html-tag.js';
@ -8,10 +10,14 @@ import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import '../../components/ha-menu-button.js'; import '../../components/ha-menu-button.js';
import '../../resources/ha-style.js'; import '../../resources/ha-style.js';
import './ha-change-password-card.js';
import EventsMixin from '../../mixins/events-mixin.js'; import EventsMixin from '../../mixins/events-mixin.js';
import './ha-change-password-card.js';
import './ha-pick-language-row.js';
import './ha-pick-theme-row.js';
import './ha-push-notifications-row.js';
/* /*
* @appliesMixin EventsMixin * @appliesMixin EventsMixin
*/ */
@ -25,10 +31,15 @@ class HaPanelProfile extends EventsMixin(PolymerElement) {
-moz-user-select: initial; -moz-user-select: initial;
} }
paper-card { .content {
display: block; display: block;
max-width: 600px; max-width: 600px;
margin: 16px auto; margin: 0 auto;
}
.content > * {
display: block;
margin: 24px 0;
} }
</style> </style>
@ -46,6 +57,20 @@ class HaPanelProfile extends EventsMixin(PolymerElement) {
You are currently logged in as [[hass.user.name]]. You are currently logged in as [[hass.user.name]].
<template is='dom-if' if='[[hass.user.is_owner]]'>You are an owner.</template> <template is='dom-if' if='[[hass.user.is_owner]]'>You are an owner.</template>
</div> </div>
<ha-pick-language-row
narrow="[[narrow]]"
hass="[[hass]]"
></ha-pick-language-row>
<ha-pick-theme-row
narrow="[[narrow]]"
hass="[[hass]]"
></ha-pick-theme-row>
<ha-push-notifications-row
narrow="[[narrow]]"
hass="[[hass]]"
></ha-push-notifications-row>
<div class='card-actions'> <div class='card-actions'>
<paper-button <paper-button
class='warning' class='warning'
@ -53,9 +78,11 @@ class HaPanelProfile extends EventsMixin(PolymerElement) {
>Log out</paper-button> >Log out</paper-button>
</div> </div>
</paper-card> </paper-card>
<template is="dom-if" if="[[_canChangePassword(hass.user)]]"> <template is="dom-if" if="[[_canChangePassword(hass.user)]]">
<ha-change-password-card hass="[[hass]]"></ha-change-password-card> <ha-change-password-card hass="[[hass]]"></ha-change-password-card>
</template> </template>
</div> </div>
</app-header-layout> </app-header-layout>
`; `;

View File

@ -5,49 +5,44 @@ import '@polymer/paper-listbox/paper-listbox.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js'; import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js'; import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import EventsMixin from '../../mixins/events-mixin.js';
import LocalizeMixin from '../../mixins/localize-mixin.js';
import '../ha-config-section.js'; import './ha-settings-row.js';
import EventsMixin from '../../../mixins/events-mixin.js';
import LocalizeMixin from '../../../mixins/localize-mixin.js';
/* /*
* @appliesMixin LocalizeMixin * @appliesMixin LocalizeMixin
* @appliesMixin EventsMixin * @appliesMixin EventsMixin
*/ */
class HaConfigSectionTranslation extends class HaPickLanguageRow extends
LocalizeMixin(EventsMixin(PolymerElement)) { LocalizeMixin(EventsMixin(PolymerElement)) {
static get template() { static get template() {
return html` return html`
<ha-config-section is-wide="[[isWide]]"> <style>
<span slot="header">[[localize('ui.panel.config.core.section.translation.header')]]</span> a { color: var(--primary-color); }
<span slot="introduction"> </style>
[[localize('ui.panel.config.core.section.translation.introduction')]] <ha-settings-row narrow='[[narrow]]'>
<span slot='heading'>[[localize('ui.panel.profile.language.header')]]</span>
<span slot='description'>
<a
href='https://developers.home-assistant.io/docs/en/internationalization_translation.html'
target='_blank'>[[localize('ui.panel.profile.language.link_promo')]]</a>
</span> </span>
<paper-dropdown-menu label="[[localize('ui.panel.profile.language.dropdown_label')]]" dynamic-align="">
<paper-card> <paper-listbox slot="dropdown-content" attr-for-selected="language-tag" selected="{{languageSelection}}">
<div class="card-content"> <template is="dom-repeat" items="[[languages]]">
<paper-dropdown-menu label="[[localize('ui.panel.config.core.section.translation.language')]]" dynamic-align=""> <paper-item language-tag$="[[item.tag]]">[[item.nativeName]]</paper-item>
<paper-listbox slot="dropdown-content" attr-for-selected="language-tag" selected="{{languageSelection}}"> </template>
<template is="dom-repeat" items="[[languages]]"> </paper-listbox>
<paper-item language-tag$="[[item.tag]]">[[item.nativeName]]</paper-item> </paper-dropdown-menu>
</template> </ha-settings-row>
</paper-listbox> `;
&gt;</paper-dropdown-menu>
</div>
</paper-card>
</ha-config-section>
`;
} }
static get properties() { static get properties() {
return { return {
hass: { hass: Object,
type: Object, narrow: Boolean,
},
isWide: {
type: Boolean,
},
languageSelection: { languageSelection: {
type: String, type: String,
observer: 'languageSelectionChanged', observer: 'languageSelectionChanged',
@ -83,4 +78,4 @@ class HaConfigSectionTranslation extends
} }
} }
customElements.define('ha-config-section-translation', HaConfigSectionTranslation); customElements.define('ha-pick-language-row', HaPickLanguageRow);

View File

@ -6,54 +6,57 @@ import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js'; import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import '../ha-config-section.js'; import EventsMixin from '../../mixins/events-mixin.js';
import EventsMixin from '../../../mixins/events-mixin.js'; import LocalizeMixin from '../../mixins/localize-mixin.js';
import LocalizeMixin from '../../../mixins/localize-mixin.js';
/* /*
* @appliesMixin LocalizeMixin * @appliesMixin LocalizeMixin
* @appliesMixin EventsMixin * @appliesMixin EventsMixin
*/ */
class HaConfigSectionThemes extends class HaPickThemeRow extends
LocalizeMixin(EventsMixin(PolymerElement)) { LocalizeMixin(EventsMixin(PolymerElement)) {
static get template() { static get template() {
return html` return html`
<ha-config-section is-wide="[[isWide]]"> <style>
<span slot="header">[[localize('ui.panel.config.core.section.themes.header')]]</span> a { color: var(--primary-color); }
<span slot="introduction"> </style>
[[localize('ui.panel.config.core.section.themes.introduction')]] <ha-settings-row narrow='[[narrow]]'>
<span slot='heading'>[[localize('ui.panel.profile.themes.header')]]</span>
<span slot='description'>
<template is='dom-if' if='[[!_hasThemes]]'>
[[localize('ui.panel.profile.themes.error_no_theme')]]
</template>
<a
href='https://www.home-assistant.io/components/frontend/#defining-themes'
target='_blank'>[[localize('ui.panel.profile.themes.link_promo')]]</a>
</span> </span>
<paper-dropdown-menu
<paper-card> label="[[localize('ui.panel.profile.themes.dropdown_label')]]"
<div class="card-content"> dynamic-align
<paper-dropdown-menu label="[[localize('ui.panel.config.core.section.themes.header')]]" dynamic-align=""> disabled='[[!_hasThemes]]'
<paper-listbox slot="dropdown-content" selected="{{selectedTheme}}"> >
<template is="dom-repeat" items="[[themes]]" as="theme"> <paper-listbox slot="dropdown-content" selected="{{selectedTheme}}">
<paper-item>[[theme]]</paper-item> <template is="dom-repeat" items="[[themes]]" as="theme">
</template> <paper-item>[[theme]]</paper-item>
</paper-listbox> </template>
</paper-dropdown-menu> </paper-listbox>
</div> </paper-dropdown-menu>
</paper-card> </ha-settings-row>
</ha-config-section> `;
`;
} }
static get properties() { static get properties() {
return { return {
hass: { hass: Object,
type: Object, narrow: Boolean,
}, _hasThemes: {
isWide: {
type: Boolean, type: Boolean,
computed: '_compHasThemes(hass)',
}, },
themes: { themes: {
type: Array, type: Array,
computed: 'computeThemes(hass)', computed: '_computeThemes(hass)',
}, },
selectedTheme: { selectedTheme: {
type: Number, type: Number,
}, },
@ -66,6 +69,11 @@ class HaConfigSectionThemes extends
]; ];
} }
_compHasThemes(hass) {
return hass.themes && hass.themes.themes &&
Object.keys(hass.themes.themes).length;
}
ready() { ready() {
super.ready(); super.ready();
if (this.hass.selectedTheme && this.themes.indexOf(this.hass.selectedTheme) > 0) { if (this.hass.selectedTheme && this.themes.indexOf(this.hass.selectedTheme) > 0) {
@ -75,7 +83,7 @@ class HaConfigSectionThemes extends
} }
} }
computeThemes(hass) { _computeThemes(hass) {
if (!hass) return []; if (!hass) return [];
return ['Backend-selected', 'default'].concat(Object.keys(hass.themes.themes).sort()); return ['Backend-selected', 'default'].concat(Object.keys(hass.themes.themes).sort());
} }
@ -91,4 +99,4 @@ class HaConfigSectionThemes extends
} }
} }
customElements.define('ha-config-section-themes', HaConfigSectionThemes); customElements.define('ha-pick-theme-row', HaPickThemeRow);

View File

@ -0,0 +1,81 @@
import '@polymer/iron-flex-layout/iron-flex-layout-classes.js';
import '@polymer/iron-label/iron-label.js';
import '@polymer/paper-card/paper-card.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import isComponentLoaded from '../../common/config/is_component_loaded.js';
import { pushSupported } from '../../components/ha-push-notifications-toggle.js';
import LocalizeMixin from '../../mixins/localize-mixin.js';
import './ha-settings-row.js';
/*
* @appliesMixin LocalizeMixin
*/
class HaPushNotificationsRow extends LocalizeMixin(PolymerElement) {
static get template() {
return html`
<style>
a { color: var(--primary-color); }
</style>
<ha-settings-row narrow='[[narrow]]'>
<span slot='heading'>[[localize('ui.panel.profile.push_notifications.header')]]</span>
<span
slot='description'
>
[[_description(_platformLoaded, _pushSupported)]]
<a
href='https://www.home-assistant.io/components/notify.html5/'
target='_blank'>[[localize('ui.panel.profile.push_notifications.link_promo')]]</a>
</span>
<ha-push-notifications-toggle
hass="[[hass]]"
disabled='[[_error]]'
></ha-push-notifications-toggle>
</ha-settings-row>
`;
}
static get properties() {
return {
hass: Object,
narrow: Boolean,
_platformLoaded: {
type: Boolean,
computed: '_compPlatformLoaded(hass)'
},
_pushSupported: {
type: Boolean,
value: pushSupported,
},
_error: {
type: Boolean,
computed: '_compError(_platformLoaded, _pushSupported)',
},
};
}
_compPlatformLoaded(hass) {
return isComponentLoaded(hass, 'notify.html5');
}
_compError(platformLoaded, pushSupported_) {
return !platformLoaded || !pushSupported_;
}
_description(platformLoaded, pushSupported_) {
let key;
if (!pushSupported_) {
key = 'error_use_https';
} else if (!platformLoaded) {
key = 'error_load_platform';
} else {
key = 'description';
}
return this.localize(`ui.panel.profile.push_notifications.${key}`);
}
}
customElements.define('ha-push-notifications-row', HaPushNotificationsRow);

View File

@ -0,0 +1,43 @@
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
class HaSettingsRow extends PolymerElement {
static get template() {
return html`
<style>
:host {
display: flex;
padding: 0 16px;
align-content: normal;
align-self: auto;
align-items: center;
}
:host([narrow]) {
align-items: normal;
flex-direction: column;
border-top: 1px solid var(--divider-color);
padding-bottom: 8px;
}
paper-item-body {
padding-right: 16px;
}
</style>
<paper-item-body two-line>
<slot name="heading"></slot>
<div secondary><slot name="description"></slot></div>
</paper-item-body>
<slot></slot>
`;
}
static get properties() {
return {
narrow: {
type: Boolean,
reflectToAttribute: true,
}
};
}
}
customElements.define('ha-settings-row', HaSettingsRow);

View File

@ -516,21 +516,6 @@
"restart": "Restart", "restart": "Restart",
"stop": "Stop" "stop": "Stop"
} }
},
"push_notifications": {
"header": "Configure push notifications",
"introduction": "Enable this setting to receive push notifications on this device",
"push_notifications": "Push notifications"
},
"translation": {
"header": "Choose a language",
"introduction": "Choose a language for the Home Assistant interface on this device",
"language": "Language"
},
"themes": {
"header": "Set a theme",
"introduction": "Choose 'Backend-selected' to use whatever theme the backend chooses or pick a theme for this device",
"theme": "Theme"
} }
} }
}, },
@ -732,6 +717,27 @@
"delete_prompt": "Delete this message?", "delete_prompt": "Delete this message?",
"delete_button": "Delete" "delete_button": "Delete"
}, },
"profile": {
"push_notifications": {
"header": "Push Notifications",
"description": "Send notifications to this device",
"error_load_platform": "Configure notify.html5.",
"error_use_https": "Requires SSL enabled for frontend.",
"push_notifications": "Push notifications",
"link_promo": "Learn more"
},
"language": {
"header": "Language",
"link_promo": "Help translating",
"dropdown_label": "Language"
},
"themes": {
"header": "Theme",
"error_no_theme": "No themes available.",
"link_promo": "Learn about themes",
"dropdown_label": "Theme"
}
},
"shopping-list": { "shopping-list": {
"clear_completed": "Clear completed", "clear_completed": "Clear completed",
"add_item": "Add item", "add_item": "Add item",