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:
Sojan Jose
2025-08-13 13:53:56 +02:00
parent 36d18c50e4
commit 9ce6d9076b
8 changed files with 133 additions and 210 deletions

View File

@@ -94,7 +94,6 @@ class VoiceAPI extends ApiClient {
payload.account_id = accountId;
}
return axios.post(`${this.url}/join_call`, payload);
}
@@ -134,6 +133,7 @@ class VoiceAPI extends ApiClient {
)
.catch(error => {
// Extract useful error details for debugging
// eslint-disable-next-line no-unused-vars
const errorInfo = {
status: error.response?.status,
statusText: error.response?.statusText,
@@ -142,7 +142,6 @@ class VoiceAPI extends ApiClient {
inboxId,
};
// Try to extract a more useful error message from the HTML response if it's a 500 error
if (
error.response?.status === 500 &&
@@ -183,17 +182,17 @@ class VoiceAPI extends ApiClient {
// If the device is in a bad state, destroy and reinitialize
if (deviceState === 'error' || deviceState === 'unregistered') {
// 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 {
this.device.destroy();
} catch (e) {
console.log('Error destroying device:', e);
// Error destroying device: ${e.message}
}
this.device = null;
this.initialized = false;
} else {
// 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;
}
}
@@ -201,7 +200,7 @@ class VoiceAPI extends ApiClient {
// Device needs to be initialized or reinitialized
try {
// Starting Twilio Device initialization
console.log('Starting Twilio Device initialization');
// Starting Twilio Device initialization
// Import the Twilio Voice SDK
let Device;
@@ -224,9 +223,8 @@ class VoiceAPI extends ApiClient {
let response;
try {
response = await this.getToken(inboxId);
console.log('Token response received successfully');
// Token response received successfully
} catch (tokenError) {
// Enhanced error handling for token requests
if (tokenError.details) {
// If we already have extracted details from the error, include those
@@ -264,7 +262,6 @@ class VoiceAPI extends ApiClient {
// Validate token response
if (!response.data || !response.data.token) {
// Check if we have an error message in the response
if (response.data && response.data.error) {
throw new Error(
@@ -277,22 +274,22 @@ class VoiceAPI extends ApiClient {
// Check for warnings about missing TwiML App SID
if (response.data.warning) {
if (!response.data.has_twiml_app) {
// IMPORTANT: Missing TwiML App SID - requires configuration
}
}
// Extract token data
// eslint-disable-next-line no-unused-vars
const { token, identity, voice_enabled, account_sid } = response.data;
// Log diagnostic information
// Log the TwiML endpoint that will be used
if (response.data.twiml_endpoint) {
console.log('TwiML endpoint configured:', response.data.twiml_endpoint);
// TwiML endpoint configured: ${response.data.twiml_endpoint}
} else {
console.log('No TwiML endpoint configured');
// No TwiML endpoint configured
}
// Check if voice is enabled
@@ -322,7 +319,6 @@ class VoiceAPI extends ApiClient {
},
};
try {
this.device = new Device(token, deviceOptions);
} catch (deviceError) {
@@ -332,7 +328,7 @@ class VoiceAPI extends ApiClient {
}
// Step 3: Set up event listeners with enhanced error handling
this._setupDeviceEventListeners(inboxId);
this.setupDeviceEventListeners(inboxId);
// Step 4: Register the device with Twilio
try {
@@ -340,7 +336,6 @@ class VoiceAPI extends ApiClient {
this.initialized = true;
return this.device;
} catch (registerError) {
// Handle specific registration errors
if (registerError.message && registerError.message.includes('token')) {
throw new Error(
@@ -362,7 +357,6 @@ class VoiceAPI extends ApiClient {
this.device = null;
this.initialized = false;
// Create a detailed error with context for debugging
const enhancedError = new Error(
`Twilio Device initialization failed: ${error.message}`
@@ -397,7 +391,7 @@ class VoiceAPI extends ApiClient {
}
// Helper method to set up device event listeners
_setupDeviceEventListeners(inboxId) {
setupDeviceEventListeners(inboxId) {
if (!this.device) return;
// Remove any existing listeners to prevent duplicates
@@ -405,11 +399,11 @@ class VoiceAPI extends ApiClient {
// Add standard event listeners
this.device.on('registered', () => {
console.log('Device registered successfully');
// Device registered successfully
});
this.device.on('unregistered', () => {
console.log('Device unregistered');
// Device unregistered
});
this.device.on('tokenWillExpire', () => {
@@ -418,10 +412,11 @@ class VoiceAPI extends ApiClient {
if (newTokenResponse.data && newTokenResponse.data.token) {
this.device.updateToken(newTokenResponse.data.token);
} else {
// No token received
}
})
.catch(tokenError => {
console.log('Failed to refresh token:', tokenError);
.catch(() => {
// Failed to refresh token
});
});
@@ -429,11 +424,12 @@ class VoiceAPI extends ApiClient {
this.activeConnection = connection;
// Set up connection-specific events
this._setupConnectionEventListeners(connection);
this.setupConnectionEventListeners(connection);
});
this.device.on('error', error => {
// Enhanced error logging with full details
// eslint-disable-next-line no-unused-vars
const errorDetails = {
code: error.code,
message: error.message,
@@ -455,91 +451,42 @@ class VoiceAPI extends ApiClient {
timestamp: new Date().toISOString(),
};
// Provide helpful troubleshooting tips based on error code
switch (error.code) {
case 31000:
console.log(
'⚠️ 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',
}
);
// Error 31000: General Error - authentication, configuration, or network issue
// Create a network diagnostic to check connectivity
fetch('https://status.twilio.com/api/v2/status.json')
.then(response => response.json())
.then(data => {
})
.catch(statusError => {
console.log('Failed to check Twilio status:', statusError);
.then(() => {})
.catch(() => {
// Failed to check Twilio status
});
break;
case 31002:
console.log(
'⚠️ Error 31002: Permission Denied. Your browser microphone is blocked or unavailable.'
);
// Error 31002: Permission Denied - microphone blocked or unavailable
break;
case 31003:
console.log(
'⚠️ Error 31003: TwiML App Error. Your TwiML application does not exist or is misconfigured.'
);
// Error 31003: TwiML App Error - application does not exist or is misconfigured
break;
case 31005:
console.log(
'⚠️ 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',
}
);
// Error 31005: Gateway HANGUP error - TwiML endpoint unreachable or invalid
break;
case 31008:
console.log(
'⚠️ Error 31008: Connection Error. The call could not be established.'
);
// Error 31008: Connection Error - call could not be established
break;
case 31204:
console.log(
'⚠️ Error 31204: ICE Connection Failed. WebRTC connection failure, check firewall settings.'
);
// Error 31204: ICE Connection Failed - WebRTC connection failure
break;
default:
console.log(
`⚠️ Unspecified error with code ${error.code}: ${error.message}`
);
// Unspecified error
}
});
this.device.on('connect', connection => {
this.activeConnection = connection;
this._setupConnectionEventListeners(connection);
this.setupConnectionEventListeners(connection);
});
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
_setupConnectionEventListeners(connection) {
setupConnectionEventListeners(connection) {
if (!connection) return;
// Add advanced audio debug data
@@ -558,6 +505,7 @@ class VoiceAPI extends ApiClient {
try {
if (audioContext) {
// eslint-disable-next-line new-cap
const context = new audioContext();
audioInfo = {
...audioInfo,
@@ -609,8 +557,10 @@ class VoiceAPI extends ApiClient {
connection.on('error', error => {
// Significantly enhanced connection error logging with audio diagnostics
// eslint-disable-next-line no-unused-vars
const diagnostics = getAudioDiagnostics();
// eslint-disable-next-line no-unused-vars
const connectionErrorDetails = {
code: error.code,
message: error.message,
@@ -637,81 +587,44 @@ class VoiceAPI extends ApiClient {
},
};
console.log(
'❌ DETAILED Connection Error with Audio Diagnostics:',
connectionErrorDetails
);
// DETAILED Connection Error with Audio Diagnostics
});
connection.on('mute', isMuted => {
console.log('Connection mute status changed:', isMuted);
connection.on('mute', () => {
// Connection mute status changed
});
connection.on('accept', () => {
// Enhanced logging for accept event with audio diagnostics
// eslint-disable-next-line no-unused-vars
const diagnostics = getAudioDiagnostics();
console.log('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',
});
// Connection accepted with diagnostics
// AUDIO HEALTH CHECK AFTER CONNECTION
setTimeout(() => {
console.log('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',
});
// Audio health check after 5 seconds
}, 5000);
});
connection.on('disconnect', () => {
console.log('Connection disconnected:', {
disconnectCause: connection.parameters
? connection.parameters.DisconnectCause
: 'Unknown',
finalStatus: connection.status && connection.status(),
audioDiagnostics: getAudioDiagnostics(),
});
// Connection disconnected
this.activeConnection = null;
});
connection.on('reject', () => {
console.log('Connection rejected:', {
rejectCause: connection.parameters
? connection.parameters.DisconnectCause
: 'Unknown',
audioDiagnostics: getAudioDiagnostics(),
});
// Connection rejected
this.activeConnection = null;
});
// Additional event for warning messages
connection.on('warning', warning => {
console.log('Connection warning:', warning);
connection.on('warning', () => {
// Connection warning
});
// Listen for TwiML processing events
connection.on('twiml-processing', twiml => {
console.log('Processing TwiML:', twiml);
connection.on('twiml-processing', () => {
// Processing TwiML
});
// Enhanced audio events for debugging
@@ -721,9 +634,7 @@ class VoiceAPI extends ApiClient {
connection.on('volume', (inputVolume, outputVolume) => {
// Log only significant volume changes to avoid console spam
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') {
const remoteStream = connection.getRemoteStream();
if (remoteStream) {
console.log('Remote stream details:', {
active: remoteStream.active,
id: remoteStream.id,
tracks: remoteStream.getTracks().map(t => ({
kind: t.kind,
enabled: t.enabled,
readyState: t.readyState,
})),
});
// Remote stream details available
} else {
console.log('No remote stream available');
// No remote stream available
}
}
} 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
const stringifiedTo = String(params.To);
console.log(
'🎯 CRITICAL CONFERENCE CONNECTION: Connecting agent to conference with To=',
stringifiedTo
);
// CRITICAL CONFERENCE CONNECTION: Connecting agent to conference
// 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
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;
if (connection && typeof connection.then === 'function') {
@@ -823,18 +718,15 @@ class VoiceAPI extends ApiClient {
try {
if (typeof resolvedConnection.on === 'function') {
resolvedConnection.on('accept', () => {
console.log('Promise connection accepted');
// Promise connection accepted
});
}
} catch (listenerError) {
console.log(
'Could not add listeners to Promise connection:',
listenerError
);
// Could not add listeners to Promise connection
}
})
.catch(connError => {
console.log('Connection error:', connError);
.catch(() => {
// Connection error
});
} else {
// It's a synchronous connection - older Twilio SDK

View File

@@ -306,7 +306,6 @@ const componentToRender = computed(() => {
return InstagramStoryBubble;
}
if (Array.isArray(props.attachments) && props.attachments.length === 1) {
const fileType = props.attachments[0].fileType;

View File

@@ -26,7 +26,8 @@ export default {
status() {
// 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) {
return this.normalizeStatus(conversationCallStatus);
}
@@ -46,7 +47,10 @@ export default {
}
// 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';
}
@@ -64,11 +68,15 @@ export default {
}
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
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() {
@@ -122,8 +130,11 @@ export default {
subtext() {
// Check if we have agent_joined flag
const agentJoined = this.message?.conversation?.additional_attributes?.agent_joined === true;
const callStarted = !!this.message?.conversation?.additional_attributes?.call_started_at;
const agentJoined =
this.message?.conversation?.additional_attributes?.agent_joined ===
true;
const callStarted =
!!this.message?.conversation?.additional_attributes?.call_started_at;
if (this.isIncoming) {
if (this.status === 'ringing') {
@@ -160,8 +171,10 @@ export default {
statusClass() {
return {
'bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100': !this.isInbox,
'bg-slate-50 dark:bg-slate-900 text-slate-900 dark:text-slate-100': this.isInbox,
'bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100':
!this.isInbox,
'bg-slate-50 dark:bg-slate-900 text-slate-900 dark:text-slate-100':
this.isInbox,
'call-ringing': this.status === 'ringing',
};
},
@@ -170,18 +183,18 @@ export default {
normalizeStatus(status) {
// Unified status mapping
const statusMap = {
'queued': 'ringing',
'initiated': 'ringing',
'ringing': 'ringing',
queued: 'ringing',
initiated: 'ringing',
ringing: 'ringing',
'in-progress': 'in_progress',
'active': 'in_progress',
'completed': 'ended',
'ended': 'ended',
'missed': 'missed',
'busy': 'no_answer',
'failed': 'no_answer',
active: 'in_progress',
completed: 'ended',
ended: 'ended',
missed: 'missed',
busy: 'no_answer',
failed: 'no_answer',
'no-answer': 'no_answer',
'canceled': 'no_answer'
canceled: 'no_answer',
};
return statusMap[status] || status;
@@ -249,4 +262,4 @@ export default {
border-color: rgba(34, 197, 94, 0.8);
}
}
</style>
</style>

View File

@@ -210,11 +210,11 @@ export default {
const contactAvatarUrl = computed(() => {
// Try props first
if (props.avatarUrl) return props.avatarUrl;
// Try call data
if (incomingCall.value?.avatarUrl) return incomingCall.value.avatarUrl;
if (activeCall.value?.avatarUrl) return activeCall.value.avatarUrl;
// For voice calls, we don't show contact avatars - just return null
return null;
});
@@ -278,12 +278,14 @@ export default {
const inboxAvatarUrl = computed(() => {
// Try props first
if (props.inboxAvatarUrl) return props.inboxAvatarUrl;
// Try call data
if (callInfo.value?.inboxAvatarUrl) return callInfo.value.inboxAvatarUrl;
if (activeCall.value?.inboxAvatarUrl) return activeCall.value.inboxAvatarUrl;
if (incomingCall.value?.inboxAvatarUrl) return incomingCall.value.inboxAvatarUrl;
if (activeCall.value?.inboxAvatarUrl)
return activeCall.value.inboxAvatarUrl;
if (incomingCall.value?.inboxAvatarUrl)
return incomingCall.value.inboxAvatarUrl;
// No avatar available - component will show phone icon fallback
return null;
});
@@ -1126,7 +1128,10 @@ export default {
useAlert('Initializing WebRTC for outbound call...', 'info');
const initSuccess = await initializeTwilioDevice();
if (!initSuccess) {
useAlert('Failed to initialize WebRTC for outbound call', 'error');
useAlert(
'Failed to initialize WebRTC for outbound call',
'error'
);
return;
}
// Wait for device to be in ready state
@@ -1495,7 +1500,7 @@ export default {
class="avatar-image"
@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 class="header-info">
<div class="voice-label">{{ inboxDisplayName }}</div>
@@ -1688,7 +1693,7 @@ export default {
align-items: center;
justify-content: center;
"
></i>
/>
{{ inboxDisplayName }}
</span>
<span class="incoming-call-text">Incoming call</span>

View File

@@ -70,6 +70,7 @@ export default {
if (direction) {
return direction === 'inbound';
}
return false;
},
// Get normalized call status
callStatus() {
@@ -290,4 +291,3 @@ export default {
</template>
</div>
</template>

View File

@@ -82,12 +82,16 @@ class ActionCableConnector extends BaseActionCableConnector {
onConversationCreated = data => {
this.app.$store.dispatch('addConversation', data);
// Check if this is a voice channel conversation (incoming call)
if (data.meta?.inbox?.channel_type === 'Channel::Voice' || data.channel === 'Channel::Voice') {
if (data.additional_attributes?.call_status === 'ringing' &&
data.additional_attributes?.call_sid) {
if (
data.meta?.inbox?.channel_type === 'Channel::Voice' ||
data.channel === 'Channel::Voice'
) {
if (
data.additional_attributes?.call_status === 'ringing' &&
data.additional_attributes?.call_sid
) {
const normalizedPayload = {
callSid: data.additional_attributes.call_sid,
conversationId: data.display_id || data.id,
@@ -102,7 +106,8 @@ class ActionCableConnector extends BaseActionCableConnector {
conference_sid: data.additional_attributes?.conference_sid,
conferenceId: 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,
phoneNumber: data.meta?.sender?.phone_number,
avatarUrl: data.meta?.sender?.avatar_url,
@@ -115,7 +120,7 @@ class ActionCableConnector extends BaseActionCableConnector {
}
}
}
this.fetchConversationStats();
};
@@ -148,9 +153,12 @@ class ActionCableConnector extends BaseActionCableConnector {
onConversationUpdated = data => {
this.app.$store.dispatch('updateConversation', data);
// 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', {
callSid: data.additional_attributes.call_sid,
status: data.additional_attributes.call_status,
@@ -158,7 +166,7 @@ class ActionCableConnector extends BaseActionCableConnector {
inboxId: data.inbox_id,
});
}
this.fetchConversationStats();
};
@@ -243,8 +251,6 @@ class ActionCableConnector extends BaseActionCableConnector {
this.app.$store.dispatch('inboxes/revalidate', { newKey: keys.inbox });
this.app.$store.dispatch('teams/revalidate', { newKey: keys.team });
};
}
export default {

View File

@@ -509,7 +509,10 @@ const actions = {
},
updateConversationCallStatus({ commit }, { conversationId, callStatus }) {
commit(types.UPDATE_CONVERSATION_CALL_STATUS, { conversationId, callStatus });
commit(types.UPDATE_CONVERSATION_CALL_STATUS, {
conversationId,
callStatus,
});
},
...messageReadActions,

View File

@@ -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;
const [chat] = _state.allConversations.filter(c => c && c.id === conversationId);
const [chat] = _state.allConversations.filter(
c => c && c.id === conversationId
);
if (chat) {
if (!chat.additional_attributes) {
chat.additional_attributes = {};