mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 11:46:42 +00:00
Split up onboarding code (#3158)
This commit is contained in:
parent
fcdb1b48a2
commit
db65af9c22
@ -1,12 +1,19 @@
|
|||||||
import { handleFetchPromise } from "../util/hass-call-api";
|
import { handleFetchPromise } from "../util/hass-call-api";
|
||||||
|
|
||||||
export interface OnboardingStep {
|
export interface OnboardingUserStepResponse {
|
||||||
step: string;
|
auth_code: string;
|
||||||
done: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UserStepResponse {
|
export interface OnboardingResponses {
|
||||||
auth_code: string;
|
user: OnboardingUserStepResponse;
|
||||||
|
bla: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ValidOnboardingStep = keyof OnboardingResponses;
|
||||||
|
|
||||||
|
export interface OnboardingStep {
|
||||||
|
step: ValidOnboardingStep;
|
||||||
|
done: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchOnboardingOverview = () =>
|
export const fetchOnboardingOverview = () =>
|
||||||
@ -18,7 +25,7 @@ export const onboardUserStep = (params: {
|
|||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
}) =>
|
}) =>
|
||||||
handleFetchPromise<UserStepResponse>(
|
handleFetchPromise<OnboardingUserStepResponse>(
|
||||||
fetch("/api/onboarding/users", {
|
fetch("/api/onboarding/users", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
credentials: "same-origin",
|
credentials: "same-origin",
|
||||||
|
@ -1,137 +1,67 @@
|
|||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import "@material/mwc-button";
|
|
||||||
import {
|
import {
|
||||||
LitElement,
|
LitElement,
|
||||||
CSSResult,
|
|
||||||
css,
|
|
||||||
html,
|
html,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
property,
|
|
||||||
customElement,
|
customElement,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
|
property,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { genClientId } from "home-assistant-js-websocket";
|
import { genClientId } from "home-assistant-js-websocket";
|
||||||
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
|
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
|
||||||
import { OnboardingStep, onboardUserStep } from "../data/onboarding";
|
import {
|
||||||
import { PolymerChangedEvent } from "../polymer-types";
|
OnboardingStep,
|
||||||
|
ValidOnboardingStep,
|
||||||
|
OnboardingResponses,
|
||||||
|
} from "../data/onboarding";
|
||||||
import { registerServiceWorker } from "../util/register-service-worker";
|
import { registerServiceWorker } from "../util/register-service-worker";
|
||||||
|
import { HASSDomEvent } from "../common/dom/fire_event";
|
||||||
|
import "./onboarding-create-user";
|
||||||
|
import "./onboarding-loading";
|
||||||
|
|
||||||
|
interface OnboardingEvent<T extends ValidOnboardingStep> {
|
||||||
|
type: T;
|
||||||
|
result: OnboardingResponses[T];
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HASSDomEvents {
|
||||||
|
"onboarding-step": OnboardingEvent<ValidOnboardingStep>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GlobalEventHandlersEventMap {
|
||||||
|
"onboarding-step": HASSDomEvent<OnboardingEvent<ValidOnboardingStep>>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@customElement("ha-onboarding")
|
@customElement("ha-onboarding")
|
||||||
class HaOnboarding extends litLocalizeLiteMixin(LitElement) {
|
class HaOnboarding extends litLocalizeLiteMixin(LitElement) {
|
||||||
public translationFragment = "page-onboarding";
|
public translationFragment = "page-onboarding";
|
||||||
|
|
||||||
@property() private _name = "";
|
@property() private _steps?: OnboardingStep[];
|
||||||
@property() private _username = "";
|
|
||||||
@property() private _password = "";
|
|
||||||
@property() private _passwordConfirm = "";
|
|
||||||
@property() private _loading = false;
|
|
||||||
@property() private _errorMsg?: string = undefined;
|
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
|
if (!this._steps) {
|
||||||
return html`
|
return html`
|
||||||
<p>
|
<onboarding-loading></onboarding-loading>
|
||||||
${this.localize("ui.panel.page-onboarding.intro")}
|
`;
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
${this.localize("ui.panel.page-onboarding.user.intro")}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
${
|
|
||||||
this._errorMsg
|
|
||||||
? html`
|
|
||||||
<p class="error">
|
|
||||||
${this.localize(
|
|
||||||
`ui.panel.page-onboarding.user.error.${this._errorMsg}`
|
|
||||||
) || this._errorMsg}
|
|
||||||
</p>
|
|
||||||
`
|
|
||||||
: ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const step = this._steps.find((stp) => !stp.done)!;
|
||||||
|
|
||||||
<form>
|
if (step.step === "user") {
|
||||||
<paper-input
|
return html`
|
||||||
autofocus
|
<onboarding-create-user
|
||||||
name="name"
|
.localize=${this.localize}
|
||||||
label="${this.localize("ui.panel.page-onboarding.user.data.name")}"
|
></onboarding-create-user>
|
||||||
.value=${this._name}
|
|
||||||
@value-changed=${this._handleValueChanged}
|
|
||||||
required
|
|
||||||
auto-validate
|
|
||||||
autocapitalize='on'
|
|
||||||
.errorMessage="${this.localize(
|
|
||||||
"ui.panel.page-onboarding.user.required_field"
|
|
||||||
)}"
|
|
||||||
@blur=${this._maybePopulateUsername}
|
|
||||||
></paper-input>
|
|
||||||
|
|
||||||
<paper-input
|
|
||||||
name="username"
|
|
||||||
label="${this.localize("ui.panel.page-onboarding.user.data.username")}"
|
|
||||||
value=${this._username}
|
|
||||||
@value-changed=${this._handleValueChanged}
|
|
||||||
required
|
|
||||||
auto-validate
|
|
||||||
autocapitalize='none'
|
|
||||||
.errorMessage="${this.localize(
|
|
||||||
"ui.panel.page-onboarding.user.required_field"
|
|
||||||
)}"
|
|
||||||
></paper-input>
|
|
||||||
|
|
||||||
<paper-input
|
|
||||||
name="password"
|
|
||||||
label="${this.localize("ui.panel.page-onboarding.user.data.password")}"
|
|
||||||
value=${this._password}
|
|
||||||
@value-changed=${this._handleValueChanged}
|
|
||||||
required
|
|
||||||
type='password'
|
|
||||||
auto-validate
|
|
||||||
.errorMessage="${this.localize(
|
|
||||||
"ui.panel.page-onboarding.user.required_field"
|
|
||||||
)}"
|
|
||||||
></paper-input>
|
|
||||||
|
|
||||||
<paper-input
|
|
||||||
name="passwordConfirm"
|
|
||||||
label="${this.localize(
|
|
||||||
"ui.panel.page-onboarding.user.data.password_confirm"
|
|
||||||
)}"
|
|
||||||
value=${this._passwordConfirm}
|
|
||||||
@value-changed=${this._handleValueChanged}
|
|
||||||
required
|
|
||||||
type='password'
|
|
||||||
.invalid=${this._password !== "" &&
|
|
||||||
this._passwordConfirm !== "" &&
|
|
||||||
this._passwordConfirm !== this._password}
|
|
||||||
.errorMessage="${this.localize(
|
|
||||||
"ui.panel.page-onboarding.user.error.password_not_match"
|
|
||||||
)}"
|
|
||||||
></paper-input>
|
|
||||||
|
|
||||||
<p class="action">
|
|
||||||
<mwc-button
|
|
||||||
raised
|
|
||||||
@click=${this._submitForm}
|
|
||||||
.disabled=${this._loading}
|
|
||||||
>
|
|
||||||
${this.localize("ui.panel.page-onboarding.user.create_account")}
|
|
||||||
</mwc-button>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected firstUpdated(changedProps: PropertyValues) {
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
this.addEventListener("keypress", (ev) => {
|
|
||||||
if (ev.keyCode === 13) {
|
|
||||||
this._submitForm();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this._fetchOnboardingSteps();
|
this._fetchOnboardingSteps();
|
||||||
registerServiceWorker(false);
|
registerServiceWorker(false);
|
||||||
|
this.addEventListener("onboarding-step", (ev) => this._handleStepDone(ev));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _fetchOnboardingSteps() {
|
private async _fetchOnboardingSteps() {
|
||||||
@ -150,82 +80,31 @@ class HaOnboarding extends litLocalizeLiteMixin(LitElement) {
|
|||||||
// Onboarding is done!
|
// Onboarding is done!
|
||||||
document.location.href = "/";
|
document.location.href = "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._steps = steps;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert("Something went wrong loading loading onboarding, try refreshing");
|
alert("Something went wrong loading loading onboarding, try refreshing");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleValueChanged(ev: PolymerChangedEvent<string>): void {
|
private async _handleStepDone(
|
||||||
const name = (ev.target as any).name;
|
ev: HASSDomEvent<OnboardingEvent<ValidOnboardingStep>>
|
||||||
this[`_${name}`] = ev.detail.value;
|
) {
|
||||||
}
|
const stepResult = ev.detail;
|
||||||
|
|
||||||
private _maybePopulateUsername(): void {
|
|
||||||
if (this._username) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parts = this._name.split(" ");
|
|
||||||
|
|
||||||
if (parts.length) {
|
|
||||||
this._username = parts[0].toLowerCase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _submitForm(): Promise<void> {
|
|
||||||
if (!this._name || !this._username || !this._password) {
|
|
||||||
this._errorMsg = "required_fields";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._password !== this._passwordConfirm) {
|
|
||||||
this._errorMsg = "password_not_match";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._loading = true;
|
|
||||||
this._errorMsg = "";
|
|
||||||
|
|
||||||
try {
|
|
||||||
const clientId = genClientId();
|
|
||||||
|
|
||||||
const { auth_code } = await onboardUserStep({
|
|
||||||
client_id: clientId,
|
|
||||||
name: this._name,
|
|
||||||
username: this._username,
|
|
||||||
password: this._password,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
if (stepResult.type === "user") {
|
||||||
|
const result = stepResult.result as OnboardingResponses["user"];
|
||||||
const state = btoa(
|
const state = btoa(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
hassUrl: `${location.protocol}//${location.host}`,
|
hassUrl: `${location.protocol}//${location.host}`,
|
||||||
clientId,
|
clientId: genClientId(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
document.location.href = `/?auth_callback=1&code=${encodeURIComponent(
|
document.location.href = `/?auth_callback=1&code=${encodeURIComponent(
|
||||||
auth_code
|
result.auth_code
|
||||||
)}&state=${state}`;
|
)}&state=${state}`;
|
||||||
} catch (err) {
|
|
||||||
// tslint:disable-next-line
|
|
||||||
console.error(err);
|
|
||||||
this._loading = false;
|
|
||||||
this._errorMsg = err.message;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
|
||||||
return css`
|
|
||||||
.error {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action {
|
|
||||||
margin: 32px 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
208
src/onboarding/onboarding-create-user.ts
Normal file
208
src/onboarding/onboarding-create-user.ts
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import "@material/mwc-button";
|
||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
CSSResult,
|
||||||
|
css,
|
||||||
|
html,
|
||||||
|
PropertyValues,
|
||||||
|
property,
|
||||||
|
customElement,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import { genClientId } from "home-assistant-js-websocket";
|
||||||
|
import { onboardUserStep } from "../data/onboarding";
|
||||||
|
import { PolymerChangedEvent } from "../polymer-types";
|
||||||
|
import { LocalizeFunc } from "../common/translations/localize";
|
||||||
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
|
||||||
|
@customElement("onboarding-create-user")
|
||||||
|
class OnboardingCreateUser extends LitElement {
|
||||||
|
@property() public localize!: LocalizeFunc;
|
||||||
|
|
||||||
|
@property() private _name = "";
|
||||||
|
@property() private _username = "";
|
||||||
|
@property() private _password = "";
|
||||||
|
@property() private _passwordConfirm = "";
|
||||||
|
@property() private _loading = false;
|
||||||
|
@property() private _errorMsg?: string = undefined;
|
||||||
|
|
||||||
|
protected render(): TemplateResult | void {
|
||||||
|
return html`
|
||||||
|
<p>
|
||||||
|
${this.localize("ui.panel.page-onboarding.intro")}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
${this.localize("ui.panel.page-onboarding.user.intro")}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
${
|
||||||
|
this._errorMsg
|
||||||
|
? html`
|
||||||
|
<p class="error">
|
||||||
|
${this.localize(
|
||||||
|
`ui.panel.page-onboarding.user.error.${this._errorMsg}`
|
||||||
|
) || this._errorMsg}
|
||||||
|
</p>
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
<form>
|
||||||
|
<paper-input
|
||||||
|
name="name"
|
||||||
|
label="${this.localize("ui.panel.page-onboarding.user.data.name")}"
|
||||||
|
.value=${this._name}
|
||||||
|
@value-changed=${this._handleValueChanged}
|
||||||
|
required
|
||||||
|
auto-validate
|
||||||
|
autocapitalize='on'
|
||||||
|
.errorMessage="${this.localize(
|
||||||
|
"ui.panel.page-onboarding.user.required_field"
|
||||||
|
)}"
|
||||||
|
@blur=${this._maybePopulateUsername}
|
||||||
|
></paper-input>
|
||||||
|
|
||||||
|
<paper-input
|
||||||
|
name="username"
|
||||||
|
label="${this.localize("ui.panel.page-onboarding.user.data.username")}"
|
||||||
|
value=${this._username}
|
||||||
|
@value-changed=${this._handleValueChanged}
|
||||||
|
required
|
||||||
|
auto-validate
|
||||||
|
autocapitalize='none'
|
||||||
|
.errorMessage="${this.localize(
|
||||||
|
"ui.panel.page-onboarding.user.required_field"
|
||||||
|
)}"
|
||||||
|
></paper-input>
|
||||||
|
|
||||||
|
<paper-input
|
||||||
|
name="password"
|
||||||
|
label="${this.localize("ui.panel.page-onboarding.user.data.password")}"
|
||||||
|
value=${this._password}
|
||||||
|
@value-changed=${this._handleValueChanged}
|
||||||
|
required
|
||||||
|
type='password'
|
||||||
|
auto-validate
|
||||||
|
.errorMessage="${this.localize(
|
||||||
|
"ui.panel.page-onboarding.user.required_field"
|
||||||
|
)}"
|
||||||
|
></paper-input>
|
||||||
|
|
||||||
|
<paper-input
|
||||||
|
name="passwordConfirm"
|
||||||
|
label="${this.localize(
|
||||||
|
"ui.panel.page-onboarding.user.data.password_confirm"
|
||||||
|
)}"
|
||||||
|
value=${this._passwordConfirm}
|
||||||
|
@value-changed=${this._handleValueChanged}
|
||||||
|
required
|
||||||
|
type='password'
|
||||||
|
.invalid=${this._password !== "" &&
|
||||||
|
this._passwordConfirm !== "" &&
|
||||||
|
this._passwordConfirm !== this._password}
|
||||||
|
.errorMessage="${this.localize(
|
||||||
|
"ui.panel.page-onboarding.user.error.password_not_match"
|
||||||
|
)}"
|
||||||
|
></paper-input>
|
||||||
|
|
||||||
|
<p class="action">
|
||||||
|
<mwc-button
|
||||||
|
raised
|
||||||
|
@click=${this._submitForm}
|
||||||
|
.disabled=${this._loading}
|
||||||
|
>
|
||||||
|
${this.localize("ui.panel.page-onboarding.user.create_account")}
|
||||||
|
</mwc-button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
setTimeout(
|
||||||
|
() => this.shadowRoot!.querySelector("paper-input")!.focus(),
|
||||||
|
100
|
||||||
|
);
|
||||||
|
this.addEventListener("keypress", (ev) => {
|
||||||
|
if (ev.keyCode === 13) {
|
||||||
|
this._submitForm();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleValueChanged(ev: PolymerChangedEvent<string>): void {
|
||||||
|
const name = (ev.target as any).name;
|
||||||
|
this[`_${name}`] = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _maybePopulateUsername(): void {
|
||||||
|
if (this._username) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts = this._name.split(" ");
|
||||||
|
|
||||||
|
if (parts.length) {
|
||||||
|
this._username = parts[0].toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _submitForm(): Promise<void> {
|
||||||
|
if (!this._name || !this._username || !this._password) {
|
||||||
|
this._errorMsg = "required_fields";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._password !== this._passwordConfirm) {
|
||||||
|
this._errorMsg = "password_not_match";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._loading = true;
|
||||||
|
this._errorMsg = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
const clientId = genClientId();
|
||||||
|
|
||||||
|
const result = await onboardUserStep({
|
||||||
|
client_id: clientId,
|
||||||
|
name: this._name,
|
||||||
|
username: this._username,
|
||||||
|
password: this._password,
|
||||||
|
});
|
||||||
|
|
||||||
|
fireEvent(this, "onboarding-step", {
|
||||||
|
type: "user",
|
||||||
|
result,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
// tslint:disable-next-line
|
||||||
|
console.error(err);
|
||||||
|
this._loading = false;
|
||||||
|
this._errorMsg = err.body.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
.error {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action {
|
||||||
|
margin: 32px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"onboarding-create-user": OnboardingCreateUser;
|
||||||
|
}
|
||||||
|
}
|
16
src/onboarding/onboarding-loading.ts
Normal file
16
src/onboarding/onboarding-loading.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { LitElement, TemplateResult, html, customElement } from "lit-element";
|
||||||
|
|
||||||
|
@customElement("onboarding-loading")
|
||||||
|
class OnboardingLoading extends LitElement {
|
||||||
|
protected render(): TemplateResult | void {
|
||||||
|
return html`
|
||||||
|
Loading…
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"onboarding-loading": OnboardingLoading;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user