orbit-studio / src /modules /util /PushManager.ts
moncefem's picture
Deploy Orbit Studio propagator
9f21d0a
Raw
History Blame Contribute Delete
3.28 kB
import dayjs from "dayjs";
export interface PushNotificationOptions extends NotificationOptions {
[key: string]: unknown;
}
interface TimerEntry {
id: ReturnType<typeof setTimeout>;
date: Date | string | number | dayjs.Dayjs;
message: string;
}
// Non-standard webkit message-handler bridge used by the iOS wrapper app
interface WebkitWindow {
webkit?: {
messageHandlers: {
iosNotify: {
postMessage(content: unknown, origin: string): void;
};
};
};
}
export class PushManager {
options: PushNotificationOptions;
timers: TimerEntry[] = [];
constructor(options: PushNotificationOptions = {}) {
this.options = options;
}
get available(): boolean {
if ("webkit" in window) {
return true;
}
if (!("Notification" in window) || !("ServiceWorkerRegistration" in window)) {
console.log("Notification API not supported!");
return false;
}
switch (Notification.permission) {
case "granted":
return true;
case "default":
this.requestPermission();
return true;
case "denied":
return false;
default:
return false;
}
}
requestPermission(): void {
Notification.requestPermission().then((result) => {
console.log(`Notifcation permission result: ${result}`);
});
}
get active(): boolean {
return this.timers.length > 0;
}
clearTimers(): void {
this.timers.forEach((timer) => {
clearTimeout(timer.id);
});
this.timers = [];
}
persistentNotification(message: string, options?: PushNotificationOptions): void {
if (!this.available) {
return;
}
const optionsMerged = { ...this.options, ...options };
try {
navigator.serviceWorker
.getRegistration()
.then((reg) => reg?.showNotification(message, optionsMerged))
.catch((err) => console.log(`Service Worker registration error: ${err}`));
} catch (err) {
console.log(`Notification API error: ${err}`);
}
}
notifyInMs(ms: number, message: string, options?: PushNotificationOptions): void {
if (!this.available) {
return;
}
console.log(`Notify "${message}" in ${ms / 1000}s`);
setTimeout(() => {
this.persistentNotification(message, options);
}, ms);
}
notifyAtDate(date: Date | string | number, message: string, options?: PushNotificationOptions): void {
if (!this.available) {
return;
}
const waitMs = dayjs(date).diff(dayjs());
if (waitMs < 0) {
return;
}
if (this.timers.some((timer) => Math.abs(dayjs(timer.date as Date).diff(date, "seconds")) < 10)) {
console.log("Ignore duplicate entry");
return;
}
console.log(`Notify "${message}" at ${date}s ${dayjs(date).unix()}`);
if ("webkit" in window) {
const content = {
date: dayjs(date).unix(),
delay: waitMs / 1000,
message,
};
const w = window as unknown as WebkitWindow;
w.webkit?.messageHandlers.iosNotify.postMessage(content, self.location.origin);
} else {
const id = setTimeout(() => {
this.persistentNotification(message, options);
}, waitMs);
this.timers.push({
id,
date,
message,
});
}
}
}