From 6bbd7dac7552b5609aebddf0697bfe8a11736584 Mon Sep 17 00:00:00 2001 From: oleghasjanov Date: Fri, 3 Oct 2025 12:12:49 +0300 Subject: [PATCH 1/4] Fix Service Worker push subscription race condition - Use navigator.serviceWorker.ready to ensure SW is active before subscribing - Remove duplicate SW registration in setupPushNotifications() - Add error handling for push subscription failures - Add null check for webpush modal element Fixes "no active Service Worker" and "invalid applicationServerKey" errors --- .../push_notification_controller.js | 65 ++++++++++--------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/app/javascript/controllers/push_notification_controller.js b/app/javascript/controllers/push_notification_controller.js index 04a68f98d..a26dd44a7 100644 --- a/app/javascript/controllers/push_notification_controller.js +++ b/app/javascript/controllers/push_notification_controller.js @@ -35,37 +35,40 @@ export default class extends Controller { } } - setupPushNotifications() { - // Check if the browser supports service workers - if ("serviceWorker" in navigator) { - // Register the service worker script (service_worker.js) - navigator.serviceWorker.register('/service-worker.js', { scope: '/' }) - .then((serviceWorkerRegistration) => { - // Check if a subscription to push notifications already exists - serviceWorkerRegistration.pushManager - .getSubscription() - .then((existingSubscription) => { - if (!existingSubscription) { - // If no subscription exists, subscribe to push notifications - serviceWorkerRegistration.pushManager - .subscribe({ - userVisibleOnly: true, - applicationServerKey: this.urlBase64ToUint8Array(this.vapidPublicValue), - }) - .then((subscription) => { - // Save the subscription on the server - this.saveSubscription(subscription); - }); - } - }); - - localStorage.setItem('block-webpush-modal', 'true'); - document.querySelector('.webpush-modal').style.display = 'none'; - }) - .catch((error) => { - console.error("Error during registration Service Worker:", error); - }); - } + setupPushNotifications(registration) { + // Wait for the service worker to be ready + navigator.serviceWorker.ready + .then((serviceWorkerRegistration) => { + // Check if a subscription to push notifications already exists + serviceWorkerRegistration.pushManager + .getSubscription() + .then((existingSubscription) => { + if (!existingSubscription) { + // If no subscription exists, subscribe to push notifications + serviceWorkerRegistration.pushManager + .subscribe({ + userVisibleOnly: true, + applicationServerKey: this.urlBase64ToUint8Array(this.vapidPublicValue), + }) + .then((subscription) => { + // Save the subscription on the server + this.saveSubscription(subscription); + }) + .catch((error) => { + console.error("Error subscribing to push notifications:", error); + }); + } + }); + + localStorage.setItem('block-webpush-modal', 'true'); + const modal = document.querySelector('.webpush-modal'); + if (modal) { + modal.style.display = 'none'; + } + }) + .catch((error) => { + console.error("Error waiting for Service Worker to be ready:", error); + }); } registerServiceWorker() { From bafa5c7a74e7448b28fe94578da396b2b1cee89d Mon Sep 17 00:00:00 2001 From: oleghasjanov Date: Mon, 6 Oct 2025 15:02:07 +0300 Subject: [PATCH 2/4] remove duplicate controller --- .../push_notification_controller.js | 115 +++++++++++------- app/views/common/_webpush_modal.html.erb | 2 +- 2 files changed, 72 insertions(+), 45 deletions(-) diff --git a/app/javascript/controllers/push_notification_controller.js b/app/javascript/controllers/push_notification_controller.js index a26dd44a7..95326e059 100644 --- a/app/javascript/controllers/push_notification_controller.js +++ b/app/javascript/controllers/push_notification_controller.js @@ -15,19 +15,58 @@ export default class extends Controller { if (element) { element.style.display = 'none'; } + return; } - // Check if the browser supports notifications + // Check if notification permission was already denied + if ("Notification" in window && Notification.permission === "denied") { + const element = document.querySelector('.webpush-modal'); + if (element) { + element.style.display = 'none'; + } + return; + } + + // Check if notification permission was already granted and subscription exists + if ("Notification" in window && Notification.permission === "granted") { + this.checkExistingSubscription(); + } + + // Modal will be shown automatically, permission will be requested only when user clicks "Accept" + } + + checkExistingSubscription() { + if ('serviceWorker' in navigator && 'PushManager' in window) { + navigator.serviceWorker.ready.then(registration => { + registration.pushManager.getSubscription().then(subscription => { + if (subscription) { + // Already subscribed, hide modal + const element = document.querySelector('.webpush-modal'); + if (element) { + element.style.display = 'none'; + } + } + }); + }); + } + } + + setupPushNotifications() { + // First request permission from the user if ("Notification" in window) { - // Request permission from the user to send notifications Notification.requestPermission().then((permission) => { if (permission === "granted") { - // If permission is granted, register the service worker - this.registerServiceWorker(); + // Permission granted, now register service worker + this.registerAndSubscribe(); } else if (permission === "denied") { console.warn("User rejected to allow notifications."); + localStorage.setItem('block-webpush-modal', 'true'); + const modal = document.querySelector('.webpush-modal'); + if (modal) { + modal.style.display = 'none'; + } } else { - console.warn("User still didn't give an answer about notifications."); + console.warn("User dismissed the permission dialog."); } }); } else { @@ -35,51 +74,39 @@ export default class extends Controller { } } - setupPushNotifications(registration) { - // Wait for the service worker to be ready - navigator.serviceWorker.ready - .then((serviceWorkerRegistration) => { - // Check if a subscription to push notifications already exists - serviceWorkerRegistration.pushManager - .getSubscription() - .then((existingSubscription) => { - if (!existingSubscription) { - // If no subscription exists, subscribe to push notifications - serviceWorkerRegistration.pushManager - .subscribe({ - userVisibleOnly: true, - applicationServerKey: this.urlBase64ToUint8Array(this.vapidPublicValue), - }) - .then((subscription) => { - // Save the subscription on the server - this.saveSubscription(subscription); - }) - .catch((error) => { - console.error("Error subscribing to push notifications:", error); - }); - } - }); - - localStorage.setItem('block-webpush-modal', 'true'); - const modal = document.querySelector('.webpush-modal'); - if (modal) { - modal.style.display = 'none'; - } - }) - .catch((error) => { - console.error("Error waiting for Service Worker to be ready:", error); - }); - } - - registerServiceWorker() { + registerAndSubscribe() { if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/service-worker.js', { scope: '/' }) .then(registration => { console.log('Service Worker registered successfully:', registration); - this.setupPushNotifications(registration); + // Wait for the service worker to be ready + return navigator.serviceWorker.ready; + }) + .then((serviceWorkerRegistration) => { + // Check if a subscription to push notifications already exists + return serviceWorkerRegistration.pushManager.getSubscription() + .then((existingSubscription) => { + if (!existingSubscription) { + // If no subscription exists, subscribe to push notifications + return serviceWorkerRegistration.pushManager.subscribe({ + userVisibleOnly: true, + applicationServerKey: this.urlBase64ToUint8Array(this.vapidPublicValue), + }); + } + return existingSubscription; + }); + }) + .then((subscription) => { + // Save the subscription on the server + this.saveSubscription(subscription); + localStorage.setItem('block-webpush-modal', 'true'); + const modal = document.querySelector('.webpush-modal'); + if (modal) { + modal.style.display = 'none'; + } }) .catch(error => { - console.error('Service Worker registration failed:', error); + console.error('Service Worker registration or subscription failed:', error); }); } else { console.warn('Service Workers are not supported in this browser.'); diff --git a/app/views/common/_webpush_modal.html.erb b/app/views/common/_webpush_modal.html.erb index 4211cbb0a..127cf2344 100644 --- a/app/views/common/_webpush_modal.html.erb +++ b/app/views/common/_webpush_modal.html.erb @@ -1,4 +1,4 @@ -
+