mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 09:46:36 +00:00
Convert authorize page to lit (#2115)
* Convert authorize page to lit * Don't use ha-markdown * Simplify CSS
This commit is contained in:
parent
07b65f37db
commit
5ab419534c
@ -2,9 +2,9 @@ import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import "@polymer/paper-button/paper-button";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import "../components/ha-form";
|
||||
import LocalizeLiteMixin from "../mixins/localize-lite-mixin";
|
||||
import { localizeLiteMixin } from "../mixins/localize-lite-mixin";
|
||||
|
||||
class HaAuthFlow extends LocalizeLiteMixin(PolymerElement) {
|
||||
class HaAuthFlow extends localizeLiteMixin(PolymerElement) {
|
||||
static get template() {
|
||||
return html`
|
||||
<style>
|
||||
|
@ -1,154 +0,0 @@
|
||||
import "@polymer/polymer/lib/elements/dom-if";
|
||||
import "@polymer/polymer/lib/elements/dom-repeat";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import "../components/ha-markdown";
|
||||
|
||||
import LocalizeLiteMixin from "../mixins/localize-lite-mixin";
|
||||
|
||||
import "./ha-auth-flow";
|
||||
|
||||
class HaAuthorize extends LocalizeLiteMixin(PolymerElement) {
|
||||
static get template() {
|
||||
return html`
|
||||
<style>
|
||||
ha-markdown {
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
ha-markdown a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
ha-markdown p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
ha-pick-auth-provider {
|
||||
display: block;
|
||||
margin-top: 48px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<template is="dom-if" if="[[!_authProviders]]">
|
||||
<p>[[localize('ui.panel.page-authorize.initializing')]]</p>
|
||||
</template>
|
||||
|
||||
<template is="dom-if" if="[[_authProviders]]">
|
||||
<ha-markdown
|
||||
content="[[_computeIntro(localize, clientId, _authProvider)]]"
|
||||
></ha-markdown>
|
||||
|
||||
<ha-auth-flow
|
||||
resources="[[resources]]"
|
||||
client-id="[[clientId]]"
|
||||
redirect-uri="[[redirectUri]]"
|
||||
oauth2-state="[[oauth2State]]"
|
||||
auth-provider="[[_authProvider]]"
|
||||
step="{{step}}"
|
||||
></ha-auth-flow>
|
||||
|
||||
<template is="dom-if" if="[[_computeMultiple(_authProviders)]]">
|
||||
<ha-pick-auth-provider
|
||||
resources="[[resources]]"
|
||||
client-id="[[clientId]]"
|
||||
auth-providers="[[_computeInactiveProvders(_authProvider, _authProviders)]]"
|
||||
on-pick="_handleAuthProviderPick"
|
||||
></ha-pick-auth-provider>
|
||||
</template>
|
||||
</template>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_authProvider: String,
|
||||
_authProviders: Array,
|
||||
clientId: String,
|
||||
redirectUri: String,
|
||||
oauth2State: String,
|
||||
translationFragment: {
|
||||
type: String,
|
||||
value: "page-authorize",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async ready() {
|
||||
super.ready();
|
||||
const query = {};
|
||||
const values = location.search.substr(1).split("&");
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
const value = values[i].split("=");
|
||||
if (value.length > 1) {
|
||||
query[decodeURIComponent(value[0])] = decodeURIComponent(value[1]);
|
||||
}
|
||||
}
|
||||
const props = {};
|
||||
if (query.client_id) props.clientId = query.client_id;
|
||||
if (query.redirect_uri) props.redirectUri = query.redirect_uri;
|
||||
if (query.state) props.oauth2State = query.state;
|
||||
this.setProperties(props);
|
||||
|
||||
import(/* webpackChunkName: "pick-auth-provider" */ "../auth/ha-pick-auth-provider");
|
||||
|
||||
// Fetch auth providers
|
||||
try {
|
||||
const response = await window.providersPromise;
|
||||
const authProviders = await response.json();
|
||||
|
||||
// Forward to main screen which will redirect to right onboarding page.
|
||||
if (
|
||||
response.status === 400 &&
|
||||
authProviders.code === "onboarding_required"
|
||||
) {
|
||||
location.href = "/";
|
||||
return;
|
||||
}
|
||||
|
||||
if (authProviders.length === 0) {
|
||||
alert("No auth providers returned. Unable to finish login.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.setProperties({
|
||||
_authProviders: authProviders,
|
||||
_authProvider: authProviders[0],
|
||||
});
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line
|
||||
console.error("Error loading auth providers", err);
|
||||
this._state = "error-loading";
|
||||
}
|
||||
}
|
||||
|
||||
_computeMultiple(array) {
|
||||
return array && array.length > 1;
|
||||
}
|
||||
|
||||
async _handleAuthProviderPick(ev) {
|
||||
this._authProvider = ev.detail;
|
||||
}
|
||||
|
||||
_computeInactiveProvders(curProvider, providers) {
|
||||
return providers.filter(
|
||||
(prv) => prv.type !== curProvider.type || prv.id !== curProvider.id
|
||||
);
|
||||
}
|
||||
|
||||
_computeIntro(localize, clientId, authProvider) {
|
||||
return (
|
||||
localize(
|
||||
"ui.panel.page-authorize.authorizing_client",
|
||||
"clientId",
|
||||
clientId
|
||||
) +
|
||||
"\n\n" +
|
||||
localize(
|
||||
"ui.panel.page-authorize.logging_in_with",
|
||||
"authProviderName",
|
||||
authProvider.name
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
customElements.define("ha-authorize", HaAuthorize);
|
158
src/auth/ha-authorize.ts
Normal file
158
src/auth/ha-authorize.ts
Normal file
@ -0,0 +1,158 @@
|
||||
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
|
||||
import { LitElement, html, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import "./ha-auth-flow";
|
||||
import { AuthProvider } from "../data/auth";
|
||||
|
||||
import(/* webpackChunkName: "pick-auth-provider" */ "../auth/ha-pick-auth-provider");
|
||||
|
||||
interface QueryParams {
|
||||
client_id?: string;
|
||||
redirect_uri?: string;
|
||||
state?: string;
|
||||
}
|
||||
|
||||
class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
||||
public clientId?: string;
|
||||
public redirectUri?: string;
|
||||
public oauth2State?: string;
|
||||
private _authProvider?: AuthProvider;
|
||||
private _authProviders?: AuthProvider[];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.translationFragment = "page-authorize";
|
||||
const query: QueryParams = {};
|
||||
const values = location.search.substr(1).split("&");
|
||||
for (const item of values) {
|
||||
const value = item.split("=");
|
||||
if (value.length > 1) {
|
||||
query[decodeURIComponent(value[0])] = decodeURIComponent(value[1]);
|
||||
}
|
||||
}
|
||||
if (query.client_id) {
|
||||
this.clientId = query.client_id;
|
||||
}
|
||||
if (query.redirect_uri) {
|
||||
this.redirectUri = query.redirect_uri;
|
||||
}
|
||||
if (query.state) {
|
||||
this.oauth2State = query.state;
|
||||
}
|
||||
}
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
_authProvider: {},
|
||||
_authProviders: {},
|
||||
clientId: {},
|
||||
redirectUri: {},
|
||||
oauth2State: {},
|
||||
};
|
||||
}
|
||||
|
||||
public render() {
|
||||
if (!this._authProviders) {
|
||||
return html`
|
||||
<p>[[localize('ui.panel.page-authorize.initializing')]]</p>
|
||||
`;
|
||||
}
|
||||
|
||||
// We don't have a good approach yet to map text markup in localization.
|
||||
// So we sanitize the translation with innerText and then inject
|
||||
// the name with a bold tag.
|
||||
const loggingInWith = document.createElement("div");
|
||||
loggingInWith.innerText = this.localize(
|
||||
"ui.panel.page-authorize.logging_in_with",
|
||||
"authProviderName",
|
||||
"NAME"
|
||||
);
|
||||
loggingInWith.innerHTML = loggingInWith.innerHTML.replace(
|
||||
"**NAME**",
|
||||
`<b>${this._authProvider!.name}</b>`
|
||||
);
|
||||
|
||||
const inactiveProviders = this._authProviders.filter(
|
||||
(prv) => prv !== this._authProvider
|
||||
);
|
||||
|
||||
return html`
|
||||
${this.renderStyle()}
|
||||
<p>
|
||||
${
|
||||
this.localize(
|
||||
"ui.panel.page-authorize.authorizing_client",
|
||||
"clientId",
|
||||
this.clientId
|
||||
)
|
||||
}
|
||||
</p>
|
||||
${loggingInWith}
|
||||
|
||||
<ha-auth-flow
|
||||
.resources="${this.resources}"
|
||||
.clientId="${this.clientId}"
|
||||
.redirectUri="${this.redirectUri}"
|
||||
.oauth2State="${this.oauth2State}"
|
||||
.authProvider="${this._authProvider}"
|
||||
.step="{{step}}"
|
||||
></ha-auth-flow>
|
||||
|
||||
${
|
||||
inactiveProviders.length > 0
|
||||
? html`
|
||||
<ha-pick-auth-provider
|
||||
.resources="${this.resources}"
|
||||
.clientId="${this.clientId}"
|
||||
.authProviders="${inactiveProviders}"
|
||||
@pick="${this._handleAuthProviderPick}"
|
||||
></ha-pick-auth-provider>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
public async firstUpdated() {
|
||||
// Fetch auth providers
|
||||
try {
|
||||
const response = await (window as any).providersPromise;
|
||||
const authProviders = await response.json();
|
||||
|
||||
// Forward to main screen which will redirect to right onboarding page.
|
||||
if (
|
||||
response.status === 400 &&
|
||||
authProviders.code === "onboarding_required"
|
||||
) {
|
||||
location.href = "/?";
|
||||
return;
|
||||
}
|
||||
|
||||
if (authProviders.length === 0) {
|
||||
alert("No auth providers returned. Unable to finish login.");
|
||||
return;
|
||||
}
|
||||
|
||||
this._authProviders = authProviders;
|
||||
this._authProvider = authProviders[0];
|
||||
} catch (err) {
|
||||
// tslint:disable-next-line
|
||||
console.error("Error loading auth providers", err);
|
||||
}
|
||||
}
|
||||
|
||||
protected renderStyle() {
|
||||
return html`
|
||||
<style>
|
||||
ha-pick-auth-provider {
|
||||
display: block;
|
||||
margin-top: 48px;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _handleAuthProviderPick(ev) {
|
||||
this._authProvider = ev.detail;
|
||||
}
|
||||
}
|
||||
customElements.define("ha-authorize", HaAuthorize);
|
@ -4,13 +4,13 @@ import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import EventsMixin from "../mixins/events-mixin";
|
||||
import LocalizeLiteMixin from "../mixins/localize-lite-mixin";
|
||||
import { localizeLiteMixin } from "../mixins/localize-lite-mixin";
|
||||
|
||||
/*
|
||||
* @appliesMixin EventsMixin
|
||||
*/
|
||||
class HaPickAuthProvider extends EventsMixin(
|
||||
LocalizeLiteMixin(PolymerElement)
|
||||
localizeLiteMixin(PolymerElement)
|
||||
) {
|
||||
static get template() {
|
||||
return html`
|
||||
|
5
src/data/auth.ts
Normal file
5
src/data/auth.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface AuthProvider {
|
||||
name: string;
|
||||
id: string;
|
||||
type: string;
|
||||
}
|
60
src/mixins/lit-localize-lite-mixin.ts
Normal file
60
src/mixins/lit-localize-lite-mixin.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import {
|
||||
Constructor,
|
||||
LitElement,
|
||||
PropertyDeclarations,
|
||||
PropertyValues,
|
||||
} from "@polymer/lit-element";
|
||||
import { HomeAssistant } from "../types";
|
||||
import { getActiveTranslation } from "../util/hass-translation";
|
||||
import { LocalizeFunc, LocalizeMixin } from "./localize-base-mixin";
|
||||
import { localizeLiteBaseMixin } from "./localize-lite-base-mixin";
|
||||
|
||||
const empty = () => "";
|
||||
|
||||
interface LitLocalizeLiteMixin {
|
||||
language: string;
|
||||
resources: {};
|
||||
translationFragment: string;
|
||||
}
|
||||
|
||||
export const litLocalizeLiteMixin = <T extends LitElement>(
|
||||
superClass: Constructor<T>
|
||||
): Constructor<T & LocalizeMixin & LitLocalizeLiteMixin> =>
|
||||
// @ts-ignore
|
||||
class extends localizeLiteBaseMixin(superClass) {
|
||||
protected hass?: HomeAssistant;
|
||||
protected localize!: LocalizeFunc;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
hass: {},
|
||||
localize: {},
|
||||
language: {},
|
||||
resources: {},
|
||||
translationFragment: {},
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
// This will prevent undefined errors if called before connected to DOM.
|
||||
this.localize = empty;
|
||||
this.language = getActiveTranslation();
|
||||
}
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this._initializeLocalizeLite();
|
||||
this.localize = this.__computeLocalize(this.language, this.resources);
|
||||
}
|
||||
|
||||
public updated(changedProperties: PropertyValues) {
|
||||
super.updated(changedProperties);
|
||||
if (
|
||||
changedProperties.has("language") ||
|
||||
changedProperties.has("resources")
|
||||
) {
|
||||
this.localize = this.__computeLocalize(this.language, this.resources);
|
||||
}
|
||||
}
|
||||
};
|
44
src/mixins/localize-lite-base-mixin.ts
Normal file
44
src/mixins/localize-lite-base-mixin.ts
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Lite base mixin to add localization without depending on the Hass object.
|
||||
*/
|
||||
import { localizeBaseMixin } from "./localize-base-mixin";
|
||||
import { getTranslation } from "../util/hass-translation";
|
||||
|
||||
/**
|
||||
* @polymerMixin
|
||||
*/
|
||||
export const localizeLiteBaseMixin = (superClass) =>
|
||||
class extends localizeBaseMixin(superClass) {
|
||||
protected _initializeLocalizeLite() {
|
||||
if (this.resources) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.translationFragment) {
|
||||
// In dev mode, we will issue a warning if after a second we are still
|
||||
// not configured correctly.
|
||||
if (__DEV__) {
|
||||
setTimeout(
|
||||
() =>
|
||||
!this.resources &&
|
||||
// tslint:disable-next-line
|
||||
console.error(
|
||||
"Forgot to pass in resources or set translationFragment for",
|
||||
this.nodeName
|
||||
),
|
||||
1000
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this._updateResources();
|
||||
}
|
||||
|
||||
private async _updateResources() {
|
||||
const { language, data } = await getTranslation(this.translationFragment);
|
||||
this.resources = {
|
||||
[language]: data,
|
||||
};
|
||||
}
|
||||
};
|
@ -1,72 +0,0 @@
|
||||
/**
|
||||
* Lite mixin to add localization without depending on the Hass object.
|
||||
*/
|
||||
import { dedupingMixin } from "@polymer/polymer/lib/utils/mixin";
|
||||
import { localizeBaseMixin } from "./localize-base-mixin";
|
||||
import { getActiveTranslation, getTranslation } from "../util/hass-translation";
|
||||
|
||||
/**
|
||||
* @polymerMixin
|
||||
*/
|
||||
export default dedupingMixin(
|
||||
(superClass) =>
|
||||
class extends localizeBaseMixin(superClass) {
|
||||
static get properties() {
|
||||
return {
|
||||
language: {
|
||||
type: String,
|
||||
value: getActiveTranslation(),
|
||||
},
|
||||
resources: Object,
|
||||
// The fragment to load.
|
||||
translationFragment: String,
|
||||
/**
|
||||
* Translates a string to the current `language`. Any parameters to the
|
||||
* string should be passed in order, as follows:
|
||||
* `localize(stringKey, param1Name, param1Value, param2Name, param2Value)`
|
||||
*/
|
||||
localize: {
|
||||
type: Function,
|
||||
computed: "__computeLocalize(language, resources, formats)",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
ready() {
|
||||
super.ready();
|
||||
|
||||
if (this.resources) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.translationFragment) {
|
||||
// In dev mode, we will issue a warning if after a second we are still
|
||||
// not configured correctly.
|
||||
if (__DEV__) {
|
||||
/* eslint-disable no-console */
|
||||
setTimeout(
|
||||
() =>
|
||||
!this.resources &&
|
||||
console.error(
|
||||
"Forgot to pass in resources or set translationFragment for",
|
||||
this.nodeName
|
||||
),
|
||||
1000
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this._updateResources();
|
||||
}
|
||||
|
||||
async _updateResources() {
|
||||
const { language, data } = await getTranslation(
|
||||
this.translationFragment
|
||||
);
|
||||
this.resources = {
|
||||
[language]: data,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
40
src/mixins/localize-lite-mixin.ts
Normal file
40
src/mixins/localize-lite-mixin.ts
Normal file
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Lite mixin to add localization without depending on the Hass object.
|
||||
*/
|
||||
import { dedupingMixin } from "@polymer/polymer/lib/utils/mixin";
|
||||
import { getActiveTranslation } from "../util/hass-translation";
|
||||
import { localizeLiteBaseMixin } from "./localize-lite-base-mixin";
|
||||
|
||||
/**
|
||||
* @polymerMixin
|
||||
*/
|
||||
export const localizeLiteMixin = dedupingMixin(
|
||||
(superClass) =>
|
||||
class extends localizeLiteBaseMixin(superClass) {
|
||||
static get properties() {
|
||||
return {
|
||||
language: {
|
||||
type: String,
|
||||
value: getActiveTranslation(),
|
||||
},
|
||||
resources: Object,
|
||||
// The fragment to load.
|
||||
translationFragment: String,
|
||||
/**
|
||||
* Translates a string to the current `language`. Any parameters to the
|
||||
* string should be passed in order, as follows:
|
||||
* `localize(stringKey, param1Name, param1Value, param2Name, param2Value)`
|
||||
*/
|
||||
localize: {
|
||||
type: Function,
|
||||
computed: "__computeLocalize(language, resources, formats)",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
this._initializeLocalizeLite();
|
||||
}
|
||||
}
|
||||
);
|
@ -4,7 +4,7 @@ import "@polymer/paper-input/paper-input";
|
||||
import "@polymer/paper-button/paper-button";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import localizeLiteMixin from "../mixins/localize-lite-mixin";
|
||||
import { localizeLiteMixin } from "../mixins/localize-lite-mixin";
|
||||
|
||||
class HaOnboarding extends localizeLiteMixin(PolymerElement) {
|
||||
static get template() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user