Skip to content

Instantly share code, notes, and snippets.

@prof3ssorSt3v3
Created March 26, 2021 22:02
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save prof3ssorSt3v3/4ae0c69283f4b555bceadcce3e62077e to your computer and use it in GitHub Desktop.
Save prof3ssorSt3v3/4ae0c69283f4b555bceadcce3e62077e to your computer and use it in GitHub Desktop.
PWA 6 - Handling Install Events
const APP = {
deferredInstall: null,
init() {
if ('serviceWorker' in navigator) {
//register our service worker
navigator.serviceWorker
.register('/sw.js', {
updateViaCache: 'none',
scope: '/',
})
.then(() => {
//finished registering
})
.catch((err) => {
console.warn('Failed to register', err.message);
});
//listen for messages
navigator.serviceWorker.addEventListener('message', ({ data }) => {
//received a message from the service worker
console.log(data, 'from service worker');
});
//listen for `appinstalled` event
window.addEventListener('appinstalled', (evt) => {
//deprecated but still runs in Chrome-based browsers.
//Not very useful event.
//Better to use the DOMContentLoaded and then look at how it was launched
});
//listen for `beforeinstallprompt` event
window.addEventListener('beforeinstallprompt', (ev) => {
// Prevent the mini-infobar from appearing on mobile
ev.preventDefault();
// Stash the event so it can be triggered later.
APP.deferredInstall = ev;
console.log('saved the install event');
// Update UI notify the user they can install the PWA
// if you want here...
});
let btn = document.getElementById('btnInstall');
btn?.addEventListener('click', APP.startChromeInstall);
}
},
startChromeInstall() {
if (APP.deferredInstall) {
console.log(APP.deferredInstall);
APP.deferredInstall.prompt();
APP.deferredInstall.userChoice.then((choice) => {
if (choice.outcome == 'accepted') {
//they installed
console.log('installed');
} else {
console.log('cancel');
}
});
}
},
};
document.addEventListener('DOMContentLoaded', APP.init);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PWA Install Events</title>
<link rel="stylesheet" href="./main.css" />
<!-- pwa manifest -->
<link rel="manifest" href="/manifest.json" />
<!-- Older android support -->
<meta name="theme-color" content="#bada55" />
<!-- ios support -->
<link rel="apple-touch-icon" href="/img/apple-touch-icon.png" />
<meta name="apple-mobile-web-app-status-bar" content="#bada55" />
</head>
<body>
<header>
<h1>PWA Install Events</h1>
</header>
<main>
<p>
Access to these events is currently limited to the Chrome|Chromium|Blink
based browsers - Chrome, Edge, and Opera.
</p>
<p>
Important to note, on iOS, all browsers are forced to use the Safari
Webkit JavaScript Engine. This means that Firefox and Chrome lose some
of their capabilities when running on an iOS device.
</p>
<p>
<button id="btnInstall">INSTALL APP</button><br />
Hide this button unless:
<dl>
<dt>Desktop</dt>
<dd>Chrome</dd>
<dd>Opera</dd>
<dd>Edge</dd>
<dt>Mobile</dt>
<dd>Chrome</dd>
<dd>Opera</dd>
<dd>Edge</dd>
<dd>Safari</dd>
<dd>Firefox</dd>
</dl><br/>
For Safari and Firefox it will have to display instructions.
</p>
</main>
<script src="app.js" defer></script>
</body>
</html>
html {
font-size: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
font-weight: 300;
line-height: 1.5;
background-color: #222;
color: #eee;
}
body {
background-color: inherit;
color: inherit;
min-height: 100vh;
margin: 0;
}
header {
padding: 1rem 2rem;
background-color: #333;
min-height: 10vw;
width: 100vw;
margin: 0;
}
main {
background-color: #222;
min-height: 85vw;
margin: 0;
padding: 5vw 2rem;
}
h1 {
color: orangered;
font-size: 4rem;
line-height: 1;
margin: 0.1rem 0;
}
h2 {
color: orange;
font-size: 2.4rem;
line-height: 2;
margin: 3rem 0 1rem;
opacity: 0.64;
}
h3 {
color: cornflowerblue;
text-shadow: 1px 1px 1px white;
font-size: 3.6rem;
}
span.isOnOff {
font-weight: 900;
color: cornflowerblue;
}
p {
padding: 0;
margin: 1rem 0;
font-size: 1.2rem;
}
img {
max-width: 100%;
margin: 1rem;
}
/* PWA specific styles */
body.pwa main {
position: relative;
padding-left: 8rem;
}
body.pwa main::before {
position: absolute;
transform-origin: top left;
transform: rotate(90deg) translateY(-4rem);
content: 'PWA Rules!';
font-size: 8rem;
width: 10ch;
line-height: 6rem;
font-weight: 900;
letter-spacing: 1px;
padding: 0 10vw 0 0;
margin: 1rem;
color: #444;
top: 0;
left: 0;
}
{
"name": "Sample App",
"short_name": "App",
"display": "standalone",
"start_url": "/index.html",
"icons": [
{
"src": "/img/icon-152x152.png",
"sizes": "152x152",
"type": "image/png"
},
{
"src": "/img/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/img/icon-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "/img/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"orientation": "portrait-primary",
"theme_color": "#bada55",
"background_color": "#eeeeee",
"prefer_related_applications": false
}
const version = 6;
const preCacheName = `static-${version}`;
const preCache = ['/', '/index.html', '/404.html'];
self.addEventListener('install', (ev) => {
//installed
ev.waitUntil(
caches
.open(preCacheName)
.then((cache) => {
console.log('caching the static files');
cache.addAll(preCache);
})
.catch(console.warn)
);
//load pre-cache
});
self.addEventListener('activate', (ev) => {
//activating
ev.waitUntil(
caches
.keys()
.then((keys) => {
return Promise.all(
keys
.filter((key) => key !== preCacheName)
.map((key) => caches.delete(key))
);
})
.catch(console.warn)
);
//delete old caches
});
self.addEventListener('fetch', (ev) => {
//fetch request received
//send back a response from cache or fetch
ev.respondWith(
caches.match(ev.request).then((cacheRes) => {
return (
cacheRes ||
fetch(ev.request).then(
(response) => {
return response;
},
(err) => {
//network failure
//send something else from the cache?
if (
ev.request.url.indexOf('.html') > -1 ||
ev.request.mode == 'navigation'
) {
return caches.match('/404.html');
}
}
)
);
})
);
});
self.addEventListener('message', (ev) => {
//message received
//do things based on message props
let data = ev.data;
console.log('SW received', data);
});
const sendMessage = async (msg) => {
let allClients = await clients.matchAll({ includeUncontrolled: true });
return Promise.all(
allClients.map((client) => {
let channel = new MessageChannel();
return client.postMessage(msg);
})
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment