All files register-service-worker.ts

13.73% Statements 7/51
0% Branches 0/20
0% Functions 0/7
13.73% Lines 7/51

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115      1x 1x 1x 1x 1x 1x   1x                                                                                                                                                                                                                
// tslint:disable:no-console
// In production, we register a service worker to serve assets from local cache.
 
import { set, random, get } from "lodash";
import { hashObject, cloneClean, PushNotifications_urlBase64ToUint8Array } from "./common";
import { commitChange } from "./data-change";
import { getDB } from "./db";
import { INotification, notifyUsers } from "./notifications";
import { IDevice, IUser, newData, signObject } from "./user";
 
export function registerServiceWorker(serviceWorkerUrl: string, deviceId: string, me: IUser, vapidPublicKey: string, appName: string) {
  return new Promise<IUser>((resolve, reject) => {
  // if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
    Iif ('serviceWorker' in navigator) {
      // // The URL constructor is available in all browsers that support SW.
      // const publicUrl = new URL(
      //   process.env.PUBLIC_URL!,
      //   window.location.toString()
      // );
      // if (publicUrl.origin !== window.location.origin) {
      //   // Our service worker won't work if PUBLIC_URL is on a different origin
      //   // from what our page is served on. This might happen if a CDN is used to
      //   // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
      //   return reject('origins do not match');
      // }
      function ready(callbackFunction) {
        if (document.readyState != 'loading')
          callbackFunction()
        else
          document.addEventListener("DOMContentLoaded", callbackFunction)
      }
      ready(async () => {
        const registration = await navigator.serviceWorker.register(serviceWorkerUrl)
        const user = await registerPushSubscription(registration, deviceId, me, vapidPublicKey, appName)
        resolve(user);
      })
      // window.addEventListener('load', () => {});
    }  
  });
}
 
async function registerPushSubscription(registration: ServiceWorkerRegistration, deviceId: string, me: IUser, vapidPublicKey: string, appName: string) {
  let subscription: PushSubscription = await registration.pushManager.getSubscription();
  const db = await getDB();
  const hashBefore = hashObject(me);
  me = await db.get(me.id);
 
  const device: IDevice = get(me, `devices.${deviceId}`) || { id: deviceId };
  device.app = appName;
  // if device has no expire time, or has expired, or will expire in 4 days, then renew expire 
  const DAY_IN_MS = 1000 * 60 * 60 * 24;
  const expiresMinus4Days = (device.expires || 0) - (DAY_IN_MS * 4)
  Iif (expiresMinus4Days < Date.now()) {
    device.expires = Date.now() + DAY_IN_MS * 5; // device expires after 5 days
  }
  // clean out expired devices
  Object.keys(me.devices || {}).forEach(deviceId => {
    Iif ((me.devices[deviceId].expires || 0) < Date.now()) {
      delete me.devices[deviceId];
    }
  })
 
  Iif (
    !subscription ||
    (device.subscriptionExpires || 0) < Date.now()
  ) {
    Iif (subscription) {
      await subscription.unsubscribe();
    }
    // const vapidPublicKey = (await GET('/web-push-public-key')).VAPID_PUBLIC_KEY;
    // TODO change this to use the same functions peerstack uses for keys
    const convertedVapidKey = PushNotifications_urlBase64ToUint8Array(vapidPublicKey);
    // const convertedVapidKey = decodeUint8ArrayFromBaseN(vapidPublicKey);
    subscription = await registration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: convertedVapidKey
    })
      .catch(err => {
        console.error('an error was thrown trying to subscribe to notifications')
        throw err;
      });
 
    device.pushSubscription = subscription as any;
    device.subscriptionExpires = Date.now() + DAY_IN_MS * 14; // expire subscription in 14 days
  }
 
  set(me, `devices.${deviceId}`, cloneClean(device));
 
  // save object if needed
  const hashAfter = hashObject(me);
  Iif (hashBefore != hashAfter) {
    me.modified += random(100, 1000, false);
    signObject(me);
    // NOTE note sure this has been completely worked out
    //  it's very desireable to do partial updates to the user object but
    //  dataChange's don't allow signatures...
    const change = await commitChange(me, { preserveModified: true })[0];
    await db.save(me, true);
 
    // TODO only send to trusted users (I'm assuming we'll differentiate at some point)
    const users = await db.find('User', 'type') as IUser[];
    const notification: INotification = {
      ...newData(),
      type: 'Notification',
      dontShow: true,
      change,
      title: 'User Updated',
    }
    signObject(notification);
    notifyUsers(users, notification);
  }
  console.log('my devices', me.devices);
  return me;
}