Análisis Profundo del Cliente (Frontend)
Este documento detalla el funcionamiento interno de las piezas clave del cliente de Streamlyra y cómo interactúan entre sí.
Ciclo de Vida de Autenticación (AuthProvider)
La autenticación es el pilar del cliente. Este es el flujo detallado:
- Arranque (
Bootstrap): Enmain.tsxse monta la aplicación.AuthProviderverifica ellocalStorage. - Validación: Si hay un usuario en el
storage, el hookuseAuthinicia una llamada aauthService.getMe().- Si el servidor responde con los datos del usuario, el estado cambia a
authenticated. - Si el servidor devuelve un error 401 (token expirado), se limpia el
storagey el estado pasa aunauthenticated.
- Si el servidor responde con los datos del usuario, el estado cambia a
- Sincronización (
AuthCallback): Al regresar de OAuth, elAuthCallbackintercambia el código, loguea al usuario y asegura la recarga forzada (await fetchConnections(true)) del estado de las conexiones antes de redirigir al Dashboard, evitando estados inconsistentes o destellos en la UI. - Redirección: El componente
ProtectedRouteobserva este estado:- Mientras el estado es
unknown(validando), muestra elSpinner. - Si es
unauthenticated, redirige automáticamente a la página de inicio o login.
- Mientras el estado es
- Cierre de Sesión Atómico: El botón de Logout dispara un proceso coordinado:
- Se activa una referencia
isLoggingOutpara blindar el componente contra validaciones automáticas concurrentes. - Se limpia el estado local (User/Status) de forma instantánea.
- Se redirige a
/antes de esperar la respuesta del servidor. - El servidor borra las cookies JWT y CSRF en milisegundos, notificando a los proveedores de chat en segundo plano.
- Se activa una referencia
- Gestión de Sesión: Un singleton (
SessionManager) escucha errores 401 en cualquier parte de la app para forzar el logout si el token es invalidado por el servidor durante el uso.
Comunicación en Tiempo Real
Streamlyra utiliza una arquitectura híbrida entre HTTP (REST) y Sockets (Socket.io).
El Hook useSocket
Es el puente entre el servidor y la interfaz. Se encarga de:
- Autenticación del Canal: Cuando el usuario entra al Dashboard, el socket se conecta enviando las credenciales de sesión.
- Normalización: Recibe eventos crudos del servidor y los transforma si es necesario antes de disparar callbacks.
- Resiliencia: Si la conexión se pierde, el Dashboard muestra un indicador visual (de
DashboardHeader) y el hook intenta reconectar automáticamente.
Procesamiento del Chat Unificado
Este es el flujo por el que pasa un mensaje desde que llega hasta que se renderiza:
- Evento de Socket: El servidor emite
chat_message. - Hook
useSocket: Recibe el mensaje crudo. - Store
useChatStore(Zustand): El mensaje se inyecta en el store global.- Batching: Los mensajes se agrupan en micro-lotes para evitar ráfagas que congelen la UI.
- Retención Inteligente: Se mantiene un límite estricto (ej: 200 mensajes) mediante un recorte automático del estado de Zustand.
- Componente
ChatFeed: React re-renderiza solo el área de mensajes observando los cambios atómicos del store. - Transformación Visual:
MessageContenttraduce emotes y enlaces en tiempo real.
Gestión de Conexiones (useConnectionsStore)
Gestionar múltiples plataformas simultáneamente requiere una sincronización atómica:
- Inicialización: Se cargan las conexiones desde la API y se inyectan en el store de Zustand.
- Sincronización de Estados: El store maneja estados complejos como
searching(buscando en vivo) owaiting_stream. - Protección de Datos (Cache Logic): El sistema detecta si un dato proviene de un Polling (API) o un Push (Socket), evitando que una respuesta lenta de la API sobrescriba un estado más reciente del Socket (ej: que un "Offline" viejo pise un "Online" nuevo).
- Rendimiento: Gracias a Zustand, las estadísticas (viewers, followers) se actualizan en componentes específicos sin re-renderizar todo el Dashboard.
Sistema de Diálogos Global
Para evitar la repetición de lógica y estado de modales en cada componente, Streamlyra utiliza un patrón de Servicio Imperativo con Promesas:
- Servicio (
dialog.service.ts): Un objeto singleton que expone métodos como.confirm(),.alert()y.prompt(). Estos métodos devuelven una Promesa que se resuelve cuando el usuario interactúa con el diálogo. - Proveedor (
DialogProvider.tsx): Un componente global en la raíz de la app que escucha los cambios en el servicio y renderiza el componenteDialogcuando es necesario. - Componente (
Dialog.tsx): Reutiliza la misma UI para todos los tipos de aviso, garantizando consistencia visual y accesibilidad (ARIA labels). Se refactorizó aislando el estado interno mediante un componenteDialogInnerContentpara asegurar una gestión limpia del input del usuario y el auto-focus. - Flujo Limpio: El código que invoca el diálogo es asíncrono y directo:tsx
const confirmed = await dialog.danger("¿Borrar?"); if (confirmed) { /* ... acción destructiva */ }
Calidad y Pruebas con Fast-Check
A diferencia de las pruebas unitarias tradicionales que usan un solo ejemplo de prueba, en Streamlyra usamos Pruebas Basadas en Propiedades (PBT) en el frontend:
- Escenario: Probar que el formateador de mensajes no rompa la UI con caracteres extraños.
- Lógica:
fast-checkgenera cadenas aleatorias de texto, incluyendo emojis, caracteres Unicode, enlaces malformados y scripts. - Garantía: Si el componente sobrevive a miles de estas entradas aleatorias sin lanzar excepciones, consideramos que es robusto.
Diseño y Estética
La UI sigue un patrón de "Glassmorphism" moderado:
- Colores: Usamos una paleta oscura (
zinc-950) con acentos en púrpura (violet-600) para la marca. - Feedback Visual: Cada baneo o borrado de mensaje muestra una notificación (Toast) para confirmar que la acción de moderación fue exitosa.
- Overlays: La página
/overlay/chatestá diseñada para ser 100% transparente para que no bloquee el video en OBS.