Add my support to supervisor (#8405)

* Add my support to supervisor

* Remove localize

* Comments

* Update ha-panel-my.ts
This commit is contained in:
Bram Kragten 2021-02-16 19:50:35 +01:00 committed by GitHub
parent e8daf88729
commit 0ec58007c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 219 additions and 24 deletions

View File

@ -9,16 +9,21 @@ import {
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import memoizeOne from "memoize-one";
import { navigate } from "../../../src/common/navigate";
import { extractSearchParam } from "../../../src/common/url/search-params";
import "../../../src/components/ha-circular-progress";
import {
fetchHassioAddonInfo,
HassioAddonDetails,
} from "../../../src/data/hassio/addon";
import "../../../src/layouts/hass-loading-screen";
import "../../../src/layouts/hass-error-screen";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import "../../../src/layouts/hass-tabs-subpage";
import type { PageNavigation } from "../../../src/layouts/hass-tabs-subpage";
@ -31,6 +36,7 @@ import "./config/hassio-addon-network";
import "./hassio-addon-router";
import "./info/hassio-addon-info";
import "./log/hassio-addon-logs";
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
@customElement("hassio-addon-dashboard")
class HassioAddonDashboard extends LitElement {
@ -44,6 +50,8 @@ class HassioAddonDashboard extends LitElement {
@property({ type: Boolean }) public narrow!: boolean;
@internalProperty() _error?: string;
private _computeTail = memoizeOne((route: Route) => {
const dividerPos = route.path.indexOf("/", 1);
return dividerPos === -1
@ -58,8 +66,14 @@ class HassioAddonDashboard extends LitElement {
});
protected render(): TemplateResult {
if (this._error) {
return html`<hass-error-screen
.error=${this._error}
></hass-error-screen>`;
}
if (!this.addon) {
return html`<ha-circular-progress active></ha-circular-progress>`;
return html`<hass-loading-screen></hass-loading-screen>`;
}
const addonTabs: PageNavigation[] = [
@ -156,7 +170,12 @@ class HassioAddonDashboard extends LitElement {
}
protected async firstUpdated(): Promise<void> {
await this._routeDataChanged(this.route);
if (this.route.path === "") {
const addon = extractSearchParam("addon");
if (addon) {
navigate(this, `/hassio/addon/${addon}`, true);
}
}
this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev));
}
@ -170,16 +189,26 @@ class HassioAddonDashboard extends LitElement {
if (path === "uninstall") {
history.back();
} else {
await this._routeDataChanged(this.route);
await this._routeDataChanged();
}
}
private async _routeDataChanged(routeData: Route): Promise<void> {
const addon = routeData.path.split("/")[1];
protected updated(changedProperties) {
if (changedProperties.has("route") && !this.addon) {
this._routeDataChanged();
}
}
private async _routeDataChanged(): Promise<void> {
const addon = this.route.path.split("/")[1];
if (!addon) {
return;
}
try {
const addoninfo = await fetchHassioAddonInfo(this.hass, addon);
this.addon = addoninfo;
} catch {
} catch (err) {
this._error = `Error fetching addon info: ${extractApiErrorMessage(err)}`;
this.addon = undefined;
}
}

View File

@ -0,0 +1,125 @@
import {
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { sanitizeUrl } from "@braintree/sanitize-url";
import {
createSearchParam,
extractSearchParamsObject,
} from "../../src/common/url/search-params";
import "../../src/layouts/hass-error-screen";
import {
ParamType,
Redirect,
Redirects,
} from "../../src/panels/my/ha-panel-my";
import { navigate } from "../../src/common/navigate";
import { HomeAssistant, Route } from "../../src/types";
const REDIRECTS: Redirects = {
supervisor_system: {
redirect: "/hassio/system",
},
supervisor_snapshots: {
redirect: "/hassio/snapshots",
},
supervisor_store: {
redirect: "/hassio/store",
},
supervisor: {
redirect: "/hassio/dashboard",
},
supervisor_addon: {
redirect: "/hassio/addon",
params: {
addon: "string",
},
},
};
@customElement("hassio-my-redirect")
class HassioMyRedirect extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public route!: Route;
@internalProperty() public _error?: TemplateResult | string;
connectedCallback() {
super.connectedCallback();
const path = this.route.path.substr(1);
const redirect = REDIRECTS[path];
if (!redirect) {
this._error = html`This redirect is not supported by your Home Assistant
instance. Check the
<a
target="_blank"
rel="noreferrer noopener"
href="https://my.home-assistant.io/faq.html#supported-pages"
>My Home Assistant FAQ</a
>
for the supported redirects and the version they where introduced.`;
return;
}
let url: string;
try {
url = this._createRedirectUrl(redirect);
} catch (err) {
this._error = "An unknown error occured";
return;
}
navigate(this, url, true);
}
protected render(): TemplateResult {
if (this._error) {
return html`<hass-error-screen
.error=${this._error}
></hass-error-screen>`;
}
return html``;
}
private _createRedirectUrl(redirect: Redirect): string {
const params = this._createRedirectParams(redirect);
return `${redirect.redirect}${params}`;
}
private _createRedirectParams(redirect: Redirect): string {
const params = extractSearchParamsObject();
if (!redirect.params && !Object.keys(params).length) {
return "";
}
const resultParams = {};
Object.entries(redirect.params || {}).forEach(([key, type]) => {
if (!params[key] || !this._checkParamType(type, params[key])) {
throw Error();
}
resultParams[key] = params[key];
});
return `?${createSearchParam(resultParams)}`;
}
private _checkParamType(type: ParamType, value: string) {
if (type === "string") {
return true;
}
if (type === "url") {
return value && value === sanitizeUrl(value);
}
return false;
}
}
declare global {
interface HTMLElementTagNameMap {
"hassio-my-redirect": HassioMyRedirect;
}
}

View File

@ -41,6 +41,10 @@ class HassioRouter extends HassRouterPage {
tag: "hassio-ingress-view",
load: () => import("./ingress-view/hassio-ingress-view"),
},
_my_redirect: {
tag: "hassio-my-redirect",
load: () => import("./hassio-my-redirect"),
},
},
};

View File

@ -380,22 +380,24 @@ export class QuickBar extends LitElement {
QuickBarNavigationItem,
"action"
>[] {
return Object.keys(this.hass.panels).map((panelKey) => {
const panel = this.hass.panels[panelKey];
const translationKey = getPanelNameTranslationKey(panel);
return Object.keys(this.hass.panels)
.filter((panelKey) => panelKey !== "_my_redirect")
.map((panelKey) => {
const panel = this.hass.panels[panelKey];
const translationKey = getPanelNameTranslationKey(panel);
const text = this.hass.localize(
"ui.dialogs.quick-bar.commands.navigation.navigate_to",
"panel",
this.hass.localize(translationKey) || panel.title || panel.url_path
);
const text = this.hass.localize(
"ui.dialogs.quick-bar.commands.navigation.navigate_to",
"panel",
this.hass.localize(translationKey) || panel.title || panel.url_path
);
return {
text,
icon: getPanelIcon(panel) || DEFAULT_NAVIGATION_ICON,
path: `/${panel.url_path}`,
};
});
return {
text,
icon: getPanelIcon(panel) || DEFAULT_NAVIGATION_ICON,
path: `/${panel.url_path}`,
};
});
}
private _generateNavigationConfigSectionCommands(): Partial<

View File

@ -13,8 +13,10 @@ import {
extractSearchParamsObject,
} from "../../common/url/search-params";
import "../../layouts/hass-error-screen";
import { isComponentLoaded } from "../../common/config/is_component_loaded";
import { domainToName } from "../../data/integration";
const REDIRECTS = {
const REDIRECTS: Redirects = {
info: {
redirect: "/config/info",
},
@ -38,10 +40,12 @@ const REDIRECTS = {
},
};
type ParamType = "url" | "string";
export type ParamType = "url" | "string";
interface Redirect {
export type Redirects = { [key: string]: Redirect };
export interface Redirect {
redirect: string;
component?: string;
params?: {
[key: string]: ParamType;
};
@ -58,7 +62,25 @@ class HaPanelMy extends LitElement {
connectedCallback() {
super.connectedCallback();
const path = this.route.path.substr(1);
const redirect: Redirect | undefined = REDIRECTS[path];
if (path.startsWith("supervisor")) {
if (!isComponentLoaded(this.hass, "hassio")) {
this._error = this.hass.localize(
"ui.panel.my.component_not_loaded",
"integration",
domainToName(this.hass.localize, "hassio")
);
return;
}
navigate(
this,
`/hassio/_my_redirect/${path}${window.location.search}`,
true
);
return;
}
const redirect = REDIRECTS[path];
if (!redirect) {
this._error = this.hass.localize(
@ -74,6 +96,18 @@ class HaPanelMy extends LitElement {
return;
}
if (
redirect.component &&
!isComponentLoaded(this.hass, redirect.component)
) {
this._error = this.hass.localize(
"ui.panel.my.component_not_loaded",
"integration",
domainToName(this.hass.localize, redirect.component)
);
return;
}
let url: string;
try {
url = this._createRedirectUrl(redirect);

View File

@ -806,6 +806,7 @@
"panel": {
"my": {
"not_supported": "This redirect is not supported by your Home Assistant instance. Check the {link} for the supported redirects and the version they where introduced.",
"component_not_loaded": "This redirect is not supported by your Home Assistant instance. You need the integration {integration} to use this redirect.",
"faq_link": "My Home Assistant FAQ",
"error": "An unknown error occured"
},