2023-06-15 09:20:48 +12:00
|
|
|
import { playSound, topicDisplayName, topicShortUrl, urlB64ToUint8Array } from "./utils";
|
2023-06-16 14:25:05 +12:00
|
|
|
import { toNotificationParams } from "./notificationUtils";
|
2022-03-03 10:16:30 +13:00
|
|
|
import prefs from "./Prefs";
|
2023-06-15 09:20:48 +12:00
|
|
|
import routes from "../components/routes";
|
2022-02-27 04:14:43 +13:00
|
|
|
|
2022-03-12 09:17:12 +13:00
|
|
|
/**
|
|
|
|
* The notifier is responsible for displaying desktop notifications. Note that not all modern browsers
|
|
|
|
* support this; most importantly, all iOS browsers do not support window.Notification.
|
|
|
|
*/
|
2022-03-06 18:02:27 +13:00
|
|
|
class Notifier {
|
2023-06-15 09:20:48 +12:00
|
|
|
async notify(subscription, notification) {
|
2022-02-27 04:14:43 +13:00
|
|
|
if (!this.supported()) {
|
2022-03-06 18:02:27 +13:00
|
|
|
return;
|
2022-02-27 04:14:43 +13:00
|
|
|
}
|
2023-05-25 07:36:01 +12:00
|
|
|
|
2023-06-15 09:20:48 +12:00
|
|
|
await this.playSound();
|
|
|
|
|
2022-03-03 10:16:30 +13:00
|
|
|
const shortUrl = topicShortUrl(subscription.baseUrl, subscription.topic);
|
2023-06-15 09:20:48 +12:00
|
|
|
const defaultTitle = topicDisplayName(subscription);
|
|
|
|
|
|
|
|
console.log(`[Notifier, ${shortUrl}] Displaying notification ${notification.id}`);
|
|
|
|
|
|
|
|
const registration = await this.serviceWorkerRegistration();
|
|
|
|
await registration.showNotification(
|
2023-06-16 14:25:05 +12:00
|
|
|
...toNotificationParams({
|
2023-06-15 09:20:48 +12:00
|
|
|
subscriptionId: subscription.id,
|
|
|
|
message: notification,
|
|
|
|
defaultTitle,
|
|
|
|
topicRoute: new URL(routes.forSubscription(subscription), window.location.origin).toString(),
|
|
|
|
})
|
|
|
|
);
|
2023-05-25 07:36:01 +12:00
|
|
|
}
|
2022-03-02 10:22:47 +13:00
|
|
|
|
2023-05-25 07:36:01 +12:00
|
|
|
async playSound() {
|
2022-03-02 16:41:49 +13:00
|
|
|
// Play sound
|
2022-03-03 10:16:30 +13:00
|
|
|
const sound = await prefs.sound();
|
2022-03-02 16:41:49 +13:00
|
|
|
if (sound && sound !== "none") {
|
|
|
|
try {
|
|
|
|
await playSound(sound);
|
2022-03-06 18:02:27 +13:00
|
|
|
} catch (e) {
|
2023-05-25 07:36:01 +12:00
|
|
|
console.log(`[Notifier] Error playing audio`, e);
|
2022-03-02 10:22:47 +13:00
|
|
|
}
|
|
|
|
}
|
2023-05-24 07:13:01 +12:00
|
|
|
}
|
2022-03-11 12:11:12 +13:00
|
|
|
|
2023-06-15 05:31:34 +12:00
|
|
|
async webPushSubscription() {
|
2023-06-02 23:22:54 +12:00
|
|
|
if (!this.pushPossible()) {
|
|
|
|
throw new Error("Unsupported or denied");
|
2023-05-25 07:36:01 +12:00
|
|
|
}
|
2023-06-02 23:22:54 +12:00
|
|
|
const pushManager = await this.pushManager();
|
2023-06-08 20:55:11 +12:00
|
|
|
const existingSubscription = await pushManager.getSubscription();
|
|
|
|
if (existingSubscription) {
|
|
|
|
return existingSubscription;
|
|
|
|
}
|
|
|
|
|
2023-06-15 05:31:34 +12:00
|
|
|
// Create a new subscription only if Web Push is enabled. It is possible that Web Push
|
|
|
|
// was previously enabled and then disabled again in which case there would be an existingSubscription.
|
|
|
|
// If, however, it was _not_ enabled previously, we create a new subscription if it is now enabled.
|
2023-06-08 20:55:11 +12:00
|
|
|
|
|
|
|
if (await this.pushEnabled()) {
|
|
|
|
return pushManager.subscribe({
|
2023-05-25 07:36:01 +12:00
|
|
|
userVisibleOnly: true,
|
2023-05-31 06:42:17 +12:00
|
|
|
applicationServerKey: urlB64ToUint8Array(config.web_push_public_key),
|
2023-06-08 20:55:11 +12:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return undefined;
|
2023-05-24 07:13:01 +12:00
|
|
|
}
|
2022-06-13 08:38:33 +12:00
|
|
|
|
2023-06-01 07:08:46 +12:00
|
|
|
async pushManager() {
|
2023-06-15 09:20:48 +12:00
|
|
|
return (await this.serviceWorkerRegistration()).pushManager;
|
|
|
|
}
|
|
|
|
|
|
|
|
async serviceWorkerRegistration() {
|
2023-06-01 07:08:46 +12:00
|
|
|
const registration = await navigator.serviceWorker.getRegistration();
|
|
|
|
if (!registration) {
|
|
|
|
throw new Error("No service worker registration found");
|
|
|
|
}
|
2023-06-15 09:20:48 +12:00
|
|
|
return registration;
|
2023-06-01 07:08:46 +12:00
|
|
|
}
|
|
|
|
|
2023-06-02 23:22:54 +12:00
|
|
|
notRequested() {
|
|
|
|
return this.supported() && Notification.permission === "default";
|
|
|
|
}
|
|
|
|
|
2023-05-25 07:36:01 +12:00
|
|
|
granted() {
|
|
|
|
return this.supported() && Notification.permission === "granted";
|
|
|
|
}
|
|
|
|
|
|
|
|
denied() {
|
|
|
|
return this.supported() && Notification.permission === "denied";
|
|
|
|
}
|
|
|
|
|
|
|
|
async maybeRequestPermission() {
|
|
|
|
if (!this.supported()) {
|
2022-03-02 10:22:47 +13:00
|
|
|
return false;
|
2023-05-24 07:13:01 +12:00
|
|
|
}
|
2023-05-25 07:36:01 +12:00
|
|
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
Notification.requestPermission((permission) => {
|
|
|
|
resolve(permission === "granted");
|
|
|
|
});
|
|
|
|
});
|
2023-05-24 07:13:01 +12:00
|
|
|
}
|
|
|
|
|
2022-06-13 08:38:33 +12:00
|
|
|
supported() {
|
|
|
|
return this.browserSupported() && this.contextSupported();
|
2023-05-24 07:13:01 +12:00
|
|
|
}
|
|
|
|
|
2022-06-13 08:38:33 +12:00
|
|
|
browserSupported() {
|
2022-03-12 09:17:12 +13:00
|
|
|
return "Notification" in window;
|
2023-05-24 07:13:01 +12:00
|
|
|
}
|
|
|
|
|
2023-05-25 07:36:01 +12:00
|
|
|
pushSupported() {
|
2023-06-01 04:43:06 +12:00
|
|
|
return config.enable_web_push && "serviceWorker" in navigator && "PushManager" in window;
|
2023-05-25 07:36:01 +12:00
|
|
|
}
|
|
|
|
|
2023-06-02 23:22:54 +12:00
|
|
|
pushPossible() {
|
|
|
|
return this.pushSupported() && this.contextSupported() && this.granted() && !this.iosSupportedButInstallRequired();
|
|
|
|
}
|
|
|
|
|
2023-06-08 19:22:56 +12:00
|
|
|
async pushEnabled() {
|
|
|
|
const enabled = await prefs.webPushEnabled();
|
|
|
|
return this.pushPossible() && enabled;
|
|
|
|
}
|
|
|
|
|
2023-05-24 07:13:01 +12:00
|
|
|
/**
|
2022-06-13 08:38:33 +12:00
|
|
|
* Returns true if this is a HTTPS site, or served over localhost. Otherwise the Notification API
|
|
|
|
* is not supported, see https://developer.mozilla.org/en-US/docs/Web/API/notification
|
2023-05-24 07:13:01 +12:00
|
|
|
*/
|
2022-06-13 08:38:33 +12:00
|
|
|
contextSupported() {
|
2023-05-24 20:20:15 +12:00
|
|
|
return window.location.protocol === "https:" || window.location.hostname.match("^127.") || window.location.hostname === "localhost";
|
2023-05-24 07:13:01 +12:00
|
|
|
}
|
2023-05-25 07:36:01 +12:00
|
|
|
|
|
|
|
iosSupportedButInstallRequired() {
|
2023-06-02 23:22:54 +12:00
|
|
|
return this.pushSupported() && "standalone" in window.navigator && window.navigator.standalone === false;
|
2023-05-25 07:36:01 +12:00
|
|
|
}
|
2022-02-27 04:14:43 +13:00
|
|
|
}
|
|
|
|
|
2022-03-06 18:02:27 +13:00
|
|
|
const notifier = new Notifier();
|
|
|
|
export default notifier;
|