Не удается получить пользовательское событие push-уведомления, работающее в PWA (Firebase) - PullRequest
0 голосов
/ 20 ноября 2018

Я искал несколько часов, как заставить работать мое настраиваемое push-уведомление.Вот как я настроил свой проект: нет интерфейсной среды, серверной части Node.js / Express.js, Firebase Cloud Messaging (FCM) в качестве менеджера push-уведомлений и сотрудника по обслуживанию.В настоящее время я размещаю приложение на localhost, у меня настроен HTTPS и manifest.json, который содержит минимальное количество полей для начала работы.Манифест.json содержит поле start_url, которое указывает на /index.html, целевую страницу приложения.Приложение поставляется в комплекте с веб-пакетом версии 4.

Back-end

На сервере я установил SDK Firebase Admin в конкретном маршрутизаторе и отправил пользовательский объект уведомлениянемного похоже на следующее для сервера FCM:

let fcMessage = {
  data : {
    title : 'Foo',
    tag : 'url to view that opens bar.html'
  }
};

Когда на бэкэнде происходит интересное событие, он получает список пользователей, который содержит маркеры регистрации FCM, и отправляет уведомление на серверы FCM.,Эта часть прекрасно работает.

Front-end

У меня есть двое сервисных работников на front-end.Внутри моего внешнего файла index.js я регистрирую настраиваемого работника службы с именем sw.js в корне домена и приказываю firebase использовать этого работника следующим образом:

if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('./sw.js')
             .then(registration => {
                  messaging.useServiceWorker(registration);
             })
             .catch(err => console.error(`OOps! ${err}`));
}

FCM и его учетные данные настроеныи пользователь может подписаться на push-уведомления.Я не буду показывать этот код здесь, так как он работает, и я не верю, что это проблема.

Теперь перейдем к самим работникам службы.У меня есть файл firebase-messaging-sw.js в корне моего домена.Он содержит следующий код:

importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase-messaging.js');

firebase.initializeApp(configuration);
const messaging = firebase.messaging();

Конфигурация является просто заполнителем для всех кредитов.Опять же, это работает.

Что я хочу сделать, это НЕ использовать push-уведомление FCM, а вместо этого создать собственное push-уведомление, содержащее URL-адрес для представления, на которое пользователь может щелкнуть и перейти к этому представлению.Следующий код почти работает и является хаком , который я нашел на другом сайте:

class CustomPushEvent extends Event {
  constructor(data) {
    super('push');

    Object.assign(this, data);
      this.custom = true;
    }
}
self.addEventListener('push', (e) => {
  console.log('[Service Worker] heard a push ', e);
  // Skip if event is our own custom event
  if (e.custom) return;

  // Keep old event data to override
  let oldData = e.data;

  // Create a new event to dispatch
  let newEvent = new CustomPushEvent({
    data: {
      json() {
        let newData = oldData.json();
        newData._notification = newData.notification;
        delete newData.notification;
        return newData;
      },
    },

    waitUntil: e.waitUntil.bind(e),
  })

    // Stop event propagation
    e.stopImmediatePropagation();

    // Dispatch the new wrapped event
    dispatchEvent(newEvent);
});

messaging.setBackgroundMessageHandler(function(payload) {
  if (payload.hasOwnProperty('_notification')) {
    return self.registration.showNotification(payload.data.title,
      {
        body : payload.data.text,
        actions : [
            { 
                action : `${payload.data.tag}`,
                title : 'Go to link'
            }
        ]
    });
  } else {
    return;
  }
});

  self.addEventListener('notificationclick', function(e) {
    console.log('CLICK');

    e.notification.close();

    e.waitUntil(clients.matchAll({ type : 'window' })
     .then(function(clientList) {
       console.log('client List ', clientList);
       const cLng = clientList.length;
       if (clientList.length > 0) {
          for (let i = 0; i < cLng; i++) {
            const client = clientList[i];

            if (client.url === '/' && 'focus' in client) {
              return client.focus();
            }
        }
    } else {
        console.log('no clients ', e.action);
        clients.openWindow(e.action)
            .then(client => {
                console.log('client ', client);
                return client.navigate(e.action);
            })
            .catch(err => console.error(`[Service Worker] cannot open client : ${err} `));
    }
  }))
});

Хак предназначен для захвата события push и полезной нагрузки уведомления по умолчанию FCM и вместо этого служит для этой полезной нагрузки.через пользовательский, сделанный через Notification API .

Приведенный выше код прекрасно работает, но ТОЛЬКО если я помещу его в файл firebase-messaging-sw.js.Это не то, что я действительно хочу сделать: я хочу вместо этого поместить его в файл sw.js, но когда я это сделаю, sw.js не может слышать какие-либо push-события, и вместо этого я получаю push-уведомление FCM по умолчанию.Я также попытался импортировать все сценарии Firebase-Messaging-SW в пользовательский сервисный работник, и он по-прежнему не будет слышать события сообщений.

Почему я хочу использовать его в своем сервисном работнике вместоFirebase один?Это должно быть в состоянии открыть приложение в представлении, переданном в поле 'tag' в теле уведомления.Если я использую сервисного работника Firebase, он говорит мне, что он не является активным зарегистрированным сервисным работником, и, хотя приложение открывается в новом окне, оно открывается только в /index.html.

Некоторые незначительные замечания, которые я 've made: массив клиентов всегда пуст, когда последний бит кода добавляется в файл firebase-messaging-sw.js.Пользовательский сервисный работник установлен правильно, потому что он обрабатывает кеш оболочки приложения и нормально прослушивает все остальные события.Работник службы Firebase-Messaging-SW также установлен правильно.

1 Ответ

0 голосов
/ 20 ноября 2018

После долгих болей и обострений я понял, в чем проблема.Это была комбинация архитектуры приложения (то есть традиционного многостраничного приложения) и неправильно сформированного URL-адреса в пользовательском сервисном сервисе, sw.js следующим образом:

sw.js

self.addEventListener('fetch', e => {
  // in this app, if a fetch event url calls the back-end, it contains api and we
  // treat it differently from the app shell

  if (!e.request.url.includes('api')) {
    switch(e.request.url) {
        case `${endpoint}/bar`: // <-- this is the problem right here
            matcher(e, '/views/bar.html');
            break;
        case `${endpoint}/bar.js`:
            matcher(e, '/scripts/bar.js');
            break;
        case `${endpoint}/index.js`:
            matcher(e, '/index.js');
            break;
        case `${endpoint}/manifest.json`:
            matcher(e, '/manifest.json');
            break;
        case `${endpoint}/baz/`:
            matcher(e, '/views/bar.html');
            break;
        case `${endpoint}/baz.js`:
            matcher(e, '/scripts/bar.js');
            break;
        default:
            console.log('default');
            matcher(e, '/index.html');
    }
  }
});

Matcher - это функция, которая сопоставляет URL-адрес запроса с файлом на сервере.Если файл уже существует в кэше, он возвращает то, что находится в кэше, но если его нет в кэше, он выбирает файл с сервера.

Каждый раз, когда пользователь нажимает на уведомление,это должно привести его / ее к представлению html бара.В коммутаторе это должно быть:

case `${endpoint}/bar/`:

, а не

case `${endpoint}/bar`:

Несмотря на то, что связанный с сообщением код все еще находится в файле firebase-messaging-sw.js, происходит следующее:он создает новый WindowClient, когда браузер находится в фоновом режиме.Этот WindowClient находится под влиянием sw.js, а не firebase-messaging-sw.js.В результате, когда окно открывается, sw.js перехватывает вызов и переходит от firebase-messaging-sw.js.

...