Skip to main content

Sockets

El frontend usa socket.io-client con dos servicios que abren cada uno su propia conexión al namespace /chat de environment.socketUrl, con transports: ['websocket']:

ServicioArchivoEventos
ChatSocketServicecore/services/ui/chat-socket.service.tsTickets, mensajes, notas, WhatsApp, mensajes programados (SOCKET_EVENTS)
InternalChatSocketServicecore/services/ui/internal-chat-socket.service.tsChat interno entre agentes (IM_SOCKET_EVENTS, prefijo im:)

Las constantes están en core/helpers/global/chat-socket.constants.ts e internal-chat-socket.constants.ts; los payloads tipados en core/interfaces/ui/chat-socket.interface.ts. Contraparte en el backend: Chat.

Ciclo de vida​

  • ContentComponent conecta ambos servicios cuando el perfil tiene _id y los desconecta en ngOnDestroy. ChatPageComponent y InternalChatPageComponent también llaman a connect; es idempotente (si ya está conectado no hace nada, y reutiliza el socket existente para no duplicar listeners).
  • Al evento connect emite authenticate con { sendType: 'agent', token, userId, lang, isPaused? }. El token es el JWT del agente (el mismo de x-api-key); el backend deriva el tenant del usuario, no se envía ningún token de tenant.
  • Estado de conexión: ChatConnectionStatus = 'idle' | 'connecting' | 'authenticated' | 'error', expuesto en connectionStatus$ (lo consume connection-status-bar, que combina ambos sockets).
  • reconnected$ emite en cada reautenticación posterior a la primera: los socket.io no reenvían los eventos perdidos durante la caída, quien escuche debe recargar su estado.
  • Reintento acotado ante io server disconnect: hasta 5 reintentos con espera creciente de 1 s por intento (SERVER_DISCONNECT_MAX_RETRIES, SERVER_DISCONNECT_RETRY_DELAY_MS), porque socket.io no reintenta ese motivo por sí solo.
  • Modo pausa del agente: setAgentStatus(isPaused) emite set-agent-status; isPaused$ refleja el valor y se reenvía al autenticar.

Eventos emitidos por el frontend (chat)​

EventoMétodoPayload
authenticateinternoAuthenticateEmit
join-ticket / leave-ticketjoinTicket, leaveTicket{ ticketId }
send-messagesendMessage{ ticketId, content, parentId?, signature? }
send-filesendFile{ ticketId, fileId, parentId?, caption? }
send-notesendNoteSendNoteEmit
mark-readmarkRead{ ticketId }
message-deliveredmarkDelivered{ ticketId, messageIds }
set-agent-statussetAgentStatus{ isPaused }

MessageService no tiene método de alta: el envío de mensajes, archivos y notas del agente usa estos emits. El enrutado al canal (WhatsApp, Instagram, Web) lo resuelve el backend; ver Meta.

Eventos recibidos​

EventoObservable / efecto
authenticated, exception, connect_error, disconnectActualizan connectionStatus$
new_message, new-file-message, new_notenewMessage$, newFileMessage$, newNote$
ticket-joinedticketJoined$
message_ack_changedmessageAckChanged$; payload { ticketId, messageIds, ack, at, failureReason? }
ticket_updatedDespacha ticketUpdatedFromSocket + ticketUpdated$
ticket_assignedDespacha ticketAssignedFromSocket + ticketAssigned$
ticket_createdticketCreated$
ticket_status_changedDespacha ticketStatusChangedFromSocket + ticketStatusChanged$
ticket_reactivated, ticket_deletedDespachan la acción de NgRx y emiten el subject
ticket_lock_changedDespacha ticketLockChangedFromSocket
contact_presence_changedDespacha contactPresenceChangedFromSocket
scheduled_message_sent, _cancelled, _failed, _created, _updatedAcciones del slice scheduledMessages
whatsapp_qrwhatsappQr$: { connectionId, qr }
whatsapp_session_statuswhatsappSessionStatus$: { connectionId, sessionState, phoneJid? }

Consumo por componentes​

  • ChatConversationComponent filtra messageAckChanged$ por su ticket y llama a applyAck; ver Estado failed.
  • ChatListComponent despacha ticketLastMessageAckChanged y ticketClearUnread (cuando ack === 'read').
  • ConnectionsTableComponent y ConnectionWhatsappModalComponent escuchan whatsapp_* para pintar el estado de la sesión y el QR; ver UI de conexiones.

Chat interno (im:)​

Eventos emitidos: im:join-conversation, im:leave-conversation, im:send-message, im:mark-read, im:typing, im:stop-typing. Eventos recibidos: im:conversation-joined, im:new-message, im:conversation-created, im:conversation-updated, im:group-updated, im:member-added, im:member-removed, im:messages-read, im:presence-changed, im:user-typing, im:user-stop-typing. Autentica con el mismo evento authenticate y comparte los eventos de estado (authenticated, exception, connect_error, disconnect).