Spaces:
Sleeping
Sleeping
File size: 2,031 Bytes
9dfccd9 68af3c5 9dfccd9 68af3c5 9dfccd9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | import { useCallback, useEffect, useRef, useState } from 'react'
import { env } from '@/config/env'
import { useUIStore } from '@/stores/uiStore'
export interface AppNotification {
id: string
type: 'query_answered' | 'escalation_spike' | 'breaking_change' | 'data_sync_failed' | 'knowledge_gap'
message: string
timestamp: string
read: boolean
}
export function useNotifications() {
const [notifications, setNotifications] = useState<AppNotification[]>([])
const wsRef = useRef<WebSocket | null>(null)
const addToast = useUIStore.getState().addToast
const connect = useCallback(() => {
const ws = new WebSocket(`${env.wsBaseUrl}/ws`)
wsRef.current = ws
ws.onmessage = (evt) => {
try {
const msg = JSON.parse(evt.data as string)
// Skip keepalive pings and any frame without a real message
if (!msg.message || msg.type === 'ping') return
const notification: AppNotification = {
...(msg as Omit<AppNotification, 'id' | 'read'>),
id: crypto.randomUUID(),
read: false,
}
setNotifications((prev) => [notification, ...prev].slice(0, 50))
addToast({ type: 'info', message: notification.message })
} catch {
// Non-JSON or unknown frame — ignore
}
}
// Gracefully no-op if endpoint is 404 or unavailable — notifications are non-critical
ws.onerror = () => {}
ws.onclose = () => {}
}, [addToast])
const markRead = useCallback((id: string) => {
setNotifications((prev) =>
prev.map((n) => (n.id === id ? { ...n, read: true } : n)),
)
}, [])
const markAllRead = useCallback(() => {
setNotifications((prev) => prev.map((n) => ({ ...n, read: true })))
}, [])
const unreadCount = notifications.filter((n) => !n.read).length
useEffect(() => {
connect()
return () => {
wsRef.current?.close()
wsRef.current = null
}
}, [connect])
return { notifications, unreadCount, markRead, markAllRead }
}
|