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,
|
||||
callDuration,
|
||||
answer,
|
||||
reject,
|
||||
hangup,
|
||||
toggleMute,
|
||||
toggleHold,
|
||||
@@ -171,7 +172,7 @@ export const CallWidget = () => {
|
||||
<Button size="md" color="primary" iconLeading={Phone01} onClick={answer}>
|
||||
Answer
|
||||
</Button>
|
||||
<Button size="md" color="primary-destructive" iconLeading={PhoneX} onClick={hangup}>
|
||||
<Button size="md" color="primary-destructive" iconLeading={PhoneX} onClick={reject}>
|
||||
Decline
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -110,6 +110,10 @@ export const useSipPhone = (config?: Partial<SIPConfig>) => {
|
||||
sipClientRef.current?.answer();
|
||||
}, []);
|
||||
|
||||
const reject = useCallback(() => {
|
||||
sipClientRef.current?.reject();
|
||||
}, []);
|
||||
|
||||
const hangup = useCallback(() => {
|
||||
sipClientRef.current?.hangup();
|
||||
}, []);
|
||||
@@ -159,6 +163,7 @@ export const useSipPhone = (config?: Partial<SIPConfig>) => {
|
||||
disconnect,
|
||||
makeCall,
|
||||
answer,
|
||||
reject,
|
||||
hangup,
|
||||
toggleMute,
|
||||
toggleHold,
|
||||
|
||||
@@ -15,13 +15,9 @@ export class SIPClient {
|
||||
) {}
|
||||
|
||||
connect(): void {
|
||||
// Enable JsSIP debug logging to diagnose connection issues
|
||||
JsSIP.debug.enable('JsSIP:*');
|
||||
|
||||
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 configuration: UAConfiguration = {
|
||||
@@ -61,13 +57,19 @@ export class SIPClient {
|
||||
|
||||
this.ua.on('newRTCSession', (data: RTCSessionEvent) => {
|
||||
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;
|
||||
|
||||
// Extract caller number
|
||||
const remoteUri = session.remote_identity?.uri?.toString() ?? '';
|
||||
const callerNumber = remoteUri.replace('sip:', '').split('@')[0] || 'Unknown';
|
||||
// Extract caller number — try multiple SIP headers
|
||||
const callerNumber = this.extractCallerNumber(session);
|
||||
|
||||
// Setup audio
|
||||
// Setup audio for this session
|
||||
session.on('peerconnection', (e: PeerConnectionEvent) => {
|
||||
const pc = e.peerconnection;
|
||||
pc.ontrack = (event: RTCTrackEvent) => {
|
||||
@@ -95,15 +97,13 @@ export class SIPClient {
|
||||
}) as CallListener);
|
||||
|
||||
session.on('failed', (_e: EndEvent) => {
|
||||
this.resetSession();
|
||||
this.onCallStateChange('failed');
|
||||
this.currentSession = null;
|
||||
this.cleanupAudio();
|
||||
});
|
||||
|
||||
session.on('ended', (_e: EndEvent) => {
|
||||
this.resetSession();
|
||||
this.onCallStateChange('ended');
|
||||
this.currentSession = null;
|
||||
this.cleanupAudio();
|
||||
});
|
||||
|
||||
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 {
|
||||
if (this.currentSession) {
|
||||
this.currentSession.terminate();
|
||||
this.currentSession = null;
|
||||
this.resetSession();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,6 +209,11 @@ export class SIPClient {
|
||||
return this.ua?.isRegistered() ?? false;
|
||||
}
|
||||
|
||||
private resetSession(): void {
|
||||
this.currentSession = null;
|
||||
this.cleanupAudio();
|
||||
}
|
||||
|
||||
private cleanupAudio(): void {
|
||||
if (this.audioElement) {
|
||||
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[] {
|
||||
const servers: RTCIceServer[] = [];
|
||||
const lines = stunConfig.split('\n').filter((line) => line.trim());
|
||||
|
||||
Reference in New Issue
Block a user