141 lines
4.7 KiB
JavaScript
141 lines
4.7 KiB
JavaScript
const MVLOG_CACHE_PREFIX = 'mvlog-pwa-';
|
|
const MVLOG_CACHE_VERSION = 'mvlog-pwa-v1';
|
|
const APP_SHELL_CACHE = `${MVLOG_CACHE_VERSION}-app-shell`;
|
|
const RUNTIME_CACHE = `${MVLOG_CACHE_VERSION}-runtime`;
|
|
|
|
const APP_SHELL_URLS = [
|
|
'/',
|
|
'/offline.html',
|
|
'/style.css',
|
|
'/js/map_lightbox.js',
|
|
'/assets/fonts/BebasNeue-Regular.ttf',
|
|
'/assets/fonts/IBMPlexSans-SemiBold.ttf',
|
|
'/assets/fonts/NotoSans-Bold.ttf',
|
|
'/assets/img/background.jpg',
|
|
'/assets/img/moto_travel.png',
|
|
'/assets/img/pwa-icon-192.png',
|
|
'/assets/img/pwa-icon-512.png',
|
|
'/assets/img/pwa-maskable-512.png',
|
|
'/assets/img/apple-touch-icon.png',
|
|
'/manifest.json'
|
|
];
|
|
|
|
self.addEventListener('install', event => {
|
|
event.waitUntil((async () => {
|
|
const cache = await caches.open(APP_SHELL_CACHE);
|
|
await Promise.all(APP_SHELL_URLS.map(url =>
|
|
cache.add(new Request(url, { cache: 'reload' })).catch(() => undefined)
|
|
));
|
|
await self.skipWaiting();
|
|
})());
|
|
});
|
|
|
|
self.addEventListener('activate', event => {
|
|
event.waitUntil((async () => {
|
|
const names = await caches.keys();
|
|
await Promise.all(names.map(name => {
|
|
if (name.startsWith(MVLOG_CACHE_PREFIX) && ![APP_SHELL_CACHE, RUNTIME_CACHE].includes(name)) {
|
|
return caches.delete(name);
|
|
}
|
|
return undefined;
|
|
}));
|
|
await clients.claim();
|
|
})());
|
|
});
|
|
|
|
function shouldBypassFetch(request, url) {
|
|
if (request.method !== 'GET') return true;
|
|
if (!['http:', 'https:'].includes(url.protocol)) return true;
|
|
if (url.origin !== self.location.origin) return false;
|
|
|
|
const path = url.pathname;
|
|
if (path === '/admin.php' || path === '/login.php' || path === '/logout.php') return true;
|
|
if (path === '/push_config.php' || path === '/push_subscribe.php' || path === '/job_status.php') return true;
|
|
if (path.startsWith('/api/') || path.startsWith('/lib/') || path.startsWith('/cache/') || path.startsWith('/in-dir/')) return true;
|
|
if (path.startsWith('/out-dir/') && /\.(mp4|m4v|mov|webm)$/i.test(path)) return true;
|
|
if (/\.(mp4|m4v|mov|webm)$/i.test(path)) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
function isStaticAsset(url) {
|
|
if (url.origin !== self.location.origin) return false;
|
|
const path = url.pathname;
|
|
return path === '/style.css'
|
|
|| path === '/manifest.json'
|
|
|| path === '/offline.html'
|
|
|| path.startsWith('/assets/')
|
|
|| path.startsWith('/js/');
|
|
}
|
|
|
|
async function networkFirstNavigation(request) {
|
|
try {
|
|
const response = await fetch(request);
|
|
if (response && response.ok) {
|
|
const cache = await caches.open(RUNTIME_CACHE);
|
|
cache.put(request, response.clone()).catch(() => undefined);
|
|
}
|
|
return response;
|
|
} catch (error) {
|
|
const exact = await caches.match(request);
|
|
if (exact) return exact;
|
|
const offline = await caches.match('/offline.html');
|
|
if (offline) return offline;
|
|
const home = await caches.match('/');
|
|
if (home) return home;
|
|
return new Response('Offline', { status: 503, headers: { 'Content-Type': 'text/plain; charset=utf-8' } });
|
|
}
|
|
}
|
|
|
|
async function staleWhileRevalidate(request) {
|
|
const cached = await caches.match(request);
|
|
const cache = await caches.open(RUNTIME_CACHE);
|
|
const network = fetch(request).then(response => {
|
|
if (response && response.ok) {
|
|
cache.put(request, response.clone()).catch(() => undefined);
|
|
}
|
|
return response;
|
|
}).catch(() => cached);
|
|
return cached || network;
|
|
}
|
|
|
|
self.addEventListener('fetch', event => {
|
|
const request = event.request;
|
|
const url = new URL(request.url);
|
|
if (shouldBypassFetch(request, url)) return;
|
|
|
|
if (request.mode === 'navigate') {
|
|
event.respondWith(networkFirstNavigation(request));
|
|
return;
|
|
}
|
|
|
|
if (isStaticAsset(url)) {
|
|
event.respondWith(staleWhileRevalidate(request));
|
|
}
|
|
});
|
|
|
|
self.addEventListener('push', event => {
|
|
let data = {};
|
|
try { data = event.data ? event.data.json() : {}; } catch (e) { data = { title: 'MVLog', body: event.data ? event.data.text() : 'New article @ MVLog!' }; }
|
|
const title = data.title || 'MVLog';
|
|
const options = {
|
|
body: data.body || 'New article @ MVLog!',
|
|
icon: 'assets/img/moto_travel.png',
|
|
badge: 'assets/img/moto_travel.png',
|
|
data: { url: data.url || './' },
|
|
tag: data.tag || 'mvlog-new-video',
|
|
renotify: true
|
|
};
|
|
event.waitUntil(self.registration.showNotification(title, options));
|
|
});
|
|
|
|
self.addEventListener('notificationclick', event => {
|
|
event.notification.close();
|
|
const url = event.notification.data && event.notification.data.url ? event.notification.data.url : './';
|
|
event.waitUntil(clients.matchAll({ type: 'window', includeUncontrolled: true }).then(clientList => {
|
|
for (const client of clientList) {
|
|
if (client.url === url && 'focus' in client) return client.focus();
|
|
}
|
|
return clients.openWindow(url);
|
|
}));
|
|
});
|