Fix router (#2943)

* Fix router

* Fix demo

* Extract update routes

* Lint
This commit is contained in:
Paulus Schoutsen 2019-03-15 23:15:16 -07:00 committed by GitHub
parent 2d75e797c7
commit 23ca1b972d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 183 additions and 159 deletions

View File

@ -96,7 +96,8 @@
</style> </style>
</head> </head>
<body> <body>
<ha-demo><div id="ha-init-skeleton"></div></ha-demo> <div id="ha-init-skeleton"></div>
<ha-demo></ha-demo>
<script> <script>
var _gaq = [["_setAccount", "UA-57927901-5"], ["_trackPageview"]]; var _gaq = [["_setAccount", "UA-57927901-5"], ["_trackPageview"]];
(function(d, t) { (function(d, t) {

View File

@ -40,8 +40,8 @@
</script> </script>
</head> </head>
<body> <body>
<div id='ha-init-skeleton'></div>
<home-assistant> <home-assistant>
<div id='ha-init-skeleton'></div>
</home-assistant> </home-assistant>
<% if (!latestBuild) { %> <% if (!latestBuild) { %>
<script src="/static/custom-elements-es5-adapter.js"></script> <script src="/static/custom-elements-es5-adapter.js"></script>

View File

@ -54,7 +54,7 @@ export class HomeAssistantAppEl extends ext(HassBaseMixin(LitElement), [
></app-location> ></app-location>
${this._panelUrl === undefined || this._route === undefined ${this._panelUrl === undefined || this._route === undefined
? "" ? ""
: hass && hass.states && hass.config && hass.panels && hass.services : hass && hass.states && hass.config && hass.services
? html` ? html`
<home-assistant-main <home-assistant-main
.hass=${this.hass} .hass=${this.hass}

View File

@ -15,12 +15,12 @@ import { haStyle } from "../resources/styles";
@customElement("hass-loading-screen") @customElement("hass-loading-screen")
class HassLoadingScreen extends LitElement { class HassLoadingScreen extends LitElement {
@property() public isRoot? = false; @property({ type: Boolean }) public rootnav? = false;
protected render(): TemplateResult | void { protected render(): TemplateResult | void {
return html` return html`
<app-toolbar> <app-toolbar>
${this.isRoot ${this.rootnav
? html` ? html`
<ha-menu-button></ha-menu-button> <ha-menu-button></ha-menu-button>
` `

View File

@ -13,14 +13,13 @@ const extractPage = (path: string, defaultPage: string) => {
: path.substr(1, subpathStart - 1); : path.substr(1, subpathStart - 1);
}; };
interface RouteOptions { export interface RouteOptions {
tag: string; tag: string;
load: () => Promise<unknown>; load: () => Promise<unknown>;
cache?: boolean; cache?: boolean;
} }
export interface RouterOptions { export interface RouterOptions {
isRoot?: boolean;
defaultPage?: string; defaultPage?: string;
preloadAll?: boolean; preloadAll?: boolean;
cacheAll?: boolean; cacheAll?: boolean;
@ -34,17 +33,19 @@ export interface RouterOptions {
const LOADING_SCREEN_THRESHOLD = 400; // ms const LOADING_SCREEN_THRESHOLD = 400; // ms
export class HassRouterPage extends UpdatingElement { export class HassRouterPage extends UpdatingElement {
protected static routerOptions: RouterOptions = { routes: {} }; @property() public route?: Route;
protected static finalize() { protected routerOptions!: RouterOptions;
super.finalize();
this._routerOptions = this.routerOptions;
}
private static _routerOptions: RouterOptions; /**
* Optional variable to define extra routes dynamically.
@property() public route!: Route; * It is preferred to use static routes.
*/
protected extraRoutes?: {
[route: string]: RouteOptions;
};
private _currentPage = ""; private _currentPage = "";
private _currentLoadProm?: Promise<void>;
private _cache = {}; private _cache = {};
protected update(changedProps: PropertyValues) { protected update(changedProps: PropertyValues) {
@ -52,15 +53,13 @@ export class HassRouterPage extends UpdatingElement {
if (!changedProps.has("route")) { if (!changedProps.has("route")) {
if (this.lastChild) { if (this.lastChild) {
this._updatePageEl(this.lastChild, changedProps); this.updatePageEl(this.lastChild, changedProps);
} }
return; return;
} }
const route = this.route; const route = this.route;
const routerOptions = this.routerOptions || { routes: {} };
const routerOptions = (this.constructor as typeof HassRouterPage)
._routerOptions;
const defaultPage = routerOptions.defaultPage || ""; const defaultPage = routerOptions.defaultPage || "";
if (route && route.path === "") { if (route && route.path === "") {
@ -71,22 +70,22 @@ export class HassRouterPage extends UpdatingElement {
if (this._currentPage === newPage) { if (this._currentPage === newPage) {
if (this.lastChild) { if (this.lastChild) {
this._updatePageEl(this.lastChild, changedProps); this.updatePageEl(this.lastChild, changedProps);
}
return;
}
const routeOptions = routerOptions.routes[newPage];
if (!routeOptions) {
this._currentPage = "";
if (this.lastChild) {
this.removeChild(this.lastChild);
} }
return; return;
} }
this._currentPage = newPage; this._currentPage = newPage;
const routeOptions = routerOptions.routes[newPage];
if (!routeOptions) {
if (this.lastChild) {
this._updatePageEl(this.lastChild, changedProps);
}
return;
}
const loadProm = routeOptions.load(); const loadProm = routeOptions.load();
// Check when loading the page source failed. // Check when loading the page source failed.
@ -125,35 +124,69 @@ export class HassRouterPage extends UpdatingElement {
if (this.lastChild) { if (this.lastChild) {
this.removeChild(this.lastChild); this.removeChild(this.lastChild);
} }
this.appendChild(this.createLoadingScreen());
const loadingEl = document.createElement("hass-loading-screen");
loadingEl.isRoot = routerOptions.isRoot;
this.appendChild(loadingEl);
}, LOADING_SCREEN_THRESHOLD); }, LOADING_SCREEN_THRESHOLD);
loadProm.then(() => { this._currentLoadProm = loadProm.then(
// Check if we're still trying to show the same page. () => {
if (this._currentPage !== newPage) { this._currentLoadProm = undefined;
return; // Check if we're still trying to show the same page.
} if (this._currentPage !== newPage) {
return;
}
created = true; created = true;
this._createPanel(routerOptions, newPage, routeOptions); this._createPanel(routerOptions, newPage, routeOptions);
}); },
() => {
this._currentLoadProm = undefined;
}
);
} }
protected firstUpdated(changedProps: PropertyValues) { protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps); super.firstUpdated(changedProps);
const options = (this.constructor as typeof HassRouterPage)._routerOptions; const options = this.routerOptions;
if (options.preloadAll) { if (options && options.preloadAll) {
Object.values(options.routes).forEach((route) => route.load()); Object.values(options.routes).forEach((route) => route.load());
return; return;
} }
} }
protected _updatePageEl(_pageEl, _changedProps?: PropertyValues) { protected createLoadingScreen() {
return document.createElement("hass-loading-screen");
}
/**
* Rebuild the current panel.
*
* Promise will resolve when rebuilding is done and DOM updated.
*/
protected async rebuild(): Promise<void> {
const oldRoute = this.route;
if (oldRoute === undefined) {
return;
}
this.route = undefined;
await this.updateComplete;
// Make sure that the parent didn't override this in the meanwhile.
if (this.route === undefined) {
this.route = oldRoute;
}
}
/**
* Promise that resolves when the page has rendered.
*/
protected get pageRendered(): Promise<void> {
return this.updateComplete.then(() => this._currentLoadProm);
}
protected updatePageEl(_pageEl, _changedProps?: PropertyValues) {
// default we do nothing // default we do nothing
} }
@ -168,7 +201,7 @@ export class HassRouterPage extends UpdatingElement {
const panelEl = const panelEl =
this._cache[page] || document.createElement(routeOptions.tag); this._cache[page] || document.createElement(routeOptions.tag);
this._updatePageEl(panelEl); this.updatePageEl(panelEl);
this.appendChild(panelEl); this.appendChild(panelEl);
if (routerOptions.cacheAll || routeOptions.cache) { if (routerOptions.cacheAll || routeOptions.cache) {

View File

@ -1,118 +1,96 @@
import { property, customElement } from "lit-element"; import { property, customElement, PropertyValues } from "lit-element";
import { PolymerElement } from "@polymer/polymer"; import { PolymerElement } from "@polymer/polymer";
import { HomeAssistant } from "../types"; import { HomeAssistant, Panels } from "../types";
import { HassRouterPage, RouterOptions } from "./hass-router-page"; import {
HassRouterPage,
RouterOptions,
RouteOptions,
} from "./hass-router-page";
const CACHE_COMPONENTS = ["lovelace", "states"];
const COMPONENTS = {
calendar: () =>
import(/* webpackChunkName: "panel-calendar" */ "../panels/calendar/ha-panel-calendar"),
config: () =>
import(/* webpackChunkName: "panel-config" */ "../panels/config/ha-panel-config"),
custom: () =>
import(/* webpackChunkName: "panel-custom" */ "../panels/custom/ha-panel-custom"),
"dev-event": () =>
import(/* webpackChunkName: "panel-dev-event" */ "../panels/dev-event/ha-panel-dev-event"),
"dev-info": () =>
import(/* webpackChunkName: "panel-dev-info" */ "../panels/dev-info/ha-panel-dev-info"),
"dev-mqtt": () =>
import(/* webpackChunkName: "panel-dev-mqtt" */ "../panels/dev-mqtt/ha-panel-dev-mqtt"),
"dev-service": () =>
import(/* webpackChunkName: "panel-dev-service" */ "../panels/dev-service/ha-panel-dev-service"),
"dev-state": () =>
import(/* webpackChunkName: "panel-dev-state" */ "../panels/dev-state/ha-panel-dev-state"),
"dev-template": () =>
import(/* webpackChunkName: "panel-dev-template" */ "../panels/dev-template/ha-panel-dev-template"),
lovelace: () =>
import(/* webpackChunkName: "panel-lovelace" */ "../panels/lovelace/ha-panel-lovelace"),
states: () =>
import(/* webpackChunkName: "panel-states" */ "../panels/states/ha-panel-states"),
history: () =>
import(/* webpackChunkName: "panel-history" */ "../panels/history/ha-panel-history"),
iframe: () =>
import(/* webpackChunkName: "panel-iframe" */ "../panels/iframe/ha-panel-iframe"),
kiosk: () =>
import(/* webpackChunkName: "panel-kiosk" */ "../panels/kiosk/ha-panel-kiosk"),
logbook: () =>
import(/* webpackChunkName: "panel-logbook" */ "../panels/logbook/ha-panel-logbook"),
mailbox: () =>
import(/* webpackChunkName: "panel-mailbox" */ "../panels/mailbox/ha-panel-mailbox"),
map: () =>
import(/* webpackChunkName: "panel-map" */ "../panels/map/ha-panel-map"),
profile: () =>
import(/* webpackChunkName: "panel-profile" */ "../panels/profile/ha-panel-profile"),
"shopping-list": () =>
import(/* webpackChunkName: "panel-shopping-list" */ "../panels/shopping-list/ha-panel-shopping-list"),
};
const getRoutes = (panels: Panels): RouterOptions => {
const routes: { [route: string]: RouteOptions } = {};
Object.values(panels).forEach((panel) => {
routes[panel.url_path] = {
load: COMPONENTS[panel.component_name],
tag: `ha-panel-${panel.component_name}`,
cache: CACHE_COMPONENTS.includes(panel.component_name),
};
});
return {
showLoading: true,
routes,
};
};
@customElement("partial-panel-resolver") @customElement("partial-panel-resolver")
class PartialPanelResolver extends HassRouterPage { class PartialPanelResolver extends HassRouterPage {
protected static routerOptions: RouterOptions = {
isRoot: true,
showLoading: true,
routes: {
calendar: {
tag: "ha-panel-calendar",
load: () =>
import(/* webpackChunkName: "panel-calendar" */ "../panels/calendar/ha-panel-calendar"),
},
config: {
tag: "ha-panel-config",
load: () =>
import(/* webpackChunkName: "panel-config" */ "../panels/config/ha-panel-config"),
},
custom: {
tag: "ha-panel-custom",
load: () =>
import(/* webpackChunkName: "panel-custom" */ "../panels/custom/ha-panel-custom"),
},
"dev-event": {
tag: "ha-panel-dev-event",
load: () =>
import(/* webpackChunkName: "panel-dev-event" */ "../panels/dev-event/ha-panel-dev-event"),
},
"dev-info": {
tag: "ha-panel-dev-info",
load: () =>
import(/* webpackChunkName: "panel-dev-info" */ "../panels/dev-info/ha-panel-dev-info"),
},
"dev-mqtt": {
tag: "ha-panel-dev-mqtt",
load: () =>
import(/* webpackChunkName: "panel-dev-mqtt" */ "../panels/dev-mqtt/ha-panel-dev-mqtt"),
},
"dev-service": {
tag: "ha-panel-dev-service",
load: () =>
import(/* webpackChunkName: "panel-dev-service" */ "../panels/dev-service/ha-panel-dev-service"),
},
"dev-state": {
tag: "ha-panel-dev-state",
load: () =>
import(/* webpackChunkName: "panel-dev-state" */ "../panels/dev-state/ha-panel-dev-state"),
},
"dev-template": {
tag: "ha-panel-dev-template",
load: () =>
import(/* webpackChunkName: "panel-dev-template" */ "../panels/dev-template/ha-panel-dev-template"),
},
lovelace: {
cache: true,
tag: "ha-panel-lovelace",
load: () =>
import(/* webpackChunkName: "panel-lovelace" */ "../panels/lovelace/ha-panel-lovelace"),
},
states: {
cache: true,
tag: "ha-panel-states",
load: () =>
import(/* webpackChunkName: "panel-states" */ "../panels/states/ha-panel-states"),
},
history: {
tag: "ha-panel-history",
load: () =>
import(/* webpackChunkName: "panel-history" */ "../panels/history/ha-panel-history"),
},
iframe: {
tag: "ha-panel-iframe",
load: () =>
import(/* webpackChunkName: "panel-iframe" */ "../panels/iframe/ha-panel-iframe"),
},
kiosk: {
tag: "ha-panel-kiosk",
load: () =>
import(/* webpackChunkName: "panel-kiosk" */ "../panels/kiosk/ha-panel-kiosk"),
},
logbook: {
tag: "ha-panel-logbook",
load: () =>
import(/* webpackChunkName: "panel-logbook" */ "../panels/logbook/ha-panel-logbook"),
},
mailbox: {
tag: "ha-panel-mailbox",
load: () =>
import(/* webpackChunkName: "panel-mailbox" */ "../panels/mailbox/ha-panel-mailbox"),
},
map: {
tag: "ha-panel-map",
load: () =>
import(/* webpackChunkName: "panel-map" */ "../panels/map/ha-panel-map"),
},
profile: {
tag: "ha-panel-profile",
load: () =>
import(/* webpackChunkName: "panel-profile" */ "../panels/profile/ha-panel-profile"),
},
"shopping-list": {
tag: "ha-panel-shopping-list",
load: () =>
import(/* webpackChunkName: "panel-shopping-list" */ "../panels/shopping-list/ha-panel-shopping-list"),
},
},
};
@property() public hass?: HomeAssistant; @property() public hass?: HomeAssistant;
@property() public narrow?: boolean; @property() public narrow?: boolean;
protected _updatePageEl(el) { protected updated(changedProps: PropertyValues) {
if (!changedProps.has("hass")) {
return;
}
const oldHass = changedProps.get("hass") as this["hass"];
if (!oldHass || oldHass.panels !== this.hass!.panels) {
this._updateRoutes();
}
}
protected createLoadingScreen() {
const el = super.createLoadingScreen();
el.rootnav = true;
return el;
}
protected updatePageEl(el) {
const hass = this.hass!; const hass = this.hass!;
if ("setProperties" in el) { if ("setProperties" in el) {
@ -130,6 +108,17 @@ class PartialPanelResolver extends HassRouterPage {
el.panel = hass.panels[hass.panelUrl]; el.panel = hass.panels[hass.panelUrl];
} }
} }
private async _updateRoutes() {
this.routerOptions = getRoutes(this.hass!.panels);
await this.rebuild();
await this.pageRendered;
const initEl = document.getElementById("ha-init-skeleton");
if (initEl) {
initEl.parentElement!.removeChild(initEl);
}
}
} }
declare global { declare global {

View File

@ -8,7 +8,11 @@ import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page";
@customElement("ha-panel-config") @customElement("ha-panel-config")
class HaPanelConfig extends HassRouterPage { class HaPanelConfig extends HassRouterPage {
protected static routerOptions: RouterOptions = { @property() public hass!: HomeAssistant;
@property() public _wideSidebar: boolean = false;
@property() public _wide: boolean = false;
protected routerOptions: RouterOptions = {
defaultPage: "dashboard", defaultPage: "dashboard",
cacheAll: true, cacheAll: true,
preloadAll: true, preloadAll: true,
@ -81,9 +85,6 @@ class HaPanelConfig extends HassRouterPage {
}, },
}; };
@property() public hass!: HomeAssistant;
@property() public _wideSidebar: boolean = false;
@property() public _wide: boolean = false;
@property() private _cloudStatus?: CloudStatus; @property() private _cloudStatus?: CloudStatus;
private _listeners: Array<() => void> = []; private _listeners: Array<() => void> = [];
@ -119,7 +120,7 @@ class HaPanelConfig extends HassRouterPage {
); );
} }
protected _updatePageEl(el) { protected updatePageEl(el) {
el.route = this.route; el.route = this.route;
el.hass = this.hass; el.hass = this.hass;
el.isWide = this.hass.dockedSidebar ? this._wideSidebar : this._wide; el.isWide = this.hass.dockedSidebar ? this._wideSidebar : this._wide;

View File

@ -85,7 +85,7 @@ class LovelacePanel extends LitElement {
} }
return html` return html`
<hass-loading-screen></hass-loading-screen> <hass-loading-screen rootnav></hass-loading-screen>
`; `;
} }