博客

网页应用的桌面通知

窗口与行为 · · 约 7 分钟

Pake 应用会自己回应页面的通知请求,报告权限已允许,再把提醒交给操作系统显示。页面通过标准的 Notification API 来问,所以你看不看得到取决于系统的通知设置和专注模式或勿扰,而不是浏览器弹出的授权框。什么都没出现时,先查这些系统设置,再看这个网站到底有没有用普通的 Notification 调用。

页面是怎么请求通知的

想提醒你的网站会调用 Notification.requestPermission(),答案是 "granted" 之后再创建 new Notification(title, { body, icon, tag })。聊天和邮件类网站通常在标签页处于后台、新消息到达时这样做。也有网站会先检查 "Notification" in window,再决定要不要显示自己的「开启通知」按钮,检查不通过就把按钮藏起来。

在浏览器里,第一次调用会弹出授权提示,答案按站点记住。Pake 应用没有这个提示,注入的脚本里保存着一个权限值,初始就是 "granted",requestPermission 立刻返回它,页面传了回调的话也会调用。所以一个等你点「允许」的网站永远等不到对话框,它问的时候答案已经是肯定的了。

Pake 怎么把提醒送出去

Pake 在网站脚本运行之前就用自己的类替换了 window.Notification,类里保留了网站期望的接口形状,包括 title、body、tag、icon、silent、onclick、close(),以及 show、click、close、error 事件。页面创建通知时,脚本会调用应用 Rust 一侧的 send_notification 命令,这一步需要 Tauri 桥。Pake 文档说明原生通知只在配置好的网站来源(协议、域名、端口都要一致)上可用,登录跳转和其他来源不行,所以打包时要用网站最终落到的地址,包括 www 或工作区子域名。

网页调用 new Notification,这个接口在页面脚本运行前已被 Pake 替换,替换后的实现调用 send_notification 命令,macOS 上走系统通知中心,Windows 和 Linux 上走 Tauri 插件,只有 macOS 会把点击回传给应用

Pake 在网站加载前就把网页的 Notification 接口换成自己的,所有提醒都经过应用发出,只有 macOS 会把点击回传回来。

到了 Rust 一侧,投递方式因系统而异。

  • macOS,Pake 通过一个小的原生代理,自己把提醒交给系统通知中心,因此能知道你点了哪一条。点击会把窗口带回前台,必要时取消最小化,再带着确切的通知去调用页面的点击处理函数,聊天网站就是这样跳到正确的会话的,应用在最前面时横幅也照样显示。
  • Windows 和 Linux,Pake 用 Tauri 的通知插件把提醒交给平台。Windows 上只有页面设了 silent 才是静音,否则 Pake 会要求系统默认提示音;Linux 用同一个插件,源码注释说它经由 notify_rust,所以你桌面运行的通知服务会负责显示。

这两个平台没法告诉页面你点了哪一条,Pake 做的是近似判断:窗口在后台时收到通知,60 秒内窗口重新获得焦点,而且这期间你没有在页面里操作过,就当作点了最新的那条通知。你自己手动切回来时可能误判。

还有两个细节。同一个 tag 的新通知会替换旧的,和浏览器一样;开了 --multi-window 时,一条消息本来会在每个窗口各弹一次,所以在 macOS 上 Pake 会丢掉一秒内来自另一个窗口的相同标题和正文。通知还会带动角标:在 Mac 上,未读通知会让 Dock 图标上的数字加一,Web Badging 的 setAppBadge 和 clearAppBadge 也接上了,你在页面里下一次点击或按键就会清掉计数。Linux 上角标走 Tauri 的任务栏角标,只在支持角标的桌面环境里显示,Windows 上 Tauri 不支持任务栏角标。

哪些系统设置会让它静音

Pake 只负责交出提醒,之后由系统决定。

  • Mac 上提醒显示在这个应用自己的名字下,所以去系统通知设置里看这个应用有没有开,再看是不是有专注模式正在生效,专注模式会拦下你没放行的应用。
  • Windows 上看这个应用的通知设置,以及是否开着任何勿扰模式。
  • Linux 上会话里没有运行通知服务就什么都不会显示,精简的窗口管理器常常没有,先装好并启动一个,再去怀疑应用。

各个系统版本的菜单名称会变,所以在系统设置里搜「通知」,不要照着固定路径找。

窗口关掉之后还想收到提醒,应用就得还在运行。常驻后台能在你关窗口后保持应用运行,Mac 上默认开,Windows 上默认关,并且在 Windows 上会同时打开托盘图标。命令行对应 --hide-on-close 加 --show-system-tray,Windows 和 Linux 上默认都是关的。菜单栏和托盘那篇把两个选项都讲了。

为什么网站没有任何提示

  1. 网站用的是网页推送。 页面关闭时到达的推送依赖 service worker 和 Push API,而不是 new Notification。Pake 的代码桥接的是 Notification 构造函数和角标调用,没有处理推送消息,也没有处理 service worker 注册上的 showNotification(),所以别指望这类提醒,用之前先拿这个网站测一下。页面打开时用 new Notification 提醒的网站是没问题的。
  2. 页面来自别的来源。 桥只授予配置的那个来源,所以打包时地址写错,或者登录后页面换到另一个子域名,都不会有原生通知。
  3. 网站自己把它关了。 很多网站在设置里有自己的「桌面通知」开关,新应用没有保存过状态,就像新建的浏览器配置,开关可能是关着的,在应用里打开一次。
  4. 通知服务或专注模式在拦截,见上文。
  5. 网站在页内显示自己的横幅,只有部分事件才用真正的通知。

发一条测试通知

构建时加 --debug 会启用开发者工具,Mac 上用 Cmd Option I 打开,Windows 和 Linux 上用 Ctrl Shift I,在 Console 里输入:

new Notification("Pake test", { body: "Hello from the page" })

系统通知区域出现提醒,说明桥、权限和操作系统都正常,问题在网站这边;这条也没有显示的话,原因在来源、系统里这个应用的通知设置、专注模式或缺少通知服务。顺便看一下 Notification.permission,应该是 granted。

下载失败的通知

Pake 自己会发一种通知:下载失败时,权限已授予就显示原生通知,否则只写到控制台。因为权限始终报告为已授予,通常都会显示。

桌面版

Pake 桌面版(Mac 和 Windows)的手册没有讲通知,做好应用后自己给自己发一条真实消息来确认。需要关窗口后仍收到提醒的应用,打开常驻后台,Windows 上托盘图标会跟着打开。登录有怪问题的话看一下登录排查,在别的来源上完成的登录拿不到这个桥。

常见问题

需要点「允许」吗?

不需要,Pake 一开始就报告权限已授予。

能只关掉某一个应用的通知吗?

用系统里这个应用的通知设置,或者网站自己的通知开关,Pake 没有这个选项。

点通知能打开正确的聊天吗?

macOS 上点击会路由到被点的那条通知;Windows 和 Linux 上 Pake 靠窗口焦点来推测,所以提醒出来后尽快点效果最好。