Skip to content

demo-projects/pwa-masterclass

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

PWA Class

Agenda

  • Build a simple webapp with API
  • Add PWA features to enhance user experience
  • Deploy to firebase and test it on a real device

(1) Get an API key

(2) Build an app

index.js

function getNews() {
  fetch('https://newsapi.org/v2/top-headlines?country=us&apiKey=<YOUR_API_KEY>')
      .then(response => response.json())
      .then(results => renderNews(results.articles))
}

function renderNews(news) {
  const cols = document.getElementById('cardContainer');

  news.map(article => {
    cols.innerHTML += buildCard(article.title, article.urlToImage, article.publishedAt)
  })
}

function buildCard(title, imgLink, date) {
  return `
      <div class="card">
          <img src=${imgLink} class="card-img-top" alt="...">
          <div class="card-body">
              <h5 class="card-title">${title}</h5>
              <p class="card-text"><small class="text-muted">${date}</small></p>
          </div>
      </div>
    `;
}

window.addEventListener('DOMContentLoaded', () => {
  getNews();
});

index.html

<div class="container-fluid p-5">
    <h1 class="display-1">Top News</h1>

    <div class="row">
        <div class="card-columns" id="cardContainer">
            <!-- Generated by JS -->
        </div>
    </div>
</div>

<script src="index.js" type="module"></script>

(3) Deploy to firebase

  • run firebase-login
  • run firebase-init (use build for build folder)
  • run npm run build
  • run filrebase deploy
  • check with lighthouse

(4) The App Manifest

  • should be added the public root
<link rel="manifest" href="manifest.json"/>

manifest.json

{
  "name": "TopNews",
  "short_name": "TopNews",
  "icons": [
    {
      "src": "/images/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/images/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "start_url": "/index.html",
  "display": "standalone",
  "background_color": "#3E4EB8",
  "theme_color": "#2F3BA2"
}
  • add images (freepik + preview)
  • ios metatags
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="TopNews PWA">
<link rel="apple-touch-icon" href="/images/icons/icon-152x152.png">
  • Can be inspected in caro dev-tools
 <meta name="theme-color" content="#2F3BA2" />

(5) Offline support for files

  • create a service worker file news-sw.js
  • check and register the service worker on index.js

index.js

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/news-sw.js')
        .then((reg) => console.log('Service worker registered.', reg));
  });
}
  • create cache name and array of files to cache:

news-sw.js

const CACHE_NAME = 'NEWS_CACHE_V1';

const CACHED_FILES = [
  '/',
  '/index.html',
  '/index.js',
  '/index.css',
];

self.addEventListener('install', function (event) {
  event.waitUntil(
      caches.open(CACHE_NAME)
          .then(function (cache) {
            return cache.addAll(CACHED_FILES);
          })
  );
});

(6) Offline support for data

self.addEventListener('fetch', function (event) {
  event.respondWith(
      caches.open(DATA_CACHE_NAME).then((cache) => {
        return cache.match(event.request)
            .then((response) => {
              return response || fetch(event.request).then(response => {
                if(response.status === 200) {
                  cache.put(event.request.url, response.clone());
                }
              })
            });
      })
  );
});

(7) Making our life easy with workbox

  • load workbox
importScripts('https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js');

if (workbox) {
  console.log(`Workbox is loaded`);
} 
  • cache api
workbox.routing.registerRoute(
    new RegExp('^https://newsapi.org'),
    new workbox.strategies.CacheFirst({
      cacheName: 'api-cache',
      plugins: [
        new workbox.cacheableResponse.Plugin({
          statuses: [0, 200],
        })
      ]
    })
);
  • cache static files
workbox.routing.registerRoute(
    /\.(?:js|css|html)$/,
    new workbox.strategies.NetworkFirst()
);
  • cache fonts with options
workbox.routing.registerRoute(
    /^https:\/\/fonts\.gstatic\.com/,
    new workbox.strategies.CacheFirst({
      cacheName: 'google-fonts-webfonts',
      plugins: [
        new workbox.cacheableResponse.Plugin({
          statuses: [0, 200],
        }),
        new workbox.expiration.Plugin({
          maxAgeSeconds: 60 * 60 * 24 * 365,
          maxEntries: 30,
        }),
      ],
    })
);

(8) Install

  • add some UI to index.html

index.html

    <div class="d-flex justify-content-between">
        <h1 class="display-1">Top News</h1>
        <div class="lead ">
            You like my app? Gee, thanks, just build it<br/>
            I want it, I got it, you want it?,
            <button class="btn btn-primary" id="btnAdd">you got it!</button>
            <br/>
        </div>
    </div>
  • ad to index.js
  let deferredPrompt;

  window.addEventListener('beforeinstallprompt', (event) => {
    deferredPrompt = event;
  });

  const btnAdd = document.getElementById('btnAdd');

  btnAdd.addEventListener('click', () => deferredPrompt.prompt());
  • test install and uninstall

(9) Deploy

  • Deploy to firebase
  • Try to install on your macOS or Android phone

About

PWA MaterClass Reference

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published