| /** |
| * @license |
| * Copyright 2022 Google LLC |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| import {FlagsService, KnownExperimentId} from './flags/flags'; |
| import { |
| areNotificationsEnabled, |
| registerServiceWorker, |
| } from '../utils/worker-util'; |
| import {UserModel} from '../models/user/user-model'; |
| import {AccountDetailInfo} from '../api/rest-api'; |
| import {until} from '../utils/async-util'; |
| |
| /** Type of incoming messages for ServiceWorker. */ |
| export enum ServiceWorkerMessageType { |
| TRIGGER_NOTIFICATIONS = 'TRIGGER_NOTIFICATIONS', |
| USER_PREFERENCE_CHANGE = 'USER_PREFERENCE_CHANGE', |
| } |
| |
| export const TRIGGER_NOTIFICATION_UPDATES_MS = 5 * 60 * 1000; |
| |
| export class ServiceWorkerInstaller { |
| initialized = false; |
| |
| account?: AccountDetailInfo; |
| |
| allowBrowserNotificationsPreference?: boolean; |
| |
| constructor( |
| private readonly flagsService: FlagsService, |
| private readonly userModel: UserModel |
| ) { |
| if (!this.flagsService.isEnabled(KnownExperimentId.PUSH_NOTIFICATIONS)) { |
| return; |
| } |
| this.userModel.account$.subscribe(acc => (this.account = acc)); |
| this.userModel.preferences$.subscribe(prefs => { |
| if ( |
| this.allowBrowserNotificationsPreference !== |
| prefs.allow_browser_notifications |
| ) { |
| this.allowBrowserNotificationsPreference = |
| prefs.allow_browser_notifications; |
| navigator.serviceWorker.controller?.postMessage({ |
| type: ServiceWorkerMessageType.USER_PREFERENCE_CHANGE, |
| allowBrowserNotificationsPreference: |
| this.allowBrowserNotificationsPreference, |
| }); |
| } |
| }); |
| Promise.all([ |
| until(this.userModel.account$, account => !!account), |
| until( |
| this.userModel.preferences$, |
| prefs => !!prefs.allow_browser_notifications |
| ), |
| ]).then(() => { |
| this.init(); |
| }); |
| } |
| |
| private async init() { |
| if (this.initialized) return; |
| if (!this.flagsService.isEnabled(KnownExperimentId.PUSH_NOTIFICATIONS)) { |
| return; |
| } |
| if (!this.areNotificationsEnabled()) return; |
| |
| if (!('serviceWorker' in navigator)) { |
| console.error('Service worker API not available'); |
| return; |
| } |
| await registerServiceWorker('/service-worker.js'); |
| const permission = await Notification.requestPermission(); |
| if (this.isPermitted(permission)) this.startTriggerTimer(); |
| this.initialized = true; |
| } |
| |
| areNotificationsEnabled() { |
| // Push Notification developer can have notification enabled even if they |
| // are disabled for this.account. |
| if ( |
| !this.flagsService.isEnabled( |
| KnownExperimentId.PUSH_NOTIFICATIONS_DEVELOPER |
| ) && |
| !areNotificationsEnabled(this.account) |
| ) { |
| return false; |
| } |
| |
| return this.allowBrowserNotificationsPreference; |
| } |
| |
| /** |
| * Every 5 minutes, we trigger service-worker to get |
| * latest updates in attention set and service-worker will create |
| * notifications. |
| */ |
| startTriggerTimer() { |
| setTimeout(() => { |
| this.startTriggerTimer(); |
| navigator.serviceWorker.controller?.postMessage({ |
| type: ServiceWorkerMessageType.TRIGGER_NOTIFICATIONS, |
| account: this.account, |
| }); |
| }, TRIGGER_NOTIFICATION_UPDATES_MS); |
| } |
| |
| isPermitted(permission: NotificationPermission) { |
| return permission === 'granted'; |
| } |
| } |