UNPKG

@socketsupply/socket

Version:

A Cross-Platform, Native Runtime for Desktop and Mobile Apps — Create apps using HTML, CSS, and JavaScript. Written from the ground up to be small and maintainable.

130 lines (105 loc) 3.73 kB
import { Notification, NotificationOptions } from '../notification.js' import { SHARED_WORKER_URL } from './instance.js' import { NotAllowedError } from '../errors.js' import { SharedWorker } from '../shared-worker/index.js' import permissions from '../internal/permissions.js' let sharedWorker = null const observedNotifications = new Set() if (globalThis.isServiceWorkerScope) { globalThis.addEventListener('notificationclose', (event) => { for (const notification of observedNotifications) { if (notification.id === event.notification.id) { notification.dispatchEvent(new Event('close')) observedNotifications.delete(notification) } } }) } function ensureSharedWorker () { if (!globalThis.isServiceWorkerScope && !sharedWorker) { sharedWorker = new SharedWorker(SHARED_WORKER_URL) sharedWorker.port.start() } } export async function showNotification (registration, title, options) { ensureSharedWorker() if (title && typeof title === 'object') { options = title title = options.title ?? '' } const info = registration[Symbol.for('socket.runtime.ServiceWorkerRegistration.info')] if (!info) { throw new TypeError('Invalid ServiceWorkerRegistration instance given') } if (!registration.active) { throw new TypeError('ServiceWorkerRegistration is not active') } const query = await permissions.query({ name: 'notifications' }) if (query.state !== 'granted') { throw new NotAllowedError('Operation not permitted') } // will throw if invalid options are given options = new NotificationOptions(options, /* allowServiceWorkerGlobalScope= */ true) const nonce = Math.random().toString(16).slice(2) const target = globalThis.isServiceWorkerScope ? globalThis : sharedWorker.port const message = { nonce, registration: { id: info.id }, showNotification: { title, ...options.toJSON() } } target.postMessage(message) await new Promise((resolve, reject) => { target.addEventListener('message', function onMessage (event) { if (event.data?.nonce === nonce) { target.removeEventListener('message', onMessage) if (event.data.error) { reject(new Error(event.data.error.message)) } else { resolve(event.data.notification) } } }) }) } export async function getNotifications (registration, options = null) { ensureSharedWorker() const info = registration[Symbol.for('socket.runtime.ServiceWorkerRegistration.info')] if (!info) { throw new TypeError('Invalid ServiceWorkerRegistration instance given') } if (!registration.active) { throw new TypeError('ServiceWorkerRegistration is not active') } const nonce = Math.random().toString(16).slice(2) const target = globalThis.isServiceWorkerScope ? globalThis : sharedWorker.port const message = { nonce, registration: { id: info.id }, getNotifications: { tag: options?.tag ?? null } } target.postMessage(message) const notifications = await new Promise((resolve, reject) => { target.addEventListener('message', function onMessage (event) { if (event.data?.nonce === nonce) { target.removeEventListener('message', onMessage) if (event.data.error) { reject(new Error(event.data.message)) } else { resolve(event.data.notifications.map((notification) => new Notification( notification.title, notification.options, notification.data ))) } } }) }) for (const notification of notifications) { observedNotifications.add(notification) } return notifications } export default { showNotification, getNotifications }