Skip to content

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:

  1. Arranque (Bootstrap): En main.tsx se monta la aplicación. AuthProvider verifica el localStorage.
  2. Validación: Si hay un usuario en el storage, el hook useAuth inicia una llamada a authService.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 storage y el estado pasa a unauthenticated.
  3. Sincronización (AuthCallback): Al regresar de OAuth, el AuthCallback intercambia 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.
  4. Redirección: El componente ProtectedRoute observa este estado:
    • Mientras el estado es unknown (validando), muestra el Spinner.
    • Si es unauthenticated, redirige automáticamente a la página de inicio o login.
  5. Cierre de Sesión Atómico: El botón de Logout dispara un proceso coordinado:
    • Se activa una referencia isLoggingOut para 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.
  6. 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:

  1. Evento de Socket: El servidor emite chat_message.
  2. Hook useSocket: Recibe el mensaje crudo.
  3. 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.
  4. Componente ChatFeed: React re-renderiza solo el área de mensajes observando los cambios atómicos del store.
  5. Transformación Visual: MessageContent traduce emotes y enlaces en tiempo real.

Gestión de Conexiones (useConnectionsStore)

Gestionar múltiples plataformas simultáneamente requiere una sincronización atómica:

  1. Inicialización: Se cargan las conexiones desde la API y se inyectan en el store de Zustand.
  2. Sincronización de Estados: El store maneja estados complejos como searching (buscando en vivo) o waiting_stream.
  3. 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).
  4. 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:

  1. 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.
  2. Proveedor (DialogProvider.tsx): Un componente global en la raíz de la app que escucha los cambios en el servicio y renderiza el componente Dialog cuando es necesario.
  3. 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 componente DialogInnerContent para asegurar una gestión limpia del input del usuario y el auto-focus.
  4. 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-check genera 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/chat está diseñada para ser 100% transparente para que no bloquee el video en OBS.

Liberado bajo la Licencia MIT.