Adicione a seguinte meta a sua página HTML:
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="shortcut icon" type="image/png" href="/android-chrome-512x512.png">
<link rel="manifest" href="/site.webmanifest">
<meta name="theme-color" content="rgb(220,220,225)">
<meta content='yes' name='apple-mobile-web-app-capable' />
<meta content='yes' name='mobile-web-app-capable' />
Adicione o seguinte script no final da página HTML
<script>
if ('serviceWorker' in navigator) {
if (navigator.serviceWorker.controller) {
console.log('[PWA] active service worker found, no need to register')
} else {
navigator.serviceWorker.register('/sw.js', {
scope: '/'
}).then(function (registration) {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
registration.pushManager.getSubscription();
}).catch(function (err) {
console.log('ServiceWorker registration failed: ', err);
});
}
} else {
console.log('ServiceWorker not supported');
}
if (!("Notification" in window)) {
console.log("This browser does not support desktop notification");
} else if (Notification.permission === "granted") {
console.log('granted');
} else if (Notification.permission !== "denied") {
Notification.requestPermission().then((permission) => {
if (permission === "granted") {
console.log('granted');
}
});
}
</script>
Crie o seguinte arquivo de manifesto (/site.manifest):
{
"name": "",
"short_name": "",
"icons": [{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
}, {
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone",
"start_url": "/",
}
Crie o seguinte service worker (/sw.js):
const CACHE = "offline-page";
const offlineFallbackPage = "/offline.html";
self.addEventListener("install", function (event) {
event.waitUntil(
caches.open(CACHE).then(function (cache) {
return cache.add(offlineFallbackPage);
})
);
});
// If any fetch fails, it will look for the request in the cache and serve it from there first
self.addEventListener("fetch", function (event) {
if (event.request.method !== "GET") return;
event.respondWith(
fetch(event.request)
.then(function (response) {
// If request was success, add or update it in the cache
event.waitUntil(updateCache(event.request, response.clone()));
return response;
})
.catch(function (error) {
return fromCache(event.request);
})
);
});
function fromCache(request) {
// Check to see if you have it in the cache
// Return response
// If not in the cache, then return the offline page
return caches.open(CACHE).then(function (cache) {
return cache.match(request).then(function (matching) {
if (!matching || matching.status === 404) {
// The following validates that the request was for a navigation to a new document
if (request.destination !== "document" || request.mode !== "navigate") {
return Promise.reject("no-match");
}
return cache.match(offlineFallbackPage);
}
return matching;
});
});
}
function updateCache(request, response) {
return caches.open(CACHE).then(function (cache) {
return cache.put(request, response);
});
}
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
self.addEventListener('push', function(e) {
console.log('PUSH -> ', e.data.text());
self.registration.showNotification(
'Meu App Web',
{
body: e.data.text(),
icon: '/favicon-32x32.png',
}
);
});
Por fim crie uma página para ser disponibilizada quando estiver offline com o nome /offline.html