Files
sure/app/views/pwa/service-worker.js
Juan José Mata d47aa2fe90 PWA offline error page + login page cleanup (#327)
* Add friendly PWA offline error page

When the PWA fails to connect to the server, users now see a branded
offline page with a friendly "technical difficulties" message, the
app logo, and a reload button. The page automatically attempts to
reload when connectivity is restored.

Changes:
- Created public/offline.html with branded offline experience
- Updated service worker to cache and serve offline page on network failures
- Added service worker registration in application.js
- Service worker now handles navigation requests with offline fallback

* Extract PWA offline logo to separate cached asset

Move the inline SVG logo from offline.html to a separate file at
public/logo-offline.svg. This makes the logo asset easily identifiable
and maintainable, as it may diverge from other logo versions in the future.

Changes:
- Created public/logo-offline.svg with the offline page logo
- Updated service worker to cache logo as part of OFFLINE_ASSETS array
- Updated fetch handler to serve cached offline assets
- Updated offline.html to reference logo file instead of inline SVG

* Update offline message for better readability

Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>

* CodeRabbit comments

* Keep 40x and 50x flowing

* Dark mode

* Logo tweaks

* Login/sign up cleanup

---------

Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-16 21:12:48 +01:00

86 lines
2.4 KiB
JavaScript

const CACHE_VERSION = 'v1';
const OFFLINE_ASSETS = [
'/offline.html',
'/logo-offline.svg'
];
// Install event - cache the offline page and assets
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_VERSION).then((cache) => {
return cache.addAll(OFFLINE_ASSETS);
})
);
// Activate immediately
self.skipWaiting();
});
// Activate event - clean up old caches
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
if (cacheName !== CACHE_VERSION) {
return caches.delete(cacheName);
}
})
);
}).then(() => {
// Take control of all pages immediately
return self.clients.claim();
})
);
});
// Fetch event - serve offline page when network fails
self.addEventListener('fetch', (event) => {
// Handle navigation requests (page loads)
if (event.request.mode === 'navigate') {
event.respondWith(
fetch(event.request).catch((error) => {
// Only show offline page for network errors
if (error.name === 'TypeError' || !navigator.onLine) {
return caches.match('/offline.html');
}
throw error;
})
);
}
// Handle offline assets (logo, etc.)
else if (OFFLINE_ASSETS.some(asset => new URL(event.request.url).pathname === asset)) {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
})
);
}
});
// Add a service worker for processing Web Push notifications:
//
// self.addEventListener("push", async (event) => {
// const { title, options } = await event.data.json()
// event.waitUntil(self.registration.showNotification(title, options))
// })
//
// self.addEventListener("notificationclick", function(event) {
// event.notification.close()
// event.waitUntil(
// clients.matchAll({ type: "window" }).then((clientList) => {
// for (let i = 0; i < clientList.length; i++) {
// let client = clientList[i]
// let clientPath = (new URL(client.url)).pathname
//
// if (clientPath == event.notification.data.path && "focus" in client) {
// return client.focus()
// }
// }
//
// if (clients.openWindow) {
// return clients.openWindow(event.notification.data.path)
// }
// })
// )
// })