{"version":3,"sources":["../src/NotificationServicesPushController/NotificationServicesPushController.ts"],"sourcesContent":["import type {\n  RestrictedControllerMessenger,\n  ControllerGetStateAction,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { AuthenticationController } from '@metamask/profile-sync-controller';\nimport log from 'loglevel';\n\nimport type { Types } from '../NotificationServicesController';\nimport { createRegToken, deleteRegToken } from './services/push/push-web';\nimport {\n  activatePushNotifications,\n  deactivatePushNotifications,\n  listenToPushNotifications,\n  updateTriggerPushNotifications,\n} from './services/services';\nimport type { PushNotificationEnv } from './types';\n\nconst controllerName = 'NotificationServicesPushController';\n\nexport type NotificationServicesPushControllerState = {\n  fcmToken: string;\n};\n\nexport type NotificationServicesPushControllerEnablePushNotificationsAction = {\n  type: `${typeof controllerName}:enablePushNotifications`;\n  handler: NotificationServicesPushController['enablePushNotifications'];\n};\n\nexport type NotificationServicesPushControllerDisablePushNotificationsAction = {\n  type: `${typeof controllerName}:disablePushNotifications`;\n  handler: NotificationServicesPushController['disablePushNotifications'];\n};\nexport type NotificationServicesPushControllerUpdateTriggerPushNotificationsAction =\n  {\n    type: `${typeof controllerName}:updateTriggerPushNotifications`;\n    handler: NotificationServicesPushController['updateTriggerPushNotifications'];\n  };\n\nexport type Actions =\n  | NotificationServicesPushControllerEnablePushNotificationsAction\n  | NotificationServicesPushControllerDisablePushNotificationsAction\n  | NotificationServicesPushControllerUpdateTriggerPushNotificationsAction\n  | ControllerGetStateAction<'state', NotificationServicesPushControllerState>;\n\nexport type AllowedActions =\n  AuthenticationController.AuthenticationControllerGetBearerToken;\n\nexport type NotificationServicesPushControllerOnNewNotificationEvent = {\n  type: `${typeof controllerName}:onNewNotifications`;\n  payload: [Types.INotification];\n};\n\nexport type NotificationServicesPushControllerPushNotificationClicked = {\n  type: `${typeof controllerName}:pushNotificationClicked`;\n  payload: [Types.INotification];\n};\n\nexport type AllowedEvents =\n  | NotificationServicesPushControllerOnNewNotificationEvent\n  | NotificationServicesPushControllerPushNotificationClicked;\n\nexport type NotificationServicesPushControllerMessenger =\n  RestrictedControllerMessenger<\n    typeof controllerName,\n    Actions | AllowedActions,\n    AllowedEvents,\n    AllowedActions['type'],\n    AllowedEvents['type']\n  >;\n\nconst metadata = {\n  fcmToken: {\n    persist: true,\n    anonymous: true,\n  },\n};\n\ntype ControllerConfig = {\n  /**\n   * Config to turn on/off push notifications.\n   * This is currently linked to MV3 builds on extension.\n   */\n  isPushEnabled: boolean;\n\n  /**\n   * Must handle when a push notification is received.\n   * You must call `registration.showNotification` or equivalent to show the notification on web/mobile\n   */\n  onPushNotificationReceived: (\n    notification: Types.INotification,\n  ) => void | Promise<void>;\n\n  /**\n   * Must handle when a push notification is clicked.\n   * You must call `event.notification.close();` or equivalent for closing and opening notification in a new window.\n   */\n  onPushNotificationClicked: (\n    event: NotificationEvent,\n    notification?: Types.INotification,\n  ) => void;\n\n  /**\n   * determine the config used for push notification services\n   */\n  platform: 'extension' | 'mobile';\n};\n\n/**\n * Manages push notifications for the application, including enabling, disabling, and updating triggers for push notifications.\n * This controller integrates with Firebase Cloud Messaging (FCM) to handle the registration and management of push notifications.\n * It is responsible for registering and unregistering the service worker that listens for push notifications,\n * managing the FCM token, and communicating with the server to register or unregister the device for push notifications.\n * Additionally, it provides functionality to update the server with new UUIDs that should trigger push notifications.\n *\n * @augments {BaseController<typeof controllerName, NotificationServicesPushControllerState, NotificationServicesPushControllerMessenger>}\n */\nexport class NotificationServicesPushController extends BaseController<\n  typeof controllerName,\n  NotificationServicesPushControllerState,\n  NotificationServicesPushControllerMessenger\n> {\n  #pushListenerUnsubscribe: (() => void) | undefined = undefined;\n\n  #env: PushNotificationEnv;\n\n  #config: ControllerConfig;\n\n  constructor({\n    messenger,\n    state,\n    env,\n    config,\n  }: {\n    messenger: NotificationServicesPushControllerMessenger;\n    state: NotificationServicesPushControllerState;\n    env: PushNotificationEnv;\n    config: ControllerConfig;\n  }) {\n    super({\n      messenger,\n      metadata,\n      name: controllerName,\n      state: {\n        fcmToken: state?.fcmToken || '',\n      },\n    });\n\n    this.#env = env;\n    this.#config = config;\n\n    this.#registerMessageHandlers();\n  }\n\n  #registerMessageHandlers(): void {\n    this.messagingSystem.registerActionHandler(\n      'NotificationServicesPushController:enablePushNotifications',\n      this.enablePushNotifications.bind(this),\n    );\n    this.messagingSystem.registerActionHandler(\n      'NotificationServicesPushController:disablePushNotifications',\n      this.disablePushNotifications.bind(this),\n    );\n    this.messagingSystem.registerActionHandler(\n      'NotificationServicesPushController:updateTriggerPushNotifications',\n      this.updateTriggerPushNotifications.bind(this),\n    );\n  }\n\n  async #getAndAssertBearerToken() {\n    const bearerToken = await this.messagingSystem.call(\n      'AuthenticationController:getBearerToken',\n    );\n    if (!bearerToken) {\n      log.error(\n        'Failed to enable push notifications: BearerToken token is missing.',\n      );\n      throw new Error('BearerToken token is missing');\n    }\n\n    return bearerToken;\n  }\n\n  /**\n   * Enables push notifications for the application.\n   *\n   * This method sets up the necessary infrastructure for handling push notifications by:\n   * 1. Registering the service worker to listen for messages.\n   * 2. Fetching the Firebase Cloud Messaging (FCM) token from Firebase.\n   * 3. Sending the FCM token to the server responsible for sending notifications, to register the device.\n   *\n   * @param UUIDs - An array of UUIDs to enable push notifications for.\n   */\n  async enablePushNotifications(UUIDs: string[]) {\n    if (!this.#config.isPushEnabled) {\n      return;\n    }\n\n    const bearerToken = await this.#getAndAssertBearerToken();\n\n    try {\n      // Activate Push Notifications\n      const regToken = await activatePushNotifications({\n        bearerToken,\n        triggers: UUIDs,\n        env: this.#env,\n        createRegToken,\n        platform: this.#config.platform,\n      });\n\n      if (!regToken) {\n        return;\n      }\n\n      this.#pushListenerUnsubscribe = await listenToPushNotifications({\n        env: this.#env,\n        listenToPushReceived: async (n) => {\n          this.messagingSystem.publish(\n            'NotificationServicesPushController:onNewNotifications',\n            n,\n          );\n          await this.#config.onPushNotificationReceived(n);\n        },\n        listenToPushClicked: (e, n) => {\n          if (n) {\n            this.messagingSystem.publish(\n              'NotificationServicesPushController:pushNotificationClicked',\n              n,\n            );\n          }\n\n          this.#config.onPushNotificationClicked(e);\n        },\n      });\n\n      // Update state\n      this.update((state) => {\n        state.fcmToken = regToken;\n      });\n    } catch (error) {\n      log.error('Failed to enable push notifications:', error);\n      throw new Error('Failed to enable push notifications');\n    }\n  }\n\n  /**\n   * Disables push notifications for the application.\n   * This method handles the process of disabling push notifications by:\n   * 1. Unregistering the service worker to stop listening for messages.\n   * 2. Sending a request to the server to unregister the device using the FCM token.\n   * 3. Removing the FCM token from the state to complete the process.\n   *\n   * @param UUIDs - An array of UUIDs for which push notifications should be disabled.\n   */\n  async disablePushNotifications(UUIDs: string[]) {\n    if (!this.#config.isPushEnabled) {\n      return;\n    }\n\n    const bearerToken = await this.#getAndAssertBearerToken();\n    let isPushNotificationsDisabled: boolean;\n\n    try {\n      // Send a request to the server to unregister the token/device\n      isPushNotificationsDisabled = await deactivatePushNotifications({\n        bearerToken,\n        triggers: UUIDs,\n        env: this.#env,\n        deleteRegToken,\n        regToken: this.state.fcmToken,\n      });\n    } catch (error) {\n      const errorMessage = `Failed to disable push notifications: ${\n        error as string\n      }`;\n      log.error(errorMessage);\n      throw new Error(errorMessage);\n    }\n\n    // Remove the FCM token from the state\n    if (!isPushNotificationsDisabled) {\n      return;\n    }\n\n    // Unsubscribe from push notifications\n    this.#pushListenerUnsubscribe?.();\n\n    // Update State\n    if (isPushNotificationsDisabled) {\n      this.update((state) => {\n        state.fcmToken = '';\n      });\n    }\n  }\n\n  /**\n   * Updates the triggers for push notifications.\n   * This method is responsible for updating the server with the new set of UUIDs that should trigger push notifications.\n   * It uses the current FCM token and a BearerToken for authentication.\n   *\n   * @param UUIDs - An array of UUIDs that should trigger push notifications.\n   */\n  async updateTriggerPushNotifications(UUIDs: string[]) {\n    if (!this.#config.isPushEnabled) {\n      return;\n    }\n\n    const bearerToken = await this.#getAndAssertBearerToken();\n\n    try {\n      const { fcmToken } = await updateTriggerPushNotifications({\n        bearerToken,\n        triggers: UUIDs,\n        env: this.#env,\n        createRegToken,\n        deleteRegToken,\n        platform: this.#config.platform,\n        regToken: this.state.fcmToken,\n      });\n\n      // update the state with the new FCM token\n      if (fcmToken) {\n        this.update((state) => {\n          state.fcmToken = fcmToken;\n        });\n      }\n    } catch (error) {\n      const errorMessage = `Failed to update triggers for push notifications: ${\n        error as string\n      }`;\n      log.error(errorMessage);\n      throw new Error(errorMessage);\n    }\n  }\n}\n"],"mappings":"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","names":[]}