1
0
This repository has been archived on 2026-03-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Timmy-time-dashboard/static/notifications.js
2026-03-05 19:45:38 -05:00

228 lines
5.8 KiB
JavaScript

/**
* Browser Push Notifications for Agent Dashboard
*
* Handles browser Notification API integration for:
* - Briefing ready notifications
* - Task completion notifications
* - Swarm event notifications
*/
(function() {
'use strict';
// Notification state
let notificationsEnabled = false;
let wsConnection = null;
/**
* Request permission for browser notifications
*/
async function requestNotificationPermission() {
if (!('Notification' in window)) {
console.log('Browser notifications not supported');
return false;
}
if (Notification.permission === 'granted') {
notificationsEnabled = true;
return true;
}
if (Notification.permission === 'denied') {
console.log('Notification permission denied');
return false;
}
const permission = await Notification.requestPermission();
notificationsEnabled = permission === 'granted';
return notificationsEnabled;
}
/**
* Show a browser notification
*/
function showNotification(title, options = {}) {
if (!notificationsEnabled || Notification.permission !== 'granted') {
return;
}
const defaultOptions = {
icon: '/static/favicon.ico',
badge: '/static/favicon.ico',
tag: 'agent-notification',
requireInteraction: false,
};
const notification = new Notification(title, { ...defaultOptions, ...options });
notification.onclick = () => {
window.focus();
notification.close();
};
return notification;
}
/**
* Show briefing ready notification
*/
function notifyBriefingReady(briefingInfo = {}) {
const approvalCount = briefingInfo.approval_count || 0;
const body = approvalCount > 0
? `Your morning briefing is ready. ${approvalCount} item(s) await your approval.`
: 'Your morning briefing is ready.';
showNotification('Morning Briefing Ready', {
body,
tag: 'briefing-ready',
requireInteraction: true,
});
}
/**
* Show task completed notification
*/
function notifyTaskCompleted(taskInfo = {}) {
const { task_id, agent_name, result } = taskInfo;
const body = result
? `Task completed by ${agent_name || 'agent'}: ${result.substring(0, 100)}${result.length > 100 ? '...' : ''}`
: `Task ${task_id?.substring(0, 8)} completed by ${agent_name || 'agent'}`;
showNotification('Task Completed', {
body,
tag: `task-${task_id}`,
});
}
/**
* Show agent joined notification
*/
function notifyAgentJoined(agentInfo = {}) {
const { name, agent_id } = agentInfo;
showNotification('Agent Joined Swarm', {
body: `${name || 'New agent'} (${agent_id?.substring(0, 8)}) has joined the swarm.`,
tag: `agent-joined-${agent_id}`,
});
}
/**
* Show task assigned notification
*/
function notifyTaskAssigned(taskInfo = {}) {
const { task_id, agent_name } = taskInfo;
showNotification('Task Assigned', {
body: `Task assigned to ${agent_name || 'agent'}`,
tag: `task-assigned-${task_id}`,
});
}
/**
* Connect to WebSocket for real-time notifications
*/
function connectWebSocket() {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}/swarm/live`;
wsConnection = new WebSocket(wsUrl);
wsConnection.onopen = () => {
console.log('WebSocket connected for notifications');
};
wsConnection.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
handleWebSocketEvent(data);
} catch (err) {
console.error('Failed to parse WebSocket message:', err);
}
};
wsConnection.onclose = () => {
console.log('WebSocket disconnected, retrying in 5s...');
setTimeout(connectWebSocket, 5000);
};
wsConnection.onerror = (err) => {
console.error('WebSocket error:', err);
};
}
/**
* Handle WebSocket events and trigger notifications
*/
function handleWebSocketEvent(event) {
if (!notificationsEnabled) return;
switch (event.event) {
case 'briefing_ready':
notifyBriefingReady(event.data);
break;
case 'task_completed':
notifyTaskCompleted(event.data);
break;
case 'agent_joined':
notifyAgentJoined(event.data);
break;
case 'task_assigned':
notifyTaskAssigned(event.data);
break;
default:
// Unknown event type, ignore
break;
}
}
/**
* Initialize notifications system
*/
async function init() {
// Request permission on user interaction
const enableBtn = document.getElementById('enable-notifications');
if (enableBtn) {
enableBtn.addEventListener('click', async () => {
const granted = await requestNotificationPermission();
if (granted) {
enableBtn.textContent = 'Notifications Enabled';
enableBtn.disabled = true;
connectWebSocket();
}
});
}
// Auto-request if permission was previously granted
if (Notification.permission === 'granted') {
notificationsEnabled = true;
connectWebSocket();
}
// Listen for briefing ready events via custom event
document.addEventListener('briefing-ready', (e) => {
notifyBriefingReady(e.detail);
});
// Listen for task completion events
document.addEventListener('task-completed', (e) => {
notifyTaskCompleted(e.detail);
});
}
// Expose public API
window.AgentNotifications = {
requestPermission: requestNotificationPermission,
show: showNotification,
notifyBriefingReady,
notifyTaskCompleted,
notifyAgentJoined,
notifyTaskAssigned,
isEnabled: () => notificationsEnabled,
};
// Initialize on DOM ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();