283 lines
8.3 KiB
JavaScript
283 lines
8.3 KiB
JavaScript
// const VER = 18
|
|
|
|
const CACHE_NAME = 'cache-v1'
|
|
const TEMP_PAGE_URL = '/preloader.html'
|
|
const STATIC_ASSETS = [TEMP_PAGE_URL, '/css/preloader.css']
|
|
|
|
self.addEventListener('install', (event) => {
|
|
event.waitUntil(caches.open(CACHE_NAME).then((cache) => cache.addAll(STATIC_ASSETS)))
|
|
})
|
|
|
|
self.addEventListener('activate', (event) => {
|
|
event.waitUntil(
|
|
caches.keys().then((cacheNames) =>
|
|
Promise.all(
|
|
cacheNames.map((cache) => {
|
|
if (cache !== CACHE_NAME) {
|
|
return caches.delete(cache)
|
|
}
|
|
}),
|
|
),
|
|
),
|
|
)
|
|
})
|
|
|
|
self.addEventListener('fetch', (event) => {
|
|
const url = new URL(event.request.url)
|
|
|
|
// Обрабатываем только HTTP/HTTPS запросы
|
|
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
return
|
|
}
|
|
|
|
if (event.request.mode === 'navigate') {
|
|
// Для навигационных запросов сначала отдаём `/preloader.html`, пока основная страница загружается
|
|
event.respondWith(handleNavigationRequest(event.request))
|
|
} else if (STATIC_ASSETS.includes(url.pathname)) {
|
|
// Кэшируем только preloader.html и его CSS
|
|
event.respondWith(caches.match(event.request))
|
|
}
|
|
})
|
|
|
|
const TEMP_CACHE = new Map() // Используем карту для временного хранения
|
|
|
|
async function handleNavigationRequest(request) {
|
|
if (TEMP_CACHE.has(request.url) && navigator.onLine) {
|
|
console.log('notifying clients about MAIN_PAGE_READY in cache')
|
|
clients.matchAll({ type: 'window' }).then((allClients) => {
|
|
allClients.forEach((client) =>
|
|
client.postMessage({
|
|
type: 'MAIN_PAGE_READY',
|
|
}),
|
|
)
|
|
})
|
|
|
|
return TEMP_CACHE.get(request.url).clone()
|
|
}
|
|
|
|
const cache = await caches.open(CACHE_NAME)
|
|
|
|
// Отдаём временную страницу из кеша
|
|
const tempResponse = await cache.match(TEMP_PAGE_URL)
|
|
|
|
// Попытка загрузить основную страницу в фоновом режиме
|
|
fetch(request)
|
|
.then(async (networkResponse) => {
|
|
if (networkResponse && networkResponse.ok) {
|
|
// Сохраняем клон в TEMP_CACHE
|
|
TEMP_CACHE.set(request.url, networkResponse)
|
|
|
|
// Сообщаем клиенту, что основная страница готова
|
|
console.log('notifying clients about MAIN_PAGE_READY after fetch')
|
|
const allClients = await clients.matchAll({ type: 'window' })
|
|
allClients.forEach((client) =>
|
|
client.postMessage({
|
|
type: 'MAIN_PAGE_READY',
|
|
}),
|
|
)
|
|
|
|
// Удаляем из TEMP_CACHE через минимальное время
|
|
setTimeout(() => {
|
|
TEMP_CACHE.delete(request.url)
|
|
}, 5000)
|
|
} else {
|
|
throw new Error('Failed to fetch main page')
|
|
}
|
|
})
|
|
.catch(async (error) => {
|
|
console.error('Error loading main page:', error)
|
|
|
|
// Сообщаем клиенту об ошибке сети
|
|
const allClients = await clients.matchAll({ type: 'window' })
|
|
allClients.forEach((client) =>
|
|
client.postMessage({
|
|
type: 'NETWORK_ERROR',
|
|
}),
|
|
)
|
|
})
|
|
|
|
// Возвращаем временную страницу
|
|
return tempResponse
|
|
}
|
|
|
|
async function updateStaticAssetsCache() {
|
|
const cache = await caches.open(CACHE_NAME)
|
|
|
|
// Обновляем ресурсы
|
|
try {
|
|
await Promise.all(
|
|
STATIC_ASSETS.map(async (asset) => {
|
|
const response = await fetch(asset, { cache: 'no-store' })
|
|
if (response.ok) {
|
|
await cache.put(asset, response.clone())
|
|
} else {
|
|
console.error(`Failed to fetch ${asset}:`, response.status)
|
|
}
|
|
}),
|
|
)
|
|
console.log('STATIC_ASSETS обновлены.')
|
|
} catch (error) {
|
|
console.error('Ошибка обновления STATIC_ASSETS:', error)
|
|
}
|
|
}
|
|
|
|
self.addEventListener('message', async (event) => {
|
|
if (event.data && event.data.type === 'SKIP_WAITING') {
|
|
self.skipWaiting()
|
|
} else if (event.data && event.data.type === 'CHECK_MAIN_PAGE_READY') {
|
|
console.log('CHECK_MAIN_PAGE_READY:', TEMP_CACHE.size ? 'ready' : 'not ready')
|
|
if (TEMP_CACHE.size === 0) {
|
|
event.source.postMessage({ type: 'MAIN_PAGE_IS_NOT_READY' })
|
|
} else {
|
|
event.source.postMessage({ type: 'MAIN_PAGE_READY' })
|
|
}
|
|
} else if (event.data && event.data.type === 'CHECK_ONLINE_STATUS') {
|
|
const isOnline = navigator.onLine // Проверяем состояние сети
|
|
event.ports[0].postMessage({ online: isOnline }) // Отправляем результат обратно
|
|
console.log('sent CHECK_ONLINE_STATUS online:', isOnline)
|
|
} else if (event.data && event.data.type === 'UPDATE_STATIC_ASSETS') {
|
|
event.waitUntil(updateStaticAssetsCache())
|
|
} else {
|
|
event.waitUntil(debug(Object.assign({ answer: true }, event.data)))
|
|
}
|
|
})
|
|
|
|
self.addEventListener('install', (event) => {
|
|
event.waitUntil(
|
|
caches.open(CACHE_NAME).then((cache) => {
|
|
return cache.addAll(STATIC_ASSETS)
|
|
}),
|
|
)
|
|
|
|
self.skipWaiting().then(() => debug({ installed: true }))
|
|
})
|
|
|
|
self.addEventListener('activate', (event) => {
|
|
event.waitUntil(
|
|
caches.keys().then((cacheNames) =>
|
|
Promise.all(
|
|
cacheNames.map((cache) => {
|
|
if (cache !== CACHE_NAME) {
|
|
return caches.delete(cache)
|
|
}
|
|
}),
|
|
),
|
|
),
|
|
)
|
|
|
|
clients.claim().then(() => debug(event.source, { activated: true }))
|
|
})
|
|
|
|
self.addEventListener('push', async (event) => {
|
|
debug({
|
|
event: 'push',
|
|
})
|
|
// PushData keys structure standart https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/showNotification
|
|
let pushData = event.data.json()
|
|
if (!pushData || !pushData.notification.title) {
|
|
debug({
|
|
error: 'Received WebPush with an empty title. Received body',
|
|
})
|
|
}
|
|
self.registration
|
|
.showNotification(pushData.notification.title, pushData.notification.options)
|
|
.then(function () {
|
|
// You can save to your analytics fact that push was shown
|
|
// fetch('https://your_backend_server.com/track_show?message_id=' + pushData.data.message_id);
|
|
})
|
|
.catch((error) => {
|
|
debug({
|
|
errorShowNotification: error.message,
|
|
})
|
|
})
|
|
})
|
|
|
|
self.addEventListener('notificationclick', (event) => {
|
|
debug({
|
|
event: 'notificationclick',
|
|
})
|
|
event.waitUntil(handleNotificationClick(event))
|
|
})
|
|
|
|
async function handleNotificationClick(event) {
|
|
event.notification.close()
|
|
|
|
if (!event.notification.data) {
|
|
await debug({
|
|
error: 'Click on WebPush with empty data, where url should be. Notification',
|
|
})
|
|
return
|
|
}
|
|
if (!event.notification.data.url) {
|
|
await debug({ error: 'Click on WebPush without url. Notification' })
|
|
return
|
|
}
|
|
|
|
await openURL(event.notification.data.url)
|
|
}
|
|
|
|
async function openURL(urlToOpen) {
|
|
let matchedClients
|
|
try {
|
|
const urlToOpenObj = new URL(urlToOpen, location.origin)
|
|
|
|
matchedClients = await clients.matchAll({ type: 'window', includeUncontrolled: true })
|
|
|
|
try {
|
|
for (const client of matchedClients) {
|
|
const clientUrlObj = new URL(client.url)
|
|
try {
|
|
await debug(client, {
|
|
urlToOpen,
|
|
'client.url': client.url,
|
|
})
|
|
|
|
if (
|
|
urlToOpenObj.pathname + urlToOpenObj.search !==
|
|
clientUrlObj.pathname + clientUrlObj.search
|
|
) {
|
|
continue
|
|
}
|
|
|
|
if (client.focused) {
|
|
debug({ 'client.focused': true })
|
|
return
|
|
}
|
|
|
|
await client.focus()
|
|
debug({ 'client.focus()': true })
|
|
return
|
|
} catch (error) {
|
|
await debug({ error: error.message })
|
|
}
|
|
}
|
|
} catch (error) {
|
|
debug({ 'get client error': error })
|
|
}
|
|
} catch (error) {
|
|
debug({ 'openURL error': error })
|
|
}
|
|
|
|
if (matchedClients && matchedClients.length) {
|
|
debug({ 'no matched clients': true })
|
|
}
|
|
|
|
try {
|
|
const client = await clients.openWindow(urlToOpen)
|
|
if (!client) {
|
|
await debug({ error: 'client is null' })
|
|
}
|
|
debug({ 'clients.openWindow()': true })
|
|
} catch (error) {
|
|
await debug(matchedClients && matchedClients[0], { error: error.message })
|
|
}
|
|
}
|
|
|
|
function debug() {}
|
|
// async function debug(...args) {
|
|
// return fetch('https://hereconnect-debug.condev.ru/debug', {
|
|
// method: 'POST',
|
|
// body: JSON.stringify(args),
|
|
// })
|
|
// }
|