Files
hereconnect/apps/frontend/public/service-worker.js
T
ti 714f5b7b51
Deploy app frontend / app frontend (push) Successful in 2m44s
try to fix infinity preload in iOS
2024-12-15 11:04:04 +02:00

274 lines
8.1 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) {
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),
// })
// }