mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-13 04:16:34 +00:00
Finish cloud login flow (#420)
* Finish cloud login flow * Address comments * Fix cache travis
This commit is contained in:
parent
3912347f3d
commit
4c96240ff6
@ -39,6 +39,7 @@
|
|||||||
"no-param-reassign": 0,
|
"no-param-reassign": 0,
|
||||||
"no-multi-assign": 0,
|
"no-multi-assign": 0,
|
||||||
"radix": 0,
|
"radix": 0,
|
||||||
|
"no-alert": 0,
|
||||||
"import/prefer-default-export": 0,
|
"import/prefer-default-export": 0,
|
||||||
"react/jsx-no-bind": [2, { "ignoreRefs": true }],
|
"react/jsx-no-bind": [2, { "ignoreRefs": true }],
|
||||||
"react/jsx-no-duplicate-props": 2,
|
"react/jsx-no-duplicate-props": 2,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
sudo: false
|
sudo: false
|
||||||
language: node_js
|
language: node_js
|
||||||
cache:
|
cache:
|
||||||
|
yarn: true
|
||||||
directories:
|
directories:
|
||||||
- node_modules
|
|
||||||
- bower_components
|
- bower_components
|
||||||
install:
|
install:
|
||||||
- yarn install
|
- yarn install
|
||||||
|
@ -3,12 +3,16 @@
|
|||||||
<link rel="import" href="../../../bower_components/paper-item/paper-item-body.html">
|
<link rel="import" href="../../../bower_components/paper-item/paper-item-body.html">
|
||||||
<link rel="import" href='../../../bower_components/paper-button/paper-button.html'>
|
<link rel="import" href='../../../bower_components/paper-button/paper-button.html'>
|
||||||
|
|
||||||
|
<link rel="import" href="../../../src/layouts/hass-subpage.html">
|
||||||
<link rel="import" href="../../../src/util/hass-mixins.html">
|
<link rel="import" href="../../../src/util/hass-mixins.html">
|
||||||
<link rel="import" href='../../../src/resources/ha-style.html'>
|
<link rel="import" href='../../../src/resources/ha-style.html'>
|
||||||
|
|
||||||
<dom-module id="ha-config-cloud-account">
|
<dom-module id="ha-config-cloud-account">
|
||||||
<template>
|
<template>
|
||||||
<style include="iron-flex ha-style">
|
<style include="iron-flex ha-style">
|
||||||
|
.content {
|
||||||
|
padding-bottom: 24px;
|
||||||
|
}
|
||||||
paper-card {
|
paper-card {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
@ -25,20 +29,36 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<paper-card>
|
<hass-subpage title='Cloud Account'>
|
||||||
<div class='account'>
|
<div class='content'>
|
||||||
<paper-item-body two-line>
|
<ha-config-section
|
||||||
[[account.first_name]] [[account.last_name]]
|
is-wide='[[isWide]]'
|
||||||
<div secondary>[[account.email]]</div>
|
>
|
||||||
</paper-item-body>
|
<span slot='header'>Home Assistant Cloud</span>
|
||||||
<paper-button
|
<span slot='introduction'>
|
||||||
class='warning'
|
The Home Assistant Cloud allows you to opt-in to functions that will bring your Home Assistant experience to the next level.
|
||||||
on-tap='handleLogout'
|
|
||||||
>Sign out</paper-button>
|
|
||||||
</div>
|
|
||||||
</paper-card>
|
|
||||||
|
|
||||||
<div class='soon'>More configuration options coming soon.</div>
|
<p><i>
|
||||||
|
Home Assistant will never share information with our cloud without your prior permission.
|
||||||
|
</i></p>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<paper-card>
|
||||||
|
<div class='account'>
|
||||||
|
<paper-item-body>
|
||||||
|
[[account.email]]
|
||||||
|
</paper-item-body>
|
||||||
|
<paper-button
|
||||||
|
class='warning'
|
||||||
|
on-tap='handleLogout'
|
||||||
|
>Sign out</paper-button>
|
||||||
|
</div>
|
||||||
|
</paper-card>
|
||||||
|
|
||||||
|
<div class='soon'>More configuration options coming soon.</div>
|
||||||
|
</ha-config-section>
|
||||||
|
</div>
|
||||||
|
</hass-subpage>
|
||||||
</template>
|
</template>
|
||||||
</dom-module>
|
</dom-module>
|
||||||
|
|
||||||
|
236
panels/config/cloud/ha-config-cloud-forgot-password.html
Normal file
236
panels/config/cloud/ha-config-cloud-forgot-password.html
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
<link rel="import" href='../../../bower_components/polymer/polymer-element.html'>
|
||||||
|
<link rel="import" href='../../../bower_components/paper-card/paper-card.html'>
|
||||||
|
<link rel="import" href='../../../bower_components/paper-button/paper-button.html'>
|
||||||
|
<link rel="import" href='../../../bower_components/paper-input/paper-input.html'>
|
||||||
|
|
||||||
|
<link rel="import" href="../../../src/layouts/hass-subpage.html">
|
||||||
|
<link rel="import" href="../../../src/util/hass-mixins.html">
|
||||||
|
<link rel="import" href='../../../src/resources/ha-style.html'>
|
||||||
|
<link rel="import" href='../../../src/components/buttons/ha-progress-button.html'>
|
||||||
|
|
||||||
|
<dom-module id="ha-config-cloud-forgot-password">
|
||||||
|
<template>
|
||||||
|
<style include="iron-flex ha-style">
|
||||||
|
.content {
|
||||||
|
padding-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
paper-card {
|
||||||
|
display: block;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
@apply(--paper-font-headline);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: var(--google-red-500);
|
||||||
|
}
|
||||||
|
.card-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.card-actions a {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<hass-subpage title="Forgot Password">
|
||||||
|
<div class='content'>
|
||||||
|
<template is='dom-if' if='[[!_hasToken]]'>
|
||||||
|
<paper-card>
|
||||||
|
<div class='card-content'>
|
||||||
|
<h1>Forgot Password</h1>
|
||||||
|
<p>
|
||||||
|
Enter your email address and we will send you a link to reset your password.
|
||||||
|
</p>
|
||||||
|
<paper-input
|
||||||
|
autofocus
|
||||||
|
label='E-mail'
|
||||||
|
value='{{email}}'
|
||||||
|
type='email'
|
||||||
|
on-keydown='_keyDown'
|
||||||
|
></paper-input>
|
||||||
|
<div class='error' hidden$='[[!error]]'>[[error]]</div>
|
||||||
|
</div>
|
||||||
|
<div class='card-actions'>
|
||||||
|
<ha-progress-button
|
||||||
|
on-tap='_handleEmailPasswordReset'
|
||||||
|
progress='[[_requestInProgress]]'
|
||||||
|
>Send reset email</ha-progress-button>
|
||||||
|
<button
|
||||||
|
class='link'
|
||||||
|
hidden='[[_requestInProgress]]'
|
||||||
|
on-click='_handleHaveToken'
|
||||||
|
>have a token?</button>
|
||||||
|
</div>
|
||||||
|
</paper-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template is='dom-if' if='[[_hasToken]]'>
|
||||||
|
<paper-card>
|
||||||
|
<div class='card-content'>
|
||||||
|
<h1>Confirm new password</h1>
|
||||||
|
<template is='dom-if' if='[[_showEmailInputForConfirmation]]'>
|
||||||
|
<paper-input
|
||||||
|
label='E-mail'
|
||||||
|
type='email'
|
||||||
|
value='{{email}}'
|
||||||
|
on-keydown='_keyDown'
|
||||||
|
></paper-input>
|
||||||
|
</template>
|
||||||
|
<paper-input
|
||||||
|
label='Confirmation code'
|
||||||
|
value='{{_confirmationCode}}'
|
||||||
|
on-keydown='_keyDown'
|
||||||
|
type='number'
|
||||||
|
></paper-input>
|
||||||
|
<paper-input
|
||||||
|
label='New password'
|
||||||
|
value='{{_newPassword}}'
|
||||||
|
on-keydown='_keyDown'
|
||||||
|
type='password'
|
||||||
|
></paper-input>
|
||||||
|
<div class='error' hidden$='[[!error]]'>[[error]]</div>
|
||||||
|
</div>
|
||||||
|
<div class='card-actions'>
|
||||||
|
<ha-progress-button
|
||||||
|
on-tap='_handleConfirmPasswordReset'
|
||||||
|
progress='[[_requestInProgress]]'
|
||||||
|
>Reset Password</ha-progress-button>
|
||||||
|
</div>
|
||||||
|
</paper-card>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</hass-subpage>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
class HaConfigCloudForgotPassword extends
|
||||||
|
window.hassMixins.NavigateMixin(
|
||||||
|
window.hassMixins.EventsMixin(Polymer.Element)) {
|
||||||
|
static get is() { return 'ha-config-cloud-forgot-password'; }
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
hass: Object,
|
||||||
|
email: {
|
||||||
|
type: String,
|
||||||
|
notify: true,
|
||||||
|
},
|
||||||
|
_hasToken: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
_newPassword: {
|
||||||
|
type: String,
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
_confirmationCode: {
|
||||||
|
type: String,
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
_showEmailInputForConfirmation: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
_requestInProgress: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static get observers() {
|
||||||
|
return [
|
||||||
|
'_inputChanged(email, _newPassword)',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
_inputChanged() {
|
||||||
|
this.error = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_keyDown(ev) {
|
||||||
|
// validate on enter
|
||||||
|
if (ev.keyCode === 13) {
|
||||||
|
if (this._hasToken) {
|
||||||
|
this._handleConfirmPasswordReset();
|
||||||
|
} else {
|
||||||
|
this._handleEmailPasswordReset();
|
||||||
|
}
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleEmailPasswordReset() {
|
||||||
|
if (!this.email) {
|
||||||
|
this.error = 'Email is required.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.error) return;
|
||||||
|
|
||||||
|
this._requestInProgress = true;
|
||||||
|
|
||||||
|
this.hass.callApi('post', 'cloud/forgot_password', {
|
||||||
|
email: this.email,
|
||||||
|
}).then(
|
||||||
|
() => {
|
||||||
|
this._hasToken = true;
|
||||||
|
this._requestInProgress = false;
|
||||||
|
}, (err) => {
|
||||||
|
this._requestInProgress = false;
|
||||||
|
this.error = err && err.body && err.body.message ?
|
||||||
|
err.body.message : 'Unknown error';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleHaveToken() {
|
||||||
|
this._error = '';
|
||||||
|
this._showEmailInputForConfirmation = true;
|
||||||
|
this._hasToken = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleConfirmPasswordReset() {
|
||||||
|
this.error = '';
|
||||||
|
if (!this.email) {
|
||||||
|
this.error += 'Email is required. ';
|
||||||
|
}
|
||||||
|
if (!this._confirmationCode) {
|
||||||
|
this.error += 'Confirmation code is required. ';
|
||||||
|
}
|
||||||
|
if (!this._newPassword) {
|
||||||
|
this.error += 'New password is required. ';
|
||||||
|
} else if (this._newPassword.length < 6) {
|
||||||
|
this.error += 'New password should be at least 6 characters.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.error) return;
|
||||||
|
|
||||||
|
this._requestInProgress = true;
|
||||||
|
|
||||||
|
this.hass.callApi('post', 'cloud/confirm_forgot_password', {
|
||||||
|
email: this.email,
|
||||||
|
confirmation_code: this._confirmationCode,
|
||||||
|
new_password: this._newPassword,
|
||||||
|
}).then(
|
||||||
|
() => {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
alert('Password reset successful! You can now login.');
|
||||||
|
this.navigate('config/cloud/login');
|
||||||
|
}, (err) => {
|
||||||
|
this._requestInProgress = false;
|
||||||
|
this.error = err && err.body && err.body.message ?
|
||||||
|
err.body.message : 'Unknown error';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define(HaConfigCloudForgotPassword.is, HaConfigCloudForgotPassword);
|
||||||
|
</script>
|
@ -3,61 +3,130 @@
|
|||||||
<link rel="import" href='../../../bower_components/paper-button/paper-button.html'>
|
<link rel="import" href='../../../bower_components/paper-button/paper-button.html'>
|
||||||
<link rel="import" href='../../../bower_components/paper-input/paper-input.html'>
|
<link rel="import" href='../../../bower_components/paper-input/paper-input.html'>
|
||||||
|
|
||||||
|
<link rel="import" href="../../../src/layouts/hass-subpage.html">
|
||||||
<link rel="import" href="../../../src/util/hass-mixins.html">
|
<link rel="import" href="../../../src/util/hass-mixins.html">
|
||||||
<link rel="import" href='../../../src/resources/ha-style.html'>
|
<link rel="import" href='../../../src/resources/ha-style.html'>
|
||||||
|
<link rel="import" href='../../../src/components/buttons/ha-progress-button.html'>
|
||||||
|
|
||||||
<dom-module id="ha-config-cloud-login">
|
<dom-module id="ha-config-cloud-login">
|
||||||
<template>
|
<template>
|
||||||
<style include="iron-flex ha-style">
|
<style include="iron-flex ha-style">
|
||||||
|
.content {
|
||||||
|
padding-bottom: 24px;
|
||||||
|
}
|
||||||
paper-card {
|
paper-card {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
paper-item {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
paper-card:last-child {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
h1 {
|
h1 {
|
||||||
@apply(--paper-font-headline);
|
@apply(--paper-font-headline);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
.error {
|
||||||
|
color: var(--google-red-500);
|
||||||
|
}
|
||||||
|
.card-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<paper-card>
|
<hass-subpage title='Cloud Login'>
|
||||||
<div class='card-content'>
|
<div class='content'>
|
||||||
<h1>Sign In</h1>
|
<ha-config-section
|
||||||
<paper-input
|
is-wide='[[isWide]]'
|
||||||
label='Username'
|
>
|
||||||
value='{{username}}'
|
<span slot='header'>Home Assistant Cloud</span>
|
||||||
error-message='Failed to login'
|
<span slot='introduction'>
|
||||||
invalid='[[error]]'
|
The Home Assistant Cloud allows you to opt-in to functions that will bring your Home Assistant experience to the next level.
|
||||||
on-keydown='_keyDown'
|
|
||||||
></paper-input>
|
<p><i>
|
||||||
<paper-input
|
Home Assistant will never share information with our cloud without your prior permission.
|
||||||
label='Password'
|
</i></p>
|
||||||
value='{{password}}'
|
</span>
|
||||||
type='password'
|
|
||||||
on-keydown='_keyDown'
|
<paper-card>
|
||||||
></paper-input>
|
<div class='card-content'>
|
||||||
|
<h1>Sign In</h1>
|
||||||
|
<paper-input
|
||||||
|
label='Email'
|
||||||
|
id='emailInput'
|
||||||
|
type='email'
|
||||||
|
value='{{email}}'
|
||||||
|
on-keydown='_keyDown'
|
||||||
|
></paper-input>
|
||||||
|
<paper-input
|
||||||
|
label='Password'
|
||||||
|
value='{{_password}}'
|
||||||
|
type='password'
|
||||||
|
on-keydown='_keyDown'
|
||||||
|
></paper-input>
|
||||||
|
<div class='error' hidden$='[[!error]]'>[[error]]</div>
|
||||||
|
</div>
|
||||||
|
<div class='card-actions'>
|
||||||
|
<ha-progress-button
|
||||||
|
on-tap='_handleLogin'
|
||||||
|
progress='[[_requestInProgress]]'
|
||||||
|
>Sign in</ha-progress-button>
|
||||||
|
<button
|
||||||
|
class='link'
|
||||||
|
hidden='[[_requestInProgress]]'
|
||||||
|
on-click='_handleForgotPassword'
|
||||||
|
>forgot password?</button>
|
||||||
|
</div>
|
||||||
|
</paper-card>
|
||||||
|
|
||||||
|
<paper-card>
|
||||||
|
<paper-item on-tap='_handleRegister'>
|
||||||
|
<paper-item-body two-line>
|
||||||
|
Create Account
|
||||||
|
<div secondary>It is free and allows easy integration with voice assistants.</div>
|
||||||
|
</paper-item-body>
|
||||||
|
<iron-icon icon='mdi:chevron-right'></iron-icon>
|
||||||
|
</paper-item>
|
||||||
|
</paper-card>
|
||||||
|
</ha-config-section>
|
||||||
</div>
|
</div>
|
||||||
<div class='card-actions'>
|
</hass-subpage>
|
||||||
<paper-button
|
|
||||||
on-tap='_handleLogin'
|
|
||||||
>Sign in</paper-button>
|
|
||||||
</div>
|
|
||||||
</paper-card>
|
|
||||||
</template>
|
</template>
|
||||||
</dom-module>
|
</dom-module>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
class HaConfigCloudLogin extends window.hassMixins.EventsMixin(Polymer.Element) {
|
class HaConfigCloudLogin extends
|
||||||
|
window.hassMixins.NavigateMixin(
|
||||||
|
window.hassMixins.EventsMixin(Polymer.Element)) {
|
||||||
static get is() { return 'ha-config-cloud-login'; }
|
static get is() { return 'ha-config-cloud-login'; }
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
username: String,
|
isWide: Boolean,
|
||||||
password: String,
|
email: {
|
||||||
|
type: String,
|
||||||
|
notify: true,
|
||||||
|
},
|
||||||
|
_password: {
|
||||||
|
type: String,
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
_requestInProgress: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static get observers() {
|
static get observers() {
|
||||||
return [
|
return [
|
||||||
'_inputChanged(username, password)'
|
'_inputChanged(email, _password)'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,19 +143,49 @@ class HaConfigCloudLogin extends window.hassMixins.EventsMixin(Polymer.Element)
|
|||||||
}
|
}
|
||||||
|
|
||||||
_handleLogin() {
|
_handleLogin() {
|
||||||
|
if (!this.email) {
|
||||||
|
this.error = 'Email is required.';
|
||||||
|
} else if (!this._password) {
|
||||||
|
this.error = 'Password is required.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.error) return;
|
||||||
|
|
||||||
|
this._requestInProgress = true;
|
||||||
|
|
||||||
this.hass.callApi('post', 'cloud/login', {
|
this.hass.callApi('post', 'cloud/login', {
|
||||||
username: this.username,
|
email: this.email,
|
||||||
password: this.password,
|
password: this._password,
|
||||||
}).then(
|
}).then(
|
||||||
(account) => {
|
(account) => {
|
||||||
this.fire('ha-account-refreshed', { account: account });
|
this.fire('ha-account-refreshed', { account: account });
|
||||||
this.username = '';
|
this.email = '';
|
||||||
this.password = '';
|
this._password = '';
|
||||||
}, () => {
|
}, (err) => {
|
||||||
this.password = '';
|
this._password = '';
|
||||||
this.error = true;
|
this._requestInProgress = false;
|
||||||
|
if (!err || !err.body || !err.body.message) {
|
||||||
|
this.error = 'Unknown error';
|
||||||
|
return;
|
||||||
|
} else if (err.body.code === 'UserNotConfirmed') {
|
||||||
|
alert('You need to confirm your email before logging in.');
|
||||||
|
this.navigate('/config/cloud/register#confirm');
|
||||||
|
return;
|
||||||
|
} else if (err.body.code === 'PasswordChangeRequired') {
|
||||||
|
alert('You need to change your password before logging in.');
|
||||||
|
this.navigate('/config/cloud/forgot-password');
|
||||||
|
}
|
||||||
|
this.error = err.body.message;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_handleRegister() {
|
||||||
|
this.navigate('/config/cloud/register');
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleForgotPassword() {
|
||||||
|
this.navigate('/config/cloud/forgot-password');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define(HaConfigCloudLogin.is, HaConfigCloudLogin);
|
customElements.define(HaConfigCloudLogin.is, HaConfigCloudLogin);
|
||||||
|
252
panels/config/cloud/ha-config-cloud-register.html
Normal file
252
panels/config/cloud/ha-config-cloud-register.html
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
<link rel="import" href='../../../bower_components/polymer/polymer-element.html'>
|
||||||
|
<link rel="import" href='../../../bower_components/paper-card/paper-card.html'>
|
||||||
|
<link rel="import" href='../../../bower_components/paper-button/paper-button.html'>
|
||||||
|
<link rel="import" href='../../../bower_components/paper-input/paper-input.html'>
|
||||||
|
|
||||||
|
<link rel="import" href="../../../src/layouts/hass-subpage.html">
|
||||||
|
<link rel="import" href="../../../src/util/hass-mixins.html">
|
||||||
|
<link rel="import" href='../../../src/resources/ha-style.html'>
|
||||||
|
<link rel="import" href='../../../src/components/buttons/ha-progress-button.html'>
|
||||||
|
|
||||||
|
<dom-module id="ha-config-cloud-register">
|
||||||
|
<template>
|
||||||
|
<style include="iron-flex ha-style">
|
||||||
|
paper-card {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
paper-item {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
paper-card:last-child {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
@apply(--paper-font-headline);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: var(--google-red-500);
|
||||||
|
}
|
||||||
|
.card-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<hass-subpage title="Register Account">
|
||||||
|
<div class='content'>
|
||||||
|
<ha-config-section
|
||||||
|
is-wide='[[isWide]]'
|
||||||
|
>
|
||||||
|
<span slot='header'>Register with the Home Assistant Cloud</span>
|
||||||
|
<span slot='introduction'>
|
||||||
|
Register today to easily connect your Home Assistant to cloud-only services.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
By registering an account you agree to the following terms and conditions.
|
||||||
|
<ul>
|
||||||
|
<li><a href='#'>Terms and Conditions</a></li>
|
||||||
|
<li><a href='#'>Privacy Policy</a></li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><i>
|
||||||
|
Home Assistant will never share information with our cloud without your prior permission.
|
||||||
|
</i></p>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<template is='dom-if' if='[[!_hasConfirmationCode]]'>
|
||||||
|
<paper-card>
|
||||||
|
<div class='card-content'>
|
||||||
|
<div class='header'>
|
||||||
|
<h1>Register</h1>
|
||||||
|
<div class='error' hidden$='[[!_error]]'>[[_error]]</div>
|
||||||
|
</div>
|
||||||
|
<paper-input
|
||||||
|
autofocus
|
||||||
|
label='Email address'
|
||||||
|
type='email'
|
||||||
|
value='{{email}}'
|
||||||
|
on-keydown='_keyDown'
|
||||||
|
></paper-input>
|
||||||
|
<paper-input
|
||||||
|
label='Password'
|
||||||
|
value='{{_password}}'
|
||||||
|
type='password'
|
||||||
|
on-keydown='_keyDown'
|
||||||
|
></paper-input>
|
||||||
|
</div>
|
||||||
|
<div class='card-actions'>
|
||||||
|
<ha-progress-button
|
||||||
|
on-tap='_handleRegister'
|
||||||
|
progress='[[_requestInProgress]]'
|
||||||
|
>Create Account</ha-progress-button>
|
||||||
|
<button
|
||||||
|
class='link'
|
||||||
|
hidden='[[_requestInProgress]]'
|
||||||
|
on-click='_handleShowVerifyAccount'
|
||||||
|
>have confirmation code?</button>
|
||||||
|
</div>
|
||||||
|
</paper-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template is='dom-if' if='[[_hasConfirmationCode]]'>
|
||||||
|
<paper-card>
|
||||||
|
<div class='card-content'>
|
||||||
|
<div class='header'>
|
||||||
|
<h1>Verify email</h1>
|
||||||
|
<div class='error' hidden$='[[!_error]]'>[[_error]]</div>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
Check your email address, we've emailed you a verification code to activate your account.
|
||||||
|
</p>
|
||||||
|
<template is='dom-if' if='[[_showEmailInputForConfirmation]]'>
|
||||||
|
<paper-input
|
||||||
|
label='Email address'
|
||||||
|
type='email'
|
||||||
|
value='{{email}}'
|
||||||
|
on-keydown='_keyDown'
|
||||||
|
></paper-input>
|
||||||
|
</template>
|
||||||
|
<paper-input
|
||||||
|
label='Confirmation code'
|
||||||
|
value='{{_confirmationCode}}'
|
||||||
|
on-keydown='_keyDown'
|
||||||
|
type='number'
|
||||||
|
></paper-input>
|
||||||
|
</div>
|
||||||
|
<div class='card-actions'>
|
||||||
|
<ha-progress-button
|
||||||
|
on-tap='_handleVerifyEmail'
|
||||||
|
progress='[[_requestInProgress]]'
|
||||||
|
>Verify Email</ha-progress-button>
|
||||||
|
</div>
|
||||||
|
</paper-card>
|
||||||
|
</template>
|
||||||
|
</ha-config-section>
|
||||||
|
</div>
|
||||||
|
</hass-subpage>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
class HaConfigCloudRegister extends
|
||||||
|
window.hassMixins.NavigateMixin(
|
||||||
|
window.hassMixins.EventsMixin(Polymer.Element)) {
|
||||||
|
static get is() { return 'ha-config-cloud-register'; }
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
hass: Object,
|
||||||
|
isWide: Boolean,
|
||||||
|
email: {
|
||||||
|
type: String,
|
||||||
|
notify: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
_requestInProgress: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
_password: {
|
||||||
|
type: String,
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
_showEmailInputForConfirmation: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
_hasConfirmationCode: {
|
||||||
|
type: Boolean,
|
||||||
|
value: () => document.location.hash === '#confirm'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static get observers() {
|
||||||
|
return [
|
||||||
|
'_inputChanged(email, _password)'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
_inputChanged() {
|
||||||
|
this._error = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_keyDown(ev) {
|
||||||
|
// validate on enter
|
||||||
|
if (ev.keyCode === 13) {
|
||||||
|
if (this._hasConfirmationCode) {
|
||||||
|
this._handleVerifyEmail();
|
||||||
|
} else {
|
||||||
|
this._handleRegister();
|
||||||
|
}
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleRegister() {
|
||||||
|
if (!this.email) {
|
||||||
|
this._error = 'Email is required.';
|
||||||
|
} else if (!this._password) {
|
||||||
|
this._error = 'Password is required.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._error) return;
|
||||||
|
|
||||||
|
this._requestInProgress = true;
|
||||||
|
|
||||||
|
this.hass.callApi('post', 'cloud/register', {
|
||||||
|
email: this.email,
|
||||||
|
password: this._password,
|
||||||
|
}).then(
|
||||||
|
() => {
|
||||||
|
this._requestInProgress = false;
|
||||||
|
this._hasConfirmationCode = true;
|
||||||
|
}, (err) => {
|
||||||
|
this._password = '';
|
||||||
|
this._requestInProgress = false;
|
||||||
|
this._error = err && err.body && err.body.message ?
|
||||||
|
err.body.message : 'Unknown error';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleShowVerifyAccount() {
|
||||||
|
this._error = '';
|
||||||
|
this._showEmailInputForConfirmation = true;
|
||||||
|
this._hasConfirmationCode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleVerifyEmail() {
|
||||||
|
if (!this.email) {
|
||||||
|
this._error = 'Email is required.';
|
||||||
|
} else if (!this._confirmationCode) {
|
||||||
|
this._error = 'Confirmation code is required.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._error) return;
|
||||||
|
|
||||||
|
this._requestInProgress = true;
|
||||||
|
|
||||||
|
this.hass.callApi('post', 'cloud/confirm_register', {
|
||||||
|
email: this.email,
|
||||||
|
confirmation_code: this._confirmationCode,
|
||||||
|
}).then(
|
||||||
|
() => {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
alert('Confirmation successful. You can now login.');
|
||||||
|
this.navigate('config/cloud/login');
|
||||||
|
}, (err) => {
|
||||||
|
this._confirmationCode = '';
|
||||||
|
this._error = err && err.body && err.body.message ?
|
||||||
|
err.body.message : 'Unknown error';
|
||||||
|
this._requestInProgress = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define(HaConfigCloudRegister.is, HaConfigCloudRegister);
|
||||||
|
</script>
|
@ -1,66 +1,68 @@
|
|||||||
<link rel="import" href='../../../bower_components/polymer/polymer-element.html'>
|
<link rel="import" href='../../../bower_components/polymer/polymer-element.html'>
|
||||||
<link rel="import" href="../../../bower_components/app-layout/app-header-layout/app-header-layout.html">
|
<link rel='import' href='../../../bower_components/app-route/app-route.html'>
|
||||||
<link rel="import" href="../../../bower_components/app-layout/app-header/app-header.html">
|
|
||||||
<link rel="import" href="../../../bower_components/app-layout/app-toolbar/app-toolbar.html">
|
|
||||||
<link rel="import" href="../../../bower_components/paper-icon-button/paper-icon-button.html">
|
|
||||||
|
|
||||||
<link rel="import" href="../ha-config-section.html">
|
<link rel="import" href="../ha-config-section.html">
|
||||||
|
|
||||||
<link rel="import" href="./ha-config-cloud-login.html">
|
<link rel="import" href="./ha-config-cloud-login.html">
|
||||||
|
<link rel="import" href="./ha-config-cloud-register.html">
|
||||||
|
<link rel="import" href="./ha-config-cloud-forgot-password.html">
|
||||||
<link rel="import" href="./ha-config-cloud-account.html">
|
<link rel="import" href="./ha-config-cloud-account.html">
|
||||||
|
|
||||||
<dom-module id="ha-config-cloud">
|
<dom-module id="ha-config-cloud">
|
||||||
<template>
|
<template>
|
||||||
<style include="iron-flex ha-style">
|
<style>
|
||||||
.content {
|
iron-pages {
|
||||||
padding-bottom: 32px;
|
height: 100%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<app-route
|
||||||
|
route='[[route]]'
|
||||||
|
pattern='/:page'
|
||||||
|
data="{{_routeData}}"
|
||||||
|
tail="{{_routeTail}}"
|
||||||
|
></app-route>
|
||||||
|
|
||||||
<app-header-layout has-scrolling-region>
|
<template is='dom-if' if='[[account]]' restamp>
|
||||||
<app-header slot="header" fixed>
|
<ha-config-cloud-account
|
||||||
<app-toolbar>
|
hass='[[hass]]'
|
||||||
<paper-icon-button
|
account='[[account]]'
|
||||||
icon='mdi:arrow-left'
|
is-wide='[[isWide]]'
|
||||||
on-tap='_backTapped'
|
></ha-config-cloud-account>
|
||||||
></paper-icon-button>
|
</template>
|
||||||
<div main-title>Cloud</div>
|
|
||||||
</app-toolbar>
|
|
||||||
</app-header>
|
|
||||||
|
|
||||||
<div class$='[[computeClasses(isWide)]]'>
|
<template is='dom-if' if='[[!account]]' restamp>
|
||||||
<ha-config-section
|
<template is='dom-if' if='[[_isLoginPage(_routeData.page)]]' restamp>
|
||||||
|
<ha-config-cloud-login
|
||||||
|
page-name='login'
|
||||||
|
hass='[[hass]]'
|
||||||
is-wide='[[isWide]]'
|
is-wide='[[isWide]]'
|
||||||
>
|
email='{{_loginEmail}}'
|
||||||
<span slot='header'>Home Assistant Cloud</span>
|
></ha-config-cloud-login>
|
||||||
<span slot='introduction'>
|
</template>
|
||||||
The Home Assistant Cloud allows you to opt-in to functions that will bring your Home Assistant experience to the next level.
|
|
||||||
|
|
||||||
<p><i>
|
<template is='dom-if' if='[[_isRegisterPage(_routeData.page)]]' restamp>
|
||||||
Home Assistant will never share information with our cloud without your prior permission.
|
<ha-config-cloud-register
|
||||||
</i></p>
|
page-name='register'
|
||||||
</span>
|
hass='[[hass]]'
|
||||||
|
is-wide='[[isWide]]'
|
||||||
|
email='{{_loginEmail}}'
|
||||||
|
></ha-config-cloud-register>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template is='dom-if' if='[[account]]'>
|
<template is='dom-if' if='[[_isForgotPasswordPage(_routeData.page)]]' restamp>
|
||||||
<ha-config-cloud-account
|
<ha-config-cloud-forgot-password
|
||||||
hass='[[hass]]'
|
page-name='forgot-password'
|
||||||
account='[[account]]'
|
hass='[[hass]]'
|
||||||
></ha-config-cloud-account>
|
is-wide='[[isWide]]'
|
||||||
</template>
|
email='{{_loginEmail}}'
|
||||||
|
></ha-config-cloud-forgot-password>
|
||||||
<template is='dom-if' if='[[!account]]'>
|
</template>
|
||||||
<ha-config-cloud-login
|
</template>
|
||||||
hass='[[hass]]'
|
|
||||||
></ha-config-cloud-login>
|
|
||||||
</template>
|
|
||||||
</ha-config-section>
|
|
||||||
</div>
|
|
||||||
</app-header-layout>
|
|
||||||
</template>
|
</template>
|
||||||
</dom-module>
|
</dom-module>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
class HaConfigCloud extends Polymer.Element {
|
class HaConfigCloud extends window.hassMixins.NavigateMixin(Polymer.Element) {
|
||||||
static get is() { return 'ha-config-cloud'; }
|
static get is() { return 'ha-config-cloud'; }
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@ -75,16 +77,35 @@ class HaConfigCloud extends Polymer.Element {
|
|||||||
type: Object,
|
type: Object,
|
||||||
value: null,
|
value: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
route: Object,
|
||||||
|
|
||||||
|
_routeData: Object,
|
||||||
|
_routeTail: Object,
|
||||||
|
_loginEmail: String,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
computeClasses(isWide) {
|
static get observers() {
|
||||||
return isWide ? 'content' : 'content narrow';
|
return [
|
||||||
|
'_checkRoute(route, account)'
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
_backTapped() {
|
_checkRoute(route, account) {
|
||||||
history.back();
|
if (!route || route.prefix !== '/config/cloud') return;
|
||||||
|
|
||||||
|
if (!account && ['/forgot-password', '/register'].indexOf(route.path) === -1) {
|
||||||
|
this.navigate('/config/cloud/login', true);
|
||||||
|
} else if (account &&
|
||||||
|
['/login', '/register', '/forgot-password'].indexOf(route.path) !== -1) {
|
||||||
|
this.navigate('/config/cloud/account', true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_isRegisterPage(page) { return page === 'register'; }
|
||||||
|
_isForgotPasswordPage(page) { return page === 'forgot-password'; }
|
||||||
|
_isLoginPage(page) { return page === 'login'; }
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define(HaConfigCloud.is, HaConfigCloud);
|
customElements.define(HaConfigCloud.is, HaConfigCloud);
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<paper-item-body two-line>
|
<paper-item-body two-line>
|
||||||
Home Assistant Cloud
|
Home Assistant Cloud
|
||||||
<template is='dom-if' if='[[account]]'>
|
<template is='dom-if' if='[[account]]'>
|
||||||
<div secondary>Logged in as [[account.first_name]] [[account.last_name]]</div>
|
<div secondary>Logged in as [[account.email]]</div>
|
||||||
</template>
|
</template>
|
||||||
<template is='dom-if' if='[[!account]]'>
|
<template is='dom-if' if='[[!account]]'>
|
||||||
<div secondary>Not logged in</div>
|
<div secondary>Not logged in</div>
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
</app-header>
|
</app-header>
|
||||||
|
|
||||||
<div class$='[[computeClasses(isWide)]]'>
|
<div class='content'>
|
||||||
<ha-config-section
|
<ha-config-section
|
||||||
is-wide='[[isWide]]'
|
is-wide='[[isWide]]'
|
||||||
>
|
>
|
||||||
@ -58,17 +58,12 @@ class HaConfigDashboard extends Polymer.Element {
|
|||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
isWide: Boolean,
|
isWide: Boolean,
|
||||||
account: {
|
account: Object,
|
||||||
type: Object,
|
narrow: Boolean,
|
||||||
value: null,
|
showMenu: Boolean,
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
computeClasses(isWide) {
|
|
||||||
return isWide ? 'content' : 'content narrow';
|
|
||||||
}
|
|
||||||
|
|
||||||
computeIsCloudLoaded(hass) {
|
computeIsCloudLoaded(hass) {
|
||||||
return window.hassUtil.isComponentLoaded(hass, 'cloud');
|
return window.hassUtil.isComponentLoaded(hass, 'cloud');
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
|
|
||||||
<ha-config-cloud
|
<ha-config-cloud
|
||||||
page-name='cloud'
|
page-name='cloud'
|
||||||
|
route='[[_routeTail]]'
|
||||||
hass='[[hass]]'
|
hass='[[hass]]'
|
||||||
is-wide='[[isWide]]'
|
is-wide='[[isWide]]'
|
||||||
account='[[account]]'
|
account='[[account]]'
|
||||||
@ -57,6 +58,8 @@
|
|||||||
hass='[[hass]]'
|
hass='[[hass]]'
|
||||||
is-wide='[[isWide]]'
|
is-wide='[[isWide]]'
|
||||||
account='[[account]]'
|
account='[[account]]'
|
||||||
|
narrow='[[narrow]]'
|
||||||
|
show-menu='[[showMenu]]'
|
||||||
></ha-config-dashboard>
|
></ha-config-dashboard>
|
||||||
|
|
||||||
<ha-config-automation
|
<ha-config-automation
|
||||||
@ -103,7 +106,10 @@ class HaPanelConfig extends window.hassMixins.EventsMixin(Polymer.Element) {
|
|||||||
hass: Object,
|
hass: Object,
|
||||||
narrow: Boolean,
|
narrow: Boolean,
|
||||||
showMenu: Boolean,
|
showMenu: Boolean,
|
||||||
account: Object,
|
account: {
|
||||||
|
type: Object,
|
||||||
|
value: null,
|
||||||
|
},
|
||||||
|
|
||||||
route: {
|
route: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -71,19 +71,6 @@ Polymer({
|
|||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
domain: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
service: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
serviceData: {
|
|
||||||
type: Object,
|
|
||||||
value: {},
|
|
||||||
},
|
|
||||||
|
|
||||||
disabled: {
|
disabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
|
42
src/layouts/hass-subpage.html
Normal file
42
src/layouts/hass-subpage.html
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<link rel="import" href='../../bower_components/polymer/polymer-element.html'>
|
||||||
|
<link rel="import" href="../../bower_components/app-layout/app-header-layout/app-header-layout.html">
|
||||||
|
<link rel="import" href="../../bower_components/app-layout/app-header/app-header.html">
|
||||||
|
<link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.html">
|
||||||
|
<link rel="import" href="../../bower_components/paper-icon-button/paper-icon-button.html">
|
||||||
|
|
||||||
|
<dom-module id="hass-subpage">
|
||||||
|
<template>
|
||||||
|
<style include="ha-style"></style>
|
||||||
|
<app-header-layout has-scrolling-region>
|
||||||
|
<app-header slot="header" fixed>
|
||||||
|
<app-toolbar>
|
||||||
|
<paper-icon-button
|
||||||
|
icon='mdi:arrow-left'
|
||||||
|
on-tap='_backTapped'
|
||||||
|
></paper-icon-button>
|
||||||
|
<div main-title>[[title]]</div>
|
||||||
|
</app-toolbar>
|
||||||
|
</app-header>
|
||||||
|
|
||||||
|
<slot></slot>
|
||||||
|
</app-header-layout>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
class HassSubpage extends Polymer.Element {
|
||||||
|
static get is() { return 'hass-subpage'; }
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
title: String
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_backTapped() {
|
||||||
|
history.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define(HassSubpage.is, HassSubpage);
|
||||||
|
</script>
|
@ -103,7 +103,19 @@
|
|||||||
@apply(--paper-font-title);
|
@apply(--paper-font-title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.link {
|
||||||
|
background: none;
|
||||||
|
color: inherit;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
font: inherit;
|
||||||
|
text-align: left;
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.card-actions paper-button:not([disabled]),
|
.card-actions paper-button:not([disabled]),
|
||||||
|
.card-actions ha-progress-button:not([disabled]),
|
||||||
.card-actions ha-call-api-button:not([disabled]),
|
.card-actions ha-call-api-button:not([disabled]),
|
||||||
.card-actions ha-call-service-button:not([disabled]) {
|
.card-actions ha-call-service-button:not([disabled]) {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
|
@ -70,8 +70,12 @@ window.hassMixins.EventsMixin = Polymer.dedupingMixin(
|
|||||||
/* @polymerMixin */
|
/* @polymerMixin */
|
||||||
window.hassMixins.NavigateMixin = Polymer.dedupingMixin(
|
window.hassMixins.NavigateMixin = Polymer.dedupingMixin(
|
||||||
superClass => class extends window.hassMixins.EventsMixin(superClass) {
|
superClass => class extends window.hassMixins.EventsMixin(superClass) {
|
||||||
navigate(path) {
|
navigate(path, replace = false) {
|
||||||
history.pushState(null, null, path);
|
if (replace) {
|
||||||
|
history.replaceState(null, null, path);
|
||||||
|
} else {
|
||||||
|
history.pushState(null, null, path);
|
||||||
|
}
|
||||||
this.fire('location-changed');
|
this.fire('location-changed');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user