diff --git a/src/layouts/hass-router-page.ts b/src/layouts/hass-router-page.ts index 4a39902dd8..ae66380088 100644 --- a/src/layouts/hass-router-page.ts +++ b/src/layouts/hass-router-page.ts @@ -34,6 +34,9 @@ export interface RouterOptions { showLoading?: boolean; // Promise that resolves when the initial data is loaded which is needed to show any route. initialLoad?: () => Promise; + // Hook that is called before rendering a new route. Allowing redirects. + // If string returned, that page will be rendered instead. + beforeRender?: (page: string) => string | undefined; routes: { // If it's a string, it is another route whose options should be adopted. [route: string]: RouteOptions | string; @@ -48,7 +51,7 @@ export class HassRouterPage extends UpdatingElement { protected routerOptions!: RouterOptions; - private _currentPage = ""; + protected _currentPage = ""; private _currentLoadProm?: Promise; private _cache = {}; private _initialLoadDone = false; @@ -101,6 +104,25 @@ export class HassRouterPage extends UpdatingElement { routeOptions = routerOptions.routes[newPage]; } + if (routerOptions.beforeRender) { + const result = routerOptions.beforeRender(newPage); + if (result !== undefined) { + newPage = result; + routeOptions = routerOptions.routes[newPage]; + + // Handle redirects + while (typeof routeOptions === "string") { + newPage = routeOptions; + routeOptions = routerOptions.routes[newPage]; + } + + // Update the url if we know where we're mounted. + if (route) { + navigate(this, `${route.prefix}/${result}`, true); + } + } + } + if (this._currentPage === newPage) { if (this.lastChild) { this.updatePageEl(this.lastChild, changedProps); @@ -245,6 +267,10 @@ export class HassRouterPage extends UpdatingElement { return this.updateComplete.then(() => this._currentLoadProm); } + protected createElement(tag: string) { + return document.createElement(tag); + } + protected updatePageEl(_pageEl, _changedProps?: PropertyValues) { // default we do nothing } @@ -262,8 +288,7 @@ export class HassRouterPage extends UpdatingElement { this.removeChild(this.lastChild); } - const panelEl = - this._cache[page] || document.createElement(routeOptions.tag); + const panelEl = this._cache[page] || this.createElement(routeOptions.tag); this.updatePageEl(panelEl); this.appendChild(panelEl); diff --git a/src/panels/config/cloud/ha-config-cloud.js b/src/panels/config/cloud/ha-config-cloud.js deleted file mode 100644 index e85d7c030b..0000000000 --- a/src/panels/config/cloud/ha-config-cloud.js +++ /dev/null @@ -1,145 +0,0 @@ -import "@polymer/app-route/app-route"; -import { timeOut } from "@polymer/polymer/lib/utils/async"; -import { Debouncer } from "@polymer/polymer/lib/utils/debounce"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -import { PolymerElement } from "@polymer/polymer/polymer-element"; - -import "../ha-config-section"; -import "./ha-config-cloud-account"; -import "./ha-config-cloud-forgot-password"; -import "./ha-config-cloud-login"; -import "./ha-config-cloud-register"; -import NavigateMixin from "../../../mixins/navigate-mixin"; - -const LOGGED_IN_URLS = ["/account"]; -const NOT_LOGGED_IN_URLS = ["/login", "/register", "/forgot-password"]; - -/* - * @appliesMixin NavigateMixin - */ -class HaConfigCloud extends NavigateMixin(PolymerElement) { - static get template() { - return html` - - - - - - - - - - `; - } - - static get properties() { - return { - hass: Object, - isWide: Boolean, - loadingAccount: { - type: Boolean, - value: false, - }, - cloudStatus: { - type: Object, - }, - _flashMessage: { - type: String, - value: "", - }, - - route: Object, - - _routeData: Object, - _routeTail: Object, - _loginEmail: String, - }; - } - - static get observers() { - return ["_checkRoute(route, cloudStatus)"]; - } - - ready() { - super.ready(); - this.addEventListener("cloud-done", (ev) => { - this._flashMessage = ev.detail.flashMessage; - this.navigate("/config/cloud/login"); - }); - } - - _checkRoute(route) { - this._debouncer = Debouncer.debounce( - this._debouncer, - timeOut.after(0), - () => { - if ( - !this.cloudStatus || - (!this.cloudStatus.logged_in && - !NOT_LOGGED_IN_URLS.includes(route.path)) - ) { - this.navigate("/config/cloud/login", true); - } else if ( - this.cloudStatus.logged_in && - !LOGGED_IN_URLS.includes(route.path) - ) { - this.navigate("/config/cloud/account", true); - } - } - ); - } - - _equals(a, b) { - return a === b; - } -} - -customElements.define("ha-config-cloud", HaConfigCloud); diff --git a/src/panels/config/cloud/ha-config-cloud.ts b/src/panels/config/cloud/ha-config-cloud.ts new file mode 100644 index 0000000000..038130495a --- /dev/null +++ b/src/panels/config/cloud/ha-config-cloud.ts @@ -0,0 +1,134 @@ +import "./ha-config-cloud-account"; +import "./ha-config-cloud-login"; +import { + HassRouterPage, + RouterOptions, +} from "../../../layouts/hass-router-page"; +import { property, customElement } from "lit-element"; +import { HomeAssistant, Route } from "../../../types"; +import { navigate } from "../../../common/navigate"; +import { CloudStatus } from "../../../data/cloud"; +import { PolymerChangedEvent } from "../../../polymer-types"; +import { PolymerElement } from "@polymer/polymer"; + +const LOGGED_IN_URLS = ["account"]; +const NOT_LOGGED_IN_URLS = ["login", "register", "forgot-password"]; + +@customElement("ha-config-cloud") +class HaConfigCloud extends HassRouterPage { + @property() public hass!: HomeAssistant; + @property() public isWide!: boolean; + @property() public route!: Route; + @property() public cloudStatus!: CloudStatus; + + protected routerOptions: RouterOptions = { + defaultPage: "login", + showLoading: true, + initialLoad: () => this._cloudStatusLoaded, + // Guard the different pages based on if we're logged in. + beforeRender: (page: string) => { + if (this.cloudStatus.logged_in) { + if (!LOGGED_IN_URLS.includes(page)) { + return "account"; + } + } else { + if (!NOT_LOGGED_IN_URLS.includes(page)) { + return "login"; + } + } + return undefined; + }, + routes: { + login: { + tag: "ha-config-cloud-login", + }, + register: { + tag: "ha-config-cloud-register", + load: () => import("./ha-config-cloud-register"), + }, + "forgot-password": { + tag: "ha-config-cloud-forgot-password", + load: () => import("./ha-config-cloud-forgot-password"), + }, + account: { + tag: "ha-config-cloud-account", + }, + }, + }; + + @property() private _flashMessage = ""; + @property() private _loginEmail = ""; + private _resolveCloudStatusLoaded!: () => void; + private _cloudStatusLoaded = new Promise((resolve) => { + this._resolveCloudStatusLoaded = resolve; + }); + + protected firstUpdated(changedProps) { + super.firstUpdated(changedProps); + this.addEventListener("cloud-done", (ev) => { + this._flashMessage = (ev as any).detail.flashMessage; + navigate(this, "/config/cloud/login"); + }); + } + + protected updated(changedProps) { + super.updated(changedProps); + + if (changedProps.has("cloudStatus")) { + const oldStatus = changedProps.get("cloudStatus") as + | CloudStatus + | undefined; + if (oldStatus === undefined) { + this._resolveCloudStatusLoaded(); + } else if (oldStatus.logged_in !== this.cloudStatus.logged_in) { + navigate(this, this.route.prefix, true); + } + } + } + + protected createElement(tag: string) { + const el = super.createElement(tag); + el.addEventListener("email-changed", (ev) => { + this._loginEmail = (ev as PolymerChangedEvent).detail.value; + }); + el.addEventListener("flash-message-changed", (ev) => { + this._flashMessage = (ev as PolymerChangedEvent).detail.value; + }); + return el; + } + + protected updatePageEl(el) { + // We are not going to update if the current page if we are not logged in + // and the current page requires being logged in. Happens when we log out. + if ( + this.cloudStatus && + !this.cloudStatus.logged_in && + LOGGED_IN_URLS.includes(this._currentPage) + ) { + return; + } + + if ("setProperties" in el) { + // As long as we have Polymer pages + (el as PolymerElement).setProperties({ + hass: this.hass, + email: this._loginEmail, + isWide: this.isWide, + cloudStatus: this.cloudStatus, + flashMessage: this._flashMessage, + }); + } else { + el.hass = this.hass; + el.email = this._loginEmail; + el.isWide = this.isWide; + el.cloudStatus = this.cloudStatus; + el.flashMessage = this._flashMessage; + } + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-config-cloud": HaConfigCloud; + } +}