Add an onboarding flow (#1452)
* Add an onboarding flow * Address comments
This commit is contained in:
parent
84b0542fb6
commit
90328cfc33
|
@ -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));
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
import '../components/ha-iconset-svg.js';
|
||||
import '../resources/roboto.js';
|
||||
import '../onboarding/ha-onboarding.js';
|
|
@ -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>
|
|
@ -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);
|
|
@ -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',
|
||||
|
|
Loading…
Reference in New Issue