import { callApi } from '../api/gramjs'; import { DEBUG } from '../config'; import { getDispatch, getGlobal } from '../lib/teact/teactn'; import { IS_SERVICE_WORKER_SUPPORTED } from './environment'; function getDeviceToken(subscription: PushSubscription) { const data = subscription.toJSON(); return JSON.stringify({ endpoint: data.endpoint, keys: data.keys }); } function checkIfSupported() { if (!IS_SERVICE_WORKER_SUPPORTED) return false; if (!('showNotification' in ServiceWorkerRegistration.prototype)) { if (DEBUG) { // eslint-disable-next-line no-console console.log('[PUSH] Push notifications aren\'t supported.'); } return false; } // Check the current Notification permission. // If its denied, it's a permanent block until the // user changes the permission if (Notification.permission === 'denied') { if (DEBUG) { // eslint-disable-next-line no-console console.log('[PUSH] The user has blocked push notifications.'); } return false; } // Check if push messaging is supported if (!('PushManager' in window)) { if (DEBUG) { // eslint-disable-next-line no-console console.log('[PUSH] Push messaging isn\'t supported.'); } return false; } return true; } const expirationTime = 12 * 60 * 60 * 1000; // 12 hours function checkIfShouldResubscribe(subscription: PushSubscription | null) { const global = getGlobal(); if (!global.push || !subscription) return true; if (getDeviceToken(subscription) !== global.push.deviceToken) return true; return Date.now() - global.push.subscribedAt > expirationTime; } async function unsubscribe(subscription: PushSubscription | null) { const global = getGlobal(); const dispatch = getDispatch(); if (subscription) { try { const deviceToken = getDeviceToken(subscription); await callApi('unregisterDevice', deviceToken); await subscription.unsubscribe(); dispatch.deleteDeviceToken(); return; } catch (error) { if (DEBUG) { // eslint-disable-next-line no-console console.log('[PUSH] Unable to unsubscribe from push.', error); } } } if (global.push) { await callApi('unregisterDevice', global.push.deviceToken); dispatch.deleteDeviceToken(); } } export async function unsubscribeFromPush() { if (!checkIfSupported()) return; const serviceWorkerRegistration = await navigator.serviceWorker.ready; const subscription = await serviceWorkerRegistration.pushManager.getSubscription(); await unsubscribe(subscription); } export async function subscribeToPush() { if (!checkIfSupported()) return; const serviceWorkerRegistration = await navigator.serviceWorker.ready; let subscription = await serviceWorkerRegistration.pushManager.getSubscription(); if (!checkIfShouldResubscribe(subscription)) return; await unsubscribe(subscription); try { subscription = await serviceWorkerRegistration.pushManager.subscribe({ userVisibleOnly: true, }); const deviceToken = getDeviceToken(subscription); if (DEBUG) { // eslint-disable-next-line no-console console.log('[PUSH] Received push subscription: ', deviceToken); } await callApi('registerDevice', deviceToken); getDispatch().setDeviceToken(deviceToken); } catch (error) { if (Notification.permission === 'denied' as NotificationPermission) { // The user denied the notification permission which // means we failed to subscribe and the user will need // to manually change the notification permission to // subscribe to push messages if (DEBUG) { // eslint-disable-next-line no-console console.log('[PUSH] Permission for Notifications was denied'); } } else if (DEBUG) { // A problem occurred with the subscription, this can // often be down to an issue or lack of the gcm_sender_id // and / or gcm_user_visible_only // eslint-disable-next-line no-console console.log('[PUSH] Unable to subscribe to push.', error); } } } // Notify service worker that client is fully loaded export function notifyClientReady() { if (!navigator.serviceWorker.controller) return; navigator.serviceWorker.controller.postMessage({ type: 'clientReady', }); }