kopia lustrzana https://github.com/elk-zone/elk
				
				
				
			
		
			
				
	
	
		
			137 wiersze
		
	
	
		
			5.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
			
		
		
	
	
			137 wiersze
		
	
	
		
			5.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
| import type { mastodon } from 'masto'
 | |
| import type {
 | |
|   CreatePushNotification,
 | |
|   PushManagerSubscriptionInfo,
 | |
|   RequiredUserLogin,
 | |
| } from '~/composables/push-notifications/types'
 | |
| import { PushSubscriptionError } from '~/composables/push-notifications/types'
 | |
| 
 | |
| export const createPushSubscription = async (
 | |
|   user: RequiredUserLogin,
 | |
|   notificationData: CreatePushNotification,
 | |
|   policy: mastodon.v1.SubscriptionPolicy = 'all',
 | |
|   force = false,
 | |
| ): Promise<mastodon.v1.WebPushSubscription | undefined> => {
 | |
|   const { server: serverEndpoint, vapidKey } = user
 | |
| 
 | |
|   return await getRegistration()
 | |
|     .then(getPushSubscription)
 | |
|     .then(({ registration, subscription }): Promise<mastodon.v1.WebPushSubscription | undefined> => {
 | |
|       if (subscription) {
 | |
|         const currentServerKey = (new Uint8Array(subscription.options.applicationServerKey!)).toString()
 | |
|         const subscriptionServerKey = urlBase64ToUint8Array(vapidKey).toString()
 | |
| 
 | |
|         // If the VAPID public key did not change and the endpoint corresponds
 | |
|         // to the endpoint saved in the backend, the subscription is valid
 | |
|         // If push subscription is not there, we need to create it: it is fetched on login
 | |
|         if (subscriptionServerKey === currentServerKey && subscription.endpoint === serverEndpoint && (!force && user.pushSubscription)) {
 | |
|           return Promise.resolve(user.pushSubscription)
 | |
|         }
 | |
|         else if (user.pushSubscription) {
 | |
|           // if we have a subscription, but it is not valid or forcing renew, we need to remove it
 | |
|           // we need to prevent removing push notification data
 | |
|           return unsubscribeFromBackend(false, false)
 | |
|             .catch(removePushNotificationDataOnError)
 | |
|             .then(() => subscribe(registration, vapidKey))
 | |
|             .then(subscription => sendSubscriptionToBackend(subscription, notificationData, policy))
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       return subscribe(registration, vapidKey).then(
 | |
|         subscription => sendSubscriptionToBackend(subscription, notificationData, policy),
 | |
|       )
 | |
|     })
 | |
|     .catch((error) => {
 | |
|       let useError: PushSubscriptionError | Error = error
 | |
|       if (error.code === 11 && error.name === 'InvalidStateError')
 | |
|         useError = new PushSubscriptionError('too_many_registrations', 'Too many registrations')
 | |
|       else if (error.code === 20 && error.name === 'AbortError')
 | |
|         useError = new PushSubscriptionError('vapid_not_supported', 'Your browser supports Web Push Notifications, but does not seem to implement the VAPID protocol.')
 | |
|       else if (error.code === 5 && error.name === 'InvalidCharacterError')
 | |
|         useError = new PushSubscriptionError('invalid_vapid_key', `The VAPID public key seems to be invalid: ${vapidKey}`)
 | |
| 
 | |
|       return getRegistration()
 | |
|         .then(getPushSubscription)
 | |
|         .then(() => unsubscribeFromBackend(true))
 | |
|         .then(() => Promise.resolve(undefined))
 | |
|         .catch((e) => {
 | |
|           console.error(e)
 | |
|           return Promise.resolve(undefined)
 | |
|         })
 | |
|         .finally(() => {
 | |
|           return Promise.reject(useError)
 | |
|         })
 | |
|     })
 | |
| }
 | |
| 
 | |
| // Taken from https://www.npmjs.com/package/web-push
 | |
| function urlBase64ToUint8Array(base64String: string) {
 | |
|   const padding = '='.repeat((4 - base64String.length % 4) % 4)
 | |
|   const base64 = `${base64String}${padding}`
 | |
|     .replace(/-/g, '+')
 | |
|     .replace(/_/g, '/')
 | |
| 
 | |
|   const rawData = window.atob(base64)
 | |
|   const outputArray = new Uint8Array(rawData.length)
 | |
| 
 | |
|   for (let i = 0; i < rawData.length; ++i)
 | |
|     outputArray[i] = rawData.charCodeAt(i)
 | |
| 
 | |
|   return outputArray
 | |
| }
 | |
| 
 | |
| function getRegistration() {
 | |
|   return navigator.serviceWorker.ready
 | |
| }
 | |
| async function getPushSubscription(registration: ServiceWorkerRegistration): Promise<PushManagerSubscriptionInfo> {
 | |
|   const subscription = await registration.pushManager.getSubscription()
 | |
|   return { registration, subscription }
 | |
| }
 | |
| 
 | |
| async function subscribe(
 | |
|   registration: ServiceWorkerRegistration,
 | |
|   applicationServerKey: string,
 | |
| ): Promise<PushSubscription> {
 | |
|   return await registration.pushManager.subscribe({
 | |
|     userVisibleOnly: true,
 | |
|     applicationServerKey: urlBase64ToUint8Array(applicationServerKey),
 | |
|   })
 | |
| }
 | |
| 
 | |
| async function unsubscribeFromBackend(fromSWPushManager: boolean, removePushNotification = true) {
 | |
|   const cu = currentUser.value
 | |
|   if (cu) {
 | |
|     await removePushNotifications(cu)
 | |
|     removePushNotification && await removePushNotificationData(cu, fromSWPushManager)
 | |
|   }
 | |
| }
 | |
| 
 | |
| async function removePushNotificationDataOnError(e: Error) {
 | |
|   const cu = currentUser.value
 | |
|   if (cu)
 | |
|     await removePushNotificationData(cu, true)
 | |
| 
 | |
|   throw e
 | |
| }
 | |
| 
 | |
| async function sendSubscriptionToBackend(
 | |
|   subscription: PushSubscription,
 | |
|   data: CreatePushNotification,
 | |
|   policy: mastodon.v1.SubscriptionPolicy,
 | |
| ): Promise<mastodon.v1.WebPushSubscription> {
 | |
|   const { endpoint, keys } = subscription.toJSON()
 | |
|   const params: mastodon.v1.CreateWebPushSubscriptionParams = {
 | |
|     policy,
 | |
|     subscription: {
 | |
|       endpoint: endpoint!,
 | |
|       keys: {
 | |
|         p256dh: keys!.p256dh!,
 | |
|         auth: keys!.auth!,
 | |
|       },
 | |
|     },
 | |
|     data,
 | |
|   }
 | |
| 
 | |
|   return await useMastoClient().v1.webPushSubscriptions.create(params)
 | |
| }
 |