Browse Source

Feature: Mobile - Improve web socket connection check for expected reconnects.

Martin Gruner 1 year ago
parent
commit
11f2d19a81

+ 2 - 2
app/frontend/shared/initializer/storeSubscriptions.ts

@@ -1,7 +1,7 @@
 // Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
 
 import { watch } from 'vue'
-import consumer from '#shared/server/action_cable/consumer.ts'
+import { triggerWebSocketReconnect } from '#shared/server/connection.ts'
 import { useLocaleStore } from '#shared/stores/locale.ts'
 import { useSessionStore } from '#shared/stores/session.ts'
 import { useApplicationStore } from '#shared/stores/application.ts'
@@ -18,7 +18,7 @@ export default function initializeStoreSubscriptions(): void {
         () => session.id,
         () => {
           // Reopen WS connection to reflect authentication state.
-          consumer.connection.reopen()
+          triggerWebSocketReconnect()
         },
       )
 

+ 22 - 1
app/frontend/shared/server/action_cable/consumer.ts

@@ -6,4 +6,25 @@ import log from '#shared/utils/log.ts'
 ActionCable.adapters.logger = log as unknown as Console
 ActionCable.logger.enabled = true
 
-export default ActionCable.createConsumer()
+export const consumer = ActionCable.createConsumer()
+
+export const reopenWebSocketConnection = () => {
+  consumer.connection.reopen()
+  return new Promise<void>((resolve, reject) => {
+    const startTime = Date.now()
+
+    const checkConnection = () => {
+      if (consumer.connection.isOpen()) {
+        resolve()
+      }
+      // to avoid infinite loop
+      else if (Date.now() - startTime > 10_000) {
+        reject(new Error('failed to reconnect'))
+      } else {
+        setTimeout(checkConnection, 100)
+      }
+    }
+
+    checkConnection()
+  })
+}

+ 1 - 1
app/frontend/shared/server/apollo/link.ts

@@ -3,7 +3,7 @@
 import type { Operation } from '@apollo/client/core'
 import { ApolloLink, createHttpLink, from } from '@apollo/client/core'
 import type { FragmentDefinitionNode, OperationDefinitionNode } from 'graphql'
-import consumer from '#shared/server/action_cable/consumer.ts'
+import { consumer } from '#shared/server/action_cable/consumer.ts'
 import { BatchHttpLink } from '@apollo/client/link/batch-http'
 import { getMainDefinition } from '@apollo/client/utilities'
 import ActionCableLink from 'graphql-ruby-client/subscriptions/ActionCableLink'

+ 22 - 2
app/frontend/shared/server/connection.ts

@@ -1,7 +1,10 @@
 // Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
 
 import { computed, ref, watch } from 'vue'
-import consumer from '#shared/server/action_cable/consumer.ts'
+import {
+  consumer,
+  reopenWebSocketConnection,
+} from '#shared/server/action_cable/consumer.ts'
 import log from '#shared/utils/log.ts'
 import {
   NotificationTypes,
@@ -10,6 +13,7 @@ import {
 import { useApplicationLoaded } from '#shared/composables/useApplicationLoaded.ts'
 
 const wsConnectionState = ref(true)
+const wsReopening = ref(false)
 const { loaded } = useApplicationLoaded()
 
 window.setInterval(() => {
@@ -19,7 +23,10 @@ window.setInterval(() => {
 let connectionNotificationId: string
 const networkConnectionState = ref(true)
 const connected = computed(() => {
-  return wsConnectionState.value && networkConnectionState.value
+  return (
+    (wsReopening.value || wsConnectionState.value) &&
+    networkConnectionState.value
+  )
 })
 
 const notifications = useNotifications()
@@ -49,3 +56,16 @@ export const recordCommunicationSuccess = (): void => {
 export const recordCommunicationFailure = (): void => {
   networkConnectionState.value = false
 }
+
+export const triggerWebSocketReconnect = (): void => {
+  wsReopening.value = true
+  reopenWebSocketConnection()
+    .then(() => {
+      // Set this before setting wsReopening, otherwise it would be set later by the interval,
+      //  causing false positives.
+      wsConnectionState.value = true
+    })
+    .finally(() => {
+      wsReopening.value = false
+    })
+}