From 6179adac226d7ba9284b3345d5e1221bec8bc0b5 Mon Sep 17 00:00:00 2001 From: Tanmay Deep Sharma Date: Sun, 3 May 2026 16:49:00 +0700 Subject: [PATCH] fix(voice): keep ringing WhatsApp calls alive across page refresh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inverted the previous decision: only the active (accepted) call gets terminated on pagehide, since its WebRTC session dies with the page and genuinely can't be rejoined. Ringing calls have no WebRTC state yet — they're just a row on the backend and a state on Meta — so killing them on refresh would needlessly hang up on the customer when the agent intended to recover their UI, not reject the call. After this, the flow on refresh during a ringing inbound is: - beforeunload prompt fires (warning the agent) - pagehide does nothing for ringing calls; active calls still terminate - new page loads, conversation messages fetch - seedCallsFromHydratedMessages picks up status='ringing' voice_call messages and populates the calls store - FloatingCallWidget reappears with Accept; agent clicks; acceptIncomingCall fetches the SDP via /whatsapp_calls/:id and the call connects --- .../dashboard/composables/useCallSession.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/app/javascript/dashboard/composables/useCallSession.js b/app/javascript/dashboard/composables/useCallSession.js index b2a4b4e6d..97191bdde 100644 --- a/app/javascript/dashboard/composables/useCallSession.js +++ b/app/javascript/dashboard/composables/useCallSession.js @@ -6,7 +6,6 @@ import { useCallsStore } from 'dashboard/stores/calls'; import { useWhatsappCallSession, sendWhatsappTerminateBeacon, - sendWhatsappCallBeacon, cleanupWhatsappSession, } from 'dashboard/composables/useWhatsappCallSession'; import { handleVoiceCallCreated } from 'dashboard/helper/voice'; @@ -70,15 +69,13 @@ export function useCallSession() { }); }; - // pagehide fires after the user confirms the prompt. Beacon a terminate for - // every WhatsApp call in the store — active OR ringing — since browser-direct - // WebRTC has no rejoin path, so any call that survives the close becomes - // permanently orphaned on Meta's side. + // pagehide fires after the user confirms the refresh prompt. Terminate the + // active call only — its WebRTC session dies with the page and can't be + // rejoined. Ringing calls intentionally stay alive on Meta so the agent can + // pick them up after the page reloads (FloatingCallWidget rehydrates them + // via seedCallsFromHydratedMessages once the conversation messages land). const handlePageHide = () => { sendWhatsappTerminateBeacon(); - callsStore.calls - .filter(c => c.provider === 'whatsapp' && !c.isActive && c.callId) - .forEach(c => sendWhatsappCallBeacon(c.callId)); }; const handleTwilioDisconnected = () => callsStore.clearActiveCall();