feat: add onCallConnected & onCallDisconnected callback

This commit is contained in:
Sabiha Khan 2026-05-06 17:17:12 +05:30
parent a82f87203b
commit 5859139f92

View file

@ -33,6 +33,8 @@
callbacks: { callbacks: {
onReady: null, onReady: null,
onCallStart: null, onCallStart: null,
onCallConnected: null,
onCallDisconnected: null,
onCallEnd: null, onCallEnd: null,
onError: null, onError: null,
onStatusChange: null onStatusChange: null
@ -627,6 +629,10 @@
async function startCall() { async function startCall() {
updateStatus('connecting', 'Connecting...', 'Please wait while we establish the connection'); updateStatus('connecting', 'Connecting...', 'Please wait while we establish the connection');
if (state.callbacks.onCallStart) {
state.callbacks.onCallStart();
}
try { try {
// Initialize session if using embed token // Initialize session if using embed token
if (state.config.token) { if (state.config.token) {
@ -784,9 +790,8 @@
updateStatus('connected', 'Connected', 'Your voice call is now active'); updateStatus('connected', 'Connected', 'Your voice call is now active');
if (!wasAlreadyConnected) { if (!wasAlreadyConnected) {
state.callStartedAt = Date.now(); state.callStartedAt = Date.now();
emitMessage('dograh:call_started', {}); if (state.callbacks.onCallConnected) {
if (state.callbacks.onCallStart) { state.callbacks.onCallConnected({
state.callbacks.onCallStart({
agentId: state.config.workflowId || null, agentId: state.config.workflowId || null,
token: state.config.token || null, token: state.config.token || null,
workflowRunId: state.workflowRunId || null workflowRunId: state.workflowRunId || null
@ -925,15 +930,11 @@
* Stop voice call * Stop voice call
*/ */
function stopCall() { function stopCall() {
// Emit end message before clearing state so identifiers are still available // Fire onCallDisconnected only if the call had actually connected, with
const durationSeconds = state.callStartedAt // identifiers and duration. Must run before we clear callStartedAt.
? Math.round((Date.now() - state.callStartedAt) / 1000) if (state.callStartedAt && state.callbacks.onCallDisconnected) {
: 0; const durationSeconds = Math.round((Date.now() - state.callStartedAt) / 1000);
emitMessage('dograh:call_ended', { durationSeconds }); state.callbacks.onCallDisconnected({
// Trigger call end callback with the same identifiers before we clear them
if (state.callbacks.onCallEnd) {
state.callbacks.onCallEnd({
agentId: state.config.workflowId || null, agentId: state.config.workflowId || null,
token: state.config.token || null, token: state.config.token || null,
workflowRunId: state.workflowRunId || null, workflowRunId: state.workflowRunId || null,
@ -944,6 +945,10 @@
updateStatus('idle', 'Call ended', 'Click below to start a new call'); updateStatus('idle', 'Call ended', 'Click below to start a new call');
if (state.callbacks.onCallEnd) {
state.callbacks.onCallEnd();
}
// Close WebSocket // Close WebSocket
if (state.ws) { if (state.ws) {
state.ws.close(); state.ws.close();
@ -976,22 +981,6 @@
setTimeout(() => startCall(), 500); setTimeout(() => startCall(), 500);
} }
/**
* Emit a postMessage event to the host window
* Allows the embedding website to listen for agent lifecycle events via:
* window.addEventListener('message', (event) => { ... })
*/
function emitMessage(eventType, detail) {
const message = {
type: eventType,
agentId: state.config.workflowId || null,
token: state.config.token || null,
workflowRunId: state.workflowRunId || null,
...detail
};
window.postMessage(message, '*');
}
/** /**
* Generate unique peer ID * Generate unique peer ID
*/ */
@ -1020,6 +1009,8 @@
getState: () => state, getState: () => state,
onReady: (callback) => { state.callbacks.onReady = callback; }, onReady: (callback) => { state.callbacks.onReady = callback; },
onCallStart: (callback) => { state.callbacks.onCallStart = callback; }, onCallStart: (callback) => { state.callbacks.onCallStart = callback; },
onCallConnected: (callback) => { state.callbacks.onCallConnected = callback; },
onCallDisconnected: (callback) => { state.callbacks.onCallDisconnected = callback; },
onCallEnd: (callback) => { state.callbacks.onCallEnd = callback; }, onCallEnd: (callback) => { state.callbacks.onCallEnd = callback; },
onError: (callback) => { state.callbacks.onError = callback; }, onError: (callback) => { state.callbacks.onError = callback; },
onStatusChange: (callback) => { state.callbacks.onStatusChange = callback; }, onStatusChange: (callback) => { state.callbacks.onStatusChange = callback; },
@ -1045,6 +1036,8 @@
// Set callbacks if provided // Set callbacks if provided
if (options.onReady) state.callbacks.onReady = options.onReady; if (options.onReady) state.callbacks.onReady = options.onReady;
if (options.onCallStart) state.callbacks.onCallStart = options.onCallStart; if (options.onCallStart) state.callbacks.onCallStart = options.onCallStart;
if (options.onCallConnected) state.callbacks.onCallConnected = options.onCallConnected;
if (options.onCallDisconnected) state.callbacks.onCallDisconnected = options.onCallDisconnected;
if (options.onCallEnd) state.callbacks.onCallEnd = options.onCallEnd; if (options.onCallEnd) state.callbacks.onCallEnd = options.onCallEnd;
if (options.onError) state.callbacks.onError = options.onError; if (options.onError) state.callbacks.onError = options.onError;
if (options.onStatusChange) state.callbacks.onStatusChange = options.onStatusChange; if (options.onStatusChange) state.callbacks.onStatusChange = options.onStatusChange;