From ef0dde8aa4cadbc9da2dc6db76bc6008ffa04028 Mon Sep 17 00:00:00 2001 From: binwiederhier Date: Thu, 28 May 2026 21:58:01 -0400 Subject: [PATCH] PWA token extend --- web/public/sw.js | 25 +++++++++++++------------ web/src/app/events.js | 6 ++++-- web/src/components/hooks.js | 13 ++++++++----- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/web/public/sw.js b/web/public/sw.js index 0e408206..0ace286e 100644 --- a/web/public/sw.js +++ b/web/public/sw.js @@ -12,8 +12,9 @@ import { EVENT_MESSAGE, EVENT_MESSAGE_CLEAR, EVENT_MESSAGE_DELETE, - WEBPUSH_EVENT_MESSAGE, - WEBPUSH_EVENT_SUBSCRIPTION_EXPIRING, + SW_PERIODIC_SYNC_EXTEND_TOKEN_TAG, + SW_WEBPUSH_EVENT_MESSAGE, + SW_WEBPUSH_EVENT_SUBSCRIPTION_EXPIRING, } from "../src/app/events"; /** @@ -88,11 +89,11 @@ const handlePushMessage = async (data) => { // Broadcast the message to potentially play a sound broadcastChannel.postMessage(message); - await extendToken(); + await maybeExtendToken(); }; -const refreshThreshold = 1000 * 60 * 60; // 1 hour -const extendToken = async () => { +const refreshTokenThreshold = 1000 * 60 * 60; // 1 hour +const maybeExtendToken = async () => { if (import.meta.env.DEV) { console.warn("[ServiceWorker] Skipping token extension in development since no config.base_url exists"); return; @@ -107,7 +108,7 @@ const extendToken = async () => { const lastExtendedAt = await session.lastExtendedAtAsync(); const now = Date.now(); - if (lastExtendedAt && now - lastExtendedAt < refreshThreshold) { + if (lastExtendedAt && now - lastExtendedAt < refreshTokenThreshold) { console.debug(`[ServiceWorker] Token extended ${Math.floor((now - lastExtendedAt) / 1000 / 60)} minutes ago, skipping`); return; } @@ -138,7 +139,7 @@ const extendToken = async () => { }; /** - * Registers a periodic-sync listener for `extend-token` (see hooks.js). + * Registers a periodic-sync listener for `extend_token` (see hooks.js). * This extends the token regardless of whether the browser is open. * * CAVEATS: @@ -147,9 +148,9 @@ const extendToken = async () => { * - Only when notifications are granted */ self.addEventListener("periodicsync", (event) => { - if (event.tag === "extend-token") { - console.log('[ServiceWorker] Received periodicsync event "extend-token"'); - event.waitUntil(extendToken()); + if (event.tag === SW_PERIODIC_SYNC_EXTEND_TOKEN_TAG) { + console.log(`[ServiceWorker] Received periodicsync event "${SW_PERIODIC_SYNC_EXTEND_TOKEN_TAG}"`); + event.waitUntil(maybeExtendToken()); } }); @@ -263,7 +264,7 @@ const handlePush = async (data) => { // - Web app: hooks.js:handleNotification() // - Web app: sw.js:handleMessage(), sw.js:handleMessageClear(), ... - if (data.event === WEBPUSH_EVENT_MESSAGE) { + if (data.event === SW_WEBPUSH_EVENT_MESSAGE) { const { message } = data; if (message.event === EVENT_MESSAGE) { return await handlePushMessage(data); @@ -272,7 +273,7 @@ const handlePush = async (data) => { } else if (message.event === EVENT_MESSAGE_CLEAR) { return await handlePushMessageClear(data); } - } else if (data.event === WEBPUSH_EVENT_SUBSCRIPTION_EXPIRING) { + } else if (data.event === SW_WEBPUSH_EVENT_SUBSCRIPTION_EXPIRING) { return await handlePushSubscriptionExpiring(data); } diff --git a/web/src/app/events.js b/web/src/app/events.js index d5c5ab88..65755106 100644 --- a/web/src/app/events.js +++ b/web/src/app/events.js @@ -8,8 +8,10 @@ export const EVENT_MESSAGE_DELETE = "message_delete"; export const EVENT_MESSAGE_CLEAR = "message_clear"; export const EVENT_POLL_REQUEST = "poll_request"; -export const WEBPUSH_EVENT_MESSAGE = "message"; -export const WEBPUSH_EVENT_SUBSCRIPTION_EXPIRING = "subscription_expiring"; +export const SW_WEBPUSH_EVENT_MESSAGE = "message"; +export const SW_WEBPUSH_EVENT_SUBSCRIPTION_EXPIRING = "subscription_expiring"; + +export const SW_PERIODIC_SYNC_EXTEND_TOKEN_TAG = "extend_token"; // Check if an event is a notification event (message, delete, or read) export const isNotificationEvent = (event) => event === EVENT_MESSAGE || event === EVENT_MESSAGE_DELETE || event === EVENT_MESSAGE_CLEAR; diff --git a/web/src/components/hooks.js b/web/src/components/hooks.js index b7d0b6f1..19149931 100644 --- a/web/src/components/hooks.js +++ b/web/src/components/hooks.js @@ -13,7 +13,7 @@ import versionChecker from "../app/VersionChecker"; import { UnauthorizedError } from "../app/errors"; import notifier from "../app/Notifier"; import prefs from "../app/Prefs"; -import { EVENT_MESSAGE_DELETE, EVENT_MESSAGE_CLEAR } from "../app/events"; +import { EVENT_MESSAGE_DELETE, EVENT_MESSAGE_CLEAR, SW_PERIODIC_SYNC_EXTEND_TOKEN_TAG } from "../app/events"; /** * Wire connectionManager and subscriptionManager so that subscriptions are updated when the connection @@ -284,13 +284,16 @@ export const useStandaloneWebPushAutoSubscribe = () => { }; /** - * Registers a periodicsync listener for `extend-token` (see sw.js). + * Registers a periodicsync listener for `extend_token` (see sw.js). * This extends the token regardless of whether the browser is open. * * CAVEATS: * - Chromium-only * - Only when the PWA is _installed_ (not just running in a browser tab) * - Only when notifications are granted + * + * This is an experimental feature: + * https://developer.mozilla.org/en-US/docs/Web/API/Web_Periodic_Background_Synchronization_API */ const usePeriodicTokenExtend = () => { const isLaunchedPWA = useIsLaunchedPWA(); @@ -315,9 +318,9 @@ const usePeriodicTokenExtend = () => { return; } - console.log(`[usePeriodicTokenExtend] Turning on periodicsync "extend-token"`); - await registration.periodicSync.register("extend-token", { - minInterval: 24 * 60 * 60 * 1000, // 24 hours + console.log(`[usePeriodicTokenExtend] Turning on periodicsync "${SW_PERIODIC_SYNC_EXTEND_TOKEN_TAG}"`); + await registration.periodicSync.register(SW_PERIODIC_SYNC_EXTEND_TOKEN_TAG, { + minInterval: 60 * 1000 // 24 * 60 * 60 * 1000 // 24 hours }); } catch (error) { console.log("[usePeriodicTokenExtend] Periodic Sync could not be registered", error);