register.ts 4.3 KB

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