diff options
Diffstat (limited to 'src/services/sw/sw.js')
| -rw-r--r-- | src/services/sw/sw.js | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/src/services/sw/sw.js b/src/services/sw/sw.js new file mode 100644 index 00000000..b25f6328 --- /dev/null +++ b/src/services/sw/sw.js @@ -0,0 +1,148 @@ +import runtime from 'serviceworker-webpack5-plugin/lib/runtime' + +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 () { + return runtime.register() + .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 } + 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()) { + Promise.all([ + deleteSubscriptionFromBackEnd(token), + getOrCreateServiceWorker() + .then((registration) => { + return unsubscribePush(registration).then((result) => [registration, result]) + }) + .then(([registration, unsubResult]) => { + if (!unsubResult) { + console.warn('Push subscription cancellation wasn\'t successful') + } + }) + ]).catch((e) => console.warn(`Failed to disable Web Push Notifications: ${e.message}`)) + } +} |
