Convert cloud dashboard to use Lit router (#3223)

This commit is contained in:
Paulus Schoutsen 2019-05-28 14:31:51 -07:00 committed by GitHub
parent e595637a10
commit b2b18cb814
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 162 additions and 148 deletions

View File

@ -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<unknown>;
// 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<void>;
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);

View File

@ -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`
<app-route
route="[[route]]"
pattern="/:page"
data="{{_routeData}}"
tail="{{_routeTail}}"
></app-route>
<template
is="dom-if"
if='[[_equals(_routeData.page, "account")]]'
restamp=""
>
<ha-config-cloud-account
hass="[[hass]]"
cloud-status="[[cloudStatus]]"
is-wide="[[isWide]]"
></ha-config-cloud-account>
</template>
<template
is="dom-if"
if='[[_equals(_routeData.page, "login")]]'
restamp=""
>
<ha-config-cloud-login
page-name="login"
hass="[[hass]]"
is-wide="[[isWide]]"
email="{{_loginEmail}}"
flash-message="{{_flashMessage}}"
></ha-config-cloud-login>
</template>
<template
is="dom-if"
if='[[_equals(_routeData.page, "register")]]'
restamp=""
>
<ha-config-cloud-register
page-name="register"
hass="[[hass]]"
is-wide="[[isWide]]"
email="{{_loginEmail}}"
></ha-config-cloud-register>
</template>
<template
is="dom-if"
if='[[_equals(_routeData.page, "forgot-password")]]'
restamp=""
>
<ha-config-cloud-forgot-password
page-name="forgot-password"
hass="[[hass]]"
email="{{_loginEmail}}"
></ha-config-cloud-forgot-password>
</template>
`;
}
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);

View File

@ -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<string>).detail.value;
});
el.addEventListener("flash-message-changed", (ev) => {
this._flashMessage = (ev as PolymerChangedEvent<string>).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;
}
}