0

I am posting this after hours of trawling old StackOverflow,Linkedin and dev.to links.

I am trying to build a React PWA. I have used create-react-app(CRA) v5.0.1 to create my React Application, which also came with a predefined service worker.

In this PWA, one of the main features is the chat function, and new notifications alerting the user that they have new messages.

I chose to use Firebase Cloud Messaging (FCM) as I was already using a few other Firebase Offerings for my chat and data storage purposes. Thus,I first followed this link to deploy a Cloud Function which will trigger on any new writes to the specific path of the various chat rooms, which then send a payload to the targeted FCM registration token of the specific device.

My payload looks like this:

const payload = {
            data: {
                title: `${senderDisplayName} replied to you`,
                body: newMessageText,
                // icon: follower.photoURL
            }
        };

        tokens = Object.keys(tokensSnapshot.val());
        const tokensToRemove: any[] = [];

        // Send individual messages to each token.
        const sendPromises = tokens.map((token) => {
            const message = {
                token: token,
                ...payload
            };

This was done mostly following this guide and GitHub repository. Note that the code segments in the guide seems to be altered and not ideal. It is probably better to reference the code in the repo instead.

I also changed the implementation for onBackgroundMessage to avoid receiving double notifications following this SO link, which states from FCM: Use notification messages when you want FCM to handle displaying a notification on your client app's behalf. Use data messages when you want to process the messages on your client app. FCM can send a notification message including an optional data payload. In such cases, FCM handles displaying the notification payload, and the client app handles the data payload.

// Scripts for firebase and firebase messaging
importScripts("https://www.gstatic.com/firebasejs/9.0.0/firebase-app-compat.js");
importScripts("https://www.gstatic.com/firebasejs/9.0.0/firebase-messaging-compat.js")


// Initialize the Firebase app in the service worker by passing in
// your app's Firebase config object.
// https://firebase.google.com/docs/web/setup#config-object
firebase.initializeApp({
    apiKey: "...",
    authDomain: "...",
    projectId: "...",
    storageBucket: "...",
    messagingSenderId: "...",
    appId: "...",
    measurementId: "....",
    databaseURL:'...',
});

// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
const messaging = firebase.messaging();

messaging.onBackgroundMessage(function(payload) {
    console.log('Received background message ', payload);

   //const notificationTitle = payload.notification.title;
   //const notificationOptions = {body: payload.notification.body,};

    const notificationTitle = payload.data['title'];
    const notificationOptions = {
        body: payload.data['body'],
    };

    self.registration.showNotification(notificationTitle,
        notificationOptions);
});


Then, instead of using the service worker provided by CRA, as the Firebase docs recommended creating a firebase-messaging-sw.js file in the public folder, I did just that.

To work around this, I followed this guide on Linkedin, where the author recommends not registering the predefined service worker and registering the custom firebase-messaging-sw.js in the index.js or whatever your entry point is.

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App.tsx';

import * as serviceWorkerRegistration from './serviceWorkerRegistration';
import reportWebVitals from './reportWebVitals';

if ('serviceWorker' in navigator && process.env.NODE_ENV !== 'production') {
    console.log('Trying to register custom sw');
    navigator.serviceWorker
        .register('./firebase-messaging-sw.js')
        .then(function (registration) {
            console.log('Registration successful, scope is:', registration.scope);
        })
        .catch(function (err) {
            console.log('Service worker registration failed, error:', err);
        });
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App/>);

// serviceWorkerRegistration.register();

Now, I am able to receive background notifications on:

  1. Chrome on Windows 10 , when the Chrome Browser is open and the webapp/website is not the active tab
  2. Chrome on Android, when the Chrome Browser App is active (aka I am using it) and the webapp/website is not the active tab, PWA not installed
  3. Chrome on Android, when the Chrome Browser App is active (aka I am using it) and the website is not open, PWA is installed

When the Chrome Browser App is not active, and I open it, the most recent notifications will be received and displayed on the notification bar on my Android device.

This is all tested on the development server, aka yarn start as well as making a production build and serving it using serve

yarn build
yarn global add serve
npx serve -s build

I have also used Loophole as a way to test it remotely on my physical Android device as service workers only run over HTTPS, for security reasons.

My question is, how do I receive notifications when the PWA is installed and the Chrome Browser App is not active/open? It seems like the service worker is not running even when the PWA is installed on my Android device. Is there something missing in my implementation process? I would love to have anyone share their knowledge on this matter.

Update 1: Updating the import scripts to match my foreground Firebase package version seems to have solved the issue.

importScripts("https://www.gstatic.com/firebasejs/9.21.0/firebase-app-compat.js");
importScripts("https://www.gstatic.com/firebasejs/9.21.0/firebase-messaging-compat.js")

Update 2: After restarting the Android device it reverts back to the original behavior

Update 3: After trying a Simple Web Push demo, it seems like it may be a problem with my Android device as the demo also only receive notifications when it is active/in the foreground.

Update 4: Background notifications are working again. Not sure what is causing the issue, perhaps someone can shed some light on how to debug?

asdj1234
  • 77
  • 1
  • 6

0 Answers0