register.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. // Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
  2. import type { RegisterSWOptions } from './types'
  3. // should service worker be updated automatically without a prompt
  4. const auto = false
  5. // should servicer worker be destroyed - should be used only if something went wrong
  6. const autoDestroy = false
  7. // eslint-disable-next-line sonarjs/cognitive-complexity
  8. export const registerSW = (options: RegisterSWOptions) => {
  9. const {
  10. path,
  11. scope,
  12. immediate = false,
  13. onNeedRefresh,
  14. onOfflineReady,
  15. onRegistered,
  16. onRegisteredSW,
  17. onRegisterError,
  18. } = options
  19. // service worker is disabled during normal development
  20. if (import.meta.env.DEV) {
  21. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  22. const sw = window.sw!
  23. // to trigger "Need refresh" run in console: `sw.triggerUpdate()`
  24. sw.ontriggerupdate = () => {
  25. onNeedRefresh?.()
  26. }
  27. // you can disable service worker in development mode by running in console: pwa.enable()
  28. // you can enable service worker, it will point to /public/vite-dev/sw.js
  29. // don't forget to unregister service worker, when you are done in console: pwa.disable()
  30. if (!sw.isEnabled()) {
  31. return () => {
  32. console.log('Updating service worker...')
  33. window.location.reload()
  34. }
  35. }
  36. }
  37. let wb: import('workbox-window').Workbox | undefined
  38. let registration: ServiceWorkerRegistration | undefined
  39. let registerPromise: Promise<void>
  40. let sendSkipWaitingMessage: () => Promise<void> | undefined
  41. const updateServiceWorker = async (reloadPage = true) => {
  42. await registerPromise
  43. if (!auto) {
  44. // Assuming the user accepted the update, set up a listener
  45. // that will reload the page as soon as the previously waiting
  46. // service worker has taken control.
  47. if (reloadPage) {
  48. wb?.addEventListener('controlling', (event) => {
  49. if (event.isUpdate) window.location.reload()
  50. })
  51. setTimeout(() => window.location.reload(), 1000)
  52. }
  53. await sendSkipWaitingMessage?.()
  54. }
  55. }
  56. const register = async () => {
  57. if (!('serviceWorker' in navigator)) {
  58. return
  59. }
  60. const { Workbox, messageSW } = await import('workbox-window')
  61. sendSkipWaitingMessage = async () => {
  62. if (registration && registration.waiting) {
  63. // Send a message to the waiting service worker,
  64. // instructing it to activate.
  65. // Note: for this to work, you have to add a message
  66. // listener in your service worker. See below.
  67. await messageSW(registration.waiting, { type: 'SKIP_WAITING' })
  68. }
  69. }
  70. wb = new Workbox(path, { scope, type: 'classic' })
  71. wb.addEventListener('activated', (event) => {
  72. // this will only controls the offline request.
  73. // event.isUpdate will be true if another version of the service
  74. // worker was controlling the page when this version was registered.
  75. if (event.isUpdate) {
  76. if (auto) window.location.reload()
  77. } else if (!autoDestroy) {
  78. onOfflineReady?.()
  79. }
  80. })
  81. if (!auto) {
  82. const showSkipWaitingPrompt = () => {
  83. // \`event.wasWaitingBeforeRegister\` will be false if this is
  84. // the first time the updated service worker is waiting.
  85. // When \`event.wasWaitingBeforeRegister\` is true, a previously
  86. // updated service worker is still waiting.
  87. // You may want to customize the UI prompt accordingly.
  88. // Assumes your app has some sort of prompt UI element
  89. // that a user can either accept or reject.
  90. onNeedRefresh?.()
  91. }
  92. // Add an event listener to detect when the registered
  93. // service worker has installed but is waiting to activate.
  94. wb.addEventListener('waiting', showSkipWaitingPrompt)
  95. // @ts-expect-error event listener provided by workbox-window
  96. wb.addEventListener('externalwaiting', showSkipWaitingPrompt)
  97. }
  98. // register the service worker
  99. wb.register({ immediate })
  100. .then((r) => {
  101. registration = r
  102. if (onRegisteredSW) onRegisteredSW(path, r)
  103. else onRegistered?.(r)
  104. })
  105. .catch((e) => {
  106. onRegisterError?.(e)
  107. })
  108. }
  109. registerPromise = register()
  110. return updateServiceWorker
  111. }