connection.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. import { computed, ref, watch } from 'vue'
  3. import {
  4. NotificationTypes,
  5. useNotifications,
  6. } from '#shared/components/CommonNotifications/index.ts'
  7. import { useApplicationLoaded } from '#shared/composables/useApplicationLoaded.ts'
  8. import {
  9. consumer,
  10. reopenWebSocketConnection,
  11. } from '#shared/server/action_cable/consumer.ts'
  12. import log from '#shared/utils/log.ts'
  13. const wsConnectionState = ref(true)
  14. const wsReopening = ref(false)
  15. const { loaded } = useApplicationLoaded()
  16. const isConnectionOpen = () => !loaded.value || consumer.connection.isOpen()
  17. const INTERVAL_CHECK_CONNECTION = 1000
  18. const TIMEOUT_CONFIRM_FAIL = 2000
  19. let faledTimeout: number | null = null
  20. let checkInterval: number | null = null
  21. const clearFailedTimeout = () => {
  22. if (faledTimeout) window.clearTimeout(faledTimeout)
  23. faledTimeout = null
  24. }
  25. const clearCheckInterval = () => {
  26. if (checkInterval) window.clearInterval(checkInterval)
  27. checkInterval = null
  28. }
  29. const checkStatus = () => {
  30. clearCheckInterval()
  31. checkInterval = window.setInterval(() => {
  32. const hasConnection = isConnectionOpen()
  33. if (hasConnection) {
  34. wsConnectionState.value = true
  35. return
  36. }
  37. // if there is no connection, let's wait a few seconds and check again
  38. // pause interval while we wait
  39. clearCheckInterval()
  40. clearFailedTimeout()
  41. faledTimeout = window.setTimeout(() => {
  42. wsConnectionState.value = isConnectionOpen()
  43. checkStatus()
  44. }, TIMEOUT_CONFIRM_FAIL)
  45. }, INTERVAL_CHECK_CONNECTION)
  46. }
  47. checkStatus()
  48. let connectionNotificationId: string
  49. const networkConnectionState = ref(true)
  50. const connected = computed(() => {
  51. return (
  52. (wsReopening.value || wsConnectionState.value) &&
  53. networkConnectionState.value
  54. )
  55. })
  56. const notifications = useNotifications()
  57. watch(
  58. () => connected.value,
  59. (connected) => {
  60. if (connected) {
  61. if (!connectionNotificationId) return
  62. log.debug('Application connection just came up.')
  63. notifications.removeNotification(connectionNotificationId)
  64. } else {
  65. log.debug('Application connection just went down.')
  66. connectionNotificationId = notifications.notify({
  67. id: 'connection-lost',
  68. message: __('The connection to the server was lost.'),
  69. type: NotificationTypes.Error,
  70. persistent: true,
  71. })
  72. }
  73. },
  74. )
  75. export const recordCommunicationSuccess = (): void => {
  76. networkConnectionState.value = true
  77. }
  78. export const recordCommunicationFailure = (): void => {
  79. networkConnectionState.value = false
  80. }
  81. export const triggerWebSocketReconnect = (): void => {
  82. wsReopening.value = true
  83. reopenWebSocketConnection()
  84. .then(() => {
  85. // Set this before setting wsReopening, otherwise it would be set later by the interval,
  86. // causing false positives.
  87. wsConnectionState.value = true
  88. })
  89. .finally(() => {
  90. wsReopening.value = false
  91. })
  92. }