ntfy/web/src/app/Notifier.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

145 lines
4.4 KiB
JavaScript
Raw Normal View History

import { openUrl, playSound, topicDisplayName, topicShortUrl, urlB64ToUint8Array } from "./utils";
import { formatMessage, formatTitleWithDefault } from "./notificationUtils";
2022-03-03 10:16:30 +13:00
import prefs from "./Prefs";
2022-03-10 09:58:21 +13:00
import logo from "../img/ntfy.png";
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 {
async notify(subscription, notification, onClickFallback) {
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
}
2022-03-03 10:16:30 +13:00
const shortUrl = topicShortUrl(subscription.baseUrl, subscription.topic);
2022-06-30 07:57:56 +12:00
const displayName = topicDisplayName(subscription);
2022-02-27 04:14:43 +13:00
const message = formatMessage(notification);
2022-06-30 07:57:56 +12:00
const title = formatTitleWithDefault(notification, displayName);
const image = notification.attachment?.name.match(/\.(png|jpe?g|gif|webp)$/i) ? notification.attachment.url : undefined;
2022-02-27 04:14:43 +13:00
2022-03-06 18:02:27 +13:00
// Show notification
2022-02-27 04:14:43 +13:00
console.log(`[Notifier, ${shortUrl}] Displaying notification ${notification.id}: ${message}`);
// Please update sw.js if formatting changes
2022-02-27 04:14:43 +13:00
const n = new Notification(title, {
body: message,
tag: subscription.id,
icon: image ?? logo,
image,
timestamp: message.time * 1_000,
2022-03-11 12:11:12 +13:00
});
2022-02-27 04:14:43 +13:00
if (notification.click) {
n.onclick = () => openUrl(notification.click);
2022-02-27 04:14:43 +13:00
} else {
n.onclick = () => onClickFallback(subscription);
}
}
async playSound() {
// Play sound
2022-03-03 10:16:30 +13:00
const sound = await prefs.sound();
if (sound && sound !== "none") {
try {
await playSound(sound);
2022-03-06 18:02:27 +13:00
} catch (e) {
console.log(`[Notifier] Error playing audio`, e);
}
}
2023-05-24 07:13:01 +12:00
}
2022-03-11 12:11:12 +13:00
async getBrowserSubscription() {
if (!this.pushPossible()) {
throw new Error("Unsupported or denied");
}
const pushManager = await this.pushManager();
const existingSubscription = await pushManager.getSubscription();
if (existingSubscription) {
return existingSubscription;
}
2023-06-09 15:09:38 +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.
// but if it was _not_ enabled previously, we reach here, and only create a new
// subscription if it is now enabled.
if (await this.pushEnabled()) {
return pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlB64ToUint8Array(config.web_push_public_key),
});
}
return undefined;
2023-05-24 07:13:01 +12:00
}
async pushManager() {
const registration = await navigator.serviceWorker.getRegistration();
if (!registration) {
throw new Error("No service worker registration found");
}
return registration.pushManager;
}
notRequested() {
return this.supported() && Notification.permission === "default";
}
granted() {
return this.supported() && Notification.permission === "granted";
}
denied() {
return this.supported() && Notification.permission === "denied";
}
async maybeRequestPermission() {
if (!this.supported()) {
return false;
2023-05-24 07:13:01 +12:00
}
return new Promise((resolve) => {
Notification.requestPermission((permission) => {
resolve(permission === "granted");
});
});
2023-05-24 07:13:01 +12:00
}
supported() {
return this.browserSupported() && this.contextSupported();
2023-05-24 07:13:01 +12:00
}
browserSupported() {
2022-03-12 09:17:12 +13:00
return "Notification" in window;
2023-05-24 07:13:01 +12:00
}
pushSupported() {
return config.enable_web_push && "serviceWorker" in navigator && "PushManager" in window;
}
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
/**
* 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
*/
contextSupported() {
return window.location.protocol === "https:" || window.location.hostname.match("^127.") || window.location.hostname === "localhost";
2023-05-24 07:13:01 +12:00
}
iosSupportedButInstallRequired() {
return this.pushSupported() && "standalone" in window.navigator && window.navigator.standalone === false;
}
2022-02-27 04:14:43 +13:00
}
2022-03-06 18:02:27 +13:00
const notifier = new Notifier();
export default notifier;