你好 ![]()
我们在 iOS PWA 设置推送通知时遇到了一些奇怪的行为(或者可能是一个 bug……)。界面显示推送通知订阅已设置,但后台实际上没有任何操作发生,用户将永远无法收到任何推送通知。
(tl;dr:我想我知道如何修复它,并可以提供 PR)
复现步骤 
问题在于在 iOS 上安装 PWA 后立即启用推送通知。
- 在 iOS 的 Mobile Safari 中打开你的 Discourse 实例
- 打开分享菜单并选择“添加到主屏幕”
- 在主屏幕上打开新的 PWA
- 你会看到一个横幅,询问你是否要启用推送通知
- 点击“启用通知”链接
- 你会看到系统对话框
“xyz”想要向你发送通知- 点击允许。
- 你会看到系统对话框
- 导航到你的通知偏好设置,你会看到
实时通知未启用。- 在服务器端检查,你也找不到该用户的任何新
PushSubscription记录。
- 在服务器端检查,你也找不到该用户的任何新
预期行为 
在第 5 步之后,你应该已经收到一条通知,提示通知已启用。数据库中应该已经为你的当前用户创建了一条新的 PushSubscription 记录,并且他们在 PWA 中接收 实时通知 的设置应该已启用。
问题 
在刚启动的 PWA 中,服务 Worker 已安装并激活,但尚未控制页面。因此,lib/push-notifications.js 中的 isPushNotificationsSupported() 检查仍会返回 false,因为 navigator.serviceWorker.controller 返回 null。以下是关键的两行代码:
navigator.serviceWorker.controller &&
navigator.serviceWorker.controller.state === "activated"
这导致界面让用户以为一切正常,但他们实际上永远收不到任何推送通知。
在首次加载时,PWA 处于未受控状态,即使用户已授予权限,subscribe() 调用也从未执行……![]()
如果用户知道如何操作,他们可以自行修复此问题:
- 完全关闭 PWA(将其从后台划掉……)
- 重新打开 PWA
- 进入通知偏好设置
- 再次启用
实时通知 - → 这次应该会创建一条新的
PushSubscription记录,通知设置成功。
潜在修复方案 
我通过注册另一个服务 Worker 来绕过此问题,该 Worker 在安装/激活时调用 skipWaiting() 和 clients.claim()。这样做可以立即接管对 PWA 的控制权,订阅得以成功执行,并创建相应的 PushSubscription 记录。
我已将此作为“热修复”部署到我的某个自定义插件中,这样我就可以在不深入修改 Discourse 核心代码的情况下修复用户的问题……
只需进行两处更改:
# plugin.rb
register_service_worker "push-notification-setup.js"
// assets/push-notification-setup.js
self.addEventListener("install", function () {
self.skipWaiting();
});
self.addEventListener("activate", function (event) {
event.waitUntil(self.clients.claim());
});
不过,我认为这个修复应该直接集成到 Discourse 核心中。目前这样做,插件会不必要地接管对 PWA 的控制。我可以提供一个 PR,相应地调整 Discourse 核心中的检查逻辑,使订阅功能真正生效。我在这里等待反馈,也欢迎大家就此话题发表意见。