dstpabuaran.com/resources/js/sw.ts
Yoga Pangestu 95be00c9d1 feat: add push notification functionality and service worker
- Implemented push notifications with a service worker (sw.ts) to handle caching and notifications.
- Added API routes for push subscription and notification management in routes/api.php.
- Created NotificationTest to validate notification service functionality and ensure correct notifications are sent based on user roles and actions.
- Updated Permissions component to manage notification permissions and subscriptions.
- Enhanced CategoryIndex component to highlight categories based on notifications.
- Excluded service worker from TypeScript compilation in tsconfig.json.
- Updated Vite configuration to include service worker in the build process.
2026-08-01 11:36:20 +07:00

107 lines
2.7 KiB
TypeScript

import { clientsClaim } from 'workbox-core';
import { ExpirationPlugin } from 'workbox-expiration';
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { CacheFirst, NetworkFirst } from 'workbox-strategies';
declare const self: ServiceWorkerGlobalScope;
precacheAndRoute(self.__WB_MANIFEST);
self.skipWaiting();
clientsClaim();
registerRoute(
({ url }) => url.origin === 'https://fonts.bunny.net',
new CacheFirst({
cacheName: 'bunny-fonts-cache',
plugins: [
new ExpirationPlugin({
maxEntries: 10,
maxAgeSeconds: 60 * 60 * 24 * 365,
}),
],
}),
);
registerRoute(
({ url }) => url.origin === 'https://fonts.googleapis.com',
new CacheFirst({
cacheName: 'google-fonts-cache',
plugins: [
new ExpirationPlugin({
maxEntries: 10,
maxAgeSeconds: 60 * 60 * 24 * 365,
}),
],
}),
);
registerRoute(
({ url }) => url.pathname.startsWith('/api/'),
new NetworkFirst({
cacheName: 'api-cache',
networkTimeoutSeconds: 10,
plugins: [
new ExpirationPlugin({
maxEntries: 50,
maxAgeSeconds: 60 * 60 * 24,
}),
],
}),
);
self.addEventListener('push', (event: PushEvent) => {
if (!event.data) {
return;
}
const data = event.data.json();
const options: NotificationOptions = {
body: data.body || '',
icon: data.icon || '/icon-192x192.png',
badge: '/icon-192x192.png',
data: data.data || {},
actions: data.actions || [],
};
event.waitUntil(
self.registration.showNotification(
data.title || 'DST Collection',
options,
),
);
});
self.addEventListener('notificationclick', (event: NotificationEvent) => {
event.notification.close();
const action = event.action;
const data = event.notification.data;
let url = '/';
if (action === 'view_category' && data.category_id) {
url = `/admin/master/categories`;
} else if (data.url) {
url = data.url;
}
event.waitUntil(
self.clients
.matchAll({ type: 'window', includeUncontrolled: true })
.then((clients) => {
for (const client of clients) {
if (client.url.includes(url) && 'focus' in client) {
return client.focus();
}
}
if (self.clients.openWindow) {
return self.clients.openWindow(url);
}
}),
);
});