mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-04-11 10:23:27 +00:00
fix: SIP disconnect on callState change + dispose sends agentId
- Fixed useEffect dependency bug: callState in deps caused cleanup (disconnectSip) to fire on every state transition, killing SIP mid-dial. Now uses useRef for callState in beforeunload handler with empty deps array — cleanup only fires on unmount. - sendBeacon auto-dispose now includes agentId from agent config - Disposition modal submit now includes agentId from agent config Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -90,9 +90,11 @@ export const ActiveCallCard = ({ lead, callerPhone, missedCallId, onCallComplete
|
||||
|
||||
// Submit disposition to sidecar
|
||||
if (callUcid) {
|
||||
const agentCfg = JSON.parse(localStorage.getItem('helix_agent_config') ?? '{}');
|
||||
const disposePayload = {
|
||||
ucid: callUcid,
|
||||
disposition,
|
||||
agentId: agentCfg.ozonetelAgentId,
|
||||
callerPhone,
|
||||
direction: callDirectionRef.current,
|
||||
durationSec: callDuration,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useCallback, type PropsWithChildren } from 'react';
|
||||
import { useEffect, useCallback, useRef, type PropsWithChildren } from 'react';
|
||||
import { useAtom, useSetAtom } from 'jotai';
|
||||
import {
|
||||
sipConnectionStatusAtom,
|
||||
@@ -93,22 +93,31 @@ export const SipProvider = ({ children }: PropsWithChildren) => {
|
||||
// Layer 2: Fire sendBeacon to auto-dispose if user confirms leave
|
||||
// These two layers protect against the "agent refreshes mid-call → stuck in ACW" bug.
|
||||
// Layer 3 (server-side ACW timeout) lives in supervisor.service.ts.
|
||||
//
|
||||
// IMPORTANT: beforeunload reads callState via a ref (not the dep array)
|
||||
// because adding callState to deps causes the cleanup to fire on every
|
||||
// state transition → disconnectSip() → kills the call mid-flight.
|
||||
const callStateRef = useRef(callState);
|
||||
callStateRef.current = callState;
|
||||
|
||||
useEffect(() => {
|
||||
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
|
||||
const ucid = localStorage.getItem('helix_active_ucid');
|
||||
const state = callStateRef.current;
|
||||
|
||||
// Layer 1: Show browser "Leave page?" dialog during active calls
|
||||
if (callState === 'active' || callState === 'ringing-in' || callState === 'ringing-out') {
|
||||
if (state === 'active' || state === 'ringing-in' || state === 'ringing-out') {
|
||||
e.preventDefault();
|
||||
e.returnValue = '';
|
||||
}
|
||||
|
||||
// Layer 2: Fire disposition beacon if there's an active UCID
|
||||
// sendBeacon is guaranteed to fire even during page unload
|
||||
if (ucid) {
|
||||
const agentCfg = JSON.parse(localStorage.getItem('helix_agent_config') ?? '{}');
|
||||
const payload = JSON.stringify({
|
||||
ucid,
|
||||
disposition: 'CALLBACK_REQUESTED',
|
||||
agentId: agentCfg.ozonetelAgentId,
|
||||
autoDisposed: true,
|
||||
});
|
||||
navigator.sendBeacon('/api/ozonetel/dispose', new Blob([payload], { type: 'application/json' }));
|
||||
@@ -125,7 +134,7 @@ export const SipProvider = ({ children }: PropsWithChildren) => {
|
||||
window.removeEventListener('unload', handleUnload);
|
||||
disconnectSip();
|
||||
};
|
||||
}, [callState]);
|
||||
}, []); // empty deps — runs once on mount, cleanup only on unmount
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user