mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-04-11 18:28:15 +00:00
fix: session reset after call end, add reject/decline for incoming calls, extract caller ID from SIP headers (P-Asserted-Identity, Remote-Party-ID)
This commit is contained in:
@@ -92,6 +92,7 @@ export const CallWidget = () => {
|
|||||||
isOnHold,
|
isOnHold,
|
||||||
callDuration,
|
callDuration,
|
||||||
answer,
|
answer,
|
||||||
|
reject,
|
||||||
hangup,
|
hangup,
|
||||||
toggleMute,
|
toggleMute,
|
||||||
toggleHold,
|
toggleHold,
|
||||||
@@ -171,7 +172,7 @@ export const CallWidget = () => {
|
|||||||
<Button size="md" color="primary" iconLeading={Phone01} onClick={answer}>
|
<Button size="md" color="primary" iconLeading={Phone01} onClick={answer}>
|
||||||
Answer
|
Answer
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="md" color="primary-destructive" iconLeading={PhoneX} onClick={hangup}>
|
<Button size="md" color="primary-destructive" iconLeading={PhoneX} onClick={reject}>
|
||||||
Decline
|
Decline
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -110,6 +110,10 @@ export const useSipPhone = (config?: Partial<SIPConfig>) => {
|
|||||||
sipClientRef.current?.answer();
|
sipClientRef.current?.answer();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const reject = useCallback(() => {
|
||||||
|
sipClientRef.current?.reject();
|
||||||
|
}, []);
|
||||||
|
|
||||||
const hangup = useCallback(() => {
|
const hangup = useCallback(() => {
|
||||||
sipClientRef.current?.hangup();
|
sipClientRef.current?.hangup();
|
||||||
}, []);
|
}, []);
|
||||||
@@ -159,6 +163,7 @@ export const useSipPhone = (config?: Partial<SIPConfig>) => {
|
|||||||
disconnect,
|
disconnect,
|
||||||
makeCall,
|
makeCall,
|
||||||
answer,
|
answer,
|
||||||
|
reject,
|
||||||
hangup,
|
hangup,
|
||||||
toggleMute,
|
toggleMute,
|
||||||
toggleHold,
|
toggleHold,
|
||||||
|
|||||||
@@ -15,13 +15,9 @@ export class SIPClient {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
connect(): void {
|
connect(): void {
|
||||||
// Enable JsSIP debug logging to diagnose connection issues
|
|
||||||
JsSIP.debug.enable('JsSIP:*');
|
JsSIP.debug.enable('JsSIP:*');
|
||||||
|
|
||||||
const socket = new JsSIP.WebSocketInterface(this.config.wsServer);
|
const socket = new JsSIP.WebSocketInterface(this.config.wsServer);
|
||||||
|
|
||||||
// Extract SIP ID from URI for authorization_user
|
|
||||||
// URI format: sip:521814@blr-pub-rtc4.ozonetel.com
|
|
||||||
const sipId = this.config.uri.replace('sip:', '').split('@')[0];
|
const sipId = this.config.uri.replace('sip:', '').split('@')[0];
|
||||||
|
|
||||||
const configuration: UAConfiguration = {
|
const configuration: UAConfiguration = {
|
||||||
@@ -61,13 +57,19 @@ export class SIPClient {
|
|||||||
|
|
||||||
this.ua.on('newRTCSession', (data: RTCSessionEvent) => {
|
this.ua.on('newRTCSession', (data: RTCSessionEvent) => {
|
||||||
const session = data.session;
|
const session = data.session;
|
||||||
|
|
||||||
|
// If we already have an active session, reject the new one
|
||||||
|
if (this.currentSession && this.currentSession !== session) {
|
||||||
|
session.terminate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.currentSession = session;
|
this.currentSession = session;
|
||||||
|
|
||||||
// Extract caller number
|
// Extract caller number — try multiple SIP headers
|
||||||
const remoteUri = session.remote_identity?.uri?.toString() ?? '';
|
const callerNumber = this.extractCallerNumber(session);
|
||||||
const callerNumber = remoteUri.replace('sip:', '').split('@')[0] || 'Unknown';
|
|
||||||
|
|
||||||
// Setup audio
|
// Setup audio for this session
|
||||||
session.on('peerconnection', (e: PeerConnectionEvent) => {
|
session.on('peerconnection', (e: PeerConnectionEvent) => {
|
||||||
const pc = e.peerconnection;
|
const pc = e.peerconnection;
|
||||||
pc.ontrack = (event: RTCTrackEvent) => {
|
pc.ontrack = (event: RTCTrackEvent) => {
|
||||||
@@ -95,15 +97,13 @@ export class SIPClient {
|
|||||||
}) as CallListener);
|
}) as CallListener);
|
||||||
|
|
||||||
session.on('failed', (_e: EndEvent) => {
|
session.on('failed', (_e: EndEvent) => {
|
||||||
|
this.resetSession();
|
||||||
this.onCallStateChange('failed');
|
this.onCallStateChange('failed');
|
||||||
this.currentSession = null;
|
|
||||||
this.cleanupAudio();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
session.on('ended', (_e: EndEvent) => {
|
session.on('ended', (_e: EndEvent) => {
|
||||||
|
this.resetSession();
|
||||||
this.onCallStateChange('ended');
|
this.onCallStateChange('ended');
|
||||||
this.currentSession = null;
|
|
||||||
this.cleanupAudio();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (session.direction === 'incoming') {
|
if (session.direction === 'incoming') {
|
||||||
@@ -158,10 +158,22 @@ export class SIPClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reject(): void {
|
||||||
|
if (this.currentSession && this.currentSession.direction === 'incoming') {
|
||||||
|
// Use 486 Busy Here for rejecting incoming calls
|
||||||
|
this.currentSession.terminate({
|
||||||
|
status_code: 486,
|
||||||
|
reason_phrase: 'Busy Here',
|
||||||
|
});
|
||||||
|
this.resetSession();
|
||||||
|
this.onCallStateChange('ended');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hangup(): void {
|
hangup(): void {
|
||||||
if (this.currentSession) {
|
if (this.currentSession) {
|
||||||
this.currentSession.terminate();
|
this.currentSession.terminate();
|
||||||
this.currentSession = null;
|
this.resetSession();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,6 +209,11 @@ export class SIPClient {
|
|||||||
return this.ua?.isRegistered() ?? false;
|
return this.ua?.isRegistered() ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private resetSession(): void {
|
||||||
|
this.currentSession = null;
|
||||||
|
this.cleanupAudio();
|
||||||
|
}
|
||||||
|
|
||||||
private cleanupAudio(): void {
|
private cleanupAudio(): void {
|
||||||
if (this.audioElement) {
|
if (this.audioElement) {
|
||||||
this.audioElement.srcObject = null;
|
this.audioElement.srcObject = null;
|
||||||
@@ -205,6 +222,45 @@ export class SIPClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extractCallerNumber(session: RTCSession): string {
|
||||||
|
// Try P-Asserted-Identity header first (most reliable for real caller ID)
|
||||||
|
try {
|
||||||
|
const request = session.direction === 'incoming' ? (session as any)._request : null;
|
||||||
|
if (request) {
|
||||||
|
// Check P-Asserted-Identity
|
||||||
|
const pai = request.getHeader('P-Asserted-Identity');
|
||||||
|
if (pai) {
|
||||||
|
const match = pai.match(/sip:(\+?\d+)@/);
|
||||||
|
if (match) return match[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check Remote-Party-ID
|
||||||
|
const rpid = request.getHeader('Remote-Party-ID');
|
||||||
|
if (rpid) {
|
||||||
|
const match = rpid.match(/sip:(\+?\d+)@/);
|
||||||
|
if (match) return match[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check X-Original-CallerID (custom header some PBX systems use)
|
||||||
|
const xCid = request.getHeader('X-Original-CallerID');
|
||||||
|
if (xCid) return xCid;
|
||||||
|
|
||||||
|
// Check From header display name (sometimes contains the real number)
|
||||||
|
const fromDisplay = request.from?.display_name;
|
||||||
|
if (fromDisplay && /^\+?\d{7,}$/.test(fromDisplay)) {
|
||||||
|
return fromDisplay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Error extracting caller ID from SIP headers:', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to remote_identity URI
|
||||||
|
const remoteUri = session.remote_identity?.uri?.toString() ?? '';
|
||||||
|
const number = remoteUri.replace('sip:', '').split('@')[0] || 'Unknown';
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
private parseStunServers(stunConfig: string): RTCIceServer[] {
|
private parseStunServers(stunConfig: string): RTCIceServer[] {
|
||||||
const servers: RTCIceServer[] = [];
|
const servers: RTCIceServer[] = [];
|
||||||
const lines = stunConfig.split('\n').filter((line) => line.trim());
|
const lines = stunConfig.split('\n').filter((line) => line.trim());
|
||||||
|
|||||||
Reference in New Issue
Block a user