diff --git a/src/data/onboarding.ts b/src/data/onboarding.ts index 83002acd6b..46e6775989 100644 --- a/src/data/onboarding.ts +++ b/src/data/onboarding.ts @@ -1,12 +1,19 @@ import { handleFetchPromise } from "../util/hass-call-api"; -export interface OnboardingStep { - step: string; - done: boolean; +export interface OnboardingUserStepResponse { + auth_code: string; } -interface UserStepResponse { - auth_code: string; +export interface OnboardingResponses { + user: OnboardingUserStepResponse; + bla: number; +} + +export type ValidOnboardingStep = keyof OnboardingResponses; + +export interface OnboardingStep { + step: ValidOnboardingStep; + done: boolean; } export const fetchOnboardingOverview = () => @@ -18,7 +25,7 @@ export const onboardUserStep = (params: { username: string; password: string; }) => - handleFetchPromise( + handleFetchPromise( fetch("/api/onboarding/users", { method: "POST", credentials: "same-origin", diff --git a/src/onboarding/ha-onboarding.ts b/src/onboarding/ha-onboarding.ts index e377ac250d..0c9398a67e 100644 --- a/src/onboarding/ha-onboarding.ts +++ b/src/onboarding/ha-onboarding.ts @@ -1,137 +1,67 @@ -import "@polymer/paper-input/paper-input"; -import "@material/mwc-button"; import { LitElement, - CSSResult, - css, html, PropertyValues, - property, customElement, TemplateResult, + property, } from "lit-element"; import { genClientId } from "home-assistant-js-websocket"; import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin"; -import { OnboardingStep, onboardUserStep } from "../data/onboarding"; -import { PolymerChangedEvent } from "../polymer-types"; +import { + OnboardingStep, + ValidOnboardingStep, + OnboardingResponses, +} from "../data/onboarding"; import { registerServiceWorker } from "../util/register-service-worker"; +import { HASSDomEvent } from "../common/dom/fire_event"; +import "./onboarding-create-user"; +import "./onboarding-loading"; + +interface OnboardingEvent { + type: T; + result: OnboardingResponses[T]; +} + +declare global { + interface HASSDomEvents { + "onboarding-step": OnboardingEvent; + } + + interface GlobalEventHandlersEventMap { + "onboarding-step": HASSDomEvent>; + } +} @customElement("ha-onboarding") class HaOnboarding extends litLocalizeLiteMixin(LitElement) { public translationFragment = "page-onboarding"; - @property() private _name = ""; - @property() private _username = ""; - @property() private _password = ""; - @property() private _passwordConfirm = ""; - @property() private _loading = false; - @property() private _errorMsg?: string = undefined; + @property() private _steps?: OnboardingStep[]; protected render(): TemplateResult | void { - return html` -

- ${this.localize("ui.panel.page-onboarding.intro")} -

- -

- ${this.localize("ui.panel.page-onboarding.user.intro")} -

- - ${ - this._errorMsg - ? html` -

- ${this.localize( - `ui.panel.page-onboarding.user.error.${this._errorMsg}` - ) || this._errorMsg} -

- ` - : "" + if (!this._steps) { + return html` + + `; } + const step = this._steps.find((stp) => !stp.done)!; -
- - - - - - - - -

- - ${this.localize("ui.panel.page-onboarding.user.create_account")} - -

- -
-`; + if (step.step === "user") { + return html` + + `; + } } protected firstUpdated(changedProps: PropertyValues) { super.firstUpdated(changedProps); - this.addEventListener("keypress", (ev) => { - if (ev.keyCode === 13) { - this._submitForm(); - } - }); this._fetchOnboardingSteps(); registerServiceWorker(false); + this.addEventListener("onboarding-step", (ev) => this._handleStepDone(ev)); } private async _fetchOnboardingSteps() { @@ -150,82 +80,31 @@ class HaOnboarding extends litLocalizeLiteMixin(LitElement) { // Onboarding is done! document.location.href = "/"; } + + this._steps = steps; } catch (err) { alert("Something went wrong loading loading onboarding, try refreshing"); } } - private _handleValueChanged(ev: PolymerChangedEvent): 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 { - 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, - }); + private async _handleStepDone( + ev: HASSDomEvent> + ) { + const stepResult = ev.detail; + if (stepResult.type === "user") { + const result = stepResult.result as OnboardingResponses["user"]; const state = btoa( JSON.stringify({ hassUrl: `${location.protocol}//${location.host}`, - clientId, + clientId: genClientId(), }) ); - document.location.href = `/?auth_callback=1&code=${encodeURIComponent( - auth_code + result.auth_code )}&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 { diff --git a/src/onboarding/onboarding-create-user.ts b/src/onboarding/onboarding-create-user.ts new file mode 100644 index 0000000000..e04fd07936 --- /dev/null +++ b/src/onboarding/onboarding-create-user.ts @@ -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` +

+ ${this.localize("ui.panel.page-onboarding.intro")} +

+ +

+ ${this.localize("ui.panel.page-onboarding.user.intro")} +

+ + ${ + this._errorMsg + ? html` +

+ ${this.localize( + `ui.panel.page-onboarding.user.error.${this._errorMsg}` + ) || this._errorMsg} +

+ ` + : "" + } + +
+ + + + + + + + +

+ + ${this.localize("ui.panel.page-onboarding.user.create_account")} + +

+ +
+`; + } + + 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): 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 { + 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; + } +} diff --git a/src/onboarding/onboarding-loading.ts b/src/onboarding/onboarding-loading.ts new file mode 100644 index 0000000000..ee8ced2dca --- /dev/null +++ b/src/onboarding/onboarding-loading.ts @@ -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; + } +}