Fix critical browser phone shortcode issues

Issues Fixed:
1. 🎵 Audio Error: Removed missing ringtone.mp3 causing 404 error
2. 📡 Twilio SDK Loading: Added proper SDK availability checking and retry mechanism
3. 🎨 Contrast Issues: Improved readability with darker text colors

Technical Changes:
- Removed hardcoded ringtone.mp3 reference causing 404 errors
- Added waitForTwilioSDK() function with 5-second timeout and retry logic
- Changed Twilio SDK loading to head (false flag) for better availability
- Enhanced error handling with specific Twilio SDK load failure messages
- Improved text contrast: #6c757d → #495057, #495057#212529
- Enhanced dark mode colors for better accessibility

UI Improvements:
- Status text: font-weight 600, color #212529 for better visibility
- Labels and selects: improved contrast ratios
- Queue info: better text color for mobile readability
- Call status: font-weight 600 for prominence
- Dark mode: complete color scheme overhaul with proper contrast

Error Handling:
- Graceful SDK loading with visual feedback
- Clear error messages for troubleshooting
- Automatic retry mechanism for network issues
- Timeout handling for slow connections

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-08-13 14:09:36 -07:00
parent 12c285dc90
commit 24e3a3fcc2
3 changed files with 110 additions and 42 deletions

View File

@@ -56,8 +56,8 @@
} }
.status-text { .status-text {
font-weight: 500; font-weight: 600;
color: #495057; color: #212529;
} }
@keyframes pulse { @keyframes pulse {
@@ -74,8 +74,8 @@
.twp-phone-selection label { .twp-phone-selection label {
display: block; display: block;
margin-bottom: 8px; margin-bottom: 8px;
font-weight: 500; font-weight: 600;
color: #495057; color: #212529;
} }
.twp-select { .twp-select {
@@ -85,7 +85,7 @@
border-radius: 8px; border-radius: 8px;
font-size: 16px; font-size: 16px;
background: #fff; background: #fff;
color: #495057; color: #212529;
appearance: none; appearance: none;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
background-position: right 12px center; background-position: right 12px center;
@@ -289,8 +289,8 @@
padding: 8px; padding: 8px;
background: #f8f9fa; background: #f8f9fa;
border-radius: 4px; border-radius: 4px;
color: #495057; color: #212529;
font-weight: 500; font-weight: 600;
} }
/* Queue Management Section */ /* Queue Management Section */
@@ -317,9 +317,10 @@
.queue-loading, .queue-loading,
.no-queues { .no-queues {
text-align: center; text-align: center;
color: #6c757d; color: #495057;
font-style: italic; font-style: italic;
padding: 20px; padding: 20px;
font-weight: 500;
} }
.no-queues { .no-queues {
@@ -377,7 +378,7 @@
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
font-size: 0.9rem; font-size: 0.9rem;
color: #6c757d; color: #495057;
} }
.queue-waiting { .queue-waiting {
@@ -418,7 +419,7 @@
justify-content: center; justify-content: center;
gap: 20px; gap: 20px;
font-size: 0.9rem; font-size: 0.9rem;
color: #6c757d; color: #495057;
} }
.queue-stats span { .queue-stats span {
@@ -552,27 +553,59 @@
/* Dark mode support */ /* Dark mode support */
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
.twp-browser-phone-container { .twp-browser-phone-container {
background: #2d3748; background: #1a202c;
color: #e2e8f0; color: #f7fafc;
} }
.twp-connection-status, .twp-connection-status,
.twp-call-info, .twp-call-info,
.twp-queue-controls { .twp-queue-controls,
background: #4a5568; .twp-queue-section {
border-color: #718096; background: #2d3748;
border-color: #4a5568;
} }
.twp-select, .twp-select,
#twp-dial-number, #twp-dial-number,
.twp-dial-btn { .twp-dial-btn {
background: #4a5568; background: #2d3748;
border-color: #718096; border-color: #4a5568;
color: #e2e8f0; color: #f7fafc;
} }
.call-status { .call-status {
background: #1a202c;
color: #f7fafc;
}
.queue-item {
background: #2d3748; background: #2d3748;
border-color: #4a5568;
color: #f7fafc;
}
.queue-item:hover,
.queue-item.selected {
background: #4a5568;
border-color: #63b3ed;
}
.queue-name,
.status-text,
.twp-phone-selection label {
color: #f7fafc;
}
.queue-info,
.queue-stats,
.queue-loading,
.no-queues {
color: #cbd5e0;
}
.no-queues {
background: #2d3748;
border-color: #4a5568;
} }
} }

View File

@@ -27,32 +27,61 @@
loadUserQueues(); loadUserQueues();
}); });
/**
* Wait for Twilio SDK to load
*/
function waitForTwilioSDK(callback) {
let attempts = 0;
const maxAttempts = 50; // 5 seconds max
function checkTwilio() {
if (typeof Twilio !== 'undefined' && Twilio.Device) {
callback();
return;
}
attempts++;
if (attempts >= maxAttempts) {
updateStatus('offline', 'SDK load timeout');
showMessage('Twilio SDK failed to load within 5 seconds. Please check your internet connection and refresh the page.', 'error');
return;
}
setTimeout(checkTwilio, 100);
}
checkTwilio();
}
/** /**
* Initialize the browser phone * Initialize the browser phone
*/ */
function initializeBrowserPhone() { function initializeBrowserPhone() {
updateStatus('connecting', 'Initializing...'); updateStatus('connecting', 'Initializing...');
// Generate capability token // Wait for Twilio SDK to load, then initialize
$.ajax({ waitForTwilioSDK(function() {
url: twp_frontend_ajax.ajax_url, // Generate capability token
method: 'POST', $.ajax({
data: { url: twp_frontend_ajax.ajax_url,
action: 'twp_generate_capability_token', method: 'POST',
nonce: twp_frontend_ajax.nonce data: {
}, action: 'twp_generate_capability_token',
success: function(response) { nonce: twp_frontend_ajax.nonce
if (response.success) { },
setupTwilioDevice(response.data.token); success: function(response) {
} else { if (response.success) {
updateStatus('offline', 'Failed to initialize'); setupTwilioDevice(response.data.token);
showMessage('Failed to initialize browser phone: ' + (response.data || 'Unknown error'), 'error'); } else {
updateStatus('offline', 'Failed to initialize');
showMessage('Failed to initialize browser phone: ' + (response.data || 'Unknown error'), 'error');
}
},
error: function() {
updateStatus('offline', 'Connection failed');
showMessage('Failed to connect to server', 'error');
} }
}, });
error: function() {
updateStatus('offline', 'Connection failed');
showMessage('Failed to connect to server', 'error');
}
}); });
} }
@@ -60,6 +89,14 @@
* Setup Twilio Device * Setup Twilio Device
*/ */
function setupTwilioDevice(token) { function setupTwilioDevice(token) {
// Check if Twilio SDK is loaded
if (typeof Twilio === 'undefined' || !Twilio.Device) {
updateStatus('offline', 'Twilio SDK not loaded');
showMessage('Twilio SDK failed to load. Please refresh the page.', 'error');
console.error('Twilio SDK is not available');
return;
}
try { try {
twilioDevice = new Twilio.Device(token, { twilioDevice = new Twilio.Device(token, {
logLevel: 1, logLevel: 1,

View File

@@ -32,7 +32,7 @@ class TWP_Shortcodes {
'https://sdk.twilio.com/js/voice/2.11.1/twilio.min.js', 'https://sdk.twilio.com/js/voice/2.11.1/twilio.min.js',
array(), array(),
'2.11.1', '2.11.1',
true false // Load in head to ensure it's available
); );
// Enqueue our browser phone script // Enqueue our browser phone script
@@ -180,10 +180,8 @@ class TWP_Shortcodes {
</div> </div>
</div> </div>
<!-- Audio elements --> <!-- Audio elements for incoming calls handled by browser -->
<audio id="twp-ringtone" preload="auto" loop> <audio id="twp-ringtone" style="display: none;"></audio>
<source src="<?php echo TWP_PLUGIN_URL; ?>assets/audio/ringtone.mp3" type="audio/mpeg">
</audio>
<!-- Error/Success Messages --> <!-- Error/Success Messages -->
<div class="twp-messages" id="twp-messages"></div> <div class="twp-messages" id="twp-messages"></div>