mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-12 20:06:33 +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-multi-assign": 0,
|
||||
"radix": 0,
|
||||
"no-alert": 0,
|
||||
"import/prefer-default-export": 0,
|
||||
"react/jsx-no-bind": [2, { "ignoreRefs": true }],
|
||||
"react/jsx-no-duplicate-props": 2,
|
||||
|
@ -1,8 +1,8 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
cache:
|
||||
yarn: true
|
||||
directories:
|
||||
- node_modules
|
||||
- bower_components
|
||||
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-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/resources/ha-style.html'>
|
||||
|
||||
<dom-module id="ha-config-cloud-account">
|
||||
<template>
|
||||
<style include="iron-flex ha-style">
|
||||
.content {
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
paper-card {
|
||||
display: block;
|
||||
}
|
||||
@ -25,20 +29,36 @@
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
<paper-card>
|
||||
<div class='account'>
|
||||
<paper-item-body two-line>
|
||||
[[account.first_name]] [[account.last_name]]
|
||||
<div secondary>[[account.email]]</div>
|
||||
</paper-item-body>
|
||||
<paper-button
|
||||
class='warning'
|
||||
on-tap='handleLogout'
|
||||
>Sign out</paper-button>
|
||||
</div>
|
||||
</paper-card>
|
||||
<hass-subpage title='Cloud Account'>
|
||||
<div class='content'>
|
||||
<ha-config-section
|
||||
is-wide='[[isWide]]'
|
||||
>
|
||||
<span slot='header'>Home Assistant Cloud</span>
|
||||
<span slot='introduction'>
|
||||
The Home Assistant Cloud allows you to opt-in to functions that will bring your Home Assistant experience to the next level.
|
||||
|
||||
<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>
|
||||
</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-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-login">
|
||||
<template>
|
||||
<style include="iron-flex ha-style">
|
||||
.content {
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
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>
|
||||
<paper-card>
|
||||
<div class='card-content'>
|
||||
<h1>Sign In</h1>
|
||||
<paper-input
|
||||
label='Username'
|
||||
value='{{username}}'
|
||||
error-message='Failed to login'
|
||||
invalid='[[error]]'
|
||||
on-keydown='_keyDown'
|
||||
></paper-input>
|
||||
<paper-input
|
||||
label='Password'
|
||||
value='{{password}}'
|
||||
type='password'
|
||||
on-keydown='_keyDown'
|
||||
></paper-input>
|
||||
<hass-subpage title='Cloud Login'>
|
||||
<div class='content'>
|
||||
<ha-config-section
|
||||
is-wide='[[isWide]]'
|
||||
>
|
||||
<span slot='header'>Home Assistant Cloud</span>
|
||||
<span slot='introduction'>
|
||||
The Home Assistant Cloud allows you to opt-in to functions that will bring your Home Assistant experience to the next level.
|
||||
|
||||
<p><i>
|
||||
Home Assistant will never share information with our cloud without your prior permission.
|
||||
</i></p>
|
||||
</span>
|
||||
|
||||
<paper-card>
|
||||
<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 class='card-actions'>
|
||||
<paper-button
|
||||
on-tap='_handleLogin'
|
||||
>Sign in</paper-button>
|
||||
</div>
|
||||
</paper-card>
|
||||
</hass-subpage>
|
||||
</template>
|
||||
</dom-module>
|
||||
|
||||
<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 properties() {
|
||||
return {
|
||||
hass: Object,
|
||||
username: String,
|
||||
password: String,
|
||||
isWide: Boolean,
|
||||
email: {
|
||||
type: String,
|
||||
notify: true,
|
||||
},
|
||||
_password: {
|
||||
type: String,
|
||||
value: '',
|
||||
},
|
||||
_requestInProgress: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
static get observers() {
|
||||
return [
|
||||
'_inputChanged(username, password)'
|
||||
'_inputChanged(email, _password)'
|
||||
];
|
||||
}
|
||||
|
||||
@ -74,19 +143,49 @@ class HaConfigCloudLogin extends window.hassMixins.EventsMixin(Polymer.Element)
|
||||
}
|
||||
|
||||
_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', {
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
email: this.email,
|
||||
password: this._password,
|
||||
}).then(
|
||||
(account) => {
|
||||
this.fire('ha-account-refreshed', { account: account });
|
||||
this.username = '';
|
||||
this.password = '';
|
||||
}, () => {
|
||||
this.password = '';
|
||||
this.error = true;
|
||||
this.email = '';
|
||||
this._password = '';
|
||||
}, (err) => {
|
||||
this._password = '';
|
||||
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);
|
||||
|
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/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">
|
||||
<link rel='import' href='../../../bower_components/app-route/app-route.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-register.html">
|
||||
<link rel="import" href="./ha-config-cloud-forgot-password.html">
|
||||
<link rel="import" href="./ha-config-cloud-account.html">
|
||||
|
||||
<dom-module id="ha-config-cloud">
|
||||
<template>
|
||||
<style include="iron-flex ha-style">
|
||||
.content {
|
||||
padding-bottom: 32px;
|
||||
<style>
|
||||
iron-pages {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<app-route
|
||||
route='[[route]]'
|
||||
pattern='/:page'
|
||||
data="{{_routeData}}"
|
||||
tail="{{_routeTail}}"
|
||||
></app-route>
|
||||
|
||||
<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>Cloud</div>
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
<template is='dom-if' if='[[account]]' restamp>
|
||||
<ha-config-cloud-account
|
||||
hass='[[hass]]'
|
||||
account='[[account]]'
|
||||
is-wide='[[isWide]]'
|
||||
></ha-config-cloud-account>
|
||||
</template>
|
||||
|
||||
<div class$='[[computeClasses(isWide)]]'>
|
||||
<ha-config-section
|
||||
<template is='dom-if' if='[[!account]]' restamp>
|
||||
<template is='dom-if' if='[[_isLoginPage(_routeData.page)]]' restamp>
|
||||
<ha-config-cloud-login
|
||||
page-name='login'
|
||||
hass='[[hass]]'
|
||||
is-wide='[[isWide]]'
|
||||
>
|
||||
<span slot='header'>Home Assistant Cloud</span>
|
||||
<span slot='introduction'>
|
||||
The Home Assistant Cloud allows you to opt-in to functions that will bring your Home Assistant experience to the next level.
|
||||
email='{{_loginEmail}}'
|
||||
></ha-config-cloud-login>
|
||||
</template>
|
||||
|
||||
<p><i>
|
||||
Home Assistant will never share information with our cloud without your prior permission.
|
||||
</i></p>
|
||||
</span>
|
||||
<template is='dom-if' if='[[_isRegisterPage(_routeData.page)]]' restamp>
|
||||
<ha-config-cloud-register
|
||||
page-name='register'
|
||||
hass='[[hass]]'
|
||||
is-wide='[[isWide]]'
|
||||
email='{{_loginEmail}}'
|
||||
></ha-config-cloud-register>
|
||||
</template>
|
||||
|
||||
<template is='dom-if' if='[[account]]'>
|
||||
<ha-config-cloud-account
|
||||
hass='[[hass]]'
|
||||
account='[[account]]'
|
||||
></ha-config-cloud-account>
|
||||
</template>
|
||||
|
||||
<template is='dom-if' if='[[!account]]'>
|
||||
<ha-config-cloud-login
|
||||
hass='[[hass]]'
|
||||
></ha-config-cloud-login>
|
||||
</template>
|
||||
</ha-config-section>
|
||||
</div>
|
||||
</app-header-layout>
|
||||
<template is='dom-if' if='[[_isForgotPasswordPage(_routeData.page)]]' restamp>
|
||||
<ha-config-cloud-forgot-password
|
||||
page-name='forgot-password'
|
||||
hass='[[hass]]'
|
||||
is-wide='[[isWide]]'
|
||||
email='{{_loginEmail}}'
|
||||
></ha-config-cloud-forgot-password>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
class HaConfigCloud extends Polymer.Element {
|
||||
class HaConfigCloud extends window.hassMixins.NavigateMixin(Polymer.Element) {
|
||||
static get is() { return 'ha-config-cloud'; }
|
||||
|
||||
static get properties() {
|
||||
@ -75,16 +77,35 @@ class HaConfigCloud extends Polymer.Element {
|
||||
type: Object,
|
||||
value: null,
|
||||
},
|
||||
|
||||
route: Object,
|
||||
|
||||
_routeData: Object,
|
||||
_routeTail: Object,
|
||||
_loginEmail: String,
|
||||
};
|
||||
}
|
||||
|
||||
computeClasses(isWide) {
|
||||
return isWide ? 'content' : 'content narrow';
|
||||
static get observers() {
|
||||
return [
|
||||
'_checkRoute(route, account)'
|
||||
];
|
||||
}
|
||||
|
||||
_backTapped() {
|
||||
history.back();
|
||||
_checkRoute(route, account) {
|
||||
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);
|
||||
|
@ -21,7 +21,7 @@
|
||||
<paper-item-body two-line>
|
||||
Home Assistant Cloud
|
||||
<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 is='dom-if' if='[[!account]]'>
|
||||
<div secondary>Not logged in</div>
|
||||
|
@ -25,7 +25,7 @@
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
|
||||
<div class$='[[computeClasses(isWide)]]'>
|
||||
<div class='content'>
|
||||
<ha-config-section
|
||||
is-wide='[[isWide]]'
|
||||
>
|
||||
@ -58,17 +58,12 @@ class HaConfigDashboard extends Polymer.Element {
|
||||
return {
|
||||
hass: Object,
|
||||
isWide: Boolean,
|
||||
account: {
|
||||
type: Object,
|
||||
value: null,
|
||||
}
|
||||
account: Object,
|
||||
narrow: Boolean,
|
||||
showMenu: Boolean,
|
||||
};
|
||||
}
|
||||
|
||||
computeClasses(isWide) {
|
||||
return isWide ? 'content' : 'content narrow';
|
||||
}
|
||||
|
||||
computeIsCloudLoaded(hass) {
|
||||
return window.hassUtil.isComponentLoaded(hass, 'cloud');
|
||||
}
|
||||
|
@ -47,6 +47,7 @@
|
||||
|
||||
<ha-config-cloud
|
||||
page-name='cloud'
|
||||
route='[[_routeTail]]'
|
||||
hass='[[hass]]'
|
||||
is-wide='[[isWide]]'
|
||||
account='[[account]]'
|
||||
@ -57,6 +58,8 @@
|
||||
hass='[[hass]]'
|
||||
is-wide='[[isWide]]'
|
||||
account='[[account]]'
|
||||
narrow='[[narrow]]'
|
||||
show-menu='[[showMenu]]'
|
||||
></ha-config-dashboard>
|
||||
|
||||
<ha-config-automation
|
||||
@ -103,7 +106,10 @@ class HaPanelConfig extends window.hassMixins.EventsMixin(Polymer.Element) {
|
||||
hass: Object,
|
||||
narrow: Boolean,
|
||||
showMenu: Boolean,
|
||||
account: Object,
|
||||
account: {
|
||||
type: Object,
|
||||
value: null,
|
||||
},
|
||||
|
||||
route: {
|
||||
type: Object,
|
||||
|
@ -71,19 +71,6 @@ Polymer({
|
||||
value: false,
|
||||
},
|
||||
|
||||
domain: {
|
||||
type: String,
|
||||
},
|
||||
|
||||
service: {
|
||||
type: String,
|
||||
},
|
||||
|
||||
serviceData: {
|
||||
type: Object,
|
||||
value: {},
|
||||
},
|
||||
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
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);
|
||||
}
|
||||
|
||||
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 ha-progress-button:not([disabled]),
|
||||
.card-actions ha-call-api-button:not([disabled]),
|
||||
.card-actions ha-call-service-button:not([disabled]) {
|
||||
color: var(--primary-color);
|
||||
|
@ -70,8 +70,12 @@ window.hassMixins.EventsMixin = Polymer.dedupingMixin(
|
||||
/* @polymerMixin */
|
||||
window.hassMixins.NavigateMixin = Polymer.dedupingMixin(
|
||||
superClass => class extends window.hassMixins.EventsMixin(superClass) {
|
||||
navigate(path) {
|
||||
history.pushState(null, null, path);
|
||||
navigate(path, replace = false) {
|
||||
if (replace) {
|
||||
history.replaceState(null, null, path);
|
||||
} else {
|
||||
history.pushState(null, null, path);
|
||||
}
|
||||
this.fire('location-changed');
|
||||
}
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user