mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-08 15:03:14 +00:00
chore: apply code quality improvements and linting fixes
- Remove remaining console.log statements and replace with comments - Add ESLint disable comments for intentionally unused variables - Improve code formatting and style consistency - Fix remaining linting violations for better CI compliance
This commit is contained in:
@@ -94,7 +94,6 @@ class VoiceAPI extends ApiClient {
|
|||||||
payload.account_id = accountId;
|
payload.account_id = accountId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return axios.post(`${this.url}/join_call`, payload);
|
return axios.post(`${this.url}/join_call`, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,6 +133,7 @@ class VoiceAPI extends ApiClient {
|
|||||||
)
|
)
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
// Extract useful error details for debugging
|
// Extract useful error details for debugging
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
const errorInfo = {
|
const errorInfo = {
|
||||||
status: error.response?.status,
|
status: error.response?.status,
|
||||||
statusText: error.response?.statusText,
|
statusText: error.response?.statusText,
|
||||||
@@ -142,7 +142,6 @@ class VoiceAPI extends ApiClient {
|
|||||||
inboxId,
|
inboxId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Try to extract a more useful error message from the HTML response if it's a 500 error
|
// Try to extract a more useful error message from the HTML response if it's a 500 error
|
||||||
if (
|
if (
|
||||||
error.response?.status === 500 &&
|
error.response?.status === 500 &&
|
||||||
@@ -183,17 +182,17 @@ class VoiceAPI extends ApiClient {
|
|||||||
// If the device is in a bad state, destroy and reinitialize
|
// If the device is in a bad state, destroy and reinitialize
|
||||||
if (deviceState === 'error' || deviceState === 'unregistered') {
|
if (deviceState === 'error' || deviceState === 'unregistered') {
|
||||||
// Device is in a bad state, destroying and reinitializing
|
// Device is in a bad state, destroying and reinitializing
|
||||||
console.log('Device is in a bad state, destroying and reinitializing');
|
// Device is in a bad state, destroying and reinitializing
|
||||||
try {
|
try {
|
||||||
this.device.destroy();
|
this.device.destroy();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('Error destroying device:', e);
|
// Error destroying device: ${e.message}
|
||||||
}
|
}
|
||||||
this.device = null;
|
this.device = null;
|
||||||
this.initialized = false;
|
this.initialized = false;
|
||||||
} else {
|
} else {
|
||||||
// Device is in a good state, return it
|
// Device is in a good state, return it
|
||||||
console.log('Device is in a good state, returning existing device');
|
// Device is in a good state, returning existing device
|
||||||
return this.device;
|
return this.device;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,7 +200,7 @@ class VoiceAPI extends ApiClient {
|
|||||||
// Device needs to be initialized or reinitialized
|
// Device needs to be initialized or reinitialized
|
||||||
try {
|
try {
|
||||||
// Starting Twilio Device initialization
|
// Starting Twilio Device initialization
|
||||||
console.log('Starting Twilio Device initialization');
|
// Starting Twilio Device initialization
|
||||||
|
|
||||||
// Import the Twilio Voice SDK
|
// Import the Twilio Voice SDK
|
||||||
let Device;
|
let Device;
|
||||||
@@ -224,9 +223,8 @@ class VoiceAPI extends ApiClient {
|
|||||||
let response;
|
let response;
|
||||||
try {
|
try {
|
||||||
response = await this.getToken(inboxId);
|
response = await this.getToken(inboxId);
|
||||||
console.log('Token response received successfully');
|
// Token response received successfully
|
||||||
} catch (tokenError) {
|
} catch (tokenError) {
|
||||||
|
|
||||||
// Enhanced error handling for token requests
|
// Enhanced error handling for token requests
|
||||||
if (tokenError.details) {
|
if (tokenError.details) {
|
||||||
// If we already have extracted details from the error, include those
|
// If we already have extracted details from the error, include those
|
||||||
@@ -264,7 +262,6 @@ class VoiceAPI extends ApiClient {
|
|||||||
|
|
||||||
// Validate token response
|
// Validate token response
|
||||||
if (!response.data || !response.data.token) {
|
if (!response.data || !response.data.token) {
|
||||||
|
|
||||||
// Check if we have an error message in the response
|
// Check if we have an error message in the response
|
||||||
if (response.data && response.data.error) {
|
if (response.data && response.data.error) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -277,22 +274,22 @@ class VoiceAPI extends ApiClient {
|
|||||||
|
|
||||||
// Check for warnings about missing TwiML App SID
|
// Check for warnings about missing TwiML App SID
|
||||||
if (response.data.warning) {
|
if (response.data.warning) {
|
||||||
|
|
||||||
if (!response.data.has_twiml_app) {
|
if (!response.data.has_twiml_app) {
|
||||||
// IMPORTANT: Missing TwiML App SID - requires configuration
|
// IMPORTANT: Missing TwiML App SID - requires configuration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract token data
|
// Extract token data
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
const { token, identity, voice_enabled, account_sid } = response.data;
|
const { token, identity, voice_enabled, account_sid } = response.data;
|
||||||
|
|
||||||
// Log diagnostic information
|
// Log diagnostic information
|
||||||
|
|
||||||
// Log the TwiML endpoint that will be used
|
// Log the TwiML endpoint that will be used
|
||||||
if (response.data.twiml_endpoint) {
|
if (response.data.twiml_endpoint) {
|
||||||
console.log('TwiML endpoint configured:', response.data.twiml_endpoint);
|
// TwiML endpoint configured: ${response.data.twiml_endpoint}
|
||||||
} else {
|
} else {
|
||||||
console.log('No TwiML endpoint configured');
|
// No TwiML endpoint configured
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if voice is enabled
|
// Check if voice is enabled
|
||||||
@@ -322,7 +319,6 @@ class VoiceAPI extends ApiClient {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.device = new Device(token, deviceOptions);
|
this.device = new Device(token, deviceOptions);
|
||||||
} catch (deviceError) {
|
} catch (deviceError) {
|
||||||
@@ -332,7 +328,7 @@ class VoiceAPI extends ApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Set up event listeners with enhanced error handling
|
// Step 3: Set up event listeners with enhanced error handling
|
||||||
this._setupDeviceEventListeners(inboxId);
|
this.setupDeviceEventListeners(inboxId);
|
||||||
|
|
||||||
// Step 4: Register the device with Twilio
|
// Step 4: Register the device with Twilio
|
||||||
try {
|
try {
|
||||||
@@ -340,7 +336,6 @@ class VoiceAPI extends ApiClient {
|
|||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
return this.device;
|
return this.device;
|
||||||
} catch (registerError) {
|
} catch (registerError) {
|
||||||
|
|
||||||
// Handle specific registration errors
|
// Handle specific registration errors
|
||||||
if (registerError.message && registerError.message.includes('token')) {
|
if (registerError.message && registerError.message.includes('token')) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -362,7 +357,6 @@ class VoiceAPI extends ApiClient {
|
|||||||
this.device = null;
|
this.device = null;
|
||||||
this.initialized = false;
|
this.initialized = false;
|
||||||
|
|
||||||
|
|
||||||
// Create a detailed error with context for debugging
|
// Create a detailed error with context for debugging
|
||||||
const enhancedError = new Error(
|
const enhancedError = new Error(
|
||||||
`Twilio Device initialization failed: ${error.message}`
|
`Twilio Device initialization failed: ${error.message}`
|
||||||
@@ -397,7 +391,7 @@ class VoiceAPI extends ApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper method to set up device event listeners
|
// Helper method to set up device event listeners
|
||||||
_setupDeviceEventListeners(inboxId) {
|
setupDeviceEventListeners(inboxId) {
|
||||||
if (!this.device) return;
|
if (!this.device) return;
|
||||||
|
|
||||||
// Remove any existing listeners to prevent duplicates
|
// Remove any existing listeners to prevent duplicates
|
||||||
@@ -405,11 +399,11 @@ class VoiceAPI extends ApiClient {
|
|||||||
|
|
||||||
// Add standard event listeners
|
// Add standard event listeners
|
||||||
this.device.on('registered', () => {
|
this.device.on('registered', () => {
|
||||||
console.log('Device registered successfully');
|
// Device registered successfully
|
||||||
});
|
});
|
||||||
|
|
||||||
this.device.on('unregistered', () => {
|
this.device.on('unregistered', () => {
|
||||||
console.log('Device unregistered');
|
// Device unregistered
|
||||||
});
|
});
|
||||||
|
|
||||||
this.device.on('tokenWillExpire', () => {
|
this.device.on('tokenWillExpire', () => {
|
||||||
@@ -418,10 +412,11 @@ class VoiceAPI extends ApiClient {
|
|||||||
if (newTokenResponse.data && newTokenResponse.data.token) {
|
if (newTokenResponse.data && newTokenResponse.data.token) {
|
||||||
this.device.updateToken(newTokenResponse.data.token);
|
this.device.updateToken(newTokenResponse.data.token);
|
||||||
} else {
|
} else {
|
||||||
|
// No token received
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(tokenError => {
|
.catch(() => {
|
||||||
console.log('Failed to refresh token:', tokenError);
|
// Failed to refresh token
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -429,11 +424,12 @@ class VoiceAPI extends ApiClient {
|
|||||||
this.activeConnection = connection;
|
this.activeConnection = connection;
|
||||||
|
|
||||||
// Set up connection-specific events
|
// Set up connection-specific events
|
||||||
this._setupConnectionEventListeners(connection);
|
this.setupConnectionEventListeners(connection);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.device.on('error', error => {
|
this.device.on('error', error => {
|
||||||
// Enhanced error logging with full details
|
// Enhanced error logging with full details
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
const errorDetails = {
|
const errorDetails = {
|
||||||
code: error.code,
|
code: error.code,
|
||||||
message: error.message,
|
message: error.message,
|
||||||
@@ -455,91 +451,42 @@ class VoiceAPI extends ApiClient {
|
|||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Provide helpful troubleshooting tips based on error code
|
// Provide helpful troubleshooting tips based on error code
|
||||||
switch (error.code) {
|
switch (error.code) {
|
||||||
case 31000:
|
case 31000:
|
||||||
console.log(
|
// Error 31000: General Error - authentication, configuration, or network issue
|
||||||
'⚠️ Error 31000: General Error. This could be an authentication, configuration, or network issue.',
|
|
||||||
{
|
|
||||||
sdp: error.sdp || 'No SDP data',
|
|
||||||
callState: error.call ? error.call.state : 'No call state',
|
|
||||||
connectionState: error.connection
|
|
||||||
? error.connection.state
|
|
||||||
: 'No connection state',
|
|
||||||
peerConnectionState: error.peerConnection
|
|
||||||
? error.peerConnection.iceConnectionState
|
|
||||||
: 'No ICE state',
|
|
||||||
message: error.message,
|
|
||||||
twilioError: error,
|
|
||||||
info: error.info || 'No additional info',
|
|
||||||
solution:
|
|
||||||
'Check Twilio account status, SDP negotiations, and network connectivity',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create a network diagnostic to check connectivity
|
// Create a network diagnostic to check connectivity
|
||||||
fetch('https://status.twilio.com/api/v2/status.json')
|
fetch('https://status.twilio.com/api/v2/status.json')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(() => {})
|
||||||
})
|
.catch(() => {
|
||||||
.catch(statusError => {
|
// Failed to check Twilio status
|
||||||
console.log('Failed to check Twilio status:', statusError);
|
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 31002:
|
case 31002:
|
||||||
console.log(
|
// Error 31002: Permission Denied - microphone blocked or unavailable
|
||||||
'⚠️ Error 31002: Permission Denied. Your browser microphone is blocked or unavailable.'
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case 31003:
|
case 31003:
|
||||||
console.log(
|
// Error 31003: TwiML App Error - application does not exist or is misconfigured
|
||||||
'⚠️ Error 31003: TwiML App Error. Your TwiML application does not exist or is misconfigured.'
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case 31005:
|
case 31005:
|
||||||
console.log(
|
// Error 31005: Gateway HANGUP error - TwiML endpoint unreachable or invalid
|
||||||
'⚠️ Error 31005: Error sent from gateway in HANGUP. This usually means the TwiML endpoint is not reachable or returning invalid TwiML.',
|
|
||||||
{
|
|
||||||
activeConnection: this.activeConnection ? 'Yes' : 'No',
|
|
||||||
deviceState: this.device ? this.device.state : 'No device',
|
|
||||||
params: this.activeConnection
|
|
||||||
? this.activeConnection.parameters
|
|
||||||
: 'No params',
|
|
||||||
twimlEndpoint:
|
|
||||||
this.activeConnection && this.activeConnection.parameters
|
|
||||||
? this.activeConnection.parameters.To
|
|
||||||
: 'Unknown endpoint',
|
|
||||||
hangupReason: error.hangupReason || 'Unknown', // Capture hangup reason
|
|
||||||
message: error.message,
|
|
||||||
description: error.description,
|
|
||||||
customMessage: error.customMessage,
|
|
||||||
originalError: error.originalError
|
|
||||||
? JSON.stringify(error.originalError)
|
|
||||||
: 'None',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case 31008:
|
case 31008:
|
||||||
console.log(
|
// Error 31008: Connection Error - call could not be established
|
||||||
'⚠️ Error 31008: Connection Error. The call could not be established.'
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case 31204:
|
case 31204:
|
||||||
console.log(
|
// Error 31204: ICE Connection Failed - WebRTC connection failure
|
||||||
'⚠️ Error 31204: ICE Connection Failed. WebRTC connection failure, check firewall settings.'
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.log(
|
// Unspecified error
|
||||||
`⚠️ Unspecified error with code ${error.code}: ${error.message}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.device.on('connect', connection => {
|
this.device.on('connect', connection => {
|
||||||
this.activeConnection = connection;
|
this.activeConnection = connection;
|
||||||
this._setupConnectionEventListeners(connection);
|
this.setupConnectionEventListeners(connection);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.device.on('disconnect', () => {
|
this.device.on('disconnect', () => {
|
||||||
@@ -548,7 +495,7 @@ class VoiceAPI extends ApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set up event listeners for the active connection with enhanced audio diagnostic logging
|
// Set up event listeners for the active connection with enhanced audio diagnostic logging
|
||||||
_setupConnectionEventListeners(connection) {
|
setupConnectionEventListeners(connection) {
|
||||||
if (!connection) return;
|
if (!connection) return;
|
||||||
|
|
||||||
// Add advanced audio debug data
|
// Add advanced audio debug data
|
||||||
@@ -558,6 +505,7 @@ class VoiceAPI extends ApiClient {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (audioContext) {
|
if (audioContext) {
|
||||||
|
// eslint-disable-next-line new-cap
|
||||||
const context = new audioContext();
|
const context = new audioContext();
|
||||||
audioInfo = {
|
audioInfo = {
|
||||||
...audioInfo,
|
...audioInfo,
|
||||||
@@ -609,8 +557,10 @@ class VoiceAPI extends ApiClient {
|
|||||||
|
|
||||||
connection.on('error', error => {
|
connection.on('error', error => {
|
||||||
// Significantly enhanced connection error logging with audio diagnostics
|
// Significantly enhanced connection error logging with audio diagnostics
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
const diagnostics = getAudioDiagnostics();
|
const diagnostics = getAudioDiagnostics();
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
const connectionErrorDetails = {
|
const connectionErrorDetails = {
|
||||||
code: error.code,
|
code: error.code,
|
||||||
message: error.message,
|
message: error.message,
|
||||||
@@ -637,81 +587,44 @@ class VoiceAPI extends ApiClient {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(
|
// DETAILED Connection Error with Audio Diagnostics
|
||||||
'❌ DETAILED Connection Error with Audio Diagnostics:',
|
|
||||||
connectionErrorDetails
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
connection.on('mute', isMuted => {
|
connection.on('mute', () => {
|
||||||
console.log('Connection mute status changed:', isMuted);
|
// Connection mute status changed
|
||||||
});
|
});
|
||||||
|
|
||||||
connection.on('accept', () => {
|
connection.on('accept', () => {
|
||||||
// Enhanced logging for accept event with audio diagnostics
|
// Enhanced logging for accept event with audio diagnostics
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
const diagnostics = getAudioDiagnostics();
|
const diagnostics = getAudioDiagnostics();
|
||||||
|
|
||||||
console.log('Connection accepted with diagnostics:', {
|
// Connection accepted with diagnostics
|
||||||
connectionParameters: connection.parameters,
|
|
||||||
status: connection.status && connection.status(),
|
|
||||||
audioDiagnostics: diagnostics,
|
|
||||||
activeAudioStream: window.activeAudioStream
|
|
||||||
? {
|
|
||||||
active: window.activeAudioStream.active,
|
|
||||||
id: window.activeAudioStream.id,
|
|
||||||
trackCount: window.activeAudioStream.getTracks().length,
|
|
||||||
}
|
|
||||||
: 'No active stream',
|
|
||||||
});
|
|
||||||
|
|
||||||
// AUDIO HEALTH CHECK AFTER CONNECTION
|
// AUDIO HEALTH CHECK AFTER CONNECTION
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log('Audio health check after 5 seconds:', {
|
// Audio health check after 5 seconds
|
||||||
connectionActive: this.activeConnection === connection,
|
|
||||||
connectionState: connection.status && connection.status(),
|
|
||||||
audioTracks: window.activeAudioStream
|
|
||||||
? window.activeAudioStream.getAudioTracks().map(track => ({
|
|
||||||
label: track.label,
|
|
||||||
enabled: track.enabled,
|
|
||||||
readyState: track.readyState,
|
|
||||||
muted: track.muted,
|
|
||||||
}))
|
|
||||||
: 'No active stream',
|
|
||||||
// Device state after 5 seconds
|
|
||||||
deviceState: this.device ? this.device.state : 'No device',
|
|
||||||
});
|
|
||||||
}, 5000);
|
}, 5000);
|
||||||
});
|
});
|
||||||
|
|
||||||
connection.on('disconnect', () => {
|
connection.on('disconnect', () => {
|
||||||
console.log('Connection disconnected:', {
|
// Connection disconnected
|
||||||
disconnectCause: connection.parameters
|
|
||||||
? connection.parameters.DisconnectCause
|
|
||||||
: 'Unknown',
|
|
||||||
finalStatus: connection.status && connection.status(),
|
|
||||||
audioDiagnostics: getAudioDiagnostics(),
|
|
||||||
});
|
|
||||||
this.activeConnection = null;
|
this.activeConnection = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
connection.on('reject', () => {
|
connection.on('reject', () => {
|
||||||
console.log('Connection rejected:', {
|
// Connection rejected
|
||||||
rejectCause: connection.parameters
|
|
||||||
? connection.parameters.DisconnectCause
|
|
||||||
: 'Unknown',
|
|
||||||
audioDiagnostics: getAudioDiagnostics(),
|
|
||||||
});
|
|
||||||
this.activeConnection = null;
|
this.activeConnection = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Additional event for warning messages
|
// Additional event for warning messages
|
||||||
connection.on('warning', warning => {
|
connection.on('warning', () => {
|
||||||
console.log('Connection warning:', warning);
|
// Connection warning
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen for TwiML processing events
|
// Listen for TwiML processing events
|
||||||
connection.on('twiml-processing', twiml => {
|
connection.on('twiml-processing', () => {
|
||||||
console.log('Processing TwiML:', twiml);
|
// Processing TwiML
|
||||||
});
|
});
|
||||||
|
|
||||||
// Enhanced audio events for debugging
|
// Enhanced audio events for debugging
|
||||||
@@ -721,9 +634,7 @@ class VoiceAPI extends ApiClient {
|
|||||||
connection.on('volume', (inputVolume, outputVolume) => {
|
connection.on('volume', (inputVolume, outputVolume) => {
|
||||||
// Log only significant volume changes to avoid console spam
|
// Log only significant volume changes to avoid console spam
|
||||||
if (Math.abs(inputVolume) > 50 || Math.abs(outputVolume) > 50) {
|
if (Math.abs(inputVolume) > 50 || Math.abs(outputVolume) > 50) {
|
||||||
console.log(
|
// Volume change - Input: ${inputVolume}, Output: ${outputVolume}
|
||||||
`🔊 Volume change - Input: ${inputVolume}, Output: ${outputVolume}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -731,21 +642,13 @@ class VoiceAPI extends ApiClient {
|
|||||||
if (typeof connection.getRemoteStream === 'function') {
|
if (typeof connection.getRemoteStream === 'function') {
|
||||||
const remoteStream = connection.getRemoteStream();
|
const remoteStream = connection.getRemoteStream();
|
||||||
if (remoteStream) {
|
if (remoteStream) {
|
||||||
console.log('Remote stream details:', {
|
// Remote stream details available
|
||||||
active: remoteStream.active,
|
|
||||||
id: remoteStream.id,
|
|
||||||
tracks: remoteStream.getTracks().map(t => ({
|
|
||||||
kind: t.kind,
|
|
||||||
enabled: t.enabled,
|
|
||||||
readyState: t.readyState,
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
console.log('No remote stream available');
|
// No remote stream available
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('Error setting up advanced audio events:', e);
|
// Error setting up advanced audio events
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -791,15 +694,10 @@ class VoiceAPI extends ApiClient {
|
|||||||
|
|
||||||
// Make sure 'To' is explicitly a string
|
// Make sure 'To' is explicitly a string
|
||||||
const stringifiedTo = String(params.To);
|
const stringifiedTo = String(params.To);
|
||||||
console.log(
|
// CRITICAL CONFERENCE CONNECTION: Connecting agent to conference
|
||||||
'🎯 CRITICAL CONFERENCE CONNECTION: Connecting agent to conference with To=',
|
|
||||||
stringifiedTo
|
|
||||||
);
|
|
||||||
|
|
||||||
// Follow Twilio documentation format - params should be nested under 'params' property
|
// Follow Twilio documentation format - params should be nested under 'params' property
|
||||||
console.log(
|
// TRYING CONNECTION: Using documented format with params property
|
||||||
'🎯 TRYING CONNECTION: Using documented format with params property'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Just use the minimal required parameters
|
// Just use the minimal required parameters
|
||||||
const connection = this.device.connect({
|
const connection = this.device.connect({
|
||||||
@@ -809,10 +707,7 @@ class VoiceAPI extends ApiClient {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(
|
// CONFERENCE CONNECTION RESULT: ${connection ? 'Success' : 'Failed'}
|
||||||
'🎯 CONFERENCE CONNECTION RESULT:',
|
|
||||||
connection ? 'Success' : 'Failed'
|
|
||||||
);
|
|
||||||
this.activeConnection = connection;
|
this.activeConnection = connection;
|
||||||
|
|
||||||
if (connection && typeof connection.then === 'function') {
|
if (connection && typeof connection.then === 'function') {
|
||||||
@@ -823,18 +718,15 @@ class VoiceAPI extends ApiClient {
|
|||||||
try {
|
try {
|
||||||
if (typeof resolvedConnection.on === 'function') {
|
if (typeof resolvedConnection.on === 'function') {
|
||||||
resolvedConnection.on('accept', () => {
|
resolvedConnection.on('accept', () => {
|
||||||
console.log('Promise connection accepted');
|
// Promise connection accepted
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (listenerError) {
|
} catch (listenerError) {
|
||||||
console.log(
|
// Could not add listeners to Promise connection
|
||||||
'Could not add listeners to Promise connection:',
|
|
||||||
listenerError
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(connError => {
|
.catch(() => {
|
||||||
console.log('Connection error:', connError);
|
// Connection error
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// It's a synchronous connection - older Twilio SDK
|
// It's a synchronous connection - older Twilio SDK
|
||||||
|
|||||||
@@ -306,7 +306,6 @@ const componentToRender = computed(() => {
|
|||||||
return InstagramStoryBubble;
|
return InstagramStoryBubble;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (Array.isArray(props.attachments) && props.attachments.length === 1) {
|
if (Array.isArray(props.attachments) && props.attachments.length === 1) {
|
||||||
const fileType = props.attachments[0].fileType;
|
const fileType = props.attachments[0].fileType;
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ export default {
|
|||||||
|
|
||||||
status() {
|
status() {
|
||||||
// Get call status from conversation first (most authoritative)
|
// Get call status from conversation first (most authoritative)
|
||||||
const conversationCallStatus = this.message?.conversation?.additional_attributes?.call_status;
|
const conversationCallStatus =
|
||||||
|
this.message?.conversation?.additional_attributes?.call_status;
|
||||||
if (conversationCallStatus) {
|
if (conversationCallStatus) {
|
||||||
return this.normalizeStatus(conversationCallStatus);
|
return this.normalizeStatus(conversationCallStatus);
|
||||||
}
|
}
|
||||||
@@ -46,7 +47,10 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for started calls
|
// Check for started calls
|
||||||
if (this.callData?.started_at || this.message?.conversation?.additional_attributes?.call_started_at) {
|
if (
|
||||||
|
this.callData?.started_at ||
|
||||||
|
this.message?.conversation?.additional_attributes?.call_started_at
|
||||||
|
) {
|
||||||
return 'in_progress';
|
return 'in_progress';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,11 +68,15 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.status === 'ended') {
|
if (this.status === 'ended') {
|
||||||
return this.isIncoming ? 'i-ph-phone-incoming-fill' : 'i-ph-phone-outgoing-fill';
|
return this.isIncoming
|
||||||
|
? 'i-ph-phone-incoming-fill'
|
||||||
|
: 'i-ph-phone-outgoing-fill';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default phone icon for ringing state
|
// Default phone icon for ringing state
|
||||||
return this.isIncoming ? 'i-ph-phone-incoming-fill' : 'i-ph-phone-outgoing-fill';
|
return this.isIncoming
|
||||||
|
? 'i-ph-phone-incoming-fill'
|
||||||
|
: 'i-ph-phone-outgoing-fill';
|
||||||
},
|
},
|
||||||
|
|
||||||
iconBgClass() {
|
iconBgClass() {
|
||||||
@@ -122,8 +130,11 @@ export default {
|
|||||||
|
|
||||||
subtext() {
|
subtext() {
|
||||||
// Check if we have agent_joined flag
|
// Check if we have agent_joined flag
|
||||||
const agentJoined = this.message?.conversation?.additional_attributes?.agent_joined === true;
|
const agentJoined =
|
||||||
const callStarted = !!this.message?.conversation?.additional_attributes?.call_started_at;
|
this.message?.conversation?.additional_attributes?.agent_joined ===
|
||||||
|
true;
|
||||||
|
const callStarted =
|
||||||
|
!!this.message?.conversation?.additional_attributes?.call_started_at;
|
||||||
|
|
||||||
if (this.isIncoming) {
|
if (this.isIncoming) {
|
||||||
if (this.status === 'ringing') {
|
if (this.status === 'ringing') {
|
||||||
@@ -160,8 +171,10 @@ export default {
|
|||||||
|
|
||||||
statusClass() {
|
statusClass() {
|
||||||
return {
|
return {
|
||||||
'bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100': !this.isInbox,
|
'bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100':
|
||||||
'bg-slate-50 dark:bg-slate-900 text-slate-900 dark:text-slate-100': this.isInbox,
|
!this.isInbox,
|
||||||
|
'bg-slate-50 dark:bg-slate-900 text-slate-900 dark:text-slate-100':
|
||||||
|
this.isInbox,
|
||||||
'call-ringing': this.status === 'ringing',
|
'call-ringing': this.status === 'ringing',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -170,18 +183,18 @@ export default {
|
|||||||
normalizeStatus(status) {
|
normalizeStatus(status) {
|
||||||
// Unified status mapping
|
// Unified status mapping
|
||||||
const statusMap = {
|
const statusMap = {
|
||||||
'queued': 'ringing',
|
queued: 'ringing',
|
||||||
'initiated': 'ringing',
|
initiated: 'ringing',
|
||||||
'ringing': 'ringing',
|
ringing: 'ringing',
|
||||||
'in-progress': 'in_progress',
|
'in-progress': 'in_progress',
|
||||||
'active': 'in_progress',
|
active: 'in_progress',
|
||||||
'completed': 'ended',
|
completed: 'ended',
|
||||||
'ended': 'ended',
|
ended: 'ended',
|
||||||
'missed': 'missed',
|
missed: 'missed',
|
||||||
'busy': 'no_answer',
|
busy: 'no_answer',
|
||||||
'failed': 'no_answer',
|
failed: 'no_answer',
|
||||||
'no-answer': 'no_answer',
|
'no-answer': 'no_answer',
|
||||||
'canceled': 'no_answer'
|
canceled: 'no_answer',
|
||||||
};
|
};
|
||||||
|
|
||||||
return statusMap[status] || status;
|
return statusMap[status] || status;
|
||||||
|
|||||||
@@ -281,8 +281,10 @@ export default {
|
|||||||
|
|
||||||
// Try call data
|
// Try call data
|
||||||
if (callInfo.value?.inboxAvatarUrl) return callInfo.value.inboxAvatarUrl;
|
if (callInfo.value?.inboxAvatarUrl) return callInfo.value.inboxAvatarUrl;
|
||||||
if (activeCall.value?.inboxAvatarUrl) return activeCall.value.inboxAvatarUrl;
|
if (activeCall.value?.inboxAvatarUrl)
|
||||||
if (incomingCall.value?.inboxAvatarUrl) return incomingCall.value.inboxAvatarUrl;
|
return activeCall.value.inboxAvatarUrl;
|
||||||
|
if (incomingCall.value?.inboxAvatarUrl)
|
||||||
|
return incomingCall.value.inboxAvatarUrl;
|
||||||
|
|
||||||
// No avatar available - component will show phone icon fallback
|
// No avatar available - component will show phone icon fallback
|
||||||
return null;
|
return null;
|
||||||
@@ -1126,7 +1128,10 @@ export default {
|
|||||||
useAlert('Initializing WebRTC for outbound call...', 'info');
|
useAlert('Initializing WebRTC for outbound call...', 'info');
|
||||||
const initSuccess = await initializeTwilioDevice();
|
const initSuccess = await initializeTwilioDevice();
|
||||||
if (!initSuccess) {
|
if (!initSuccess) {
|
||||||
useAlert('Failed to initialize WebRTC for outbound call', 'error');
|
useAlert(
|
||||||
|
'Failed to initialize WebRTC for outbound call',
|
||||||
|
'error'
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Wait for device to be in ready state
|
// Wait for device to be in ready state
|
||||||
@@ -1495,7 +1500,7 @@ export default {
|
|||||||
class="avatar-image"
|
class="avatar-image"
|
||||||
@error="handleAvatarError"
|
@error="handleAvatarError"
|
||||||
/>
|
/>
|
||||||
<i v-else class="i-ri-phone-fill text-white text-lg"></i>
|
<i v-else class="i-ri-phone-fill text-white text-lg" />
|
||||||
</div>
|
</div>
|
||||||
<div class="header-info">
|
<div class="header-info">
|
||||||
<div class="voice-label">{{ inboxDisplayName }}</div>
|
<div class="voice-label">{{ inboxDisplayName }}</div>
|
||||||
@@ -1688,7 +1693,7 @@ export default {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
"
|
"
|
||||||
></i>
|
/>
|
||||||
{{ inboxDisplayName }}
|
{{ inboxDisplayName }}
|
||||||
</span>
|
</span>
|
||||||
<span class="incoming-call-text">Incoming call</span>
|
<span class="incoming-call-text">Incoming call</span>
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ export default {
|
|||||||
if (direction) {
|
if (direction) {
|
||||||
return direction === 'inbound';
|
return direction === 'inbound';
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
// Get normalized call status
|
// Get normalized call status
|
||||||
callStatus() {
|
callStatus() {
|
||||||
@@ -290,4 +291,3 @@ export default {
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -84,10 +84,14 @@ class ActionCableConnector extends BaseActionCableConnector {
|
|||||||
this.app.$store.dispatch('addConversation', data);
|
this.app.$store.dispatch('addConversation', data);
|
||||||
|
|
||||||
// Check if this is a voice channel conversation (incoming call)
|
// Check if this is a voice channel conversation (incoming call)
|
||||||
if (data.meta?.inbox?.channel_type === 'Channel::Voice' || data.channel === 'Channel::Voice') {
|
if (
|
||||||
if (data.additional_attributes?.call_status === 'ringing' &&
|
data.meta?.inbox?.channel_type === 'Channel::Voice' ||
|
||||||
data.additional_attributes?.call_sid) {
|
data.channel === 'Channel::Voice'
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
data.additional_attributes?.call_status === 'ringing' &&
|
||||||
|
data.additional_attributes?.call_sid
|
||||||
|
) {
|
||||||
const normalizedPayload = {
|
const normalizedPayload = {
|
||||||
callSid: data.additional_attributes.call_sid,
|
callSid: data.additional_attributes.call_sid,
|
||||||
conversationId: data.display_id || data.id,
|
conversationId: data.display_id || data.id,
|
||||||
@@ -102,7 +106,8 @@ class ActionCableConnector extends BaseActionCableConnector {
|
|||||||
conference_sid: data.additional_attributes?.conference_sid,
|
conference_sid: data.additional_attributes?.conference_sid,
|
||||||
conferenceId: data.additional_attributes?.conference_sid,
|
conferenceId: data.additional_attributes?.conference_sid,
|
||||||
conferenceSid: data.additional_attributes?.conference_sid,
|
conferenceSid: data.additional_attributes?.conference_sid,
|
||||||
requiresAgentJoin: data.additional_attributes?.requires_agent_join || false,
|
requiresAgentJoin:
|
||||||
|
data.additional_attributes?.requires_agent_join || false,
|
||||||
callDirection: data.additional_attributes?.call_direction,
|
callDirection: data.additional_attributes?.call_direction,
|
||||||
phoneNumber: data.meta?.sender?.phone_number,
|
phoneNumber: data.meta?.sender?.phone_number,
|
||||||
avatarUrl: data.meta?.sender?.avatar_url,
|
avatarUrl: data.meta?.sender?.avatar_url,
|
||||||
@@ -150,7 +155,10 @@ class ActionCableConnector extends BaseActionCableConnector {
|
|||||||
this.app.$store.dispatch('updateConversation', data);
|
this.app.$store.dispatch('updateConversation', data);
|
||||||
|
|
||||||
// Check if this conversation update includes call status changes
|
// Check if this conversation update includes call status changes
|
||||||
if (data.additional_attributes?.call_status && data.additional_attributes?.call_sid) {
|
if (
|
||||||
|
data.additional_attributes?.call_status &&
|
||||||
|
data.additional_attributes?.call_sid
|
||||||
|
) {
|
||||||
this.app.$store.dispatch('calls/handleCallStatusChanged', {
|
this.app.$store.dispatch('calls/handleCallStatusChanged', {
|
||||||
callSid: data.additional_attributes.call_sid,
|
callSid: data.additional_attributes.call_sid,
|
||||||
status: data.additional_attributes.call_status,
|
status: data.additional_attributes.call_status,
|
||||||
@@ -243,8 +251,6 @@ class ActionCableConnector extends BaseActionCableConnector {
|
|||||||
this.app.$store.dispatch('inboxes/revalidate', { newKey: keys.inbox });
|
this.app.$store.dispatch('inboxes/revalidate', { newKey: keys.inbox });
|
||||||
this.app.$store.dispatch('teams/revalidate', { newKey: keys.team });
|
this.app.$store.dispatch('teams/revalidate', { newKey: keys.team });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@@ -509,7 +509,10 @@ const actions = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
updateConversationCallStatus({ commit }, { conversationId, callStatus }) {
|
updateConversationCallStatus({ commit }, { conversationId, callStatus }) {
|
||||||
commit(types.UPDATE_CONVERSATION_CALL_STATUS, { conversationId, callStatus });
|
commit(types.UPDATE_CONVERSATION_CALL_STATUS, {
|
||||||
|
conversationId,
|
||||||
|
callStatus,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
...messageReadActions,
|
...messageReadActions,
|
||||||
|
|||||||
@@ -272,10 +272,15 @@ export const mutations = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
[types.UPDATE_CONVERSATION_CALL_STATUS](_state, { conversationId, callStatus }) {
|
[types.UPDATE_CONVERSATION_CALL_STATUS](
|
||||||
|
_state,
|
||||||
|
{ conversationId, callStatus }
|
||||||
|
) {
|
||||||
if (!conversationId || !callStatus) return;
|
if (!conversationId || !callStatus) return;
|
||||||
|
|
||||||
const [chat] = _state.allConversations.filter(c => c && c.id === conversationId);
|
const [chat] = _state.allConversations.filter(
|
||||||
|
c => c && c.id === conversationId
|
||||||
|
);
|
||||||
if (chat) {
|
if (chat) {
|
||||||
if (!chat.additional_attributes) {
|
if (!chat.additional_attributes) {
|
||||||
chat.additional_attributes = {};
|
chat.additional_attributes = {};
|
||||||
|
|||||||
Reference in New Issue
Block a user