Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add themeConfig.serviceWorker.updatePopup option (fixes #453) #533

Merged
merged 6 commits into from
Jul 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions docs/default-theme-config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,31 @@ Note that it's `off` by default. If given `string`, it will be displayed as a pr
Since `lastUpdated` is based on `git`, so you can only use it in a `git` repository.
:::

## Service Workers

The `themeConfig.serviceWorker` option allows you to configure about service workers.

### Popup UI to refresh contents

The `themeConfig.serviceWorker.updatePopup` option enables the popup to refresh contents. The popup will be shown when the site is updated (the service worker is updated). It provides `refresh` button to allow users to refresh contents immediately.

::: tip NOTE
If without the `refresh` button, the new service worker will be active after all clients are closed.
This means that visitors cannot see new contents until they close all tabs of your site.

But the `refresh` button activates the new service worker immediately.
:::

``` js
module.exports = {
themeConfig: {
serviceWorker {
updatePopup: true | {message: "New content is available.", buttonText: "Refresh"}
}
}
}
```

## Prev / Next Links

Prev and next links are automatically inferred based on the sidebar order of the active page. You can also explicitly overwrite or disable them using `YAML front matter`:
Expand Down
43 changes: 43 additions & 0 deletions lib/app/SWUpdateEvent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
export default class SWUpdateEvent {
constructor (registration) {
Object.defineProperty(this, 'registration', {
value: registration,
configurable: true,
writable: true
})
}

/**
* Check if the new service worker exists or not.
*/
update () {
return this.registration.update()
}

/**
* Activate new service worker to work 'location.reload()' with new data.
*/
skipWaiting () {
const worker = this.registration.waiting
if (!worker) {
return Promise.resolve()
}

console.log('[vuepress:sw] Doing worker.skipWaiting().')
return new Promise((resolve, reject) => {
const channel = new MessageChannel()

channel.port1.onmessage = (event) => {
console.log('[vuepress:sw] Done worker.skipWaiting().')
if (event.data.error) {
reject(event.data.error)
} else {
resolve(event.data)
}
}

worker.postMessage({ type: 'skip-waiting' }, [channel.port2])
})
}
}

9 changes: 5 additions & 4 deletions lib/app/clientEntry.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* global BASE_URL, GA_ID, ga, SW_ENABLED, VUEPRESS_VERSION, LAST_COMMIT_HASH*/

import { createApp } from './app'
import SWUpdateEvent from './SWUpdateEvent'
import { register } from 'register-service-worker'

const { app, router } = createApp()
Expand Down Expand Up @@ -46,13 +47,13 @@ router.onReady(() => {
console.log('[vuepress:sw] Service worker is active.')
app.$refs.layout.$emit('sw-ready')
},
cached () {
cached (registration) {
console.log('[vuepress:sw] Content has been cached for offline use.')
app.$refs.layout.$emit('sw-cached')
app.$refs.layout.$emit('sw-cached', new SWUpdateEvent(registration))
},
updated () {
updated (registration) {
console.log('[vuepress:sw] Content updated.')
app.$refs.layout.$emit('sw-updated')
app.$refs.layout.$emit('sw-updated', new SWUpdateEvent(registration))
},
offline () {
console.log('[vuepress:sw] No internet connection found. App is running in offline mode.')
Expand Down
7 changes: 6 additions & 1 deletion lib/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,16 @@ module.exports = async function build (sourceDir, cliOptions = {}) {
if (options.siteConfig.serviceWorker) {
logger.wait('\nGenerating service worker...')
const wbb = require('workbox-build')
wbb.generateSW({
await wbb.generateSW({
swDest: path.resolve(outDir, 'service-worker.js'),
globDirectory: outDir,
globPatterns: ['**\/*.{js,css,html,png,jpg,jpeg,gif,svg,woff,woff2,eot,ttf,otf}']
})
await fs.writeFile(
path.resolve(outDir, 'service-worker.js'),
await fs.readFile(path.resolve(__dirname, 'service-worker/skip-waiting.js'), 'utf8'),
{ flag: 'a' }
)
}

// DONE.
Expand Down
12 changes: 10 additions & 2 deletions lib/default-theme/Layout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<slot name="page-top" slot="top"/>
<slot name="page-bottom" slot="bottom"/>
</Page>
<SWUpdatePopup :updateEvent="swUpdateEvent" />
</div>
</template>

Expand All @@ -27,13 +28,15 @@ import Home from './Home.vue'
import Navbar from './Navbar.vue'
import Page from './Page.vue'
import Sidebar from './Sidebar.vue'
import SWUpdatePopup from './SWUpdatePopup.vue'
import { resolveSidebarItems } from './util'

export default {
components: { Home, Page, Sidebar, Navbar },
components: { Home, Page, Sidebar, Navbar, SWUpdatePopup },
data () {
return {
isSidebarOpen: false
isSidebarOpen: false,
swUpdateEvent: null
}
},

Expand Down Expand Up @@ -101,6 +104,8 @@ export default {
nprogress.done()
this.isSidebarOpen = false
})

this.$on('sw-updated', this.onSWUpdated)
},

methods: {
Expand All @@ -124,6 +129,9 @@ export default {
this.toggleSidebar(false)
}
}
},
onSWUpdated (e) {
this.swUpdateEvent = e
}
}
}
Expand Down
82 changes: 82 additions & 0 deletions lib/default-theme/SWUpdatePopup.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<template>
<transition name="sw-update-popup">
<div v-if="enabled" class="sw-update-popup">
{{message}}<br>
<button @click="reload">{{buttonText}}</button>
</div>
</transition>
</template>

<script>
export default {
props: {
updateEvent: {
type: Object,
default: null
}
},

computed: {
popupConfig () {
for (const config of [this.$themeLocaleConfig, this.$site.themeConfig]) {
const sw = config.serviceWorker
if (sw && sw.updatePopup) {
return typeof sw.updatePopup === 'object' ? sw.updatePopup : {}
}
}
return null
},

enabled () {
return Boolean(this.popupConfig && this.updateEvent)
},

message () {
const c = this.popupConfig
return (c && c.message) || 'New content is available.'
},

buttonText () {
const c = this.popupConfig
return (c && c.buttonText) || 'Refresh'
}
},

methods: {
reload () {
if (this.updateEvent) {
this.updateEvent.skipWaiting().then(() => {
location.reload(true)
})
this.updateEvent = null
}
}
}
}
</script>

<style lang="stylus">
@import './styles/config.styl'

.sw-update-popup
position fixed
right 1em
bottom 1em
padding 1em
border 1px solid $accentColor
border-radius 3px
background #fff
box-shadow 0 4px 16px rgba(0, 0, 0, 0.5)
text-align center

button
margin-top 0.5em
padding 0.25em 2em

.sw-update-popup-enter-active, .sw-update-popup-leave-active
transition opacity 0.3s, transform 0.3s

.sw-update-popup-enter, .sw-update-popup-leave-to
opacity 0
transform translate(0, 50%) scale(0.5)
</style>
12 changes: 12 additions & 0 deletions lib/service-worker/skip-waiting.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
addEventListener('message', event => {
const replyPort = event.ports[0]
const message = event.data
if (replyPort && message && message.type === 'skip-waiting') {
event.waitUntil(
self.skipWaiting().then(
() => replyPort.postMessage({ error: null }),
error => replyPort.postMessage({ error })
)
)
}
})
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
"portfinder": "^1.0.13",
"postcss-loader": "^2.1.5",
"prismjs": "^1.13.0",
"register-service-worker": "^1.2.0",
"register-service-worker": "^1.3.0",
"semver": "^5.5.0",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.2",
Expand Down
6 changes: 3 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6941,9 +6941,9 @@ regexpu-core@^4.1.3, regexpu-core@^4.1.4:
unicode-match-property-ecmascript "^1.0.4"
unicode-match-property-value-ecmascript "^1.0.2"

register-service-worker@^1.2.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/register-service-worker/-/register-service-worker-1.4.1.tgz#4b4c9b4200fc697942c6ae7d611349587b992b2f"
register-service-worker@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/register-service-worker/-/register-service-worker-1.3.0.tgz#02a0b7c40413b3c5ed1d801d764deb3aab1c3397"

registry-auth-token@^3.0.1:
version "3.3.2"
Expand Down