Enhance browser phone with real-time updates and audible alerts
- Fix token expiration: Extend refresh buffer to 10 minutes for reliability - Add real-time queue updates: Reduce polling to 5 seconds for instant feedback - Implement audible alert system: 30-second repeating notifications with user toggle - Optimize Discord/Slack notifications: Non-blocking requests for immediate delivery - Add persistent alert preferences: Toggle button with localStorage integration - Clean up debug file: Remove unused debug-phone-numbers.php 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,11 @@
|
||||
let selectedQueue = null;
|
||||
let tokenRefreshTimer = null;
|
||||
let tokenExpiry = null;
|
||||
let queuePollingTimer = null;
|
||||
let lastQueueUpdate = {};
|
||||
let alertSound = null;
|
||||
let alertInterval = null;
|
||||
let alertEnabled = false;
|
||||
|
||||
// Initialize when document is ready
|
||||
$(document).ready(function() {
|
||||
@@ -263,6 +268,7 @@
|
||||
// Accept queue call button
|
||||
$('#twp-accept-queue-call').on('click', function() {
|
||||
acceptQueueCall();
|
||||
stopAlert(); // Stop alert when accepting call
|
||||
});
|
||||
|
||||
// Refresh queues button
|
||||
@@ -270,6 +276,11 @@
|
||||
loadUserQueues();
|
||||
});
|
||||
|
||||
// Alert toggle button
|
||||
$(document).on('click', '#twp-alert-toggle', function() {
|
||||
toggleAlert();
|
||||
});
|
||||
|
||||
// Queue item selection
|
||||
$(document).on('click', '.queue-item', function() {
|
||||
const queueId = $(this).data('queue-id');
|
||||
@@ -313,6 +324,9 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop alerts when making a call
|
||||
stopAlert();
|
||||
|
||||
updateCallState('connecting');
|
||||
showCallInfo('Connecting...');
|
||||
|
||||
@@ -420,12 +434,20 @@
|
||||
updateCallState('idle');
|
||||
hideCallInfo();
|
||||
$('.twp-browser-phone-container').removeClass('incoming-call');
|
||||
|
||||
// Restart alerts if enabled and there are waiting calls
|
||||
if (alertEnabled) {
|
||||
const hasWaitingCalls = userQueues.some(q => parseInt(q.current_waiting) > 0);
|
||||
if (hasWaitingCalls) {
|
||||
setTimeout(startAlert, 1000); // Small delay to avoid immediate alert
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load user's assigned queues
|
||||
*/
|
||||
function loadUserQueues() {
|
||||
function loadUserQueues(silent = false) {
|
||||
$.ajax({
|
||||
url: twp_frontend_ajax.ajax_url,
|
||||
method: 'POST',
|
||||
@@ -435,18 +457,44 @@
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
// Check for new calls in queues
|
||||
checkForNewCalls(response.data);
|
||||
userQueues = response.data;
|
||||
displayQueues();
|
||||
} else {
|
||||
} else if (!silent) {
|
||||
showMessage('Failed to load queues: ' + (response.data || 'Unknown error'), 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showMessage('Failed to load queues', 'error');
|
||||
if (!silent) {
|
||||
showMessage('Failed to load queues', 'error');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for new calls in queues and trigger alerts
|
||||
*/
|
||||
function checkForNewCalls(newQueues) {
|
||||
newQueues.forEach(function(queue) {
|
||||
const queueId = queue.id;
|
||||
const currentWaiting = parseInt(queue.current_waiting) || 0;
|
||||
const previousWaiting = lastQueueUpdate[queueId] || 0;
|
||||
|
||||
// If waiting count increased, we have new calls
|
||||
if (currentWaiting > previousWaiting) {
|
||||
console.log('New call detected in queue:', queue.queue_name);
|
||||
// Trigger alert if enabled
|
||||
if (alertEnabled && !currentCall) {
|
||||
startAlert();
|
||||
}
|
||||
}
|
||||
|
||||
lastQueueUpdate[queueId] = currentWaiting;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Display queues in the UI
|
||||
*/
|
||||
@@ -653,16 +701,29 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Periodic status updates
|
||||
setInterval(function() {
|
||||
if (isConnected) {
|
||||
loadUserQueues(); // This will refresh all queue data including waiting counts
|
||||
// Start queue polling with faster interval
|
||||
startQueuePolling();
|
||||
|
||||
/**
|
||||
* Start polling for queue updates
|
||||
*/
|
||||
function startQueuePolling() {
|
||||
// Clear any existing timer
|
||||
if (queuePollingTimer) {
|
||||
clearInterval(queuePollingTimer);
|
||||
}
|
||||
}, 30000); // Every 30 seconds
|
||||
|
||||
// Poll every 5 seconds for real-time updates
|
||||
queuePollingTimer = setInterval(function() {
|
||||
if (isConnected) {
|
||||
loadUserQueues(true); // Silent update
|
||||
}
|
||||
}, 5000); // Every 5 seconds
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule token refresh
|
||||
* Refreshes token 5 minutes before expiry
|
||||
* Refreshes token 10 minutes before expiry for safety
|
||||
*/
|
||||
function scheduleTokenRefresh() {
|
||||
// Clear any existing timer
|
||||
@@ -672,11 +733,17 @@
|
||||
|
||||
if (!tokenExpiry) {
|
||||
console.error('Token expiry time not set');
|
||||
// Retry in 30 seconds if token expiry not set
|
||||
setTimeout(function() {
|
||||
if (tokenExpiry) {
|
||||
scheduleTokenRefresh();
|
||||
}
|
||||
}, 30000);
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate time until refresh (5 minutes before expiry)
|
||||
const refreshBuffer = 5 * 60 * 1000; // 5 minutes in milliseconds
|
||||
// Calculate time until refresh (10 minutes before expiry for extra safety)
|
||||
const refreshBuffer = 10 * 60 * 1000; // 10 minutes in milliseconds
|
||||
const timeUntilRefresh = tokenExpiry - Date.now() - refreshBuffer;
|
||||
|
||||
if (timeUntilRefresh <= 0) {
|
||||
@@ -734,14 +801,127 @@
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize alert sound
|
||||
*/
|
||||
function initAlertSound() {
|
||||
// Create audio element for alert sound
|
||||
alertSound = new Audio();
|
||||
alertSound.src = 'data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQAAAAA='; // Simple beep sound
|
||||
// Use Web Audio API for better sound
|
||||
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
|
||||
// Create a simple beep sound
|
||||
function playBeep() {
|
||||
const oscillator = audioContext.createOscillator();
|
||||
const gainNode = audioContext.createGain();
|
||||
|
||||
oscillator.connect(gainNode);
|
||||
gainNode.connect(audioContext.destination);
|
||||
|
||||
oscillator.frequency.value = 800; // Frequency in Hz
|
||||
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
|
||||
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5);
|
||||
|
||||
oscillator.start(audioContext.currentTime);
|
||||
oscillator.stop(audioContext.currentTime + 0.5);
|
||||
}
|
||||
|
||||
return playBeep;
|
||||
}
|
||||
|
||||
const playAlertSound = initAlertSound();
|
||||
|
||||
/**
|
||||
* Start alert for new calls
|
||||
*/
|
||||
function startAlert() {
|
||||
if (!alertEnabled || alertInterval) return;
|
||||
|
||||
// Play initial alert
|
||||
playAlertSound();
|
||||
|
||||
// Repeat every 30 seconds
|
||||
alertInterval = setInterval(function() {
|
||||
if (alertEnabled && !currentCall) {
|
||||
playAlertSound();
|
||||
} else {
|
||||
stopAlert();
|
||||
}
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop alert
|
||||
*/
|
||||
function stopAlert() {
|
||||
if (alertInterval) {
|
||||
clearInterval(alertInterval);
|
||||
alertInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle alert on/off
|
||||
*/
|
||||
function toggleAlert() {
|
||||
alertEnabled = !alertEnabled;
|
||||
localStorage.setItem('twp_alert_enabled', alertEnabled);
|
||||
|
||||
// Update button state
|
||||
updateAlertButton();
|
||||
|
||||
if (!alertEnabled) {
|
||||
stopAlert();
|
||||
showMessage('Queue alerts disabled', 'info');
|
||||
} else {
|
||||
showMessage('Queue alerts enabled', 'success');
|
||||
// Check if there are waiting calls
|
||||
const hasWaitingCalls = userQueues.some(q => parseInt(q.current_waiting) > 0);
|
||||
if (hasWaitingCalls && !currentCall) {
|
||||
startAlert();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update alert button UI
|
||||
*/
|
||||
function updateAlertButton() {
|
||||
const $btn = $('#twp-alert-toggle');
|
||||
if (alertEnabled) {
|
||||
$btn.removeClass('alert-off').addClass('alert-on').html('🔔 Alerts ON');
|
||||
} else {
|
||||
$btn.removeClass('alert-on').addClass('alert-off').html('🔕 Alerts OFF');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load alert preference from localStorage
|
||||
*/
|
||||
function loadAlertPreference() {
|
||||
const saved = localStorage.getItem('twp_alert_enabled');
|
||||
alertEnabled = saved === null ? true : saved === 'true';
|
||||
updateAlertButton();
|
||||
}
|
||||
|
||||
// Clean up on page unload
|
||||
$(window).on('beforeunload', function() {
|
||||
if (tokenRefreshTimer) {
|
||||
clearTimeout(tokenRefreshTimer);
|
||||
}
|
||||
if (queuePollingTimer) {
|
||||
clearInterval(queuePollingTimer);
|
||||
}
|
||||
if (alertInterval) {
|
||||
clearInterval(alertInterval);
|
||||
}
|
||||
if (twilioDevice) {
|
||||
twilioDevice.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
// Load alert preference on init
|
||||
loadAlertPreference();
|
||||
|
||||
})(jQuery);
|
Reference in New Issue
Block a user