All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 51s
- Agregar boton debug en MessageBubble para ver objeto completo - Agregar boton debug en ChatItem para ver objeto completo - Crear useRealtime composable para conectar a SSE - Agregar indicador de estado SSE en mensajes - Agregar panel debug para ver ultimo evento SSE - Auto-recargar chats/mensajes cuando llegan nuevos mensajes
131 lines
3.6 KiB
TypeScript
131 lines
3.6 KiB
TypeScript
/**
|
|
* Composable for real-time updates via Server-Sent Events
|
|
*/
|
|
|
|
export interface RealtimeEvents {
|
|
'message.received': (data: { instanceId: string; message: any }) => void
|
|
'message.sent': (data: { instanceId: string; message: any }) => void
|
|
'message.status': (data: { instanceId: string; messageId: string; status: string }) => void
|
|
'instance.status': (data: { instanceId: string; status: string; phoneNumber?: string }) => void
|
|
'instance.qr': (data: { instanceId: string; qr: string; qrDataUrl: string }) => void
|
|
}
|
|
|
|
export const useRealtime = () => {
|
|
const eventSource = useState<EventSource | null>('sse', () => null)
|
|
const isConnected = useState('sseConnected', () => false)
|
|
const lastEvent = useState<{ type: string; data: any; timestamp: Date } | null>('sseLastEvent', () => null)
|
|
|
|
// Event listeners
|
|
const listeners = useState<Map<string, Set<Function>>>('sseListeners', () => new Map())
|
|
|
|
const connect = () => {
|
|
// Only run on client
|
|
if (import.meta.server) return
|
|
|
|
// Already connected
|
|
if (eventSource.value?.readyState === EventSource.OPEN) return
|
|
|
|
console.log('[SSE] Connecting to /api/events/stream...')
|
|
|
|
const es = new EventSource('/api/events/stream')
|
|
|
|
es.onopen = () => {
|
|
console.log('[SSE] Connected')
|
|
isConnected.value = true
|
|
}
|
|
|
|
es.onerror = (error) => {
|
|
console.error('[SSE] Error:', error)
|
|
isConnected.value = false
|
|
|
|
// Reconnect after 5 seconds
|
|
setTimeout(() => {
|
|
if (eventSource.value?.readyState === EventSource.CLOSED) {
|
|
connect()
|
|
}
|
|
}, 5000)
|
|
}
|
|
|
|
// Listen for specific event types
|
|
const eventTypes = [
|
|
'message.received',
|
|
'message.sent',
|
|
'message.status',
|
|
'instance.status',
|
|
'instance.qr',
|
|
'instance.pairing'
|
|
]
|
|
|
|
eventTypes.forEach(eventType => {
|
|
es.addEventListener(eventType, (e: MessageEvent) => {
|
|
try {
|
|
const data = JSON.parse(e.data)
|
|
console.log(`[SSE] ${eventType}:`, data)
|
|
|
|
// Update last event for debugging
|
|
lastEvent.value = { type: eventType, data, timestamp: new Date() }
|
|
|
|
// Call registered listeners
|
|
const eventListeners = listeners.value.get(eventType)
|
|
if (eventListeners) {
|
|
eventListeners.forEach(listener => listener(data))
|
|
}
|
|
} catch (err) {
|
|
console.error(`[SSE] Error parsing ${eventType}:`, err)
|
|
}
|
|
})
|
|
})
|
|
|
|
// Generic message handler for debugging
|
|
es.onmessage = (e) => {
|
|
console.log('[SSE] Generic message:', e.data)
|
|
}
|
|
|
|
eventSource.value = es
|
|
}
|
|
|
|
const disconnect = () => {
|
|
if (eventSource.value) {
|
|
eventSource.value.close()
|
|
eventSource.value = null
|
|
isConnected.value = false
|
|
console.log('[SSE] Disconnected')
|
|
}
|
|
}
|
|
|
|
const on = <K extends keyof RealtimeEvents>(event: K, handler: RealtimeEvents[K]) => {
|
|
if (!listeners.value.has(event)) {
|
|
listeners.value.set(event, new Set())
|
|
}
|
|
listeners.value.get(event)!.add(handler as Function)
|
|
|
|
// Return cleanup function
|
|
return () => {
|
|
listeners.value.get(event)?.delete(handler as Function)
|
|
}
|
|
}
|
|
|
|
const off = <K extends keyof RealtimeEvents>(event: K, handler: RealtimeEvents[K]) => {
|
|
listeners.value.get(event)?.delete(handler as Function)
|
|
}
|
|
|
|
// Auto-connect on mount, disconnect on unmount
|
|
onMounted(() => {
|
|
connect()
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
// Don't disconnect on unmount as other components may still need it
|
|
// disconnect()
|
|
})
|
|
|
|
return {
|
|
isConnected,
|
|
lastEvent,
|
|
connect,
|
|
disconnect,
|
|
on,
|
|
off
|
|
}
|
|
}
|