Notification Fixes (#22599)

* Fix iOS having notification token revoked

* Try to handle iOS stacked notifications

* Fix typo

* Improve updating of notification script
This commit is contained in:
Nicolas Mowen 2026-03-24 05:30:48 -06:00 committed by GitHub
parent 573a5ede62
commit d27ee166bc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 34 additions and 5 deletions

View File

@ -19,8 +19,7 @@ self.addEventListener("push", function (event) {
break;
}
// @ts-expect-error we know this exists
self.registration.showNotification(data.title, {
const notificationOptions = {
body: data.message,
icon: "/images/maskable-icon.png",
image: data.image,
@ -28,7 +27,33 @@ self.addEventListener("push", function (event) {
tag: data.id,
data: { id: data.id, link: data.direct_url },
actions,
});
};
// iOS Safari does not auto-coalesce notifications by tag (WebKit bug #258922).
// On iOS 18.3+ close() works, so we manually close duplicates before showing.
// On other platforms, tag-based replacement works natively — skip the extra work.
const isIOS =
/iPad|iPhone|iPod/.test(navigator.userAgent) && !self.MSStream;
const show = () =>
// @ts-expect-error we know this exists
self.registration.showNotification(data.title, notificationOptions);
// event.waitUntil is required on iOS Safari — without it, the browser
// may consider this a "silent push" and revoke the subscription after 3 occurrences.
event.waitUntil(
isIOS
? // @ts-expect-error we know this exists
self.registration
.getNotifications({ tag: data.id })
.then((existing) => {
for (const n of existing) {
n.close();
}
})
.then(show)
: show(), // eslint-disable-line comma-dangle
);
} else {
// pass
// This push event has no data

View File

@ -58,7 +58,7 @@ import type { ConfigSectionData, JsonObject } from "@/types/configForm";
import { sanitizeSectionData } from "@/utils/configUtil";
import type { SectionRendererProps } from "./registry";
const NOTIFICATION_SERVICE_WORKER = "/notification-worker.js";
const NOTIFICATION_SERVICE_WORKER = "/notifications-worker.js";
import {
SettingsGroupCard,
SPLIT_ROW_CLASS_NAME,
@ -126,6 +126,8 @@ export default function NotificationsSettingsExtras({
.getRegistration(NOTIFICATION_SERVICE_WORKER)
.then((worker) => {
if (worker) {
// Trigger a check for an updated service worker script
worker.update().catch(() => {});
setRegistration(worker);
} else {
setRegistration(null);
@ -633,7 +635,9 @@ export default function NotificationsSettingsExtras({
Notification.requestPermission().then((permission) => {
if (permission === "granted") {
navigator.serviceWorker
.register(NOTIFICATION_SERVICE_WORKER)
.register(NOTIFICATION_SERVICE_WORKER, {
updateViaCache: "none",
})
.then((workerRegistration) => {
setRegistration(workerRegistration);