Push Notifications Doesn't Show Up in Chrome

Viewed 937

Setup

  1. Operating System: OS X
  2. Node Version: 12
  3. web-push Version: 3.4.3

Please select any browsers that you are experiencing problems with:

  1. [x] Chrome version 80.0.3987.132 (Official Build) (64-bit)
  2. [ ] Firefox Developer Edition
  3. [x] Safari on iPhone 8+
  4. [x] Other

Problem

Service Worker registers but push notifications doesn't pop up in Chrome version 80.0.3987.132 (Official Build) (64-bit), but it works in Firefox Developers Edition. I am able to see the push notification data logged in my console on Chrome, but the browser notification doesn't show up. In the Application tab in Chrome, I am able to pick up the notification in the Push Messaging and Notifications section.

Expected

I expect to see the browser notification in Chrome and other browsers (including mobile)

Client

A button to turn on notifications calls the attemptToRegisterSW function.

const attemptToRegisterSW = async dispatch => {
  if (
    !('serviceWorker' in navigator) ||
    !('PushManager' in window) ||
    !('Notification' in window)
  ) {
    dispatch(setNotificationMessage(`Sorry, your browser doesn't support push notifications`));
    return;
  }

  try {
    const components = await Fingerprint2.getPromise({
      excludes: { webdriver: true, cpuClass: true, fonts: true },
    });

    const fingerPrint = Fingerprint2.x64hash128(
      components.map(component => component.value).join(''),
      31,
    );

    const register = await navigator.serviceWorker.register('/service-worker.js', { scope: '/' });

    await navigator.serviceWorker.ready;

    const permissionResult = await askPermission();

    if (permissionResult === 'granted') {
      const subscription = await register.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(publicVapidKey),
      });

      const token = getItemFromLocalStorage(INNERYOU_JWT);

      if (fingerPrint) {
        const req = await fetch('/api/alerts/subscribe', {
          method: 'POST',
          headers: {
            'content-type': 'application/json',
            accept: 'application/json',
            authorization: `Bearer ${token}`,
          },
          body: JSON.stringify({ subscription, fingerPrint }),
        });

        if (req.status === 201) dispatch(setNotificationMessage(`Push notifications turned on`));
      }
    } else {
      dispatch(
        setNotificationMessage(
          `Push notifications are either blocked or denied, please adjust your browser settings`,
        ),
      );
    }
  } catch (error) {
    dispatch(
      setNotificationMessage(`Error while turning on push notifications, please try again.`),
    );
    console.error(`Error while registering serviceWorker ${error}`);
  }
};

Helper Functions

Check if Permission already granted

const getNotificationPermissionState = async () => {
  try {
    if (navigator.permissions) {
      const navigatorPermissions = await navigator.permissions.query({ name: 'notifications' });
      return navigatorPermissions.state;
    }

    return await Notification.permission;
  } catch (error) {
    console.error(`Error while getting notification status ${error}`);
    throw new Error("We weren't granted permission.");
  }
};

Ask User for Permission

export const askPermission = async () => {
  try {
    const notificationStatus = await getNotificationPermissionState();

    if (notificationStatus === 'granted') {
      return notificationStatus;
    }

    return await Notification.requestPermission();
  } catch (error) {
    console.error(`Error while requesting notification permission ${error}`);
    throw new Error("We weren't granted permission.");
  }
};

Server

import webpush from 'web-push';

const publicVapidKey = process.env.PUBLICVAPIDKEY;
const privateVapidKey = process.env.PRIVATEVAPIDKEY;

webpush.setVapidDetails('mailto:noresponse@inneryou.io', publicVapidKey, privateVapidKey);

alertRouter.post('/subscribe', verifyUserAuthStatus, async (req, res, next) => {
  try {
    const userId = res?.locals?.user?.id;
    const { subscription, fingerPrint } = req.body;

    if (!fingerPrint || !subscription) {
      res.status(400);
      res.locals.error = 'Bad Request';
      throw new Error('Bad Request');
    }

    const isExistingDevice: DeviceModel = await Devices.findOne({
      fingerPrint,
      'subscription.keys.auth': subscription.keys.auth,
    });
    let newDevice: DeviceModel = null;

    if (!isExistingDevice) {
      newDevice = await Devices.create({ user: userId, fingerPrint, subscription });
    }

    if (newDevice) {
      await User.findByIdAndUpdate(userId, {
        $push: {
          devices: newDevice._id,
        },
      });
    }

    res.status(201).json({});

    const notification = {
      title: 'Push Notification',
      body: 'Subscribed to push notifications',
      icon: '',
    };

    const payload = buildCommonMessage(notification);
    const devices: DeviceModel[] = await Devices.find({ user: userId }).lean();

    for (const device of devices) {
      await webpush.sendNotification(device.subscription, payload).catch(err => {
        if (err.statusCode === 410) {
          // DELETE DEVICE IF SUBSCRIPTION IS INVALID
          console.error(`Deleting invalid subscription: ${err}`);
          return Devices.findByIdAndRemove(device._id);
        } else {
          console.error(`Subscription is no longer valid: ${err}`);
        }
      });
    }
  } catch (error) {
    console.error(`Error with push notification ${error}`);
  }
});

Server Helper

export const buildCommonMessage = ({ title, body, icon }: CommonMessage): any => {
  return JSON.stringify({
    title: title,
    body: body,
    icon: icon || NOTIFICATION_IMAGE_URL,
  });
};

Service Worker

/* eslint-disable no-restricted-globals */

self.addEventListener('push', (e) => {
  console.log('[Service Worker] Push Received.');
  const data = e.data.json();
  const title = data.title || 'InnerYou Notification';
  self.registration.showNotification(title, {
    body: data.body,
    icon: data.icon,
  });
});
0 Answers
Related