Files
mono/components/desktop/src-pwa/custom-service-worker.ts
2025-11-01 17:14:23 +05:00

184 lines
5.4 KiB
TypeScript

export {}
// Импортируем workbox (v6 API)
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate, CacheFirst } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';
import { CacheableResponsePlugin } from 'workbox-cacheable-response';
// Отключаем шумное логирование Workbox
// Используем self.__WB_DISABLE_DEV_LOGS для отключения логирования в development
(self as any).__WB_DISABLE_DEV_LOGS = true;
// Типы для push уведомлений
interface NotificationAction {
action: string;
title: string;
icon?: string;
}
interface PushNotificationData {
title: string;
body: string;
icon?: string;
badge?: string;
url?: string;
tag?: string;
actions?: NotificationAction[];
}
interface PushEvent extends ExtendableEvent {
data: {
text(): string;
json(): any;
} | null;
}
// Precache all assets generated by the build process.
precacheAndRoute((self as any).__WB_MANIFEST);
// Cache images with a Cache First strategy
registerRoute(
({ request }) => request.destination === 'image',
new CacheFirst({
cacheName: 'images',
plugins: [
new ExpirationPlugin({
maxEntries: 60,
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
}),
new CacheableResponsePlugin({
statuses: [0, 200],
}),
],
}),
);
// Cache CSS, JS, and Web Worker requests with a Stale While Revalidate strategy
registerRoute(
({ request }) =>
request.destination === 'style' ||
request.destination === 'script' ||
request.destination === 'worker',
new StaleWhileRevalidate({
cacheName: 'static-resources',
}),
);
// Cache Google Fonts with a Cache First strategy
registerRoute(
({ request }) =>
request.url.includes('fonts.googleapis.com') ||
request.url.includes('fonts.gstatic.com'),
new CacheFirst({
cacheName: 'google-fonts',
plugins: [
new ExpirationPlugin({
maxEntries: 30,
maxAgeSeconds: 60 * 60 * 24 * 365, // 1 year
}),
new CacheableResponsePlugin({
statuses: [0, 200],
}),
],
}),
);
// Handle messages from the main thread
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
// Handle service worker activation
self.addEventListener('activate', () => {
// console.log отключен для уменьшения шума в консоли
// Take control of all pages immediately
(self as any).clients.claim();
});
// Handle service worker installation
self.addEventListener('install', () => {
// console.log отключен для уменьшения шума в консоли
// Skip waiting to activate immediately
(self as any).skipWaiting();
});
// Handle fetch events for offline functionality
self.addEventListener('fetch', () => {
// You can add custom fetch handling here if needed
// For now, let the default behavior handle requests
});
/**
* Обработка входящих push уведомлений
*/
self.addEventListener('push', (event: PushEvent) => {
console.log('Push уведомление получено:', event);
let notificationData: PushNotificationData;
try {
// Пытаемся извлечь данные из push уведомления
if (event.data) {
const rawData = event.data.text();
notificationData = JSON.parse(rawData);
} else {
console.log('Нет данных в push событии, используем fallback');
// Fallback уведомление
notificationData = {
title: 'Новое уведомление',
body: 'У вас есть новое уведомление',
icon: '/icons/icon-192x192.png',
badge: '/icons/icon-192x192.png',
};
}
} catch (error) {
console.error('Ошибка парсинга push данных:', error);
notificationData = {
title: 'Новое уведомление',
body: 'У вас есть новое уведомление',
icon: '/icons/icon-192x192.png',
badge: '/icons/icon-192x192.png',
};
}
// Показываем уведомление
const notificationOptions: NotificationOptions = {
body: notificationData.body,
icon: notificationData.icon || '/icons/icon-192x192.png',
badge: notificationData.badge || '/icons/icon-192x192.png',
tag: notificationData.tag || 'general',
data: {
url: notificationData.url || '/',
timestamp: Date.now(),
},
requireInteraction: false,
silent: false,
};
// Добавляем actions если поддерживаются
if (notificationData.actions && notificationData.actions.length > 0) {
(notificationOptions as any).actions = notificationData.actions;
}
console.log('Показываем уведомление с данными:', {
title: notificationData.title,
options: notificationOptions,
});
const showNotificationPromise = self.registration
.showNotification(notificationData.title, notificationOptions)
.then(() => {
console.log('Уведомление успешно показано');
})
.catch((error) => {
console.error('Ошибка показа уведомления:', error);
});
event.waitUntil(showNotificationPromise);
});