mirror of
https://github.com/hugo-fixit/FixIt.git
synced 2026-08-24 15:28:57 +00:00
5cad4d3df9
- Service worker: versioned cache name, navigation preload, stale-while-revalidate for images
- New PWAModule extracted from MiscModule with async registration and update detection
- SW update notification toast in Hugo template with i18n support (16 languages)
- Web app manifest template (site.webmanifest) generated from [params.app] config
- [params.app] keys migrated to snake_case: name, short_name, pwa, no_favicon, svg_favicon, mask_color, tile_color, theme_color
- enablePWA moved from [params] to [params.app].pwa
- theme_color no longer accepts single-value, only {light, dark} map
- EventBus: new fixit:sw-update event
- SCSS: new _sw-toast.scss widget for update notification
73 lines
2.3 KiB
TypeScript
73 lines
2.3 KiB
TypeScript
import type { CoreService, PWAService } from '../core/tokens'
|
|
import { eventBus } from '../core/event-bus'
|
|
|
|
/**
|
|
* PWA module — service worker registration and update notification.
|
|
*
|
|
* Responsibilities:
|
|
* - Register service worker with update detection.
|
|
* - Periodically check for service worker updates.
|
|
* - Show update notification toast and handle refresh.
|
|
*/
|
|
export class PWAModule implements PWAService {
|
|
constructor(private readonly core: CoreService) {}
|
|
|
|
/** Register the service worker with update detection and notification binding. */
|
|
async setup() {
|
|
const pwa = this.core.config.PWA
|
|
if (!pwa?.enable || !('serviceWorker' in navigator))
|
|
return
|
|
|
|
const registration = await navigator.serviceWorker
|
|
.register(pwa.serviceWorkerURL)
|
|
.catch((error: unknown) => {
|
|
console.error('Service Worker registration failed:', error)
|
|
return null
|
|
})
|
|
|
|
if (!registration)
|
|
return
|
|
|
|
// Check for updates periodically (every hour)
|
|
setInterval(() => registration.update(), 3600000)
|
|
|
|
// Listen for new service worker installing
|
|
registration.addEventListener('updatefound', () => {
|
|
const newWorker = registration.installing
|
|
if (!newWorker)
|
|
return
|
|
|
|
newWorker.addEventListener('statechange', () => {
|
|
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
|
|
eventBus.emit('fixit:sw-update')
|
|
}
|
|
})
|
|
})
|
|
|
|
// Reload when a new service worker takes control
|
|
navigator.serviceWorker.addEventListener('controllerchange', () => {
|
|
window.location.reload()
|
|
})
|
|
|
|
this.bindUpdateNotification(registration)
|
|
}
|
|
|
|
/**
|
|
* Bind the template-rendered update notification toast.
|
|
* Shows the toast on `fixit:sw-update` and wires the refresh button to skip waiting.
|
|
*/
|
|
private bindUpdateNotification(registration: ServiceWorkerRegistration) {
|
|
const toast = document.querySelector<HTMLElement>('.sw-update-notification')
|
|
if (!toast)
|
|
return
|
|
|
|
eventBus.on('fixit:sw-update', () => {
|
|
toast.querySelector('.sw-update-btn')!.addEventListener('click', () => {
|
|
registration.waiting?.postMessage({ type: 'SKIP_WAITING' })
|
|
toast.classList.remove('visible')
|
|
}, { once: true })
|
|
requestAnimationFrame(() => toast.classList.add('visible'))
|
|
})
|
|
}
|
|
}
|