~koehr/koehr.tech

f5644636d855a2e0578e4e6d75dcacbe5860de4c — koehr 5 years ago 28dd7e8
adds offline and update events and a notification bar
5 files changed, 122 insertions(+), 5 deletions(-)

M src/App.vue
A src/components/Notifications.vue
M src/registerServiceWorker.js
A src/sw.js
A vue.config.js
M src/App.vue => src/App.vue +34 -2
@@ 2,6 2,7 @@
  <div id="app">
    <main-menu :visible.sync="navVisible" />
    <div class="content">
      <notifications v-bind="appStatus" @refresh="refresh" />
      <router-view />
    </div>
  </div>


@@ 9,10 10,11 @@

<script>
import MainMenu from '@/components/MainMenu.vue'
import Notifications from '@/components/Notifications.vue'

export default {
  name: 'app',
  components: { MainMenu },
  components: { MainMenu, Notifications },
  watch: {
    $route () {
      this.navVisible = false


@@ 20,7 22,37 @@ export default {
  },
  data () {
    return {
      navVisible: false
      navVisible: false,
      swRegistration: null,
      appStatus: {
        hasUpdate: false,
        isOffline: false,
        isUpdating: false
      }
    }
  },
  mounted () {
    document.addEventListener('swUpdated', registration => {
      this.swRegistration = registration
      this.appStatus.hasUpdate = true
    }, { once: true })

    document.addEventListener('swOffline', registration => {
      this.appStatus.isOffline = true
    })

    navigator.serviceWorker.addEventListener('controllerchange', () => {
      if (this.appStatus.isUpdating) return
      this.appStatus.isUpdating = true
      window.location.reload()
    })
  },
  methods: {
    refresh () {
      this.appStatus.hasUpdate = false
      if (!this.swRegistration || !this.swRegistration.waiting) return

      this.swRegistration.waiting.postMessage('skipWaiting')
    }
  }
}

A src/components/Notifications.vue => src/components/Notifications.vue +46 -0
@@ 0,0 1,46 @@
<template>
  <div id="notifications" :class="{ visible }">
    <div id="update-notification" v-if="hasUpdate">
      There's an update available. Please
      <a href="/" @click.prevent="$emit('refresh')">refresh</a>.
    </div>
    <div id="updating-notification" v-if="isUpdating">
      App is updating...
    </div>
    <div id="offline-notification" v-if="isOffline">
      You are offline. Only cached articles will be available.
    </div>
  </div>
</template>

<script>
export default {
  name: 'notifications',
  props: {
    hasUpdate: Boolean,
    isOffline: Boolean,
    isUpdating: Boolean
  },
  computed: {
    visible () {
      return this.hasUpdate || this.isOffline || this.isUpdating
    }
  }
}
</script>

<style scoped>
#notifications {
  position: absolute;
  top: -3em;
  width: 100%;
  max-width: calc(70rem - 4em);
  padding: 1em 2em;
  background: #FFF3;
  text-align: center;
  transition: transform .3s;
}
#notifications.visible {
  transform: translate(0, 3em);
}
</style>

M src/registerServiceWorker.js => src/registerServiceWorker.js +12 -3
@@ 10,8 10,11 @@ if (process.env.NODE_ENV === 'production') {
        'For more details, visit https://goo.gl/AFskqB'
      )
    },
    registered () {
    registered (registration) {
      console.log('Service worker has been registered.')
      setInterval(() => {
        registration.update()
      }, 60 * 60 * 1000) // hourly check for updates
    },
    cached () {
      console.log('Content has been cached for offline use.')


@@ 19,11 22,17 @@ if (process.env.NODE_ENV === 'production') {
    updatefound () {
      console.log('New content is downloading.')
    },
    updated () {
    updated (registration) {
      console.log('New content is available; please refresh.')
      document.dispatchEvent(
        new CustomEvent('swUpdated', { detail: registration })
      )
    },
    offline () {
    offline (registration) {
      console.log('No internet connection found. App is running in offline mode.')
      document.dispatchEvent(
        new CustomEvent('swOffline', { detail: registration })
      )
    },
    error (error) {
      console.error('Error during service worker registration:', error)

A src/sw.js => src/sw.js +21 -0
@@ 0,0 1,21 @@
// This is the code piece that GenerateSW mode can't provide for us.
// This code listens for the user's confirmation to update the app.
self.addEventListener('message', (e) => {
  if (!e.data) { return }

  switch (e.data) {
    case 'skipWaiting':
      self.skipWaiting()
      break
    default:
      // NOOP
      break
  }
})

workbox.clientsClaim()

// The precaching code provided by Workbox.
self.__precacheManifest = [].concat(self.__precacheManifest || [])
workbox.precaching.suppressWarnings()
workbox.precaching.precacheAndRoute(self.__precacheManifest, {})

A vue.config.js => vue.config.js +9 -0
@@ 0,0 1,9 @@
module.exports = {
  pwa: {
    workboxPluginMode: 'InjectManifest',
    workboxOptions: {
      swSrc: './src/sw.js',
      swDest: 'service-worker.js'
    }
  }
}