mirror of
https://github.com/home-assistant/frontend.git
synced 2025-04-26 06:17:20 +00:00
Convert cloud account config to Lit (#10350)
This commit is contained in:
parent
7c2e0aea92
commit
95c6adc739
@ -78,7 +78,6 @@
|
|||||||
"@polymer/paper-input": "^3.2.1",
|
"@polymer/paper-input": "^3.2.1",
|
||||||
"@polymer/paper-item": "^3.0.1",
|
"@polymer/paper-item": "^3.0.1",
|
||||||
"@polymer/paper-listbox": "^3.0.1",
|
"@polymer/paper-listbox": "^3.0.1",
|
||||||
"@polymer/paper-ripple": "^3.0.2",
|
|
||||||
"@polymer/paper-slider": "^3.0.1",
|
"@polymer/paper-slider": "^3.0.1",
|
||||||
"@polymer/paper-styles": "^3.0.1",
|
"@polymer/paper-styles": "^3.0.1",
|
||||||
"@polymer/paper-tabs": "^3.1.0",
|
"@polymer/paper-tabs": "^3.1.0",
|
||||||
|
@ -62,6 +62,8 @@ export type CloudStatus = CloudStatusNotLoggedIn | CloudStatusLoggedIn;
|
|||||||
|
|
||||||
export interface SubscriptionInfo {
|
export interface SubscriptionInfo {
|
||||||
human_description: string;
|
human_description: string;
|
||||||
|
provider: string;
|
||||||
|
plan_renewal_date?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CloudWebhook {
|
export interface CloudWebhook {
|
||||||
@ -76,6 +78,39 @@ export interface ThingTalkConversion {
|
|||||||
placeholders: PlaceholderContainer;
|
placeholders: PlaceholderContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const cloudLogin = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
email: string,
|
||||||
|
password: string
|
||||||
|
) =>
|
||||||
|
hass.callApi("POST", "cloud/login", {
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const cloudLogout = (hass: HomeAssistant) =>
|
||||||
|
hass.callApi("POST", "cloud/logout");
|
||||||
|
|
||||||
|
export const cloudForgotPassword = (hass: HomeAssistant, email: string) =>
|
||||||
|
hass.callApi("POST", "cloud/forgot_password", {
|
||||||
|
email,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const cloudRegister = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
email: string,
|
||||||
|
password: string
|
||||||
|
) =>
|
||||||
|
hass.callApi("POST", "cloud/register", {
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const cloudResendVerification = (hass: HomeAssistant, email: string) =>
|
||||||
|
hass.callApi("POST", "cloud/resend_confirm", {
|
||||||
|
email,
|
||||||
|
});
|
||||||
|
|
||||||
export const fetchCloudStatus = (hass: HomeAssistant) =>
|
export const fetchCloudStatus = (hass: HomeAssistant) =>
|
||||||
hass.callWS<CloudStatus>({ type: "cloud/status" });
|
hass.callWS<CloudStatus>({ type: "cloud/status" });
|
||||||
|
|
||||||
|
@ -1,246 +0,0 @@
|
|||||||
import "@material/mwc-button";
|
|
||||||
import "@polymer/paper-item/paper-item-body";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import { formatDateTime } from "../../../../common/datetime/format_date_time";
|
|
||||||
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
|
|
||||||
import "../../../../components/buttons/ha-call-api-button";
|
|
||||||
import "../../../../components/ha-card";
|
|
||||||
import { fetchCloudSubscriptionInfo } from "../../../../data/cloud";
|
|
||||||
import "../../../../layouts/hass-subpage";
|
|
||||||
import { EventsMixin } from "../../../../mixins/events-mixin";
|
|
||||||
import LocalizeMixin from "../../../../mixins/localize-mixin";
|
|
||||||
import "../../../../styles/polymer-ha-style";
|
|
||||||
import "../../ha-config-section";
|
|
||||||
import "./cloud-alexa-pref";
|
|
||||||
import "./cloud-google-pref";
|
|
||||||
import "./cloud-remote-pref";
|
|
||||||
import "./cloud-tts-pref";
|
|
||||||
import "./cloud-webhooks";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @appliesMixin EventsMixin
|
|
||||||
* @appliesMixin LocalizeMixin
|
|
||||||
*/
|
|
||||||
class CloudAccount extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex ha-style">
|
|
||||||
[slot="introduction"] {
|
|
||||||
margin: -1em 0;
|
|
||||||
}
|
|
||||||
[slot="introduction"] a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
.content {
|
|
||||||
padding-bottom: 24px;
|
|
||||||
}
|
|
||||||
.account-row {
|
|
||||||
display: flex;
|
|
||||||
padding: 0 16px;
|
|
||||||
}
|
|
||||||
mwc-button {
|
|
||||||
align-self: center;
|
|
||||||
}
|
|
||||||
.soon {
|
|
||||||
font-style: italic;
|
|
||||||
margin-top: 24px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.nowrap {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.wrap {
|
|
||||||
white-space: normal;
|
|
||||||
}
|
|
||||||
.status {
|
|
||||||
text-transform: capitalize;
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<hass-subpage
|
|
||||||
hass="[[hass]]"
|
|
||||||
narrow="[[narrow]]"
|
|
||||||
header="Home Assistant Cloud"
|
|
||||||
>
|
|
||||||
<div class="content">
|
|
||||||
<ha-config-section is-wide="[[isWide]]">
|
|
||||||
<span slot="header">Home Assistant Cloud</span>
|
|
||||||
<div slot="introduction">
|
|
||||||
<p>
|
|
||||||
[[localize('ui.panel.config.cloud.account.thank_you_note')]]
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ha-card
|
|
||||||
header="[[localize('ui.panel.config.cloud.account.nabu_casa_account')]]"
|
|
||||||
>
|
|
||||||
<div class="account-row">
|
|
||||||
<paper-item-body two-line="">
|
|
||||||
[[cloudStatus.email]]
|
|
||||||
<div secondary class="wrap">
|
|
||||||
[[_formatSubscription(_subscription)]]
|
|
||||||
</div>
|
|
||||||
</paper-item-body>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="account-row">
|
|
||||||
<paper-item-body
|
|
||||||
>[[localize('ui.panel.config.cloud.account.connection_status')]]</paper-item-body
|
|
||||||
>
|
|
||||||
<div class="status">
|
|
||||||
[[_computeConnectionStatus(cloudStatus.cloud)]]
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-actions">
|
|
||||||
<a
|
|
||||||
href="https://account.nabucasa.com"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
<mwc-button
|
|
||||||
>[[localize('ui.panel.config.cloud.account.manage_account')]]</mwc-button
|
|
||||||
>
|
|
||||||
</a>
|
|
||||||
<mwc-button style="float: right" on-click="handleLogout"
|
|
||||||
>[[localize('ui.panel.config.cloud.account.sign_out')]]</mwc-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
</ha-config-section>
|
|
||||||
|
|
||||||
<ha-config-section is-wide="[[isWide]]">
|
|
||||||
<span slot="header"
|
|
||||||
>[[localize('ui.panel.config.cloud.account.integrations')]]</span
|
|
||||||
>
|
|
||||||
<div slot="introduction">
|
|
||||||
<p>
|
|
||||||
[[localize('ui.panel.config.cloud.account.integrations_introduction')]]
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
[[localize('ui.panel.config.cloud.account.integrations_introduction2')]]
|
|
||||||
<a
|
|
||||||
href="https://www.nabucasa.com"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
[[localize('ui.panel.config.cloud.account.integrations_link_all_features')]]</a
|
|
||||||
>.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<cloud-remote-pref
|
|
||||||
hass="[[hass]]"
|
|
||||||
cloud-status="[[cloudStatus]]"
|
|
||||||
dir="[[_rtlDirection]]"
|
|
||||||
></cloud-remote-pref>
|
|
||||||
|
|
||||||
<cloud-tts-pref
|
|
||||||
hass="[[hass]]"
|
|
||||||
cloud-status="[[cloudStatus]]"
|
|
||||||
dir="[[_rtlDirection]]"
|
|
||||||
></cloud-tts-pref>
|
|
||||||
|
|
||||||
<cloud-alexa-pref
|
|
||||||
hass="[[hass]]"
|
|
||||||
cloud-status="[[cloudStatus]]"
|
|
||||||
dir="[[_rtlDirection]]"
|
|
||||||
></cloud-alexa-pref>
|
|
||||||
|
|
||||||
<cloud-google-pref
|
|
||||||
hass="[[hass]]"
|
|
||||||
cloud-status="[[cloudStatus]]"
|
|
||||||
dir="[[_rtlDirection]]"
|
|
||||||
></cloud-google-pref>
|
|
||||||
|
|
||||||
<cloud-webhooks
|
|
||||||
hass="[[hass]]"
|
|
||||||
cloud-status="[[cloudStatus]]"
|
|
||||||
dir="[[_rtlDirection]]"
|
|
||||||
></cloud-webhooks>
|
|
||||||
</ha-config-section>
|
|
||||||
</div>
|
|
||||||
</hass-subpage>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
isWide: Boolean,
|
|
||||||
narrow: Boolean,
|
|
||||||
cloudStatus: Object,
|
|
||||||
_subscription: {
|
|
||||||
type: Object,
|
|
||||||
value: null,
|
|
||||||
},
|
|
||||||
_rtlDirection: {
|
|
||||||
type: Boolean,
|
|
||||||
computed: "_computeRTLDirection(hass)",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ready() {
|
|
||||||
super.ready();
|
|
||||||
this._fetchSubscriptionInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
_computeConnectionStatus(status) {
|
|
||||||
return status === "connected"
|
|
||||||
? this.hass.localize("ui.panel.config.cloud.account.connected")
|
|
||||||
: status === "disconnected"
|
|
||||||
? this.hass.localize("ui.panel.config.cloud.account.not_connected")
|
|
||||||
: this.hass.localize("ui.panel.config.cloud.account.connecting");
|
|
||||||
}
|
|
||||||
|
|
||||||
async _fetchSubscriptionInfo() {
|
|
||||||
this._subscription = await fetchCloudSubscriptionInfo(this.hass);
|
|
||||||
if (
|
|
||||||
this._subscription.provider &&
|
|
||||||
this.cloudStatus &&
|
|
||||||
this.cloudStatus.cloud !== "connected"
|
|
||||||
) {
|
|
||||||
this.fire("ha-refresh-cloud-status");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleLogout() {
|
|
||||||
this.hass
|
|
||||||
.callApi("post", "cloud/logout")
|
|
||||||
.then(() => this.fire("ha-refresh-cloud-status"));
|
|
||||||
}
|
|
||||||
|
|
||||||
_formatSubscription(subInfo) {
|
|
||||||
if (subInfo === null) {
|
|
||||||
return this.hass.localize(
|
|
||||||
"ui.panel.config.cloud.account.fetching_subscription"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let description = subInfo.human_description;
|
|
||||||
|
|
||||||
if (subInfo.plan_renewal_date) {
|
|
||||||
description = description.replace(
|
|
||||||
"{periodEnd}",
|
|
||||||
formatDateTime(
|
|
||||||
new Date(subInfo.plan_renewal_date * 1000),
|
|
||||||
this.hass.locale
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
_computeRTLDirection(hass) {
|
|
||||||
return computeRTLDirection(hass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("cloud-account", CloudAccount);
|
|
268
src/panels/config/cloud/account/cloud-account.ts
Normal file
268
src/panels/config/cloud/account/cloud-account.ts
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
|
import "@polymer/paper-item/paper-item-body";
|
||||||
|
import { LitElement, css, html, PropertyValues } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { formatDateTime } from "../../../../common/datetime/format_date_time";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
|
||||||
|
import "../../../../components/buttons/ha-call-api-button";
|
||||||
|
import "../../../../components/ha-card";
|
||||||
|
import {
|
||||||
|
cloudLogout,
|
||||||
|
CloudStatusLoggedIn,
|
||||||
|
fetchCloudSubscriptionInfo,
|
||||||
|
SubscriptionInfo,
|
||||||
|
} from "../../../../data/cloud";
|
||||||
|
import "../../../../layouts/hass-subpage";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
import "../../ha-config-section";
|
||||||
|
import "./cloud-alexa-pref";
|
||||||
|
import "./cloud-google-pref";
|
||||||
|
import "./cloud-remote-pref";
|
||||||
|
import "./cloud-tts-pref";
|
||||||
|
import "./cloud-webhooks";
|
||||||
|
|
||||||
|
@customElement("cloud-account")
|
||||||
|
export class CloudAccount extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public isWide = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public cloudStatus!: CloudStatusLoggedIn;
|
||||||
|
|
||||||
|
@state() private _subscription?: SubscriptionInfo;
|
||||||
|
|
||||||
|
@state() private _rtlDirection: "rtl" | "ltr" = "rtl";
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
return html`
|
||||||
|
<hass-subpage
|
||||||
|
.hass=${this.hass}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
header="Home Assistant Cloud"
|
||||||
|
>
|
||||||
|
<div class="content">
|
||||||
|
<ha-config-section .isWide=${this.isWide}>
|
||||||
|
<span slot="header">Home Assistant Cloud</span>
|
||||||
|
<div slot="introduction">
|
||||||
|
<p>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.account.thank_you_note"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ha-card
|
||||||
|
.header=${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.account.nabu_casa_account"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div class="account-row">
|
||||||
|
<paper-item-body two-line>
|
||||||
|
${this.cloudStatus.email}
|
||||||
|
<div secondary class="wrap">
|
||||||
|
${this._subscription
|
||||||
|
? this._subscription.human_description.replace(
|
||||||
|
"{periodEnd}",
|
||||||
|
this._subscription.plan_renewal_date
|
||||||
|
? formatDateTime(
|
||||||
|
new Date(
|
||||||
|
this._subscription.plan_renewal_date * 1000
|
||||||
|
),
|
||||||
|
this.hass.locale
|
||||||
|
)
|
||||||
|
: ""
|
||||||
|
)
|
||||||
|
: this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.account.fetching_subscription"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</paper-item-body>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="account-row">
|
||||||
|
<paper-item-body>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.account.connection_status"
|
||||||
|
)}
|
||||||
|
</paper-item-body>
|
||||||
|
<div class="status">
|
||||||
|
${this.cloudStatus.cloud === "connected"
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.account.connected"
|
||||||
|
)
|
||||||
|
: this.cloudStatus.cloud === "disconnected"
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.account.not_connected"
|
||||||
|
)
|
||||||
|
: this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.account.connecting"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-actions">
|
||||||
|
<a
|
||||||
|
href="https://account.nabucasa.com"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
<mwc-button>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.account.manage_account"
|
||||||
|
)}
|
||||||
|
</mwc-button>
|
||||||
|
</a>
|
||||||
|
<mwc-button @click=${this._handleLogout}
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.account.sign_out"
|
||||||
|
)}</mwc-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
</ha-config-section>
|
||||||
|
|
||||||
|
<ha-config-section .isWide=${this.isWide}>
|
||||||
|
<span slot="header"
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.account.integrations"
|
||||||
|
)}</span
|
||||||
|
>
|
||||||
|
<div slot="introduction">
|
||||||
|
<p>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.account.integrations_introduction"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.account.integrations_introduction2"
|
||||||
|
)}
|
||||||
|
<a
|
||||||
|
href="https://www.nabucasa.com"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.account.integrations_link_all_features"
|
||||||
|
)}</a
|
||||||
|
>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<cloud-remote-pref
|
||||||
|
.hass=${this.hass}
|
||||||
|
.cloudStatus=${this.cloudStatus}
|
||||||
|
dir=${this._rtlDirection}
|
||||||
|
></cloud-remote-pref>
|
||||||
|
|
||||||
|
<cloud-tts-pref
|
||||||
|
.hass=${this.hass}
|
||||||
|
.cloudStatus=${this.cloudStatus}
|
||||||
|
dir=${this._rtlDirection}
|
||||||
|
></cloud-tts-pref>
|
||||||
|
|
||||||
|
<cloud-alexa-pref
|
||||||
|
.hass=${this.hass}
|
||||||
|
.cloudStatus=${this.cloudStatus}
|
||||||
|
dir=${this._rtlDirection}
|
||||||
|
></cloud-alexa-pref>
|
||||||
|
|
||||||
|
<cloud-google-pref
|
||||||
|
.hass=${this.hass}
|
||||||
|
.cloudStatus=${this.cloudStatus}
|
||||||
|
dir=${this._rtlDirection}
|
||||||
|
></cloud-google-pref>
|
||||||
|
|
||||||
|
<cloud-webhooks
|
||||||
|
.hass=${this.hass}
|
||||||
|
.cloudStatus=${this.cloudStatus}
|
||||||
|
dir=${this._rtlDirection}
|
||||||
|
></cloud-webhooks>
|
||||||
|
</ha-config-section>
|
||||||
|
</div>
|
||||||
|
</hass-subpage>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
this._fetchSubscriptionInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updated(changedProps: PropertyValues) {
|
||||||
|
if (changedProps.has("hass")) {
|
||||||
|
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||||
|
if (!oldHass || oldHass.locale !== this.hass.locale) {
|
||||||
|
this._rtlDirection = computeRTLDirection(this.hass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _fetchSubscriptionInfo() {
|
||||||
|
this._subscription = await fetchCloudSubscriptionInfo(this.hass);
|
||||||
|
if (
|
||||||
|
this._subscription.provider &&
|
||||||
|
this.cloudStatus &&
|
||||||
|
this.cloudStatus.cloud !== "connected"
|
||||||
|
) {
|
||||||
|
fireEvent(this, "ha-refresh-cloud-status");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _handleLogout() {
|
||||||
|
await cloudLogout(this.hass);
|
||||||
|
fireEvent(this, "ha-refresh-cloud-status");
|
||||||
|
}
|
||||||
|
|
||||||
|
_computeRTLDirection(hass) {
|
||||||
|
return computeRTLDirection(hass);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
[slot="introduction"] {
|
||||||
|
margin: -1em 0;
|
||||||
|
}
|
||||||
|
[slot="introduction"] a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding-bottom: 24px;
|
||||||
|
}
|
||||||
|
.account-row {
|
||||||
|
display: flex;
|
||||||
|
padding: 0 16px;
|
||||||
|
}
|
||||||
|
.card-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.card-actions a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
mwc-button {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
.wrap {
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
.status {
|
||||||
|
text-transform: capitalize;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("cloud-account", CloudAccount);
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"cloud-account": CloudAccount;
|
||||||
|
}
|
||||||
|
}
|
@ -1,152 +0,0 @@
|
|||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "../../../../components/buttons/ha-progress-button";
|
|
||||||
import "../../../../components/ha-card";
|
|
||||||
import "../../../../layouts/hass-subpage";
|
|
||||||
import { EventsMixin } from "../../../../mixins/events-mixin";
|
|
||||||
import LocalizeMixin from "../../../../mixins/localize-mixin";
|
|
||||||
import "../../../../styles/polymer-ha-style";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @appliesMixin EventsMixin
|
|
||||||
* @appliesMixin LocalizeMixin
|
|
||||||
*/
|
|
||||||
class CloudForgotPassword extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex ha-style">
|
|
||||||
.content {
|
|
||||||
padding-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ha-card {
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 0 auto;
|
|
||||||
margin-top: 24px;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
@apply --paper-font-headline;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.error {
|
|
||||||
color: var(--error-color);
|
|
||||||
}
|
|
||||||
.card-actions {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.card-actions a {
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
[hidden] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<hass-subpage
|
|
||||||
hass="[[hass]]"
|
|
||||||
narrow="[[narrow]]"
|
|
||||||
header="[[localize('ui.panel.config.cloud.forgot_password.title')]]"
|
|
||||||
>
|
|
||||||
<div class="content">
|
|
||||||
<ha-card
|
|
||||||
header="[[localize('ui.panel.config.cloud.forgot_password.subtitle')]]"
|
|
||||||
>
|
|
||||||
<div class="card-content">
|
|
||||||
<p>
|
|
||||||
[[localize('ui.panel.config.cloud.forgot_password.instructions')]]
|
|
||||||
</p>
|
|
||||||
<div class="error" hidden$="[[!_error]]">[[_error]]</div>
|
|
||||||
<paper-input
|
|
||||||
autofocus=""
|
|
||||||
id="email"
|
|
||||||
label="[[localize('ui.panel.config.cloud.forgot_password.email')]]"
|
|
||||||
value="{{email}}"
|
|
||||||
type="email"
|
|
||||||
on-keydown="_keyDown"
|
|
||||||
error-message="[[localize('ui.panel.config.cloud.forgot_password.email_error_msg')]]"
|
|
||||||
></paper-input>
|
|
||||||
</div>
|
|
||||||
<div class="card-actions">
|
|
||||||
<ha-progress-button
|
|
||||||
on-click="_handleEmailPasswordReset"
|
|
||||||
progress="[[_requestInProgress]]"
|
|
||||||
>[[localize('ui.panel.config.cloud.forgot_password.send_reset_email')]]</ha-progress-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
</div>
|
|
||||||
</hass-subpage>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
narrow: Boolean,
|
|
||||||
email: {
|
|
||||||
type: String,
|
|
||||||
notify: true,
|
|
||||||
observer: "_emailChanged",
|
|
||||||
},
|
|
||||||
_requestInProgress: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
_error: {
|
|
||||||
type: String,
|
|
||||||
value: "",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_emailChanged() {
|
|
||||||
this._error = "";
|
|
||||||
this.$.email.invalid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_keyDown(ev) {
|
|
||||||
// validate on enter
|
|
||||||
if (ev.keyCode === 13) {
|
|
||||||
this._handleEmailPasswordReset();
|
|
||||||
ev.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleEmailPasswordReset() {
|
|
||||||
if (!this.email || !this.email.includes("@")) {
|
|
||||||
this.$.email.invalid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.$.email.invalid) return;
|
|
||||||
|
|
||||||
this._requestInProgress = true;
|
|
||||||
|
|
||||||
this.hass
|
|
||||||
.callApi("post", "cloud/forgot_password", {
|
|
||||||
email: this.email,
|
|
||||||
})
|
|
||||||
.then(
|
|
||||||
() => {
|
|
||||||
this._requestInProgress = false;
|
|
||||||
this.fire("cloud-done", {
|
|
||||||
flashMessage: this.hass.localize(
|
|
||||||
"ui.panel.config.cloud.forgot_password.check_your_email"
|
|
||||||
),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
(err) =>
|
|
||||||
this.setProperties({
|
|
||||||
_requestInProgress: false,
|
|
||||||
_error:
|
|
||||||
err && err.body && err.body.message
|
|
||||||
? err.body.message
|
|
||||||
: "Unknown error",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("cloud-forgot-password", CloudForgotPassword);
|
|
156
src/panels/config/cloud/forgot-password/cloud-forgot-password.ts
Normal file
156
src/panels/config/cloud/forgot-password/cloud-forgot-password.ts
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
import "@material/mwc-textfield/mwc-textfield";
|
||||||
|
import type { TextField } from "@material/mwc-textfield/mwc-textfield";
|
||||||
|
import { css, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import "../../../../components/buttons/ha-progress-button";
|
||||||
|
import "../../../../components/ha-alert";
|
||||||
|
import "../../../../components/ha-card";
|
||||||
|
import { cloudForgotPassword } from "../../../../data/cloud";
|
||||||
|
import "../../../../layouts/hass-subpage";
|
||||||
|
import { haStyle } from "../../../../resources/styles";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
|
||||||
|
@customElement("cloud-forgot-password")
|
||||||
|
export class CloudForgotPassword extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
|
@property() public email?: string;
|
||||||
|
|
||||||
|
@state() public _requestInProgress = false;
|
||||||
|
|
||||||
|
@state() private _error?: string;
|
||||||
|
|
||||||
|
@query("#email", true) private _emailField!: TextField;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<hass-subpage
|
||||||
|
.hass=${this.hass}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
.header=${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.forgot_password.title"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div class="content">
|
||||||
|
<ha-card
|
||||||
|
.header=${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.forgot_password.subtitle"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div class="card-content">
|
||||||
|
<p>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.forgot_password.instructions"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
${this._error
|
||||||
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
|
: ""}
|
||||||
|
<mwc-textfield
|
||||||
|
autofocus
|
||||||
|
id="email"
|
||||||
|
label=${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.forgot_password.email"
|
||||||
|
)}
|
||||||
|
.value=${this.email}
|
||||||
|
type="email"
|
||||||
|
required
|
||||||
|
@keydown=${this._keyDown}
|
||||||
|
.validationMessage=${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.forgot_password.email_error_msg"
|
||||||
|
)}
|
||||||
|
></mwc-textfield>
|
||||||
|
</div>
|
||||||
|
<div class="card-actions">
|
||||||
|
<ha-progress-button
|
||||||
|
@click=${this._handleEmailPasswordReset}
|
||||||
|
.progress=${this._requestInProgress}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.forgot_password.send_reset_email"
|
||||||
|
)}
|
||||||
|
</ha-progress-button>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
</div>
|
||||||
|
</hass-subpage>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _keyDown(ev: KeyboardEvent) {
|
||||||
|
if (ev.key === "Enter") {
|
||||||
|
this._handleEmailPasswordReset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _handleEmailPasswordReset() {
|
||||||
|
const emailField = this._emailField;
|
||||||
|
|
||||||
|
const email = emailField.value;
|
||||||
|
|
||||||
|
if (!emailField.reportValidity()) {
|
||||||
|
emailField.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._requestInProgress = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await cloudForgotPassword(this.hass, email);
|
||||||
|
// @ts-ignore
|
||||||
|
fireEvent(this, "email-changed", { value: email });
|
||||||
|
this._requestInProgress = false;
|
||||||
|
// @ts-ignore
|
||||||
|
fireEvent(this, "cloud-done", {
|
||||||
|
flashMessage: this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.forgot_password.check_your_email"
|
||||||
|
),
|
||||||
|
});
|
||||||
|
} catch (err: any) {
|
||||||
|
this._requestInProgress = false;
|
||||||
|
this._error =
|
||||||
|
err && err.body && err.body.message
|
||||||
|
? err.body.message
|
||||||
|
: "Unknown error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
.content {
|
||||||
|
padding-bottom: 24px;
|
||||||
|
}
|
||||||
|
ha-card {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
mwc-textfield {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.card-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.card-actions a {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"cloud-forgot-password": CloudForgotPassword;
|
||||||
|
}
|
||||||
|
}
|
@ -1,336 +0,0 @@
|
|||||||
import "@material/mwc-button";
|
|
||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-item/paper-item-body";
|
|
||||||
import "@polymer/paper-ripple/paper-ripple";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import { computeRTL } from "../../../../common/util/compute_rtl";
|
|
||||||
import "../../../../components/buttons/ha-progress-button";
|
|
||||||
import "../../../../components/ha-card";
|
|
||||||
import "../../../../components/ha-icon";
|
|
||||||
import "../../../../components/ha-icon-button";
|
|
||||||
import "../../../../components/ha-icon-next";
|
|
||||||
import "../../../../layouts/hass-subpage";
|
|
||||||
import { EventsMixin } from "../../../../mixins/events-mixin";
|
|
||||||
import LocalizeMixin from "../../../../mixins/localize-mixin";
|
|
||||||
import NavigateMixin from "../../../../mixins/navigate-mixin";
|
|
||||||
import "../../../../styles/polymer-ha-style";
|
|
||||||
import "../../ha-config-section";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @appliesMixin NavigateMixin
|
|
||||||
* @appliesMixin EventsMixin
|
|
||||||
* @appliesMixin LocalizeMixin
|
|
||||||
*/
|
|
||||||
class CloudLogin extends LocalizeMixin(
|
|
||||||
NavigateMixin(EventsMixin(PolymerElement))
|
|
||||||
) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex ha-style">
|
|
||||||
.content {
|
|
||||||
padding-bottom: 24px;
|
|
||||||
}
|
|
||||||
[slot="introduction"] {
|
|
||||||
margin: -1em 0;
|
|
||||||
}
|
|
||||||
[slot="introduction"] a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
paper-item {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
ha-card {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
ha-card .card-header {
|
|
||||||
margin-bottom: -8px;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
@apply --paper-font-headline;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.error {
|
|
||||||
color: var(--error-color);
|
|
||||||
}
|
|
||||||
.card-actions {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
[hidden] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.flash-msg {
|
|
||||||
padding-right: 44px;
|
|
||||||
}
|
|
||||||
.flash-msg ha-icon-button {
|
|
||||||
position: absolute;
|
|
||||||
top: 4px;
|
|
||||||
right: 8px;
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
:host([rtl]) .flash-msg ha-icon-button {
|
|
||||||
right: auto;
|
|
||||||
left: 8px;
|
|
||||||
}
|
|
||||||
.login-form {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.pwd-forgot-link {
|
|
||||||
color: var(--secondary-text-color) !important;
|
|
||||||
text-align: right !important;
|
|
||||||
align-self: flex-end;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<hass-subpage
|
|
||||||
hass="[[hass]]"
|
|
||||||
narrow="[[narrow]]"
|
|
||||||
header="Home Assistant Cloud"
|
|
||||||
>
|
|
||||||
<div class="content">
|
|
||||||
<ha-config-section is-wide="[[isWide]]">
|
|
||||||
<span slot="header">Home Assistant Cloud</span>
|
|
||||||
<div slot="introduction">
|
|
||||||
<p>[[localize('ui.panel.config.cloud.login.introduction')]]</p>
|
|
||||||
<p>
|
|
||||||
[[localize('ui.panel.config.cloud.login.introduction2')]]
|
|
||||||
<a
|
|
||||||
href="https://www.nabucasa.com"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
Nabu Casa, Inc</a
|
|
||||||
>[[localize('ui.panel.config.cloud.login.introduction2a')]]
|
|
||||||
</p>
|
|
||||||
<p>[[localize('ui.panel.config.cloud.login.introduction3')]]</p>
|
|
||||||
<p>
|
|
||||||
<a
|
|
||||||
href="https://www.nabucasa.com"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
[[localize('ui.panel.config.cloud.login.learn_more_link')]]
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ha-card hidden$="[[!flashMessage]]">
|
|
||||||
<div class="card-content flash-msg">
|
|
||||||
[[flashMessage]]
|
|
||||||
<ha-icon-button
|
|
||||||
label="[[localize('ui.panel.config.cloud.login.dismiss')]]"
|
|
||||||
on-click="_dismissFlash"
|
|
||||||
>
|
|
||||||
<ha-icon icon="hass:close"></ha-icon>
|
|
||||||
</ha-icon-button>
|
|
||||||
<paper-ripple id="flashRipple" noink=""></paper-ripple>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
|
|
||||||
<ha-card
|
|
||||||
header="[[localize('ui.panel.config.cloud.login.sign_in')]]"
|
|
||||||
>
|
|
||||||
<div class="card-content login-form">
|
|
||||||
<div class="error" hidden$="[[!_error]]">[[_error]]</div>
|
|
||||||
<paper-input
|
|
||||||
label="[[localize('ui.panel.config.cloud.login.email')]]"
|
|
||||||
id="email"
|
|
||||||
type="email"
|
|
||||||
value="{{email}}"
|
|
||||||
on-keydown="_keyDown"
|
|
||||||
error-message="[[localize('ui.panel.config.cloud.login.email_error_msg')]]"
|
|
||||||
></paper-input>
|
|
||||||
<paper-input
|
|
||||||
id="password"
|
|
||||||
label="[[localize('ui.panel.config.cloud.login.password')]]"
|
|
||||||
value="{{_password}}"
|
|
||||||
type="password"
|
|
||||||
on-keydown="_keyDown"
|
|
||||||
error-message="[[localize('ui.panel.config.cloud.login.password_error_msg')]]"
|
|
||||||
></paper-input>
|
|
||||||
<button
|
|
||||||
class="link pwd-forgot-link"
|
|
||||||
hidden="[[_requestInProgress]]"
|
|
||||||
on-click="_handleForgotPassword"
|
|
||||||
>
|
|
||||||
[[localize('ui.panel.config.cloud.login.forgot_password')]]
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="card-actions">
|
|
||||||
<ha-progress-button
|
|
||||||
on-click="_handleLogin"
|
|
||||||
progress="[[_requestInProgress]]"
|
|
||||||
>[[localize('ui.panel.config.cloud.login.sign_in')]]</ha-progress-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
|
|
||||||
<ha-card>
|
|
||||||
<paper-item on-click="_handleRegister">
|
|
||||||
<paper-item-body two-line="">
|
|
||||||
[[localize('ui.panel.config.cloud.login.start_trial')]]
|
|
||||||
<div secondary="">
|
|
||||||
[[localize('ui.panel.config.cloud.login.trial_info')]]
|
|
||||||
</div>
|
|
||||||
</paper-item-body>
|
|
||||||
<ha-icon-next></ha-icon-next>
|
|
||||||
</paper-item>
|
|
||||||
</ha-card>
|
|
||||||
</ha-config-section>
|
|
||||||
</div>
|
|
||||||
</hass-subpage>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
isWide: Boolean,
|
|
||||||
narrow: Boolean,
|
|
||||||
email: {
|
|
||||||
type: String,
|
|
||||||
notify: true,
|
|
||||||
},
|
|
||||||
_password: {
|
|
||||||
type: String,
|
|
||||||
value: "",
|
|
||||||
},
|
|
||||||
_requestInProgress: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
flashMessage: {
|
|
||||||
type: String,
|
|
||||||
notify: true,
|
|
||||||
},
|
|
||||||
rtl: {
|
|
||||||
type: Boolean,
|
|
||||||
reflectToAttribute: true,
|
|
||||||
computed: "_computeRTL(hass)",
|
|
||||||
},
|
|
||||||
_error: String,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static get observers() {
|
|
||||||
return ["_inputChanged(email, _password)"];
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
super.connectedCallback();
|
|
||||||
if (this.flashMessage) {
|
|
||||||
// Wait for DOM to be drawn
|
|
||||||
requestAnimationFrame(() =>
|
|
||||||
requestAnimationFrame(() => this.$.flashRipple.simulatedRipple())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_inputChanged() {
|
|
||||||
this.$.email.invalid = false;
|
|
||||||
this.$.password.invalid = false;
|
|
||||||
this._error = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_keyDown(ev) {
|
|
||||||
// validate on enter
|
|
||||||
if (ev.keyCode === 13) {
|
|
||||||
this._handleLogin();
|
|
||||||
ev.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleLogin() {
|
|
||||||
let invalid = false;
|
|
||||||
|
|
||||||
if (!this.email || !this.email.includes("@")) {
|
|
||||||
this.$.email.invalid = true;
|
|
||||||
this.$.email.focus();
|
|
||||||
invalid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._password.length < 8) {
|
|
||||||
this.$.password.invalid = true;
|
|
||||||
|
|
||||||
if (!invalid) {
|
|
||||||
invalid = true;
|
|
||||||
this.$.password.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (invalid) return;
|
|
||||||
|
|
||||||
this._requestInProgress = true;
|
|
||||||
|
|
||||||
this.hass
|
|
||||||
.callApi("post", "cloud/login", {
|
|
||||||
email: this.email,
|
|
||||||
password: this._password,
|
|
||||||
})
|
|
||||||
.then(
|
|
||||||
() => {
|
|
||||||
this.fire("ha-refresh-cloud-status");
|
|
||||||
this.setProperties({
|
|
||||||
email: "",
|
|
||||||
_password: "",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
(err) => {
|
|
||||||
// Do this before setProperties because changing it clears errors.
|
|
||||||
this._password = "";
|
|
||||||
|
|
||||||
const errCode = err && err.body && err.body.code;
|
|
||||||
if (errCode === "PasswordChangeRequired") {
|
|
||||||
alert(
|
|
||||||
"[[localize('ui.panel.config.cloud.login.alert_password_change_required')]]"
|
|
||||||
);
|
|
||||||
this.navigate("/config/cloud/forgot-password");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = {
|
|
||||||
_requestInProgress: false,
|
|
||||||
_error:
|
|
||||||
err && err.body && err.body.message
|
|
||||||
? err.body.message
|
|
||||||
: "Unknown error",
|
|
||||||
};
|
|
||||||
|
|
||||||
if (errCode === "UserNotConfirmed") {
|
|
||||||
props._error =
|
|
||||||
"[[localize('ui.panel.config.cloud.login.alert_email_confirm_necessary')]]";
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setProperties(props);
|
|
||||||
this.$.email.focus();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleRegister() {
|
|
||||||
this.flashMessage = "";
|
|
||||||
this.navigate("/config/cloud/register");
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleForgotPassword() {
|
|
||||||
this.flashMessage = "";
|
|
||||||
this.navigate("/config/cloud/forgot-password");
|
|
||||||
}
|
|
||||||
|
|
||||||
_dismissFlash() {
|
|
||||||
// give some time to let the ripple finish.
|
|
||||||
setTimeout(() => {
|
|
||||||
this.flashMessage = "";
|
|
||||||
}, 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
_computeRTL(hass) {
|
|
||||||
return computeRTL(hass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("cloud-login", CloudLogin);
|
|
310
src/panels/config/cloud/login/cloud-login.ts
Normal file
310
src/panels/config/cloud/login/cloud-login.ts
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
|
import "@material/mwc-textfield/mwc-textfield";
|
||||||
|
import type { TextField } from "@material/mwc-textfield/mwc-textfield";
|
||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import "@polymer/paper-item/paper-item";
|
||||||
|
import "@polymer/paper-item/paper-item-body";
|
||||||
|
import { css, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { navigate } from "../../../../common/navigate";
|
||||||
|
import "../../../../components/buttons/ha-progress-button";
|
||||||
|
import "../../../../components/ha-alert";
|
||||||
|
import "../../../../components/ha-card";
|
||||||
|
import "../../../../components/ha-icon-next";
|
||||||
|
import { cloudLogin } from "../../../../data/cloud";
|
||||||
|
import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box";
|
||||||
|
import "../../../../layouts/hass-subpage";
|
||||||
|
import { haStyle } from "../../../../resources/styles";
|
||||||
|
import "../../../../styles/polymer-ha-style";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
import "../../ha-config-section";
|
||||||
|
|
||||||
|
@customElement("cloud-login")
|
||||||
|
export class CloudLogin extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public isWide = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
|
@property() public email?: string;
|
||||||
|
|
||||||
|
@property() public flashMessage?: string;
|
||||||
|
|
||||||
|
@state() private _password?: string;
|
||||||
|
|
||||||
|
@state() private _requestInProgress = false;
|
||||||
|
|
||||||
|
@state() private _error?: string;
|
||||||
|
|
||||||
|
@query("#email", true) private _emailField!: TextField;
|
||||||
|
|
||||||
|
@query("#password", true) private _passwordField!: TextField;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<hass-subpage
|
||||||
|
.hass=${this.hass}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
header="Home Assistant Cloud"
|
||||||
|
>
|
||||||
|
<div class="content">
|
||||||
|
<ha-config-section isWide=${this.isWide}>
|
||||||
|
<span slot="header">Home Assistant Cloud</span>
|
||||||
|
<div slot="introduction">
|
||||||
|
<p>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.login.introduction"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.login.introduction2"
|
||||||
|
)}
|
||||||
|
<a
|
||||||
|
href="https://www.nabucasa.com"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
Nabu Casa, Inc</a
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.login.introduction2a"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.login.introduction3"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a
|
||||||
|
href="https://www.nabucasa.com"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.login.learn_more_link"
|
||||||
|
)}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${this.flashMessage
|
||||||
|
? html`<ha-alert
|
||||||
|
dismissable
|
||||||
|
@alert-dismissed-clicked=${this._dismissFlash}
|
||||||
|
>
|
||||||
|
${this.flashMessage}
|
||||||
|
</ha-alert>`
|
||||||
|
: ""}
|
||||||
|
|
||||||
|
<ha-card
|
||||||
|
.header=${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.login.sign_in"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div class="card-content login-form">
|
||||||
|
${this._error
|
||||||
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
|
: ""}
|
||||||
|
<mwc-textfield
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.login.email"
|
||||||
|
)}
|
||||||
|
id="email"
|
||||||
|
type="email"
|
||||||
|
required
|
||||||
|
.value=${this.email}
|
||||||
|
@keydown=${this._keyDown}
|
||||||
|
.disabled=${this._requestInProgress}
|
||||||
|
.validationMessage=${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.login.email_error_msg"
|
||||||
|
)}
|
||||||
|
></mwc-textfield>
|
||||||
|
<mwc-textfield
|
||||||
|
id="password"
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.login.password"
|
||||||
|
)}
|
||||||
|
.value=${this._password || ""}
|
||||||
|
type="password"
|
||||||
|
required
|
||||||
|
minlength="8"
|
||||||
|
@keydown=${this._keyDown}
|
||||||
|
.disabled=${this._requestInProgress}
|
||||||
|
.validationMessage=${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.login.password_error_msg"
|
||||||
|
)}
|
||||||
|
></mwc-textfield>
|
||||||
|
<button
|
||||||
|
class="link pwd-forgot-link"
|
||||||
|
.disabled=${this._requestInProgress}
|
||||||
|
@click=${this._handleForgotPassword}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.login.forgot_password"
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="card-actions">
|
||||||
|
<ha-progress-button
|
||||||
|
@click=${this._handleLogin}
|
||||||
|
.progress=${this._requestInProgress}
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.login.sign_in"
|
||||||
|
)}</ha-progress-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
|
||||||
|
<ha-card>
|
||||||
|
<paper-item @click=${this._handleRegister}>
|
||||||
|
<paper-item-body two-line>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.login.start_trial"
|
||||||
|
)}
|
||||||
|
<div secondary>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.login.trial_info"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</paper-item-body>
|
||||||
|
<ha-icon-next></ha-icon-next>
|
||||||
|
</paper-item>
|
||||||
|
</ha-card>
|
||||||
|
</ha-config-section>
|
||||||
|
</div>
|
||||||
|
</hass-subpage>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _keyDown(ev: KeyboardEvent) {
|
||||||
|
if (ev.key === "Enter") {
|
||||||
|
this._handleLogin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _handleLogin() {
|
||||||
|
const emailField = this._emailField;
|
||||||
|
const passwordField = this._passwordField;
|
||||||
|
|
||||||
|
const email = emailField.value;
|
||||||
|
const password = passwordField.value;
|
||||||
|
|
||||||
|
if (!emailField.reportValidity()) {
|
||||||
|
passwordField.reportValidity();
|
||||||
|
emailField.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!passwordField.reportValidity()) {
|
||||||
|
passwordField.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._requestInProgress = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await cloudLogin(this.hass, email, password);
|
||||||
|
fireEvent(this, "ha-refresh-cloud-status");
|
||||||
|
this.email = "";
|
||||||
|
this._password = "";
|
||||||
|
} catch (err: any) {
|
||||||
|
const errCode = err && err.body && err.body.code;
|
||||||
|
if (errCode === "PasswordChangeRequired") {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
title: this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.login.alert_password_change_required"
|
||||||
|
),
|
||||||
|
});
|
||||||
|
navigate("/config/cloud/forgot-password");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._password = "";
|
||||||
|
this._requestInProgress = false;
|
||||||
|
|
||||||
|
if (errCode === "UserNotConfirmed") {
|
||||||
|
this._error = this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.login.alert_email_confirm_necessary"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this._error =
|
||||||
|
err && err.body && err.body.message
|
||||||
|
? err.body.message
|
||||||
|
: "Unknown error";
|
||||||
|
}
|
||||||
|
|
||||||
|
emailField.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleRegister() {
|
||||||
|
this._dismissFlash();
|
||||||
|
// @ts-ignore
|
||||||
|
fireEvent(this, "email-changed", { value: this._emailField.value });
|
||||||
|
navigate("/config/cloud/register");
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleForgotPassword() {
|
||||||
|
this._dismissFlash();
|
||||||
|
// @ts-ignore
|
||||||
|
fireEvent(this, "email-changed", { value: this._emailField.value });
|
||||||
|
navigate("/config/cloud/forgot-password");
|
||||||
|
}
|
||||||
|
|
||||||
|
private _dismissFlash() {
|
||||||
|
// @ts-ignore
|
||||||
|
fireEvent(this, "flash-message-changed", { value: "" });
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
.content {
|
||||||
|
padding-bottom: 24px;
|
||||||
|
}
|
||||||
|
[slot="introduction"] {
|
||||||
|
margin: -1em 0;
|
||||||
|
}
|
||||||
|
[slot="introduction"] a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
paper-item {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
ha-card {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
ha-card .card-header {
|
||||||
|
margin-bottom: -8px;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.card-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.login-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.pwd-forgot-link {
|
||||||
|
color: var(--secondary-text-color) !important;
|
||||||
|
text-align: right !important;
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"cloud-login": CloudLogin;
|
||||||
|
}
|
||||||
|
}
|
@ -1,229 +0,0 @@
|
|||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "../../../../components/buttons/ha-progress-button";
|
|
||||||
import "../../../../components/ha-card";
|
|
||||||
import "../../../../layouts/hass-subpage";
|
|
||||||
import { EventsMixin } from "../../../../mixins/events-mixin";
|
|
||||||
import LocalizeMixin from "../../../../mixins/localize-mixin";
|
|
||||||
import "../../../../styles/polymer-ha-style";
|
|
||||||
import { documentationUrl } from "../../../../util/documentation-url";
|
|
||||||
import "../../ha-config-section";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @appliesMixin EventsMixin
|
|
||||||
* @appliesMixin LocalizeMixin
|
|
||||||
*/
|
|
||||||
class CloudRegister extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex ha-style">
|
|
||||||
[slot=introduction] {
|
|
||||||
margin: -1em 0;
|
|
||||||
}
|
|
||||||
[slot=introduction] a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
paper-item {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
@apply --paper-font-headline;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.error {
|
|
||||||
color: var(--error-color);
|
|
||||||
}
|
|
||||||
.card-actions {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
[hidden] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<hass-subpage hass="[[hass]]" narrow="[[narrow]]" header="[[localize('ui.panel.config.cloud.register.title')]]">
|
|
||||||
<div class="content">
|
|
||||||
<ha-config-section is-wide="[[isWide]]">
|
|
||||||
<span slot="header">[[localize('ui.panel.config.cloud.register.headline')]]</span>
|
|
||||||
<div slot="introduction">
|
|
||||||
<p>
|
|
||||||
[[localize('ui.panel.config.cloud.register.information')]]
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
[[localize('ui.panel.config.cloud.register.information2')]]
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>[[localize('ui.panel.config.cloud.register.feature_remote_control')]]</li>
|
|
||||||
<li>[[localize('ui.panel.config.cloud.register.feature_google_home')]]</li>
|
|
||||||
<li>[[localize('ui.panel.config.cloud.register.feature_amazon_alexa')]]</li>
|
|
||||||
<li>[[localize('ui.panel.config.cloud.register.feature_webhook_apps')]]</li>
|
|
||||||
</ul>
|
|
||||||
<p>
|
|
||||||
[[localize('ui.panel.config.cloud.register.information3')]] <a href='https://www.nabucasa.com' target='_blank'>Nabu Casa, Inc</a>[[localize('ui.panel.config.cloud.register.information3a')]]
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
[[localize('ui.panel.config.cloud.register.information4')]]
|
|
||||||
</p><ul>
|
|
||||||
<li><a href="[[_computeDocumentationUrlTos(hass)]]" target="_blank" rel="noreferrer">[[localize('ui.panel.config.cloud.register.link_terms_conditions')]]</a></li>
|
|
||||||
<li><a href="[[_computeDocumentationUrlPrivacy(hass)]]" target="_blank" rel="noreferrer">[[localize('ui.panel.config.cloud.register.link_privacy_policy')]]</a></li>
|
|
||||||
</ul>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ha-card header="[[localize('ui.panel.config.cloud.register.create_account')]]">
|
|
||||||
<div class="card-content">
|
|
||||||
<div class="header">
|
|
||||||
<div class="error" hidden$="[[!_error]]">[[_error]]</div>
|
|
||||||
</div>
|
|
||||||
<paper-input autofocus="" id="email" label="[[localize('ui.panel.config.cloud.register.email_address')]]" type="email" value="{{email}}" on-keydown="_keyDown" error-message="[[localize('ui.panel.config.cloud.register.email_error_msg')]]"></paper-input>
|
|
||||||
<paper-input id="password" label="Password" value="{{_password}}" type="password" on-keydown="_keyDown" error-message="[[localize('ui.panel.config.cloud.register.password_error_msg')]]"></paper-input>
|
|
||||||
</div>
|
|
||||||
<div class="card-actions">
|
|
||||||
<ha-progress-button on-click="_handleRegister" progress="[[_requestInProgress]]">[[localize('ui.panel.config.cloud.register.start_trial')]]</ha-progress-button>
|
|
||||||
<button class="link" hidden="[[_requestInProgress]]" on-click="_handleResendVerifyEmail">[[localize('ui.panel.config.cloud.register.resend_confirmation_email')]]</button>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
</ha-config-section>
|
|
||||||
</div>
|
|
||||||
</hass-subpage>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
isWide: Boolean,
|
|
||||||
narrow: Boolean,
|
|
||||||
email: {
|
|
||||||
type: String,
|
|
||||||
notify: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
_requestInProgress: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
_password: {
|
|
||||||
type: String,
|
|
||||||
value: "",
|
|
||||||
},
|
|
||||||
_error: {
|
|
||||||
type: String,
|
|
||||||
value: "",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static get observers() {
|
|
||||||
return ["_inputChanged(email, _password)"];
|
|
||||||
}
|
|
||||||
|
|
||||||
_inputChanged() {
|
|
||||||
this._error = "";
|
|
||||||
this.$.email.invalid = false;
|
|
||||||
this.$.password.invalid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_keyDown(ev) {
|
|
||||||
// validate on enter
|
|
||||||
if (ev.keyCode === 13) {
|
|
||||||
this._handleRegister();
|
|
||||||
ev.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_computeDocumentationUrlTos(hass) {
|
|
||||||
return documentationUrl(hass, "/tos/");
|
|
||||||
}
|
|
||||||
|
|
||||||
_computeDocumentationUrlPrivacy(hass) {
|
|
||||||
return documentationUrl(hass, "/privacy/");
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleRegister() {
|
|
||||||
let invalid = false;
|
|
||||||
|
|
||||||
if (!this.email || !this.email.includes("@")) {
|
|
||||||
this.$.email.invalid = true;
|
|
||||||
this.$.email.focus();
|
|
||||||
invalid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._password.length < 8) {
|
|
||||||
this.$.password.invalid = true;
|
|
||||||
|
|
||||||
if (!invalid) {
|
|
||||||
invalid = true;
|
|
||||||
this.$.password.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (invalid) return;
|
|
||||||
|
|
||||||
this._requestInProgress = true;
|
|
||||||
|
|
||||||
this.hass
|
|
||||||
.callApi("post", "cloud/register", {
|
|
||||||
email: this.email,
|
|
||||||
password: this._password,
|
|
||||||
})
|
|
||||||
.then(
|
|
||||||
() => this._verificationEmailSent(),
|
|
||||||
(err) => {
|
|
||||||
// Do this before setProperties because changing it clears errors.
|
|
||||||
this._password = "";
|
|
||||||
|
|
||||||
this.setProperties({
|
|
||||||
_requestInProgress: false,
|
|
||||||
_error:
|
|
||||||
err && err.body && err.body.message
|
|
||||||
? err.body.message
|
|
||||||
: "Unknown error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleResendVerifyEmail() {
|
|
||||||
if (!this.email) {
|
|
||||||
this.$.email.invalid = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hass
|
|
||||||
.callApi("post", "cloud/resend_confirm", {
|
|
||||||
email: this.email,
|
|
||||||
})
|
|
||||||
.then(
|
|
||||||
() => this._verificationEmailSent(),
|
|
||||||
(err) =>
|
|
||||||
this.setProperties({
|
|
||||||
_error:
|
|
||||||
err && err.body && err.body.message
|
|
||||||
? err.body.message
|
|
||||||
: "Unknown error",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_verificationEmailSent() {
|
|
||||||
this.setProperties({
|
|
||||||
_requestInProgress: false,
|
|
||||||
_password: "",
|
|
||||||
});
|
|
||||||
this.fire("cloud-done", {
|
|
||||||
flashMessage: this.hass.localize(
|
|
||||||
"ui.panel.config.cloud.register.account_created"
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("cloud-register", CloudRegister);
|
|
293
src/panels/config/cloud/register/cloud-register.ts
Normal file
293
src/panels/config/cloud/register/cloud-register.ts
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
import "@material/mwc-textfield/mwc-textfield";
|
||||||
|
import type { TextField } from "@material/mwc-textfield/mwc-textfield";
|
||||||
|
import { css, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import "../../../../components/buttons/ha-progress-button";
|
||||||
|
import "../../../../components/ha-alert";
|
||||||
|
import "../../../../components/ha-card";
|
||||||
|
import { cloudRegister, cloudResendVerification } from "../../../../data/cloud";
|
||||||
|
import "../../../../layouts/hass-subpage";
|
||||||
|
import { haStyle } from "../../../../resources/styles";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
import "../../ha-config-section";
|
||||||
|
|
||||||
|
@customElement("cloud-register")
|
||||||
|
export class CloudRegister extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public isWide = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
|
@property() public email?: string;
|
||||||
|
|
||||||
|
@state() private _requestInProgress = false;
|
||||||
|
|
||||||
|
@state() private _password = "";
|
||||||
|
|
||||||
|
@state() private _error?: string;
|
||||||
|
|
||||||
|
@query("#email", true) private _emailField!: TextField;
|
||||||
|
|
||||||
|
@query("#password", true) private _passwordField!: TextField;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<hass-subpage
|
||||||
|
.hass=${this.hass}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
.header=${this.hass.localize("ui.panel.config.cloud.register.title")}
|
||||||
|
>
|
||||||
|
<div class="content">
|
||||||
|
<ha-config-section .isWide=${this.isWide}>
|
||||||
|
<span slot="header"
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.register.headline"
|
||||||
|
)}</span
|
||||||
|
>
|
||||||
|
<div slot="introduction">
|
||||||
|
<p>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.register.information"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.register.information2"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.register.feature_remote_control"
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.register.feature_google_home"
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.register.feature_amazon_alexa"
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.register.feature_webhook_apps"
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.register.information3"
|
||||||
|
)}
|
||||||
|
<a href="https://www.nabucasa.com" target="_blank"
|
||||||
|
>Nabu Casa, Inc</a
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.register.information3a"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.register.information4"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://www.nabucasa.com/tos/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.register.link_terms_conditions"
|
||||||
|
)}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://www.nabucasa.com/privacy_policy/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.register.link_privacy_policy"
|
||||||
|
)}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<ha-card
|
||||||
|
.header=${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.register.create_account"
|
||||||
|
)}
|
||||||
|
><div class="card-content register-form">
|
||||||
|
${this._error
|
||||||
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
|
: ""}
|
||||||
|
<mwc-textfield
|
||||||
|
autofocus
|
||||||
|
id="email"
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.register.email_address"
|
||||||
|
)}
|
||||||
|
type="email"
|
||||||
|
required
|
||||||
|
.value=${this.email}
|
||||||
|
@keydown=${this._keyDown}
|
||||||
|
validationMessage=${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.register.email_error_msg"
|
||||||
|
)}
|
||||||
|
></mwc-textfield>
|
||||||
|
<mwc-textfield
|
||||||
|
id="password"
|
||||||
|
label="Password"
|
||||||
|
.value=${this._password}
|
||||||
|
type="password"
|
||||||
|
minlength="8"
|
||||||
|
required
|
||||||
|
@keydown=${this._keyDown}
|
||||||
|
validationMessage=${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.register.password_error_msg"
|
||||||
|
)}
|
||||||
|
></mwc-textfield>
|
||||||
|
</div>
|
||||||
|
<div class="card-actions">
|
||||||
|
<ha-progress-button
|
||||||
|
@click=${this._handleRegister}
|
||||||
|
.progress=${this._requestInProgress}
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.register.start_trial"
|
||||||
|
)}</ha-progress-button
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="link"
|
||||||
|
.disabled=${this._requestInProgress}
|
||||||
|
@click=${this._handleResendVerifyEmail}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.register.resend_confirm_email"
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
</ha-config-section>
|
||||||
|
</div>
|
||||||
|
</hass-subpage>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _keyDown(ev: KeyboardEvent) {
|
||||||
|
if (ev.key === "Enter") {
|
||||||
|
this._handleRegister();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _handleRegister() {
|
||||||
|
const emailField = this._emailField;
|
||||||
|
const passwordField = this._passwordField;
|
||||||
|
|
||||||
|
const email = emailField.value;
|
||||||
|
const password = passwordField.value;
|
||||||
|
|
||||||
|
if (!emailField.reportValidity()) {
|
||||||
|
passwordField.reportValidity();
|
||||||
|
emailField.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!passwordField.reportValidity()) {
|
||||||
|
passwordField.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._requestInProgress = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await cloudRegister(this.hass, email, password);
|
||||||
|
this._verificationEmailSent(email);
|
||||||
|
} catch (err: any) {
|
||||||
|
this._password = "";
|
||||||
|
this._requestInProgress = false;
|
||||||
|
this._error =
|
||||||
|
err && err.body && err.body.message
|
||||||
|
? err.body.message
|
||||||
|
: "Unknown error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _handleResendVerifyEmail() {
|
||||||
|
const emailField = this._emailField;
|
||||||
|
|
||||||
|
const email = emailField.value;
|
||||||
|
|
||||||
|
if (!emailField.reportValidity()) {
|
||||||
|
emailField.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await cloudResendVerification(this.hass, email);
|
||||||
|
this._verificationEmailSent(email);
|
||||||
|
} catch (err: any) {
|
||||||
|
this._error =
|
||||||
|
err && err.body && err.body.message
|
||||||
|
? err.body.message
|
||||||
|
: "Unknown error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _verificationEmailSent(email: string) {
|
||||||
|
this._requestInProgress = false;
|
||||||
|
this._password = "";
|
||||||
|
// @ts-ignore
|
||||||
|
fireEvent(this, "email-changed", { value: email });
|
||||||
|
// @ts-ignore
|
||||||
|
fireEvent(this, "cloud-done", {
|
||||||
|
flashMessage: this.hass.localize(
|
||||||
|
"ui.panel.config.cloud.register.account_created"
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
[slot="introduction"] {
|
||||||
|
margin: -1em 0;
|
||||||
|
}
|
||||||
|
[slot="introduction"] a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
paper-item {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.register-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.card-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"cloud-register": CloudRegister;
|
||||||
|
}
|
||||||
|
}
|
@ -3343,7 +3343,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@polymer/paper-ripple@npm:^3.0.0-pre.26, @polymer/paper-ripple@npm:^3.0.2":
|
"@polymer/paper-ripple@npm:^3.0.0-pre.26":
|
||||||
version: 3.0.2
|
version: 3.0.2
|
||||||
resolution: "@polymer/paper-ripple@npm:3.0.2"
|
resolution: "@polymer/paper-ripple@npm:3.0.2"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -9038,7 +9038,6 @@ fsevents@^1.2.7:
|
|||||||
"@polymer/paper-input": ^3.2.1
|
"@polymer/paper-input": ^3.2.1
|
||||||
"@polymer/paper-item": ^3.0.1
|
"@polymer/paper-item": ^3.0.1
|
||||||
"@polymer/paper-listbox": ^3.0.1
|
"@polymer/paper-listbox": ^3.0.1
|
||||||
"@polymer/paper-ripple": ^3.0.2
|
|
||||||
"@polymer/paper-slider": ^3.0.1
|
"@polymer/paper-slider": ^3.0.1
|
||||||
"@polymer/paper-styles": ^3.0.1
|
"@polymer/paper-styles": ^3.0.1
|
||||||
"@polymer/paper-tabs": ^3.1.0
|
"@polymer/paper-tabs": ^3.1.0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user