{"version":3,"file":"push-utils.mjs","sourceRoot":"","sources":["../../../src/NotificationServicesPushController/web/push-utils.ts"],"names":[],"mappings":"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","sourcesContent":["// We are defining that this file uses a webworker global scope.\n// eslint-disable-next-line spaced-comment\n/// <reference lib=\"webworker\" />\nimport type { FirebaseApp } from 'firebase/app';\nimport { getApp, initializeApp } from 'firebase/app';\nimport { getToken, deleteToken } from 'firebase/messaging';\nimport {\n  getMessaging,\n  onBackgroundMessage,\n  isSupported,\n} from 'firebase/messaging/sw';\nimport type { Messaging, MessagePayload } from 'firebase/messaging/sw';\nimport log from 'loglevel';\n\nimport type { Types } from '../../NotificationServicesController';\nimport {\n  isOnChainRawNotification,\n  safeProcessNotification,\n} from '../../NotificationServicesController';\nimport { toRawAPINotification } from '../../shared/to-raw-notification';\nimport type { NotificationServicesPushControllerMessenger } from '../NotificationServicesPushController';\nimport type { PushNotificationEnv } from '../types/firebase';\n\ndeclare const self: ServiceWorkerGlobalScope;\n\n// Exported to help testing\n// eslint-disable-next-line import-x/no-mutable-exports\nexport let supportedCache: boolean | null = null;\n\nconst getPushAvailability = async (): Promise<boolean> => {\n  // Race condition is acceptable here - worst case is isSupported() is called\n  // multiple times during initialization, which is harmless for caching a boolean\n  // eslint-disable-next-line require-atomic-updates\n  supportedCache ??= await isSupported();\n  return supportedCache;\n};\n\nconst createFirebaseApp = async (\n  env: PushNotificationEnv,\n): Promise<FirebaseApp> => {\n  try {\n    return getApp();\n  } catch {\n    const firebaseConfig = {\n      apiKey: env.apiKey,\n      authDomain: env.authDomain,\n      storageBucket: env.storageBucket,\n      projectId: env.projectId,\n      messagingSenderId: env.messagingSenderId,\n      appId: env.appId,\n      measurementId: env.measurementId,\n    };\n    return initializeApp(firebaseConfig);\n  }\n};\n\nconst getFirebaseMessaging = async (\n  env: PushNotificationEnv,\n): Promise<Messaging | null> => {\n  const supported = await getPushAvailability();\n  if (!supported) {\n    return null;\n  }\n\n  const app = await createFirebaseApp(env);\n  return getMessaging(app);\n};\n\n/**\n * Creates a registration token for Firebase Cloud Messaging.\n *\n * @param env - env to configure push notifications\n * @returns A promise that resolves with the registration token or null if an error occurs.\n */\nexport async function createRegToken(\n  env: PushNotificationEnv,\n): Promise<string | null> {\n  try {\n    const messaging = await getFirebaseMessaging(env);\n    if (!messaging) {\n      return null;\n    }\n\n    const token = await getToken(messaging, {\n      serviceWorkerRegistration: self.registration,\n      vapidKey: env.vapidKey,\n    });\n    return token;\n  } catch {\n    return null;\n  }\n}\n\n/**\n * Deletes the Firebase Cloud Messaging registration token.\n *\n * @param env - env to configure push notifications\n * @returns A promise that resolves with true if the token was successfully deleted, false otherwise.\n */\nexport async function deleteRegToken(\n  env: PushNotificationEnv,\n): Promise<boolean> {\n  try {\n    const messaging = await getFirebaseMessaging(env);\n    if (!messaging) {\n      return true;\n    }\n\n    await deleteToken(messaging);\n    return true;\n  } catch {\n    return false;\n  }\n}\n\n/**\n * Service Worker Listener for when push notifications are received.\n *\n * @param env - push notification environment\n * @param handler - handler to actually showing notification, MUST BE PROVIDED\n * @returns unsubscribe handler\n */\nasync function listenToPushNotificationsReceived(\n  env: PushNotificationEnv,\n  handler?: (notification: Types.INotification) => void | Promise<void>,\n): Promise<(() => void) | null> {\n  const messaging = await getFirebaseMessaging(env);\n  if (!messaging) {\n    return null;\n  }\n\n  const unsubscribePushNotifications = onBackgroundMessage(\n    messaging,\n    // eslint-disable-next-line @typescript-eslint/no-misused-promises\n    async (payload: MessagePayload): Promise<void> => {\n      try {\n        // MessagePayload shapes are not known\n        // TODO - provide open-api unfied backend/frontend types\n        // TODO - we will replace the underlying Data payload with the same Notification payload used by mobile\n        const data: unknown | null = JSON.parse(payload?.data?.data ?? 'null');\n\n        if (!data) {\n          return;\n        }\n\n        if (!isOnChainRawNotification(data)) {\n          return;\n        }\n\n        const notificationData = toRawAPINotification(data);\n        const notification = safeProcessNotification(notificationData);\n\n        if (!notification) {\n          return;\n        }\n\n        await handler?.(notification);\n      } catch (error) {\n        // Do Nothing, cannot parse a bad notification\n        log.error('Unable to send push notification:', {\n          notification: payload?.data?.data,\n          error,\n        });\n      }\n    },\n  );\n\n  const unsubscribe = (): void => unsubscribePushNotifications();\n  return unsubscribe;\n}\n\n/**\n * Service Worker Listener for when a notification is clicked\n *\n * @param handler - listen to NotificationEvent from the service worker\n * @returns unsubscribe handler\n */\nfunction listenToPushNotificationsClicked(\n  handler: (e: NotificationEvent, notification: Types.INotification) => void,\n): () => void {\n  const clickHandler = (event: NotificationEvent): void => {\n    // Get Data\n    const data: Types.INotification = event?.notification?.data;\n    handler(event, data);\n  };\n\n  self.addEventListener('notificationclick', clickHandler);\n  const unsubscribe = (): void =>\n    self.removeEventListener('notificationclick', clickHandler);\n  return unsubscribe;\n}\n\n/**\n * A creator function that assists creating web-specific push notification subscription:\n * 1. Creates subscriptions for receiving and clicking notifications\n * 2. Creates click events when a notification is clicked\n * 3. Publishes controller messenger events\n *\n * @param props - props for this creator function.\n * @param props.onReceivedHandler - allows the developer to handle showing a notification\n * @param props.onClickHandler - allows the developer to handle clicking the notification\n * @param props.messenger - the controller messenger to publish the `onNewNotifications` and `pushNotificationsClicked` events\n * @returns a function that can be used by the controller\n */\nexport function createSubscribeToPushNotifications(props: {\n  onReceivedHandler: (\n    notification: Types.INotification,\n  ) => void | Promise<void>;\n  onClickHandler: (\n    e: NotificationEvent,\n    notification: Types.INotification,\n  ) => void;\n  messenger: NotificationServicesPushControllerMessenger;\n}): (env: PushNotificationEnv) => Promise<() => void> {\n  return async function (env: PushNotificationEnv): Promise<() => void> {\n    const onBackgroundMessageSub = await listenToPushNotificationsReceived(\n      env,\n      async (notification): Promise<void> => {\n        props.messenger.publish(\n          'NotificationServicesPushController:onNewNotifications',\n          notification,\n        );\n        await props.onReceivedHandler(notification);\n      },\n    );\n    const onClickSub = listenToPushNotificationsClicked(\n      (event, notification): void => {\n        props.messenger.publish(\n          'NotificationServicesPushController:pushNotificationClicked',\n          notification,\n        );\n        props.onClickHandler(event, notification);\n      },\n    );\n\n    const unsubscribe = (): void => {\n      onBackgroundMessageSub?.();\n      onClickSub();\n    };\n\n    return unsubscribe;\n  };\n}\n"]}