https://d226lax1qjow5r.cloudfront.net/blog/blogposts/create-a-video-help-chat-with-node-js-and-svelte/blog_node-js_svelte_video_1200x600.png

Crear un Video Chat de Ayuda con Node.js y Svelte

Publicado el December 16, 2020

Tiempo de lectura: 9 minutos

Un caso de uso como un chat de ayuda por vídeo para su sitio web es un gran argumento para un marco de front-end. Tal vez el resto del sitio no tiene nada que ver con el chat de vídeo, o tal vez usted desea utilizar el chat en varios lugares o con múltiples configuraciones. Por una variedad de razones, es el tipo de cosa que probablemente desea construir en un componente.

Svelte, en el extremo más nuevo del espectro de marcos front-end, podría ser una opción más accesible. Si aún no estás comprometido con un framework, puede ser más rápido empezar con algo que utiliza HTML, CSS y JavaScript de una manera tan familiar. Y con la variedad de piezas necesarias para hacer que el trabajo de vídeo, menos que aprender es una característica útil para este ejemplo.

Requisitos previos

Para empezar con un nuevo proyecto Svelte, la recomendación es utilizar degituna práctica herramienta que descarga y descomprime sin problemas plantillas de aplicaciones. Puede iniciar un nuevo proyecto Svelte utilizando npx:

npx degit sveltejs/template video-help-chat

Una vez que tengas tu copia, ejecuta npm install para instalar las dependencias.

Ahora puedes navegar al directorio video-help-chat y verás el andamiaje para un nuevo proyecto Svelte. Antes de saltar al código en sí, necesitarás algunas cosas para que el Video chat funcione:

Si no tienes una cuenta de Video API de Vonage, primero deberás inscribirte para una prueba. Todas las herramientas para crear tu servidor de Video se pueden instalar desde npm:

npm -i opentok express concurrently dotenv

Svelte y Express, juntos

Concurrently le permite ejecutar varios comandos a la vez desde un único script en su package.json. Puede utilizarlo para iniciar su servidor Express mientras ejecuta Rollup para reconstruir Svelte si algo cambia. Como ya tiene comandos en scripts para manejar Svelte, puede superponer scripts npm, añadiendo un nuevo script que ejecute los ya existentes:

"scripts": {
    "build": "rollup -c",
    "dev": "rollup -c -w",
    "start": "sirv public",
    "serve": "concurrently \"node server.js\" \"npm run dev\""
  },

Establecer sus credenciales

Es una buena práctica crear un lugar seguro para su clave API y su secreto desde el principio. Cree un archivo .env para almacenar estos y otros datos confidenciales. Antes de confirmar su código, asegúrese de añadir el archivo a .gitignore para que no se registre accidentalmente. Si aloja este código más tarde, su anfitrión probablemente tendrá una manera segura para que usted pueda repoblar la .env allí.

Por ahora, sólo necesitas dos propiedades en .env: tu clave y secreto de la Video API de Vonage. Ambas deben estar entre comillas, sin espacios:

VONAGE_VIDEO_API_KEY="12345678" VONAGE_VIDEO_SECRET="12a3b4c567d89e0f1234567890ab12345678c901"

Servidor

Tu servidor usará las credenciales que proporcionaste para crear un cliente de Video API de Vonage.

La Video API de Vonage se conocía anteriormente como OpenTok. El nombre aún se usa en algunos códigos, y lo seguiremos usando aquí para que el código se parezca más a nuestros tutoriales anteriores.

Empiece por importar Express y crear la aplicación Express. A continuación, puede crear una ruta para /chat. A menudo ves páginas estáticas manejadas cerca de la parte superior de un servidor Express, pero aquí las manejarás casi al final. Esto es para limitar cuanta responsabilidad le damos al lado Svelte de la aplicación, para que no esté tratando de manejar nuestros endpoints del servidor. Finalmente, puedes decirle al servidor que escuche en el puerto 5000.

En /chat es donde ocurre lo interesante. Esta función devolverá las credenciales necesarias para crear el chat de vídeo en el front-end. También gestiona la sesión de videochat de forma muy básica, devolviendo la existente o creando una nueva si no la hay.

require('dotenv').config();
const express = require('express');
const app = express();
app.use(express.json());

let sessionId;

app.get('/chat', function(request, response) {
  if (sessionId) {
    response.send({
      apiKey: process.env.VONAGE_VIDEO_API_KEY,
      sessionId: sessionId,
      token: opentok.generateToken(sessionId)
    });
  } else {
    opentok.createSession(function(err, session) {
      sessionId = session.sessionId;
      
      response.send({
        apiKey: process.env.VONAGE_VIDEO_API_KEY,
        sessionId: sessionId,
        token: opentok.generateToken(sessionId)
      });
    });
  } 
});

app.use(express.static('public'));
app.get('/', function(request, response) {
  response.sendFile(__dirname + '/public/index.html');
});

app.listen(5000);

Para empezar a integrar este ejemplo en un sitio real, la variable sessionId es su punto de entrada. Siempre puede crear una nueva sesión desde el /chat y añadir los ID a una pila, en lugar de a una única variable. Su equipo responsable de responder a los chats de Video puede entonces acceder a esos IDs de sesión para unirse a las llamadas en espera.

Cliente

El modelo de solicitud debe incluir un archivo App.svelte en el archivo /public/src. Debería ser posible ejecutar y probar esto ahora mismo yendo a su terminal y escribiendo:

npm run serve

Deberías ver la página Svelte hello world si abres tu navegador y vas a 'localhost:5000'.

Mientras tienes App.svelte abierto, sigue adelante y añade referencias al componente que crearemos a continuación. Primero, importa el componente, que llamaremos Chat.svelte:

<script>
	export let name;

	import Chat from './Chat.svelte';
</script>

En la parte inferior de la página, encima de la etiqueta <style> añada el componente:

<main>
	<h1>Hello {name}!</h1>
	<p>Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn how to build Svelte apps.</p>
</main>

<Chat/>

Componente de chat

Es posible que su aplicación en ejecución tenga algunas quejas en este momento, así que añada rápidamente el archivo Chat.svelte al archivo /public/src. Mientras lo haces, también puedes añadir ChatButton.svelte y VideoChat.svelte archivos.

El componente Chat va a ser simplemente un contenedor para el chat, que tiene dos estados. Inicialmente, al usuario se le presentará un enlace para iniciar el chat. Una vez que hagan clic en el enlace, entrarán en el chat de vídeo propiamente dicho. El componente Chat gestiona el cambio entre los dos, que serán encapsulados en sus propios componentes.

Si no has trabajado con muchos componentes de Svelte, este es un buen componente mínimo para echar un vistazo. Puedes ver que es sólo JavaScript, HTML y CSS. Se parece mucho a una página HTML estática, menos el contenido y la meta información:

<script>
  export let collapsed = true;

  import ChatButton from './ChatButton.svelte';
  import Conversation from './VideoChat.svelte';
</script>


<div class="chatContainer" class:expanded="{!collapsed}">
  {#if collapsed}
    <ChatButton bind:showButton={collapsed}/>
  {:else}
    <Conversation/>
  {/if}
</div>

<style>
  .chatContainer {
    position: fixed;
    bottom: 2em;
    right: 2em;
  }
  .expanded {
    width: auto;
    left: 2em;
    min-height: 10em;
  }
</style>

Dado que es sólo un contenedor para el estado, tiene sentido que el componente tenga sólo una propiedad, collapsed. Importa los otros dos componentes y determina cuál mostrar utilizando una estructura condicional dentro del marcado. Finalmente, algo de CSS pega el contenedor a la parte inferior derecha. Si el chat está abierto, una clase CSS condicional estirará el contenedor.

Funcionalidad del botón de chat

La mayor parte del componente ChatButton es CSS decorativo para que parezca un pequeño globo sonoro. Sin embargo, también es un ejemplo de cómo Svelte maneja eventos y se comunica entre componentes.

El componente sólo exporta una propiedad, showButton. No se utiliza dentro del componente, sino en su padre. Estaba vinculada a la propiedad collapsed del componente Chat dentro del marcado Chat:

    <ChatButton bind:showButton={collapsed}/>

Cuando se pulsa el botón, se llama a la función openChat cambiando el valor de showButton a false y, en consecuencia, establece collapsed a false en el padre. Esto abrirá la interfaz de chat.

<script>
  export let showButton = true;

  function openChat() {
    showButton = false;
  }
</script>

<button on:click={openChat}>Chat now</button>

<style>
  button {
    position: relative;
    background: #ac57c8;
    border: none;
    border-radius: 10px;
    padding: 20px;
    color: #fff;
    font-weight: bold;
    cursor: pointer;
    box-shadow: 5px 15px 10px rgba(0,0,0,.5);
  }
  button:hover, button:active {
    color: #83C4F1;
  }
  button:after {
    content: '';
    position: absolute;
    right: 0;
    top: 60%;
    width: 0;
    height: 0;
    border: 20px solid transparent;
    border-left-color: #ac57c8;
    border-right: 0;
    border-bottom: 0;
    margin-top: -10px;
    margin-right: -20px;
  }
</style>

Videochat

Hemos dejado lo bueno (el JavaScript, por supuesto) para el final. No hay mucho HTML y CSS en el componente VideoChat. Hay marcadores de posición para el editor y el suscriptor (vistas de usted y la persona con la que está chateando), y más estilo de globo de habla para el contenedor. El resto es JS.

Para crear un Videochat, tendrás que hacer varias cosas:

  • Obtener la sesión (recién creada o en curso, al cliente le da igual) del servidor.

  • Inicialícelo

  • Esperar a que se cree un flujo y suscribirse a él

  • Esperar a que se desconecte la sesión

  • Inicializar la publicación (envío de vídeo y audio)

  • Conectarse a la sesión y publicar en ella

La mayor parte del trabajo se realizará en una función llamada initSessionuna vez que el cliente haya obtenido una sesión del servidor. Con la carne de initSession el componente no es demasiado complejo. Utiliza fetch para obtener la sesión y define un controlador de errores. A continuación, define los marcadores de posición de marcado para el chat de vídeo y el CSS para diseñarlo:

<script>
  fetch('/chat').then(function fetch(res) {
    return res.json();
  }).then(function fetchJson(json) {
    if (json.error) {
      handleError(error);
    } else {
      initSession(json.apiKey, json.sessionId, json.token);
    }
  }).catch(function catchErr(error) {
    handleError(error);
  });

  function handleError(error) {
    if (error) {
      console.error(error);
    }
  }

  function initSession(apiKey, sessionId, token) {}
</script>


<div class="conversation">
  <div id="subscriber"></div>
  <div id="publisher"></div>
</div>

<style>
  .conversation {
    position: relative;
    width: auto;
    min-height: 10em;
    background: #ac57c8;
    border: none;
    border-radius: 10px;
    padding: 20px;
    box-shadow: 5px 15px 10px rgba(0,0,0,.5);
  }
  .conversation:after {
    content: '';
    position: absolute;
    right: 0;
    top: 60%;
    width: 0;
    height: 0;
    border: 20px solid transparent;
    border-left-color: #ac57c8;
    border-right: 0;
    border-bottom: 0;
    margin-top: -10px;
    margin-right: -20px;
  }
</style>

El contenido de initSession son principalmente llamadas de retorno. Primero se crea una escucha para streamCreatedy dentro de él define un objeto options y lo utiliza para llamar a session.subscribe(). También puedes crear una escucha para sessionDisconnected. Actualmente, este componente no da información a su padre, pero sería más robusto si avisara cuando el chat termina. El manejador sessionDisconnected sería un lugar para hacerlo.

Puede crear publisherOptions que tengan el mismo aspecto que su subscriberOptions y simplemente indicar que el elemento Video debe anexarse a su contenedor al 100% de altura y anchura. A continuación, puede inicializar el editor.

Por último, con su editor listo para funcionar, conéctese a la sesión con session.connect(). Una vez conectado, puede empezar a publicar con session.publish().

function initSession(apiKey, sessionId, token) {
    var session = OT.initSession(apiKey, sessionId);

    // Subscribe to a newly created stream
    session.on('streamCreated', function streamCreated(event) {
      var subscriberOptions = {
        insertMode: 'append',
        width: '100%',
        height: '100%'
      };
      session.subscribe(event.stream, 'subscriber', subscriberOptions, handleError);
    });

    session.on('sessionDisconnected', function sessionDisconnected(event) {
      console.log('You were disconnected from the session.', event.reason);
    });

    // initialize the publisher
    var publisherOptions = {
      insertMode: 'append',
      width: '100%',
      height: '100%'
    };
    var publisher = OT.initPublisher('publisher', publisherOptions, handleError);

    // Connect to the session
    session.connect(token, function callback(error) {
      if (error) {
        handleError(error);
      } else {
        // If the connection is successful, publish the publisher to the session
        session.publish(publisher, handleError);
      }
    });
  }

Pruébelo

Si tu servidor ha estado funcionando todo este tiempo, deberías poder ver parte o todo tu código funcionando en la pestaña abierta de tu navegador. Sin embargo, probablemente necesites reiniciar tu servidor. En tu terminal, apaga todo presionando Ctrl+C, luego inicia Express y Svelte de nuevo con npm run serve.

Abra o vuelva a localhost:5000 en su navegador. La forma más fácil de probar el chat es abrir otro navegador y también ir a localhost:5000. Esto no es muy sofisticado, pero el eco que oigas debería confirmarte que el chat funciona.

Próximos pasos

Ahora que tienes un chat básico, hay mucho más que puedes hacer con la Video API de Vonage. O puedes optar por mantener las funciones reducidas por ahora y realizar algunos cambios en la forma en que el servidor suministra las sesiones para que los usuarios puedan "preguntar" desde esta interfaz y los usuarios internos puedan "responder" desde otra.

También hay muchas más cosas que puedes hacer con Svelte, mostradas en su excelente tutorial. Si vas a incorporar más funciones a tu chat (por ejemplo, recopilar el nombre o el correo electrónico del usuario), estas herramientas pueden resultarte muy útiles.

Compartir:

https://a.storyblok.com/f/270183/250x250/f231d97f1b/garann-means.png
Garann MeansDesarrollador Educador

Soy desarrollador de JavaScript y educador de desarrolladores en Vonage. A lo largo de los años me han entusiasmado las plantillas, Node.js, las aplicaciones web progresivas y las estrategias offline-first, pero lo que siempre me ha encantado es una API útil y bien documentada. Mi objetivo es hacer que tu experiencia usando nuestras APIs sea la mejor posible.