diff --git a/public/index.html b/public/index.html index fec0a0375..6597046db 100644 --- a/public/index.html +++ b/public/index.html @@ -53,12 +53,5 @@ } - diff --git a/src/index.tsx b/src/index.tsx index d15198680..d3e8c2a41 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,10 +1,13 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import registerServiceWorker from './registerServiceWorker'; import App from './App'; ReactDOM.render(, document.getElementById('root')); +registerServiceWorker(); + // Hot Module Replacement (HMR) - Remove this snippet to remove HMR. // Learn more: https://snowpack.dev/concepts/hot-module-replacement if (import.meta.hot) { diff --git a/src/registerServiceWorker.ts b/src/registerServiceWorker.ts new file mode 100644 index 000000000..f3252c6d9 --- /dev/null +++ b/src/registerServiceWorker.ts @@ -0,0 +1,117 @@ +// In production, we register a service worker to serve assets from local cache. + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on the "N+1" visit to a page, since previously +// cached resources are updated in the background. + +// To learn more about the benefits of this model, read https://goo.gl/KwvDNy. +// This link also includes instructions on opting out of this behavior. + +const isLocalhost = Boolean(window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.1/8 is considered localhost for IPv4. + window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)); + +export default function register() { + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + window.addEventListener('load', () => { + const swUrl = '/service-worker.js'; + + if (!isLocalhost) { + // Is not local host. Just register service worker + registerValidSW(swUrl); + } else { + // This is running on localhost. Lets check if a service worker still exists or not. + checkValidServiceWorker(swUrl); + } + }); + } +} + +function registerValidSW(swUrl: string) { + let refreshing = false; + + // this will reload all open tabs when we update the service worker + if (navigator.serviceWorker.controller) { + navigator.serviceWorker.addEventListener('controllerchange', () => { + if (refreshing) { + return; + } + + refreshing = true; + window.location.reload(); + }); + } + + navigator.serviceWorker + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + + if (!installingWorker) { + return; + } + + const onServiceWorkerChange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the old content will have been purged and + // the fresh content will have been added to the cache. + // It's the perfect time to display a "New content is + // available; please refresh." message in your web app. + const event = new CustomEvent('updateavailable', { detail: installingWorker }); + + window.dispatchEvent(event); + } + } else { + console.info('Service worker ready') + } + }; + + // listen for state change events + installingWorker.onstatechange = onServiceWorkerChange; + + // check the current service worker state (in Safari this can be "installed" initially). + onServiceWorkerChange(); + }; + }) + .catch(error => { + console.error('Error during service worker registration:', error); + }); +} + +function checkValidServiceWorker(swUrl: string) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl) + .then(response => { + // Ensure service worker exists, and that we really are getting a JS file. + if ( + response.status === 404 || + response.headers.get('content-type')?.indexOf('javascript') === -1 + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then(registration => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl); + } + }) + .catch(() => { + console.info('No internet connection found. App is running in offline mode.'); + }); +} + +export function unregister() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready.then(registration => { + registration.unregister(); + }); + } +}