Skip to content

Instantly share code, notes, and snippets.

@Antoinebr
Last active January 7, 2020 15:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Antoinebr/11bcf7c38a874e4070cac4b2db2e06b8 to your computer and use it in GitHub Desktop.
Save Antoinebr/11bcf7c38a874e4070cac4b2db2e06b8 to your computer and use it in GitHub Desktop.

bit.ly/pwa-tips

Show an offline page when you have a cache miss

1 create an offline page and add the URL to the preache

// cache our offline page
// revision should be updated manually here
workbox.precaching.precacheAndRoute([{
  "url": "/offline/",
  "revision": "d3ab5addsdef6bsse1864b1b4719e5e54ff21"
}]); 

2 throw the offline page when you get a cache miss

offline  page

/*
| --------------------------------------------------------------------------
|  Caching of the product pages
| -------------------------------------------------------------------------- 
*/
const productsNetworkFirstHandler = workbox.strategies.networkFirst({
  cacheName: 'products',

  plugins: [
    new workbox.expiration.Plugin({
      maxEntries: 30,
      maxAgeSeconds: 2 * 30 * 24 * 60 * 60,
    }),
    new workbox.cacheableResponse.Plugin({
      statuses: [200]
    }),

  ]

});


workbox.routing.registerRoute(/\/produit\//, (args) => {

  return productsNetworkFirstHandler.handle(args).then((response) => (!response) ? caches.match('/offline/') : response);

});

Query the cache and retrieve the cached requests

/**
* List the caches names
* @returns {promise} cache names
*/

async function listCaches(){
  
  const cacheNames = await caches.keys();
  
  if(!cacheNames){
    throw new Error('no caches');
  }
  
  return cacheNames;
}
    
/**
*
* Get the requests saved ina given cache
* @param {string} cacheName
* @returns {promise} keylist
*/
async function getCachedRequest(cacheName){
    
  const requests = await caches.open(cacheName);
  
  return await requests.keys()
  
}



(async () => {

    const caches = await listCaches();
  
    for( const cache of caches ){
        
        const requests = await getCachedRequest(cache)
        
        // now you have all the requests feel free to inject them on the page etc...
        console.log(requests)
      
    }



})().catch(console.log)

Inject the manifest and workbox config :

module.exports = {
    globDirectory: './',
    globPatterns: [
      'css/app.css',
      'js/libs/jquery.js',
      'js/libs/turbolinks.js',
      'js/app.bundle.js',
      'img/header-logo-white.png',
      'img/header-logo-small-black.png'
    ],
    swSrc: './serviceworker-dev.js',
    swDest: './serviceworker.js'
  };
  

serviceworker-dev :

importScripts("https://storage.googleapis.com/workbox-cdn/releases/3.0.1/workbox-sw.js");

workbox.skipWaiting();
workbox.clientsClaim();

workbox.core.setLogLevel(workbox.core.LOG_LEVELS.debug);

/**
 * The workboxSW.precacheAndRoute() method efficiently caches and responds to
 * requests for URLs in the manifest.
 * See https://goo.gl/S9QRab
 */
workbox.precaching.precacheAndRoute([]);

// cache our offline page
// revision should be updated manually here
workbox.precaching.precacheAndRoute([{
  "url": "/offline/",
  "revision": "d3ab5addsdef6bsse1864b1b4719e5e54ff21"
}]);

And run npx workbox injectManifest workbox-config.js

How to change the UI based on the current connectivity ?

Offline UI

Add a class to the html main tag when offline

const $html = document.querySelector('html');

function addOfflineElements(){
    
    $html.classList.add('offline');

}


function removeOfflineElements(){

    $html.classList.remove('offline');

}



if( ! navigator.onLine )  addOfflineElements();

if( navigator.onLine )  removeOfflineElements();


addEventListener("offline", addOfflineElements );

addEventListener("online", removeOfflineElements );

The CSS :

Change the style of element or hide them when offline

/*
*  All elements which should be hidden when offline
*/
.offline form.cart .bouton,
.offline .menu-account,
.offline .menu-cart
{

display: none;

}

Inform the user if a link is available offline

offline links

 // Offline producst management 
            if ( document.querySelector('.product-category') !== null ){
                
                // we query the cache named products (which contains the products URLs )
                getRequestsFromCache('products')
                .then( requests => {
                
                    const offlineUrls = [];
                    
                    // we pursh the urls to the array offlineUrls
                    requests.forEach( r => offlineUrls.push(r.url) );
                   
                    //  we loop through our product list and check if the current URL is in the array 
                    document.querySelectorAll('.product-category a').forEach( a =>{

                        if( ! offlineUrls.includes(a.href) && a.querySelector('article') !== null) {
                            // if not offline we add the class not-offline
                            a.querySelector('article').classList.add('not-offline');
                        }

                    });

                });

            }
            
.not-offline{
    border: solid #0202020d 1px;
    opacity:0.2;
}

Add a basic offline banner

function injectOfflineBanner(){

  let elem = document.createElement('div');
  
  elem.style.cssText = `
  position: fixed;
  background-color: #6d6d6d;
  bottom: 0;
  left: 0;
  right: 0;
  height: 46px;
  line-height: 40px;
  text-align: center;
  color: #FFF;
  z-index: 9999999999;
  `;

  elem.id = "offline-banner";

  elem.innerText = "Heads up  : You are offline";

  document.body.appendChild(elem);

}


function removeOfflineBanner(){

  const offlineBanner = document.querySelector("#offline-banner");

  if( offlineBanner !== null ) offlineBanner.parentNode.removeChild(offlineBanner);

}



if( ! navigator.onLine ) injectOfflineBanner();

if( navigator.onLine ) removeOfflineBanner();


addEventListener("offline", () => injectOfflineBanner() );


addEventListener("online", () => removeOfflineBanner() );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment