Add activity push notifications and registration patches

This commit is contained in:
2026-09-15 08:31:45 +02:00
parent 7c801dfddd
commit 55174684af
38 changed files with 1096 additions and 42 deletions
+34 -3
View File
@@ -15,6 +15,17 @@
let sendQueue = Promise.resolve();
let panel;
async function notificationTarget(notification) {
const data = notification?.data || {};
if (!/^[a-f0-9-]{36}$/.test(data.notification_id || '') || !/^[a-f0-9]{64}$/.test(data.session_tag || '')) return null;
const response = await fetch('/api/push/session', {credentials: 'same-origin', cache: 'no-store'});
if (!response.ok || stopped) return null;
const session = await response.json();
const native = await device.getInfo();
if (!session.authenticated || session.session_tag !== data.session_tag || native.binding !== data.session_tag) return null;
return '/notifications/' + data.notification_id;
}
function notice(message, offerPermission = false) {
if (!panel) {
panel = document.createElement('section');
@@ -130,9 +141,29 @@
});
}),
push.addListener('registrationError', () => { if (!stopped) notice(texts.failed); }),
push.addListener('pushNotificationActionPerformed', () => {
// Do not navigate to arbitrary URLs supplied by a notification payload.
location.assign('/');
push.addListener('pushNotificationActionPerformed', async event => {
try {
const target = await notificationTarget(event.notification);
if (target) location.assign(target);
} catch (_) { /* A tap never bypasses current-session authorization. */ }
}),
push.addListener('pushNotificationReceived', async notification => {
try {
const target = await notificationTarget(notification);
if (!target) return;
document.getElementById('push-in-app-notice')?.remove();
const banner = document.createElement('section');
banner.id = 'push-in-app-notice';
banner.className = 'native-push-panel';
banner.setAttribute('role', 'status');
const link = document.createElement('a');
link.className = 'button';
link.href = target;
// Generic localized text; never insert remote HTML or private message content.
link.textContent = texts.openNotification;
banner.append(link);
(document.querySelector('main') || document.body).prepend(banner);
} catch (_) { /* Push reception cannot interrupt use of the app. */ }
})
]).then(() => {
synchronize();