Откройте PWA при нажатии на push-уведомление, обрабатываемое работником сервиса ng7 + android

У нас есть PWA, реализованный на angular 7, и NodeJS в качестве бэкэнда. Push-уведомления отправляются из бэкэнда с помощью web-push и обрабатываются службой угловой службы.

Поведение, которое мы надеемся иметь в Android, заключается в том, что когда пользователь нажимает на уведомление, приложение открывается на устройстве (т.е. PWA появляется из фона и кажется видимым для пользователя).

В настоящее время код выполняется в фоновом режиме, но PWA не появляется в фоновом режиме (т.е. Пользователь нажимает на push-уведомление, но ничего не происходит).

Push-уведомления обрабатываются угловой службой SwPush на внешней стороне и доставляются в приложение с использованием следующего кода в типичном компоненте AppComponent углового приложения:

export class AppComponent implements OnInit {
    constructor(private swPush: SwPush) { }

    ngOnInit(): void {
        try {
            if (this.swPush.isEnabled) {
                this.swPush.notificationClicks.subscribe(
                    event => {
                        window.focus();
                        window.open(event.notification.data.url, '_self');
                    },
                    error => {
                        // handle error
                    }
                );
            }
        } catch (err) {
            // handle error
        }
    }
}

Я провел поиск по всему SO сайту и не нашел именно этого вопроса. Комментарии по другим вопросам SO, чтобы избежать двусторонних ссылок на другие ответы SO:

  • Я прочитал этот вопрос, и в моем случае журнал консоли внутри обработчика выполняется без проблем
  • этот другой вопрос предлагает обходной путь добавления кода в библиотеку сервис-работника, но, насколько я понимаю, это потому, что они используют угловой 5 вместо углового 7 (где сервис SwPush включает обработчик уведомленияclick). В любом случае мне нужно смоделировать эту строку:

    event.waitUtil(clients.openWindow(URL));

Любая идея о том, как открыть PWA, когда пользователь нажимает на push-уведомления при использовании службы SwPush Angular 7?

Ответ 1

К сожалению, вы не сможете открыть свой PWA из углового кода. Интерфейс SwPush будет хорошо работать, если приложение в данный момент открыто.

Если ваше приложение закрыто, будет запущен только ваш обслуживающий работник (при условии, что он был успешно зарегистрирован). Таким образом, решение вашей проблемы - редактирование рабочего файла угловой службы.

В angular 7 (в частности, "@angular/service-worker" v ~ 7.2.0), после того как вы ng build --prod свое приложение с помощью ng build --prod, проверьте папку /dist и найдите файл ngsw-worker.js. Откройте его в редакторе.

На линии 1885 вы найдете:

this.scope.addEventListener('notificationclick', (event) => this.onClick(event));

Измените это на:

this.scope.addEventListener('notificationclick', (event) => {
    event.notification.close();
    if (clients.openWindow && event.notification.data.url) {
        event.waitUntil(clients.openWindow(event.notification.data.url));
    }
});

Это означает, что вы передали в уведомлении полезную нагрузку объекта данных с нужным URL-адресом. например:

{ 
    title: "Hello", 
    body: "Here, open this URL",
    data: { 
        url : "/somewhere/over/the/rainbow/42"
    } 
}

Что нужно иметь в виду:

  • Если у вас есть какие-либо автоматические конвейеры сборки (CD), вам придется вручную изменить файл ngsw-worker.js (но вы сможете развернуть файл рабочего сервиса после завершения процесса ng build --prod);
  • Каждый раз, когда вы собираете prod, ngsw-worker.js будет перезаписываться.
  • Я знаю, что вы разрабатываете на Android, но если у вас возникают проблемы с отображением изменений, вы можете использовать инструменты Chrome Dev, чтобы [A] принудительно обновить работника сервиса или [B], чтобы открыть текущий файл ngsw-worker.js и проверить, если это желаемая версия. Решение работает и для установленных приложений на вашем рабочем столе.

enter image description here

Ссылки по теме:

Как я могу инициировать PWA (прогрессивное веб-приложение), открытое нажатием на push-уведомление?

https://github.com/angular/angular/issues/20956

Ответ 2

Добавьте это в свой проект Angular внутри node_modules/@angular/service-worker/ngsw-worker.js

this.scope.addEventListener('notificationclick', (event) => {
            console.log('[Service Worker] Notification click Received. event:%s', event);
            event.notification.close();
            if (clients.openWindow && event.notification.data.url) {
                event.waitUntil(clients.openWindow(event.notification.data.url));
            }
        });

Вы можете ввести вышеуказанный код, где вы можете найти эту строку внутри файла

this.scope.addEventListener('notificationclick', (event) => ..

И вы должны построить дистрибутив снова, чтобы это работало. А в бэкэнде вам нужно будет использовать этот формат:

{"notification":{"body":"This is a message.","title":"PUSH MESSAGE","vibrate":[300,100,400,100,400,100,400],"icon":"https://upload.wikimedia.org/wikipedia/en/thumb/3/34/AlthepalHappyface.svg/256px-AlthepalHappyface.svg.png","tag":"push demo","requireInteraction":true,"renotify":true,"data":{"url":"https://maps.google.com"}}}

Внутри URL вы можете ввести свой URL, и при нажатии на уведомление ваше push-уведомление откроет данную ссылку и сфокусирует ее в браузере. Вам не нужно ничего менять внутри dist.

Ответ 3

Хотя предоставленные решения работают, было бы неплохо иметь подход, который не изменяет код работника службы в node_modules или сгенерированный код.

Подход может заключаться в создании другого сценария с именем custom-service-worker.js

В этом файле

importScripts('./ngsw-worker.js');

(function () {
    'use strict';

    self.addEventListener('notificationclick', (event) => {
        console.log("This is custom service worker notificationclick method.");
        console.log('Notification details: ', event.notification);
        // Write the code to open
        if (clients.openWindow && event.notification.data.url) {
            event.waitUntil(clients.openWindow(event.notification.data.url));
        }
    });}
());

Зарегистрируйте custom-service-worker.js в качестве работника службы вместо ngsw-worker.js в разделе импорта, где будет записан ServiceWorkerModule.register.

Также не забудьте добавить этот скрипт как ресурс в angular.json, чтобы он копировался в папку dist.

Я полагаю, что такой подход избавляет нас от необходимости делать что-то дополнительно для редактирования исходного файла рабочего сервиса.