Ran command: git status --short

Ran command: `git diff --cached`
Ran command: `git diff --cached SPECIFICATION.md src/components/layout/AppShell.tsx`

feat(ws): handle nested WS payload envelope and handle assigned conversation events

* Automatically unwrap double-nested WS payloads (`payload.payload`) in `WsClient`.
* Handle `conversation_assigned` WS event in `AppShell` by fetching details via REST.
* Deduplicate `upsertConversation` calls and update conversation handling logic.
* Add comprehensive test suites for WS unwrapping and store deduplication.
This commit is contained in:
2026-07-31 14:13:57 -05:00
parent 83e3ec2cff
commit 9f86dc0d0d
5 changed files with 550 additions and 24 deletions
+19 -1
View File
@@ -105,7 +105,7 @@ class WsClient {
this.ws.onmessage = (event: MessageEvent) => {
try {
const data = JSON.parse(event.data as string);
let data: Record<string, unknown> = JSON.parse(event.data as string);
// Handle auth response first
if (data.status === 'authenticated') {
@@ -125,6 +125,24 @@ class WsClient {
return;
}
// Normalizar doble envoltura (nested envelope del backend)
// El backend envía: { type, eventId, payload: { type, eventId, payload: { datos } } }
// Debemos aplanar a: { type, eventId, payload: { datos } }
if (
data.payload &&
typeof data.payload === 'object' &&
!Array.isArray(data.payload) &&
!data.status && // NUNCA para mensajes de control como {"status":"authenticated"}
(data.payload as Record<string, unknown>).type &&
(data.payload as Record<string, unknown>).payload
) {
// Conservar el type y eventId externos, usar el payload interno
data = {
...data,
payload: (data.payload as Record<string, unknown>).payload,
};
}
// Delegate business events to registered callback
if (this.onMessageCallback) {
const envelope: WSEnvelope = data;