Simplify the cloud screens (#967)

* Simplify the cloud

* lint

* Simplify registration flash message text

* Address comments

* Update title
This commit is contained in:
Paulus Schoutsen 2018-03-05 13:42:56 -08:00 committed by GitHub
parent 710139bf4b
commit bec9162198
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 226 additions and 302 deletions

View File

@ -1,6 +1,5 @@
<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">
@ -42,78 +41,37 @@
</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-click='_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-click='_handleConfirmPasswordReset'
progress='[[_requestInProgress]]'
>Reset Password</ha-progress-button>
</div>
</paper-card>
</template>
<paper-card>
<div class='card-content'>
<h1>Forgot your password?</h1>
<p>
Enter your email address and we will send you a link to reset your password.
</p>
<div class='error' hidden$='[[!_error]]'>[[_error]]</div>
<paper-input
autofocus
id='email'
label='E-mail'
value='{{email}}'
type='email'
on-keydown='_keyDown'
error-message='Invalid email'
></paper-input>
</div>
<div class='card-actions'>
<ha-progress-button
on-click='_handleEmailPasswordReset'
progress='[[_requestInProgress]]'
>Send reset email</ha-progress-button>
</div>
</paper-card>
</div>
</hass-subpage>
</template>
</dom-module>
<script>
class HaConfigCloudForgotPassword extends
window.hassMixins.NavigateMixin(window.hassMixins.EventsMixin(Polymer.Element)) {
class HaConfigCloudForgotPassword extends window.hassMixins.EventsMixin(Polymer.Element) {
static get is() { return 'ha-config-cloud-forgot-password'; }
static get properties() {
@ -122,110 +80,53 @@ class HaConfigCloudForgotPassword extends
email: {
type: String,
notify: true,
},
_hasToken: {
type: Boolean,
value: false,
},
_newPassword: {
type: String,
value: '',
},
_confirmationCode: {
type: String,
value: '',
},
_showEmailInputForConfirmation: {
type: Boolean,
value: false,
observer: '_emailChanged'
},
_requestInProgress: {
type: Boolean,
value: false,
},
_error: {
type: String,
value: '',
}
};
}
static get observers() {
return [
'_inputChanged(email, _newPassword)',
];
}
_inputChanged() {
this.error = false;
_emailChanged() {
this._error = '';
this.$.email.invalid = false;
}
_keyDown(ev) {
// validate on enter
if (ev.keyCode === 13) {
if (this._hasToken) {
this._handleConfirmPasswordReset();
} else {
this._handleEmailPasswordReset();
}
this._handleEmailPasswordReset();
ev.preventDefault();
}
}
_handleEmailPasswordReset() {
if (!this.email) {
this.error = 'Email is required.';
if (!this.email || !this.email.includes('@')) {
this.$.email.invalid = true;
}
if (this.error) return;
if (this.$.email.invalid) 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';
});
this.fire('cloud-done', {
flashMessage: 'Check your email for instructions on how to reset your password.'
});
}, err => this.setProperties({
_requestInProgress: false,
_error: err && err.body && err.body.message ?
err.body.message : 'Unknown error',
}));
}
}

View File

@ -1,7 +1,9 @@
<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-icon-button/paper-icon-button.html'>
<link rel="import" href='../../../bower_components/paper-input/paper-input.html'>
<link rel="import" href='../../../bower_components/paper-ripple/paper-ripple.html'>
<link rel="import" href="../../../src/layouts/hass-subpage.html">
<link rel="import" href="../../../src/util/hass-mixins.html">
@ -41,6 +43,15 @@
[hidden] {
display: none;
}
.flash-msg {
padding-right: 44px;
}
.flash-msg paper-icon-button {
position: absolute;
top: 8px;
right: 8px;
color: var(--secondary-text-color);
}
</style>
<hass-subpage title='Cloud Login'>
<div class='content'>
@ -53,23 +64,37 @@
<p><a href='https://home-assistant.io/components/cloud/' target='_blank'>Learn more</a></p>
</span>
<paper-card hidden$='[[!flashMessage]]'>
<div class='card-content flash-msg'>
[[flashMessage]]
<paper-icon-button
icon='mdi:close'
on-click='_dismissFlash'
>Dismiss</paper-icon-button>
<paper-ripple id='flashRipple' noink></paper-ripple>
</div>
</paper-card>
<paper-card>
<div class='card-content'>
<h1>Sign In</h1>
<div class='error' hidden$='[[!_error]]'>[[_error]]</div>
<paper-input
label='Email'
id='emailInput'
id='email'
type='email'
value='{{email}}'
on-keydown='_keyDown'
error-message='Invalid email'
></paper-input>
<paper-input
id='password'
label='Password'
value='{{_password}}'
type='password'
on-keydown='_keyDown'
error-message='Passwords are at least 8 characters'
></paper-input>
<div class='error' hidden$='[[!error]]'>[[error]]</div>
</div>
<div class='card-actions'>
<ha-progress-button
@ -120,6 +145,11 @@ class HaConfigCloudLogin extends
type: Boolean,
value: false,
},
flashMessage: {
type: String,
notify: true,
},
_error: String,
};
}
@ -129,8 +159,20 @@ class HaConfigCloudLogin extends
];
}
connectedCallback() {
super.connectedCallback();
if (this.flashMessage) {
// Wait for DOM to be drawn
requestAnimationFrame(() =>
requestAnimationFrame(() =>
this.$.flashRipple.simulatedRipple()));
}
}
_inputChanged() {
this.error = false;
this.$.email.invalid = false;
this.$.password.invalid = false;
this._error = false;
}
_keyDown(ev) {
@ -142,13 +184,24 @@ class HaConfigCloudLogin extends
}
_handleLogin() {
if (!this.email) {
this.error = 'Email is required.';
} else if (!this._password) {
this.error = 'Password is required.';
let invalid = false;
if (!this.email || !this.email.includes('@')) {
this.$.email.invalid = true;
this.$.email.focus();
invalid = true;
}
if (this.error) return;
if (this._password.length < 8) {
this.$.password.invalid = true;
if (!invalid) {
invalid = true;
this.$.password.focus();
}
}
if (invalid) return;
this._requestInProgress = true;
@ -157,33 +210,49 @@ class HaConfigCloudLogin extends
password: this._password,
}).then((account) => {
this.fire('ha-account-refreshed', { account: account });
this.email = '';
this._password = '';
this.setProperties({
email: '',
_password: '',
});
}, (err) => {
// Do this before setProperties because changing it clears errors.
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') {
const errCode = err && err.body && err.body.code;
if (errCode === 'PasswordChangeRequired') {
alert('You need to change your password before logging in.');
this.navigate('/config/cloud/forgot-password');
return;
}
this.error = err.body.message;
const props = {
_requestInProgress: false,
_error: (err && err.body && err.body.message) ? err.body.message : 'Unknown error',
};
if (errCode === 'UserNotConfirmed') {
props._error = 'You need to confirm your email before logging in.';
}
this.setProperties(props);
this.$.email.focus();
});
}
_handleRegister() {
this.flashMessage = '';
this.navigate('/config/cloud/register');
}
_handleForgotPassword() {
this.flashMessage = '';
this.navigate('/config/cloud/forgot-password');
}
_dismissFlash() {
// give some time to let the ripple finish.
setTimeout(() => { this.flashMessage = ''; }, 200);
}
}
customElements.define(HaConfigCloudLogin.is, HaConfigCloudLogin);

View File

@ -1,6 +1,5 @@
<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">
@ -57,77 +56,42 @@
</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>
<paper-card>
<div class='card-content'>
<div class='header'>
<h1>Register</h1>
<div class='error' hidden$='[[!_error]]'>[[_error]]</div>
</div>
<div class='card-actions'>
<ha-progress-button
on-click='_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-click='_handleVerifyEmail'
progress='[[_requestInProgress]]'
>Verify Email</ha-progress-button>
<paper-button
on-click='_handleResendVerifyEmail'
>Resend Verify Email</paper-button>
</div>
</paper-card>
</template>
<paper-input
autofocus
id='email'
label='Email address'
type='email'
value='{{email}}'
on-keydown='_keyDown'
error-message='Invalid email'
></paper-input>
<paper-input
id='password'
label='Password'
value='{{_password}}'
type='password'
on-keydown='_keyDown'
error-message='Your password needs to be at least 8 characters'
></paper-input>
</div>
<div class='card-actions'>
<ha-progress-button
on-click='_handleRegister'
progress='[[_requestInProgress]]'
>Create Account</ha-progress-button>
<button
class='link'
hidden='[[_requestInProgress]]'
on-click='_handleResendVerifyEmail'
>Resend confirmation email</button>
</div>
</paper-card>
</ha-config-section>
</div>
</hass-subpage>
@ -135,8 +99,7 @@
</dom-module>
<script>
class HaConfigCloudRegister extends
window.hassMixins.NavigateMixin(window.hassMixins.EventsMixin(Polymer.Element)) {
class HaConfigCloudRegister extends window.hassMixins.EventsMixin(Polymer.Element) {
static get is() { return 'ha-config-cloud-register'; }
static get properties() {
@ -156,14 +119,10 @@ class HaConfigCloudRegister extends
type: String,
value: '',
},
_showEmailInputForConfirmation: {
type: Boolean,
value: false,
_error: {
type: String,
value: '',
},
_hasConfirmationCode: {
type: Boolean,
value: () => document.location.hash === '#confirm'
}
};
}
@ -174,93 +133,75 @@ class HaConfigCloudRegister extends
}
_inputChanged() {
this._error = false;
this._error = '';
this.$.email.invalid = false;
this.$.password.invalid = false;
}
_keyDown(ev) {
// validate on enter
if (ev.keyCode === 13) {
if (this._hasConfirmationCode) {
this._handleVerifyEmail();
} else {
this._handleRegister();
}
this._handleRegister();
ev.preventDefault();
}
}
_handleRegister() {
if (!this.email) {
this._error = 'Email is required.';
} else if (!this._password) {
this._error = 'Password is required.';
let invalid = false;
if (!this.email || !this.email.includes('@')) {
this.$.email.invalid = true;
this.$.email.focus();
invalid = true;
}
if (this._error) return;
if (this._password.length < 8) {
this.$.password.invalid = true;
if (!invalid) {
invalid = true;
this.$.password.focus();
}
}
if (invalid) return;
this._requestInProgress = true;
this.hass.callApi('post', 'cloud/register', {
email: this.email,
password: this._password,
}).then(() => {
this._requestInProgress = false;
this._hasConfirmationCode = true;
}, (err) => {
}).then(() => this._verificationEmailSent(), (err) => {
// Do this before setProperties because changing it clears errors.
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;
this.setProperties({
_requestInProgress: false,
_error: err && err.body && err.body.message ? err.body.message : 'Unknown error'
});
});
}
_handleResendVerifyEmail() {
if (!this.email) {
this._error = 'Email is required.';
this.$.email.invalid = true;
return;
}
if (this._error) return;
this.hass.callApi('post', 'cloud/resend_confirm', {
email: this.email,
}).then(() => {
// eslint-disable-next-line
alert('Email resend.');
}, (err) => {
this._error = err && err.body && err.body.message ?
err.body.message : 'Unknown error';
}).then(() => this._verificationEmailSent(), err => this.setProperties({
_error: err && err.body && err.body.message ? err.body.message : 'Unknown error'
}));
}
_verificationEmailSent() {
this.setProperties({
_requestInProgress: false,
_password: '',
});
this.fire('cloud-done', {
flashMessage: 'Account created! Check your email for instructions on how to activate your account.'
});
}
}

View File

@ -32,6 +32,7 @@
hass='[[hass]]'
is-wide='[[isWide]]'
email='{{_loginEmail}}'
flash-message='{{_flashMessage}}'
></ha-config-cloud-login>
</template>
@ -80,6 +81,10 @@
account: {
type: Object,
},
_flashMessage: {
type: String,
value: '',
},
route: Object,
@ -95,6 +100,14 @@
];
}
ready() {
super.ready();
this.addEventListener('cloud-done', (ev) => {
this._flashMessage = ev.detail.flashMessage;
this.navigate('/config/cloud/login');
});
}
_checkRoute(route) {
if (!route || route.path.substr(0, 6) !== '/cloud') return;