Skip to content

Commit

Permalink
Feature/pwa (#68)
Browse files Browse the repository at this point in the history
* ➕ Adding >> @nuxtjs/pwa

* ✨ PWA >> Adding empty settings for setup and a plugin for handling updates.

* ✨ App >> PWA Plugin for checking installable and controlling it.

* 💄 PWA >> Button for installing PWA if possible.

* 📝 Changelog >> Adding pwa feature.
  • Loading branch information
borsTiHD authored Jul 31, 2021
1 parent ecdbdbc commit a2658eb
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Themes: New default theme has been added, moreover users can choose between several themes and even create their own.
- App: Adding new icons.
- PWA: Adding pwa functionality. User can install the app on their devices (add to home screen).
- Swagger: Adding Swagger documentation for the backends REST Api. Only for developer mode.
- Developing: New '.env' variable added. Set 'DEV_USER' to 'true' and you can login with default user for developing purpose.
- Dependencies: Bumping packages to newer versions.
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"devDependencies": {
"@nuxtjs/eslint-config": "^6.0.1",
"@nuxtjs/eslint-module": "^3.0.2",
"@nuxtjs/pwa": "^3.3.5",
"@nuxtjs/vuetify": "^1.12.1",
"archiver": "^5.3.0",
"babel-eslint": "^10.1.0",
Expand Down
47 changes: 47 additions & 0 deletions src/client/components/display/AppPwaChip.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<template>
<v-tooltip
v-if="isInstallable"
left
>
<template #activator="{ on, attrs }">
<v-chip
class="mr-2"
color="info"
bordered
small
v-bind="attrs"
v-on="on"
@click="installPwa"
>
Add to Home Screen
</v-chip>
</template>
<span>Install it as a PWA app and access it from your home screen</span>
</v-tooltip>
</template>

<script>
export default {
name: 'AppPwaChip',
data() {
return {
}
},
computed: {
isInstallable() {
// If app is already installed, don't show install button
if (this.$pwa.installed) {
return false
}
return this.$pwa.installable
}
},
methods: {
installPwa() {
this.$pwa.install().catch((err) => {
this.$toast.error(err.message)
})
}
}
}
</script>
2 changes: 1 addition & 1 deletion src/client/components/display/AppUpdateChip.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
v-on="on"
@click="goToNewRelease"
>
update
Update
</v-chip>
</template>
<span>Update available</span>
Expand Down
5 changes: 4 additions & 1 deletion src/client/components/layout/Header.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<v-toolbar-title style="cursor: pointer" @click="$router.push('/')" v-text="title" />
<v-spacer />

<app-pwa-chip />
<app-update-chip />
<v-btn icon @click.stop="rightDrawer = !rightDrawer">
<v-icon>mdi-cogs</v-icon>
Expand All @@ -17,11 +18,13 @@
import { mapGetters, mapActions } from 'vuex'
import pkg from 'projRoot/package.json'
import AppUpdateChip from '~/components/display/AppUpdateChip.vue'
import AppPwaChip from '~/components/display/AppPwaChip.vue'
export default {
name: 'Header',
components: {
AppUpdateChip
AppUpdateChip,
AppPwaChip
},
data() {
return {
Expand Down
24 changes: 22 additions & 2 deletions src/client/nuxt.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ export default {
plugins: [
{ mode: 'client', src: '@/plugins/persistedState.client.js' },
{ mode: 'client', src: '@/plugins/run-script.js' },
{ mode: 'client', src: '@/plugins/change-theme.js' }
{ mode: 'client', src: '@/plugins/change-theme.js' },
{ mode: 'client', src: '@/plugins/pwa.client.js' },
{ mode: 'client', src: '@/plugins/pwa-update.client.js' }
],

// Auto import components: https://go.nuxtjs.dev/config-components
Expand All @@ -123,7 +125,9 @@ export default {
// https://go.nuxtjs.dev/eslint
'@nuxtjs/eslint-module',
// https://go.nuxtjs.dev/vuetify
'@nuxtjs/vuetify'
'@nuxtjs/vuetify',
// https://pwa.nuxtjs.org/
'@nuxtjs/pwa'
],

// Modules: https://go.nuxtjs.dev/config-modules
Expand Down Expand Up @@ -198,6 +202,22 @@ export default {
}
},

// PWA config - https://pwa.nuxtjs.org/setup
pwa: {
icon: {
/* icon options */
},
meta: {
/* meta options */
},
manifest: {
/* manifest options */
},
workbox: {
/* workbox options */
}
},

// Generate Configuration: https://nuxtjs.org/docs/2.x/configuration-glossary/configuration-generate
generate: {
dir: path.join(DIST_DIR, 'client')
Expand Down
18 changes: 18 additions & 0 deletions src/client/plugins/pwa-update.client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export default async(context) => {
const workbox = await window.$workbox

if (!workbox) {
console.debug('[PWA] -> Workbox couldn\'t be loaded.')
return
}

workbox.addEventListener('installed', (event) => {
if (!event.isUpdate) {
console.debug('[PWA] -> The PWA is on the latest version.')
return
}

console.debug('[PWA] -> There is an update for the PWA, reloading...')
window.location.reload()
})
}
77 changes: 77 additions & 0 deletions src/client/plugins/pwa.client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Check: https://web.dev/customize-install/
export default ({ app }, inject) => {
// Window Listener for installable event
function initInstallableListener(pwa) {
// Window Listener -> Event is triggered when the app can be installed
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault() // Prevent the mini-infobar from appearing on mobile
pwa.deferredPrompt = e // Stash the event so it can be triggered later
pwa.showInstallPromotion() // Update UI notify the user they can install the PWA
console.log('[PWA] -> "beforeinstallprompt" event was fired.')
})
}

// Window Listener for installed event
function initInstalledListener(pwa) {
window.addEventListener('appinstalled', () => {
pwa.installed = true // App is installed
pwa.hideInstallPromotion() // Hide the app-provided install promotion
pwa.deferredPrompt = null // Clear the deferredPrompt so it can be garbage collected
console.log('[PWA] -> App was installed.')
})
}

// Plugin object
const pwa = {
deferredPrompt: null, // Initialize deferredPrompt
installable: false, // Check to show browser install prompt
installed: false, // Check if 'pwa' is installed
async install() {
// Hide the possibility to install app
this.hideInstallPromotion()

// Checks if install prompt is saved from the event listener
if (this.deferredPrompt) {
// Show the install prompt
this.deferredPrompt.prompt()

// Wait for the user to respond to the prompt
const { outcome } = await this.deferredPrompt.userChoice
console.log(`[PWA] -> User response to the install prompt: ${outcome}`)

// We've used the prompt, and can't use it again, throw it away
this.deferredPrompt = null

// Init Listener again
initInstallableListener(this)
return true
} else {
console.error('[PWA] -> Install prompt did not exist.')
throw new Error('App could not be installed.')
}
},
showInstallPromotion() {
this.installable = true
},
hideInstallPromotion() {
this.installable = false
},
getPWADisplayMode() {
// Track how the PWA was launched - https://web.dev/customize-install/#track-how-the-pwa-was-launched
const isStandalone = window.matchMedia('(display-mode: standalone)').matches
if (document.referrer.startsWith('android-app://')) {
return 'twa'
} else if (navigator.standalone || isStandalone) {
return 'standalone'
}
return 'browser'
}
}

// Init Event Listener
initInstallableListener(pwa)
initInstalledListener(pwa)

// Inject $pwa in Vue, context and store
inject('pwa', pwa)
}
Binary file added src/client/static/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1401,6 +1401,21 @@
dependencies:
http-proxy-middleware "^1.0.6"

"@nuxtjs/pwa@^3.3.5":
version "3.3.5"
resolved "https://registry.yarnpkg.com/@nuxtjs/pwa/-/pwa-3.3.5.tgz#db7c905536ebe8a464a347b6ae3215810642c044"
integrity sha512-8tTmW8DBspWxlJwTimOHTkwfkwPpL9wIcGmy75Gcmin+c9YtX2Ehxmhgt/TLFOC9XsLAqojqynw3/Agr/9OE1w==
dependencies:
clone-deep "^4.0.1"
defu "^3.2.2"
execa "^5.0.0"
fs-extra "^9.1.0"
hasha "^5.2.2"
jimp-compact "^0.16.1"
lodash.template "^4.5.0"
serve-static "^1.14.1"
workbox-cdn "^5.1.4"

"@nuxtjs/vuetify@^1.12.1":
version "1.12.1"
resolved "https://registry.yarnpkg.com/@nuxtjs/vuetify/-/vuetify-1.12.1.tgz#87dd1e1517b0120c660f4a43a22627c9fe124189"
Expand Down Expand Up @@ -6369,6 +6384,11 @@ jest-worker@^27.0.2:
merge-stream "^2.0.0"
supports-color "^8.0.0"

jimp-compact@^0.16.1:
version "0.16.1"
resolved "https://registry.yarnpkg.com/jimp-compact/-/jimp-compact-0.16.1.tgz#9582aea06548a2c1e04dd148d7c3ab92075aefa3"
integrity sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==

jiti@^1.9.2:
version "1.9.2"
resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.9.2.tgz#2ee44830883dbb1b2e222adc053c3052d0bf3b61"
Expand Down Expand Up @@ -11118,6 +11138,11 @@ word-wrap@^1.2.3:
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==

workbox-cdn@^5.1.4:
version "5.1.4"
resolved "https://registry.yarnpkg.com/workbox-cdn/-/workbox-cdn-5.1.4.tgz#dbd8acee70b1978be70106207590bbb76af935cf"
integrity sha512-04gM3mi8QGutokkSaA9xunVfjURnLbo9TTWyi8+pSDCEW5cD8u5GbJiliLK1vB9CShk/9OY1UDfW+XcmD+d6KQ==

worker-farm@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8"
Expand Down

0 comments on commit a2658eb

Please sign in to comment.