/* global process */ function urlBase64ToUint8Array(base64String) { const padding = '='.repeat((4 - (base64String.length % 4)) % 4) const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/') const rawData = window.atob(base64) return Uint8Array.from([...rawData].map((char) => char.charCodeAt(0))) } export function isSWSupported() { return 'serviceWorker' in navigator } function isPushSupported() { return 'PushManager' in window } function getOrCreateServiceWorker() { if (!isSWSupported()) return const swType = process.env.HAS_MODULE_SERVICE_WORKER ? 'module' : 'classic' return navigator.serviceWorker .register('/sw-pleroma.js', { type: swType }) .catch((err) => console.error('Unable to get or create a service worker.', err), ) } function subscribePush(registration, isEnabled, vapidPublicKey) { if (!isEnabled) return Promise.reject(new Error('Web Push is disabled in config')) if (!vapidPublicKey) return Promise.reject(new Error('VAPID public key is not found')) const subscribeOptions = { userVisibleOnly: false, applicationServerKey: urlBase64ToUint8Array(vapidPublicKey), } return registration.pushManager.subscribe(subscribeOptions) } function unsubscribePush(registration) { return registration.pushManager.getSubscription().then((subscription) => { if (subscription === null) { return Promise.resolve('No subscription') } return subscription.unsubscribe() }) } function deleteSubscriptionFromBackEnd(token) { return fetch('/api/v1/push/subscription/', { method: 'DELETE', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, }).then((response) => { if (!response.ok) throw new Error('Bad status code from server.') return response }) } function sendSubscriptionToBackEnd( subscription, token, notificationVisibility, ) { return window .fetch('/api/v1/push/subscription/', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, body: JSON.stringify({ subscription, data: { alerts: { follow: notificationVisibility.follows, favourite: notificationVisibility.likes, mention: notificationVisibility.mentions, reblog: notificationVisibility.repeats, move: notificationVisibility.moves, }, }, }), }) .then((response) => { if (!response.ok) throw new Error('Bad status code from server.') return response.json() }) .then((responseData) => { if (!responseData.id) throw new Error('Bad response from server.') return responseData }) } export async function initServiceWorker(store) { if (!isSWSupported()) return await getOrCreateServiceWorker() navigator.serviceWorker.addEventListener('message', (event) => { const { dispatch } = store const { type, ...rest } = event.data switch (type) { case 'notificationClicked': dispatch('notificationClicked', { id: rest.id }) } }) } export async function showDesktopNotification(content) { if (!isSWSupported) return const { active: sw } = (await window.navigator.serviceWorker.getRegistration()) || {} if (!sw) return console.error('No serviceworker found!') sw.postMessage({ type: 'desktopNotification', content }) } export async function closeDesktopNotification({ id }) { if (!isSWSupported) return const { active: sw } = (await window.navigator.serviceWorker.getRegistration()) || {} if (!sw) return console.error('No serviceworker found!') if (id >= 0) { sw.postMessage({ type: 'desktopNotificationClose', content: { id } }) } else { sw.postMessage({ type: 'desktopNotificationClose', content: { all: true } }) } } export async function updateFocus() { if (!isSWSupported) return const { active: sw } = (await window.navigator.serviceWorker.getRegistration()) || {} if (!sw) return console.error('No serviceworker found!') sw.postMessage({ type: 'updateFocus' }) } export function registerPushNotifications( isEnabled, vapidPublicKey, token, notificationVisibility, ) { if (isPushSupported()) { getOrCreateServiceWorker() .then((registration) => subscribePush(registration, isEnabled, vapidPublicKey), ) .then((subscription) => sendSubscriptionToBackEnd(subscription, token, notificationVisibility), ) .catch((e) => console.warn(`Failed to setup Web Push Notifications: ${e.message}`), ) } } export function unregisterPushNotifications(token) { if (isPushSupported()) { getOrCreateServiceWorker() .then((registration) => { return unsubscribePush(registration).then((result) => [ registration, result, ]) }) .then(([, unsubResult]) => { if (unsubResult === 'No subscription') return if (!unsubResult) { console.warn("Push subscription cancellation wasn't successful") } return deleteSubscriptionFromBackEnd(token) }) .catch((e) => { console.warn(`Failed to disable Web Push Notifications: ${e.message}`) }) } } export const shouldCache = process.env.NODE_ENV === 'production' export const cacheKey = 'pleroma-fe' export const emojiCacheKey = 'pleroma-fe-emoji' export const clearCache = (key) => caches.delete(key) export { getOrCreateServiceWorker }