Skip to content

Commit

Permalink
fix(client): multiple clients with prompt for update (#394)
Browse files Browse the repository at this point in the history
  • Loading branch information
userquin committed Dec 12, 2022
1 parent d8d2d36 commit ea1a58b
Show file tree
Hide file tree
Showing 17 changed files with 56 additions and 63 deletions.
31 changes: 31 additions & 0 deletions client.d.ts
Expand Up @@ -20,6 +20,12 @@ declare module 'virtual:pwa-register' {
onRegisterError?: (error: any) => void
}

/**
* Registers the service worker returning a callback to reload the current page when an update is found.
*
* @param options the options to register the service worker.
* @return (reloadPage?: boolean) => Promise<void> From version 0.13.2+ `reloadPage` param is not used anymore.
*/
export function registerSW(options?: RegisterSWOptions): (reloadPage?: boolean) => Promise<void>
}

Expand Down Expand Up @@ -52,6 +58,11 @@ declare module 'virtual:pwa-register/vue' {
export function useRegisterSW(options?: RegisterSWOptions): {
needRefresh: Ref<boolean>
offlineReady: Ref<boolean>
/**
* Reloads the current window to allow the service worker take the control.
*
* @param reloadPage From version 0.13.2+ this param is not used anymore.
*/
updateServiceWorker: (reloadPage?: boolean) => Promise<void>
}
}
Expand Down Expand Up @@ -85,6 +96,11 @@ declare module 'virtual:pwa-register/svelte' {
export function useRegisterSW(options?: RegisterSWOptions): {
needRefresh: Writable<boolean>
offlineReady: Writable<boolean>
/**
* Reloads the current window to allow the service worker take the control.
*
* @param reloadPage From version 0.13.2+ this param is not used anymore.
*/
updateServiceWorker: (reloadPage?: boolean) => Promise<void>
}
}
Expand Down Expand Up @@ -118,6 +134,11 @@ declare module 'virtual:pwa-register/react' {
export function useRegisterSW(options?: RegisterSWOptions): {
needRefresh: [boolean, Dispatch<SetStateAction<boolean>>]
offlineReady: [boolean, Dispatch<SetStateAction<boolean>>]
/**
* Reloads the current window to allow the service worker take the control.
*
* @param reloadPage From version 0.13.2+ this param is not used anymore.
*/
updateServiceWorker: (reloadPage?: boolean) => Promise<void>
}
}
Expand Down Expand Up @@ -151,6 +172,11 @@ declare module 'virtual:pwa-register/solid' {
export function useRegisterSW(options?: RegisterSWOptions): {
needRefresh: [Accessor<boolean>, Setter<boolean>]
offlineReady: [Accessor<boolean>, Setter<boolean>]
/**
* Reloads the current window to allow the service worker take the control.
*
* @param reloadPage From version 0.13.2+ this param is not used anymore.
*/
updateServiceWorker: (reloadPage?: boolean) => Promise<void>
}
}
Expand Down Expand Up @@ -184,6 +210,11 @@ declare module 'virtual:pwa-register/preact' {
export function useRegisterSW(options?: RegisterSWOptions): {
needRefresh: [boolean, StateUpdater<boolean>]
offlineReady: [boolean, StateUpdater<boolean>]
/**
* Reloads the current window to allow the service worker take the control.
*
* @param reloadPage From version 0.13.2+ this param is not used anymore.
*/
updateServiceWorker: (reloadPage?: boolean) => Promise<void>
}
}
1 change: 0 additions & 1 deletion docs/.vitepress/components.d.ts
Expand Up @@ -13,7 +13,6 @@ declare module '@vue/runtime-core' {
ExamplesInjectManifest: typeof import('./theme/components/ExamplesInjectManifest.md')['default']
GenerateSWCleanupOutdatedCaches: typeof import('./theme/components/GenerateSWCleanupOutdatedCaches.md')['default']
GenerateSWSourceMap: typeof import('./theme/components/GenerateSWSourceMap.md')['default']
HeuristicWorkboxWindow: typeof import('./theme/components/HeuristicWorkboxWindow.md')['default']
HomePage: typeof import('./theme/components/HomePage.vue')['default']
InjectManifestCleanupOutdatedCaches: typeof import('./theme/components/InjectManifestCleanupOutdatedCaches.md')['default']
InjectManifestSourceMap: typeof import('./theme/components/InjectManifestSourceMap.md')['default']
Expand Down
5 changes: 0 additions & 5 deletions docs/.vitepress/theme/components/HeuristicWorkboxWindow.md

This file was deleted.

3 changes: 0 additions & 3 deletions docs/examples/preact.md
Expand Up @@ -31,9 +31,6 @@ Done. Now run:

To test `new content available`, you should rerun the corresponding script, and then refresh the page.

If you are running an example with `Periodic SW updates`, you will need to wait 1 minute:
<HeuristicWorkboxWindow />

## Executing the examples

<RunExamples />
Expand Down
3 changes: 0 additions & 3 deletions docs/examples/react.md
Expand Up @@ -31,9 +31,6 @@ Done. Now run:

To test `new content available`, you should rerun the corresponding script, and then refresh the page.

If you are running an example with `Periodic SW updates`, you will need to wait 1 minute:
<HeuristicWorkboxWindow />

## Executing the examples

<RunExamples />
Expand Down
3 changes: 0 additions & 3 deletions docs/examples/solidjs.md
Expand Up @@ -20,9 +20,6 @@ npx degit solidjs/templates/ts-router solid-router

To test `new content available`, you should rerun the corresponding script, and then refresh the page.

If you are running an example with `Periodic SW updates`, you will need to wait 1 minute:
<HeuristicWorkboxWindow />

## Executing the examples

<RunExamples />
Expand Down
3 changes: 0 additions & 3 deletions docs/examples/svelte.md
Expand Up @@ -30,9 +30,6 @@ Done. Now run:

To test `new content available`, you should rerun the corresponding script, and then refresh the page.

If you are running an example with `Periodic SW updates`, you will need to wait 1 minute:
<HeuristicWorkboxWindow />

## Executing the examples

<RunExamples />
Expand Down
4 changes: 0 additions & 4 deletions docs/examples/sveltekit.md
Expand Up @@ -47,10 +47,6 @@ Next steps:
To test `new content available`, you should rerun the corresponding script, and then refresh the page.
If you are running an example with `Periodic SW updates`, you will need to wait 1 minute:
<HeuristicWorkboxWindow />
## Executing the examples
<RunExamples />
Expand Down
3 changes: 0 additions & 3 deletions docs/examples/vue.md
Expand Up @@ -31,9 +31,6 @@ Done. Now run:

To test `new content available`, you should rerun the corresponding script, and then refresh the page.

If you are running an example with `Periodic SW updates`, you will need to wait 1 minute:
<HeuristicWorkboxWindow />

## Executing the examples

<RunExamples />
Expand Down
2 changes: 0 additions & 2 deletions docs/frameworks/preact.md
Expand Up @@ -157,5 +157,3 @@ const updateServiceWorker = useRegisterSW({
```

The interval must be in milliseconds, in the example above it is configured to check the service worker every hour.

<HeuristicWorkboxWindow />
2 changes: 0 additions & 2 deletions docs/frameworks/react.md
Expand Up @@ -158,5 +158,3 @@ const updateServiceWorker = useRegisterSW({
```

The interval must be in milliseconds, in the example above it is configured to check the service worker every hour.

<HeuristicWorkboxWindow />
2 changes: 0 additions & 2 deletions docs/frameworks/solidjs.md
Expand Up @@ -162,5 +162,3 @@ const updateServiceWorker = useRegisterSW({
```

The interval must be in milliseconds, in the example above it is configured to check the service worker every hour.

<HeuristicWorkboxWindow />
3 changes: 0 additions & 3 deletions docs/frameworks/svelte.md
Expand Up @@ -150,6 +150,3 @@ const updateServiceWorker = useRegisterSW({
```

The interval must be in milliseconds, in the example above it is configured to check the service worker every hour.

<HeuristicWorkboxWindow />

4 changes: 0 additions & 4 deletions docs/frameworks/vue.md
Expand Up @@ -140,8 +140,6 @@ const updateServiceWorker = useRegisterSW({

The interval must be in milliseconds, in the example above it is configured to check the service worker every hour.

<HeuristicWorkboxWindow />

## Vue 2

Since this plugin only supports `Vue 3`, you cannot use the virtual module `virtual:pwa-register/vue`.
Expand Down Expand Up @@ -297,5 +295,3 @@ export default {
```

The interval must be in milliseconds, in the example above it is configured to check the service worker every hour.

<HeuristicWorkboxWindow />
4 changes: 0 additions & 4 deletions docs/guide/development.md
Expand Up @@ -191,10 +191,6 @@ if ('serviceWorker' in navigator) {
}
```
When you change your service worker source code, `Vite` will force a full reload, since we're using `workbox-window` to register it (by default, you can register it manually) you may have some problems with the service worker events.
<HeuristicWorkboxWindow />
## Example
You can find an example here: [vue-router](https://github.com/antfu/vite-plugin-pwa/tree/main/examples/vue-router).
Expand Down
9 changes: 5 additions & 4 deletions docs/guide/periodic-sw-updates.md
Expand Up @@ -56,8 +56,11 @@ const updateSW = registerSW({
return

const resp = await fetch(swUrl, {
'cache': 'no-store',
'cache-control': 'no-cache'
cache: 'no-store',
headers: {
'cache': 'no-store',
'cache-control': 'no-cache',
},
})

if (resp?.status === 200)
Expand All @@ -67,5 +70,3 @@ const updateSW = registerSW({
})
```
:::

<HeuristicWorkboxWindow />
37 changes: 20 additions & 17 deletions src/client/build/register.ts
Expand Up @@ -29,43 +29,38 @@ export function registerSW(options: RegisterSWOptions = {}) {
let registerPromise: Promise<void>
let sendSkipWaitingMessage: () => Promise<void> | undefined

const updateServiceWorker = async (reloadPage = true) => {
const updateServiceWorker = async (_reloadPage = true) => {
await registerPromise
if (!auto) {
// Assuming the user accepted the update, set up a listener
// that will reload the page as soon as the previously waiting
// service worker has taken control.
if (reloadPage) {
wb?.addEventListener('controlling', (event) => {
if (event.isUpdate)
window.location.reload()
})
}

await sendSkipWaitingMessage?.()
}
}

async function register() {
if ('serviceWorker' in navigator) {
const { Workbox, messageSW } = await import('workbox-window')
const { Workbox } = await import('workbox-window')
// __SW__, __SCOPE__ and __TYPE__ will be replaced by virtual module
wb = new Workbox('__SW__', { scope: '__SCOPE__', type: '__TYPE__' })
sendSkipWaitingMessage = async () => {
if (registration && registration.waiting) {
// Send a message to the waiting service worker,
// instructing it to activate.
// Note: for this to work, you have to add a message
// listener in your service worker. See below.
await messageSW(registration.waiting, { type: 'SKIP_WAITING' })
await wb?.messageSkipWaiting()
}
}
// __SW__, __SCOPE__ and __TYPE__ will be replaced by virtual module
wb = new Workbox('__SW__', { scope: '__SCOPE__', type: '__TYPE__' })

wb.addEventListener('activated', (event) => {
// this will only controls the offline request.
// This will only controls the offline request.
// event.isUpdate will be true if another version of the service
// worker was controlling the page when this version was registered.
if (event.isUpdate)
// When using multiple clients, if the client that fires the update is not the current one,
// workbox-window will fire this event with isUpdate=undefined and isExternal=true
// we only need to check this case and force reloading the page, otherwise use current logic
if (!event.isUpdate && event.isExternal)
window.location.reload()
else if (event.isUpdate)
auto && window.location.reload()
else if (!autoDestroy)
onOfflineReady?.()
Expand All @@ -81,6 +76,14 @@ export function registerSW(options: RegisterSWOptions = {}) {

// Assumes your app has some sort of prompt UI element
// that a user can either accept or reject.
// Assuming the user accepted the update, set up a listener
// that will reload the page as soon as the previously waiting
// service worker has taken control.
wb?.addEventListener('controlling', (event) => {
if (event.isUpdate)
window.location.reload()
})

onNeedRefresh?.()
}

Expand Down

0 comments on commit ea1a58b

Please sign in to comment.