Add an onboarding flow (#1452)

* Add an onboarding flow

* Address comments
This commit is contained in:
Paulus Schoutsen 2018-07-17 10:05:27 +02:00 committed by GitHub
parent 84b0542fb6
commit 90328cfc33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 222 additions and 3 deletions

View File

@ -0,0 +1,43 @@
const gulp = require('gulp');
const path = require('path');
const replace = require('gulp-batch-replace');
const rename = require('gulp-rename');
const md5 = require('../common/md5');
const url = require('url');
const config = require('../config');
const minifyStream = require('../common/transform').minifyStream;
const buildReplaces = {
'/frontend_latest/onboarding.js': 'onboarding.js',
};
const es5Extra = "<script src='/static/custom-elements-es5-adapter.js'></script>";
async function buildOnboarding(es6) {
const targetPath = es6 ? config.output : config.output_es5;
const targetUrl = es6 ? '/frontend_latest/' : '/frontend_es5/';
const frontendPath = es6 ? 'frontend_latest' : 'frontend_es5';
const toReplace = [
['<!--EXTRA_SCRIPTS-->', es6 ? '' : es5Extra],
['/home-assistant-polymer/hass_frontend/onboarding.js', `/${frontendPath}/onboarding.js`],
];
for (const [replaceSearch, filename] of Object.entries(buildReplaces)) {
const parsed = path.parse(filename);
const hash = md5(path.resolve(targetPath, filename));
toReplace.push([
replaceSearch,
url.resolve(targetUrl, `${parsed.name}-${hash}${parsed.ext}`)]);
}
const stream = gulp.src(path.resolve(config.polymer_dir, 'src/onboarding.html'))
.pipe(replace(toReplace));
return minifyStream(stream, /* es6= */ es6)
.pipe(rename('onboarding.html'))
.pipe(gulp.dest(es6 ? config.output : config.output_es5));
}
gulp.task('gen-onboarding-html-es5', () => buildOnboarding(/* es6= */ false));
gulp.task('gen-onboarding-html', () => buildOnboarding(/* es6= */ true));

View File

@ -29,5 +29,5 @@ echo "CREATED_AT = `date +%s`" >> $OUTPUT_DIR_ES5/__init__.py
# Generate index.htmls with the MD5 hash of the builds
./node_modules/.bin/gulp \
gen-index-html gen-index-html-es5 \
gen-authorize-html gen-authorize-html-es5
gen-authorize-html gen-authorize-html-es5 \
gen-onboarding-html gen-onboarding-html-es5

View File

@ -15,5 +15,6 @@ cp -r public/__init__.py $OUTPUT_DIR_ES5/
./node_modules/.bin/gulp build-translations gen-icons
cp src/authorize.html $OUTPUT_DIR
cp src/onboarding.html $OUTPUT_DIR
./node_modules/.bin/webpack --watch --progress

View File

@ -4,6 +4,17 @@
<meta charset="utf-8">
<title>Home Assistant</title>
<!--EXTRA_SCRIPTS-->
<style>
body {
font-family: 'Roboto', 'Noto', sans-serif;
font-weight: 400;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
margin: 0;
padding: 0;
height: 100vh;
}
</style>
</head>
<body>
<ha-authorize>Loading</ha-authorize>

View File

@ -24,7 +24,7 @@ import '../resources/ha-style.js';
import '../util/ha-pref-storage.js';
import { getActiveTranslation, getTranslation } from '../util/hass-translation.js';
import '../util/legacy-support';
import '../util/roboto.js';
import '../resources/roboto.js';
import hassCallApi from '../util/hass-call-api.js';
import makeDialogManager from '../dialogs/dialog-manager.js';
import registerServiceWorker from '../util/register-service-worker.js';

View File

@ -5,6 +5,7 @@ import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import '../components/ha-iconset-svg.js';
import '../resources/roboto.js';
import '../auth/ha-auth-flow.js';
import '../auth/ha-pick-auth-provider.js';

View File

@ -0,0 +1,3 @@
import '../components/ha-iconset-svg.js';
import '../resources/roboto.js';
import '../onboarding/ha-onboarding.js';

34
src/onboarding.html Normal file
View File

@ -0,0 +1,34 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Home Assistant</title>
<!--EXTRA_SCRIPTS-->
<style>
body {
font-family: 'Roboto', 'Noto', sans-serif;
font-weight: 400;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
margin: 0;
padding: 0;
height: 100vh;
}
</style>
</head>
<body>
<ha-onboarding>Loading</ha-onboarding>
<script>
var webComponentsSupported = (
'customElements' in window &&
'content' in document.createElement('template'));
if (!webComponentsSupported) {
var e = document.createElement('script');
e.src = '/static/webcomponents-bundle.js';
document.write(e.outerHTML);
}
</script>
<script src="/frontend_latest/onboarding.js"></script>
<script src='/frontend_latest/hass-icons.js' async></script>
</body>
</html>

View File

@ -0,0 +1,125 @@
import '@polymer/iron-flex-layout/iron-flex-layout-classes.js';
import '@polymer/polymer/lib/elements/dom-if.js';
import '@polymer/polymer/lib/elements/dom-repeat.js';
import '@polymer/paper-input/paper-input.js';
import '@polymer/paper-button/paper-button.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import hassCallApi from '../util/hass-call-api.js';
const callApi = (method, path, data) => hassCallApi('', {}, method, path, data);
class HaOnboarding extends PolymerElement {
static get template() {
return html`
<style include="iron-flex iron-positioning"></style>
<style>
.layout {
padding-top: 20px;
}
.error {
color: red;
font-weight: bold;
}
</style>
<div class="layout vertical center fit">
<img src="/static/icons/favicon-192x192.png" height="192">
<p>Create your owner user account.</p>
<p><i>It is not possible yet to change your password. Coming soon!</i></p>
<template is='dom-if' if='[[_error]]'>
<p class='error'>[[_error]]</p>
</template>
<paper-input
autofocus
label='Name'
value='{{_name}}'
required
auto-validate
error-message='Required'
on-blur='_maybePopulateUsername'
></paper-input>
<paper-input
label='Username'
value='{{_username}}'
required
auto-validate
error-message='Required'
></paper-input>
<paper-input
label='Password'
value='{{_password}}'
required
type='password'
auto-validate
error-message='Required'
></paper-input>
<template is='dom-if' if='[[!_loading]]'>
<p>
<paper-button on-click='_submitForm'>Submit</paper-button>
</p>
</template>
</div>
`;
}
static get properties() {
return {
_name: String,
_username: String,
_password: String,
_loading: {
type: Boolean,
value: false,
}
};
}
async ready() {
super.ready();
this.addEventListener('keypress', (ev) => {
if (ev.keyCode === 13) {
this._submitForm();
}
});
const steps = await callApi('get', 'onboarding');
if (steps.every(step => step.done)) {
// Onboarding is done!
document.location = '/';
}
}
_maybePopulateUsername() {
if (!this._username) {
this._username = this._name.toLowerCase().replace(/ /g, '');
}
}
async _submitForm() {
if (!this._name || !this._username || !this._password) return;
try {
await callApi('post', 'onboarding/users', {
name: this._name,
username: this._username,
password: this._password,
});
document.location = '/';
} catch (err) {
// eslint-disable-next-line
console.error(err);
this.setProperties({
_loading: false,
_error: err.message,
});
}
}
}
customElements.define('ha-onboarding', HaOnboarding);

View File

@ -20,6 +20,7 @@ function createConfig(isProdBuild, latestBuild) {
const entry = {
app: './src/entrypoints/app.js',
authorize: './src/entrypoints/authorize.js',
onboarding: './src/entrypoints/onboarding.js',
core: './src/entrypoints/core.js',
compatibility: './src/entrypoints/compatibility.js',
'custom-panel': './src/entrypoints/custom-panel.js',