Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate push notification toggle from Polymer to Lit #18565

Merged
merged 4 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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 &&
Expand All @@ -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();

Expand All @@ -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 {
Expand All @@ -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) {
Expand All @@ -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();
Expand All @@ -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 {
Expand All @@ -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;
declare global {
interface HTMLElementTagNameMap {
"ha-push-notifications-toggle": HaPushNotificationsToggle;
}
}

customElements.define(
"ha-push-notifications-toggle",
HaPushNotificationsToggle
);
91 changes: 0 additions & 91 deletions src/panels/profile/ha-push-notifications-row.js

This file was deleted.

72 changes: 72 additions & 0 deletions src/panels/profile/ha-push-notifications-row.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading