Migrate push notification toggle from Polymer to Lit (#18565)

This commit is contained in:
Simon Lamon 2023-11-24 15:47:09 +01:00 committed by GitHub
parent 3da7025d78
commit 1d2dc37f75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 115 additions and 148 deletions

View File

@ -1,10 +1,10 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { LitElement, TemplateResult, html } from "lit";
import { customElement, property, state } from "lit/decorators";
import { getAppKey } from "../data/notify_html5";
import { showPromptDialog } from "../dialogs/generic/show-dialog-box";
import { EventsMixin } from "../mixins/events-mixin";
import "./ha-switch";
import { HaSwitch } from "./ha-switch";
import { HomeAssistant } from "../types";
import { fireEvent } from "../common/dom/fire_event";
export const pushSupported =
"serviceWorker" in navigator &&
@ -13,39 +13,27 @@ export const pushSupported =
document.location.hostname === "localhost" ||
document.location.hostname === "127.0.0.1");
/*
* @appliesMixin EventsMixin
*/
class HaPushNotificationsToggle extends EventsMixin(PolymerElement) {
static get template() {
@customElement("ha-push-notifications-toggle")
class HaPushNotificationsToggle extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _disabled: boolean = false;
@state() private _pushChecked: boolean =
"Notification" in window && Notification.permission === "granted";
@state() private _loading: boolean = true;
protected render(): TemplateResult {
return html`
<ha-switch
disabled="[[_compDisabled(disabled, loading)]]"
checked="{{pushChecked}}"
on-change="handlePushChange"
.disabled=${this._disabled || this._loading}
.checked=${this._pushChecked}
@change=${this._handlePushChange}
></ha-switch>
`;
}
static get properties() {
return {
hass: { type: Object, value: null },
disabled: {
type: Boolean,
value: false,
},
pushChecked: {
type: Boolean,
value:
"Notification" in window && Notification.permission === "granted",
},
loading: {
type: Boolean,
value: true,
},
};
}
async connectedCallback() {
super.connectedCallback();
@ -57,32 +45,33 @@ class HaPushNotificationsToggle extends EventsMixin(PolymerElement) {
return;
}
reg.pushManager.getSubscription().then((subscription) => {
this.loading = false;
this.pushChecked = !!subscription;
this._loading = false;
this._pushChecked = !!subscription;
});
} catch (err) {
// We don't set loading to `false` so we remain disabled
}
}
handlePushChange(event) {
private _handlePushChange(ev: Event) {
// Somehow this is triggered on Safari on page load causing
// it to get into a loop and crash the page.
if (!pushSupported) return;
if (event.target.checked) {
this.subscribePushNotifications();
const pushnotifications = (ev.target as HaSwitch).checked;
if (pushnotifications) {
this._subscribePushNotifications();
} else {
this.unsubscribePushNotifications();
this._unsubscribePushNotifications();
}
}
async subscribePushNotifications() {
private async _subscribePushNotifications() {
const reg = await navigator.serviceWorker.ready;
let sub;
try {
let browserName;
let browserName: string;
if (navigator.userAgent.toLowerCase().indexOf("firefox") > -1) {
browserName = "firefox";
} else {
@ -98,11 +87,11 @@ class HaPushNotificationsToggle extends EventsMixin(PolymerElement) {
),
});
if (name == null) {
this.pushChecked = false;
this._pushChecked = false;
return;
}
let applicationServerKey;
let applicationServerKey: Uint8Array | null;
try {
applicationServerKey = await getAppKey(this.hass);
} catch (ex) {
@ -123,7 +112,7 @@ class HaPushNotificationsToggle extends EventsMixin(PolymerElement) {
browser: browserName,
name,
});
} catch (err) {
} catch (err: any) {
const message = err.message || "Notification registration failed.";
if (sub) {
await sub.unsubscribe();
@ -132,12 +121,12 @@ class HaPushNotificationsToggle extends EventsMixin(PolymerElement) {
// eslint-disable-next-line
console.error(err);
this.fire("hass-notification", { message });
this.pushChecked = false;
fireEvent(this, "hass-notification", { message });
this._pushChecked = false;
}
}
async unsubscribePushNotifications() {
private async _unsubscribePushNotifications() {
const reg = await navigator.serviceWorker.ready;
try {
@ -147,24 +136,21 @@ class HaPushNotificationsToggle extends EventsMixin(PolymerElement) {
await this.hass.callApi("DELETE", "notify.html5", { subscription: sub });
await sub.unsubscribe();
} catch (err) {
} catch (err: any) {
const message =
err.message || "Failed unsubscribing for push notifications.";
// eslint-disable-next-line
console.error("Error in unsub push", err);
this.fire("hass-notification", { message });
this.pushChecked = true;
fireEvent(this, "hass-notification", { message });
this._pushChecked = true;
}
}
_compDisabled(disabled, loading) {
return disabled || loading;
}
}
customElements.define(
"ha-push-notifications-toggle",
HaPushNotificationsToggle
);
declare global {
interface HTMLElementTagNameMap {
"ha-push-notifications-toggle": HaPushNotificationsToggle;
}
}

View File

@ -1,91 +0,0 @@
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { isComponentLoaded } from "../../common/config/is_component_loaded";
import { pushSupported } from "../../components/ha-push-notifications-toggle";
import "../../components/ha-settings-row";
import LocalizeMixin from "../../mixins/localize-mixin";
import { documentationUrl } from "../../util/documentation-url";
/*
* @appliesMixin LocalizeMixin
*/
class HaPushNotificationsRow extends LocalizeMixin(PolymerElement) {
static get template() {
return html`
<style>
a {
color: var(--primary-color);
}
</style>
<ha-settings-row narrow="[[narrow]]">
<span slot="heading"
>[[localize('ui.panel.profile.push_notifications.header')]]</span
>
<span slot="description">
[[localize(_descrLocalizeKey)]]
<a
href="[[_computeDocumentationUrl(hass)]]"
target="_blank"
rel="noreferrer"
>[[localize('ui.panel.profile.push_notifications.link_promo')]]</a
>
</span>
<ha-push-notifications-toggle
hass="[[hass]]"
disabled="[[_error]]"
></ha-push-notifications-toggle>
</ha-settings-row>
`;
}
static get properties() {
return {
hass: Object,
narrow: Boolean,
_descrLocalizeKey: {
type: String,
computed: "_descriptionKey(_platformLoaded, _pushSupported)",
},
_platformLoaded: {
type: Boolean,
computed: "_compPlatformLoaded(hass)",
},
_pushSupported: {
type: Boolean,
value: pushSupported,
},
_error: {
type: Boolean,
computed: "_compError(_platformLoaded, _pushSupported)",
},
};
}
_computeDocumentationUrl(hass) {
return documentationUrl(hass, "/integrations/html5");
}
_compPlatformLoaded(hass) {
return isComponentLoaded(hass, "notify.html5");
}
_compError(platformLoaded, pushSupported_) {
return !platformLoaded || !pushSupported_;
}
_descriptionKey(platformLoaded, pushSupported_) {
let key;
if (!pushSupported_) {
key = "error_use_https";
} else if (!platformLoaded) {
key = "error_load_platform";
} else {
key = "description";
}
return `ui.panel.profile.push_notifications.${key}`;
}
}
customElements.define("ha-push-notifications-row", HaPushNotificationsRow);

View File

@ -0,0 +1,72 @@
import { LitElement, TemplateResult, css, html } from "lit";
import { customElement, property } from "lit/decorators";
import { isComponentLoaded } from "../../common/config/is_component_loaded";
import { pushSupported } from "../../components/ha-push-notifications-toggle";
import "../../components/ha-settings-row";
import { documentationUrl } from "../../util/documentation-url";
import { HomeAssistant } from "../../types";
@customElement("ha-push-notifications-row")
class HaPushNotificationsRow extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public narrow!: boolean;
protected render(): TemplateResult {
const platformLoaded = isComponentLoaded(this.hass, "notify.html5");
let descriptionKey:
| "error_use_https"
| "error_load_platform"
| "description";
if (!pushSupported) {
descriptionKey = "error_use_https";
} else if (!platformLoaded) {
descriptionKey = "error_load_platform";
} else {
descriptionKey = "description";
}
const isDisabled = !platformLoaded || !pushSupported;
return html`
<ha-settings-row .narrow=${this.narrow}>
<span slot="heading"
>${this.hass.localize(
"ui.panel.profile.push_notifications.header"
)}</span
>
<span slot="description">
${this.hass.localize(
`ui.panel.profile.push_notifications.${descriptionKey}`
)}
<a
href=${documentationUrl(this.hass, "/integrations/html5")}
target="_blank"
rel="noreferrer"
>${this.hass.localize(
"ui.panel.profile.push_notifications.link_promo"
)}</a
>
</span>
<ha-push-notifications-toggle
.hass=${this.hass}
.disabled=${isDisabled}
></ha-push-notifications-toggle>
</ha-settings-row>
`;
}
static get styles() {
return css`
a {
color: var(--primary-color);
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-push-notifications-row": HaPushNotificationsRow;
}
}