单击 service-worker ng7 + android 处理的推送通知时打开 PWA

Posted

技术标签:

【中文标题】单击 service-worker ng7 + android 处理的推送通知时打开 PWA【英文标题】:Open PWA when clicking on push notification handled by service-worker ng7 + android 【发布时间】:2019-06-05 22:01:39 【问题描述】:

我们在 Angular 7 和 NodeJS 作为后端实现了 PWA。 推送通知通过web-push 从后端发送,并由angular service worker service 处理。

我们希望在 android 中具有的行为是,当用户点击通知时,应用程序会在设备中打开(即 PWA 从后台出现并且对用户可见)。

目前,代码接缝在后台执行,但 PWA 没有从后台出现(即用户点击推送通知但没有任何反应)。

推送通知由前端名为 SwPush 的 Angular 服务处理,并使用 Angular 应用的典型 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 答案:

我已阅读this question,在我的情况下,处理程序内的控制台日志执行没有问题

this other question seams to 提出了一种在 service-worker 库中添加代码的解决方法,但据我了解,这是因为他们使用的是 angular 5 而不是 angular 7(其中 SwPush 服务包括 notificationclick 处理程序)。无论如何,我需要的是模拟这条线:

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

关于在使用 Angular 7 的 SwPush 服务时用户点击推送通知时如何打开 PWA 的任何想法?

【问题讨论】:

【参考方案1】:

很遗憾,您将无法通过 Angular 代码打开您的 PWA。如果应用当前处于打开状态,SwPush 界面会很好地工作。

如果您的应用程序已关闭,则唯一运行的将是您的服务工作者(前提是它已成功注册)。所以解决你的问题的方法是编辑 Angular 的 service worker 文件。

在 Angular 7 中(具体指“@angular/service-worker”v~7.2.0),在使用 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 进程完成后设法部署您的 service worker 文件); 每次构建到 prod 时,ngsw-worker.js 都会被覆盖。 @Jay 的 answer below 提供了一种更好的方法来避免这个问题。 我知道您在 android 上进行开发,但如果您无法看到更改,您可以使用 Chrome 开发工具[A] 强制更新 service worker 或 [B] 打开当前的 ngsw-worker.js 文件并验证它是否是所需的版本。该解决方案也适用于您桌面上已安装的应用。

相关链接:

How can I initiate a PWA (progressive webapp) open from a click on a push notification?

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

【讨论】:

嗨@tomblue,我之前看到并尝试过这种方式,但我真的不喜欢它,因为它需要更改角度编译......任何方式似乎都是唯一可能的方式完成要求。我说服了自己,我最终在 package.json 上添加了一个脚本步骤,例如 edit-sw-push: ts-node edit-sw-push.ts,并使用您建议的替换。这样做,只要编译 angular 项目,ngsw-worker.js 文件就会被修改,一切似乎都正常工作。无论如何......一个开箱即用的角度解决方案会很棒!【参考方案2】:

在 node_modules/@angular/service-worker/ngsw-worker.js 中的 Angular 项目中添加它

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) => ..

您必须再次构建 dist 才能使其正常工作。在后端,您需要使用这种格式:

"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"

您可以在网址内输入您的网址,点击通知后,您的推送通知将打开给定的链接并将其聚焦在浏览器中。您不需要更改 dist 内的任何内容。

【讨论】:

【参考方案3】:

虽然提供的解决方案有效,但最好有一种方法不会修改 node_modules 中的 service worker 代码或生成的代码。

方法可以是创建另一个名为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));
        
    );
());

在将写入 ServiceWorkerModule.register 的导入部分中,将 custom-service-worker.js 注册为服务工作者以代替 ngsw-worker.js

另外不要忘记将此脚本作为资产添加到 angular.json 中,以便将其复制到 dist 文件夹中。

我相信这种方法可以为我们省去额外编辑原始服务工作人员文件的麻烦。

【讨论】:

clients 变量从何而来? 对我们来说工作得非常好 - 感谢这个干净的解决方案,这比在构建 imo 后操作编译代码要好得多。 @Cétia clients 是 service worker 中可用的全局变量,例如纯 javascript 中的窗口或文档。【参考方案4】:

最好的方法是扩展 Angular Cli Generated Service Worker。 强烈不建议更改 node_modules 中的任何内容。

第 1 步: 只需在根文件夹中创建一个新的 my-svc-wkr.js 并实现任意数量的事件侦听器,例如:

importScripts('./ngsw-worker.js'); // This will import the default Angular Service Worker Functionality to your custom file.

// Add additional Event Listners like Below:
self.addEventListener('notificationclick', (event) => 
  console.log('notification clicked!')
);

第 2 步: 现在替换 app.module.ts 中的 service worker 文件,如下所示:

ServiceWorkerModule.register('my-svc-wkr.js',  enabled: environment.production )

第 3 步: 在您的 angular.json 中添加以下内容,以便在您的 dist 目录中的构建过程中复制此文件:

"assets": 
  ...,
  "src/my-svc-wkr.js"

您可以阅读这篇文章以获取有关扩展 Service Worker 功能的更详细说明: https://medium.com/@smarth55/extending-the-angular-cli-service-worker-44bfc205894c

【讨论】:

以上是关于单击 service-worker ng7 + android 处理的推送通知时打开 PWA的主要内容,如果未能解决你的问题,请参考以下文章

service-worker实践

Rails 对 service-worker.js 的未知调用

Service-Worker,“TypeError:<anonymous> 处的请求失败”

与 Angular.js 控制器通信 service-worker

service-worker 可以停止 GCM api 发送的推送通知吗?

使用 create-react-app 添加更多 service-worker 功能