Add background notifications and fix Discord/Slack notification bugs

Background Notification Features:
- Implement Web Push Notifications for alerts when browser is minimized
- Add Service Worker for persistent background notification support
- Integrate Page Visibility API to detect when page is in background
- Add browser notification permission request with user consent flow
- Create more aggressive background polling (10 seconds) when page hidden
- Add vibration patterns for mobile device alerts

Bug Fixes:
- Fix Discord/Slack notification parameter order bug preventing delivery
- Add comprehensive logging for notification debugging
- Correct webhook function signatures for proper data passing

Mobile Enhancements:
- Support system notifications on Android browsers
- Add click-to-focus functionality for notifications
- Implement background alert system for multitasking
- Add notification button with visual feedback

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-08-15 16:51:47 -07:00
parent 9832f899c3
commit d050f1538b
5 changed files with 352 additions and 3 deletions

View File

@@ -20,6 +20,9 @@
let alertSound = null;
let alertInterval = null;
let alertEnabled = false;
let notificationPermission = 'default';
let backgroundAlertInterval = null;
let isPageVisible = true;
// Initialize when document is ready
$(document).ready(function() {
@@ -33,6 +36,8 @@
loadPhoneNumbers();
loadUserQueues();
initVoicemailSection();
initializeNotifications();
initializePageVisibility();
});
/**
@@ -525,6 +530,20 @@
if (currentWaiting > previousWaiting) {
console.log('New call detected in queue:', queue.queue_name);
newCallDetected = true;
// Show browser notification for new call
if (notificationPermission === 'granted') {
showBrowserNotification('📞 New Call in Queue!', {
body: `${queue.queue_name}: ${currentWaiting} call${currentWaiting > 1 ? 's' : ''} waiting`,
icon: '📞',
vibrate: [300, 200, 300],
requireInteraction: true,
tag: `queue-${queue.id}`,
data: {
queueId: queue.id
}
});
}
}
lastQueueUpdate[queueId] = currentWaiting;
@@ -980,6 +999,9 @@
if (alertInterval) {
clearInterval(alertInterval);
}
if (backgroundAlertInterval) {
clearInterval(backgroundAlertInterval);
}
if (twilioDevice) {
twilioDevice.destroy();
}
@@ -1140,6 +1162,217 @@
});
}
/**
* Initialize browser notifications
*/
function initializeNotifications() {
// Register service worker for background notifications
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/wp-content/plugins/twilio-wp-plugin/assets/js/twp-service-worker.js')
.then(function(registration) {
console.log('Service Worker registered:', registration);
})
.catch(function(error) {
console.log('Service Worker registration failed:', error);
});
}
// Check if browser supports notifications
if (!('Notification' in window)) {
console.log('This browser does not support notifications');
return;
}
// Check current permission status
notificationPermission = Notification.permission;
// Request permission if not already granted or denied
if (notificationPermission === 'default') {
// Add a button to request permission
if ($('#twp-queue-global-actions').length > 0) {
const $notificationBtn = $('<button>')
.attr('id', 'twp-enable-notifications')
.addClass('twp-btn twp-btn-info')
.html('🔔 Enable Notifications')
.on('click', requestNotificationPermission);
$('#twp-queue-global-actions .global-queue-actions').append($notificationBtn);
}
} else if (notificationPermission === 'granted') {
console.log('Notifications are enabled');
}
}
/**
* Request notification permission from user
*/
function requestNotificationPermission() {
Notification.requestPermission().then(function(permission) {
notificationPermission = permission;
if (permission === 'granted') {
showMessage('Notifications enabled! You will be alerted even when the browser is in the background.', 'success');
$('#twp-enable-notifications').hide();
// Show test notification
showBrowserNotification('Notifications Enabled', {
body: 'You will now receive alerts for incoming calls',
icon: '📞',
tag: 'test-notification'
});
} else if (permission === 'denied') {
showMessage('Notifications blocked. Please enable them in your browser settings.', 'error');
$('#twp-enable-notifications').text('❌ Notifications Blocked');
}
});
}
/**
* Show browser notification
*/
function showBrowserNotification(title, options = {}) {
if (notificationPermission !== 'granted') {
return;
}
const defaultOptions = {
body: '',
icon: '/wp-content/plugins/twilio-wp-plugin/assets/images/phone-icon.png',
badge: '/wp-content/plugins/twilio-wp-plugin/assets/images/phone-badge.png',
vibrate: [200, 100, 200],
requireInteraction: true, // Keep notification visible until clicked
tag: 'twp-call-notification',
data: options.data || {}
};
const notificationOptions = Object.assign(defaultOptions, options);
try {
const notification = new Notification(title, notificationOptions);
// Handle notification click
notification.onclick = function(event) {
event.preventDefault();
window.focus();
notification.close();
// If there's queue data, select that queue
if (event.target.data && event.target.data.queueId) {
selectQueue(event.target.data.queueId);
}
};
// Auto-close after 30 seconds if not required interaction
if (!notificationOptions.requireInteraction) {
setTimeout(function() {
notification.close();
}, 30000);
}
return notification;
} catch (error) {
console.error('Failed to show notification:', error);
// Fallback to service worker notification if available
if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {
navigator.serviceWorker.ready.then(function(registration) {
registration.showNotification(title, notificationOptions);
});
}
}
}
/**
* Initialize page visibility handling
*/
function initializePageVisibility() {
// Set up visibility change detection
let hidden, visibilityChange;
if (typeof document.hidden !== 'undefined') {
hidden = 'hidden';
visibilityChange = 'visibilitychange';
} else if (typeof document.msHidden !== 'undefined') {
hidden = 'msHidden';
visibilityChange = 'msvisibilitychange';
} else if (typeof document.webkitHidden !== 'undefined') {
hidden = 'webkitHidden';
visibilityChange = 'webkitvisibilitychange';
}
// Handle visibility change
document.addEventListener(visibilityChange, function() {
isPageVisible = !document[hidden];
if (isPageVisible) {
console.log('Page is now visible');
// Resume normal operations
if (backgroundAlertInterval) {
clearInterval(backgroundAlertInterval);
backgroundAlertInterval = null;
}
} else {
console.log('Page is now hidden/background');
// Start more aggressive notifications for background
if (alertEnabled && userQueues.some(q => parseInt(q.current_waiting) > 0)) {
startBackgroundAlerts();
}
}
}, false);
// Also handle window focus/blur for better mobile support
$(window).on('focus', function() {
isPageVisible = true;
if (backgroundAlertInterval) {
clearInterval(backgroundAlertInterval);
backgroundAlertInterval = null;
}
});
$(window).on('blur', function() {
isPageVisible = false;
});
}
/**
* Start background alerts with notifications
*/
function startBackgroundAlerts() {
if (backgroundAlertInterval) return;
// Check and notify every 10 seconds when in background
backgroundAlertInterval = setInterval(function() {
const waitingQueues = userQueues.filter(q => parseInt(q.current_waiting) > 0);
if (waitingQueues.length > 0 && !currentCall) {
// Count total waiting calls
const totalWaiting = waitingQueues.reduce((sum, q) => sum + parseInt(q.current_waiting), 0);
// Show browser notification
showBrowserNotification(`${totalWaiting} Call${totalWaiting > 1 ? 's' : ''} Waiting!`, {
body: waitingQueues.map(q => `${q.queue_name}: ${q.current_waiting} waiting`).join('\n'),
icon: '📞',
vibrate: [300, 200, 300, 200, 300],
requireInteraction: true,
tag: 'queue-alert',
data: {
queueId: waitingQueues[0].id
}
});
// Also try to play sound if possible
try {
playAlertSound();
} catch (e) {
// Sound might be blocked in background
}
} else if (waitingQueues.length === 0) {
// No more calls, stop background alerts
clearInterval(backgroundAlertInterval);
backgroundAlertInterval = null;
}
}, 10000); // Every 10 seconds in background
}
// Load alert preference on init
loadAlertPreference();