Skip to main content
Glama
NotificationToast.tsx3.79 kB
import { useState, useEffect } from 'react'; import './NotificationToast.css'; interface Notification { id: string; monitorId: string; location: string; timestamp: string; imageDetails?: { resolution?: string; provider?: string; captureDate?: string; }; } interface NotificationToastProps { notification: Notification; onClose: () => void; } export function NotificationToast({ notification, onClose }: NotificationToastProps) { const [isVisible, setIsVisible] = useState(true); useEffect(() => { // Auto-dismiss after 10 seconds const timer = setTimeout(() => { setIsVisible(false); setTimeout(onClose, 300); // Wait for fade animation }, 10000); return () => clearTimeout(timer); }, [onClose]); const handleClose = () => { setIsVisible(false); setTimeout(onClose, 300); }; return ( <div className={`notification-toast ${isVisible ? 'visible' : 'hidden'}`}> <div className="toast-header"> <span className="toast-icon">🛰️</span> <span className="toast-title">New Satellite Imagery Available</span> <button className="toast-close" onClick={handleClose}>×</button> </div> <div className="toast-body"> <p className="toast-location">{notification.location}</p> {notification.imageDetails?.resolution && ( <p className="toast-detail"> <strong>Resolution:</strong> {notification.imageDetails.resolution} </p> )} {notification.imageDetails?.provider && ( <p className="toast-detail"> <strong>Provider:</strong> {notification.imageDetails.provider} </p> )} {notification.imageDetails?.captureDate && ( <p className="toast-detail"> <strong>Captured:</strong> {new Date(notification.imageDetails.captureDate).toLocaleDateString()} </p> )} </div> </div> ); } interface NotificationCenterProps { onNotification?: (notification: Notification) => void; } export function NotificationCenter({ onNotification }: NotificationCenterProps) { const [notifications, setNotifications] = useState<Notification[]>([]); useEffect(() => { console.log('📡 Connecting to SSE endpoint...'); const eventSource = new EventSource('http://localhost:3001/api/notifications/stream'); eventSource.onopen = () => { console.log('✅ SSE connection established'); }; eventSource.onmessage = (event) => { try { const data = JSON.parse(event.data); console.log('📨 SSE message received:', data); if (data.type === 'new_imagery') { const notification = data.notification; setNotifications((prev) => [...prev, notification]); onNotification?.(notification); // Play notification sound (optional) const audio = new Audio('/notification.mp3'); audio.play().catch(() => console.log('No notification sound available')); } } catch (error) { console.error('Error parsing SSE message:', error); } }; eventSource.onerror = (error) => { console.error('❌ SSE connection error:', error); eventSource.close(); }; return () => { console.log('📡 Closing SSE connection'); eventSource.close(); }; }, [onNotification]); const removeNotification = (id: string) => { setNotifications((prev) => prev.filter((n) => n.id !== id)); }; return ( <div className="notification-center"> {notifications.map((notification) => ( <NotificationToast key={notification.id} notification={notification} onClose={() => removeNotification(notification.id)} /> ))} </div> ); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mfuechec/SkyFiMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server