https://d226lax1qjow5r.cloudfront.net/blog/blogposts/video-with-text-chat/Blog_Vonage-Video-API_Chat_1200x600.png

Añade la función de mensajes de texto a un videochat con la Video API de Vonage

Publicado el April 29, 2021

Tiempo de lectura: 10 minutos

Esta serie de tutoriales explorará la Video API de Vonage (anteriormente TokBox OpenTok) y lo que puedes crear con ella. La Video API es muy robusta y altamente personalizable, y en cada post mostraremos cómo implementar una función específica usando la API. En esta ocasión veremos cómo añadir mensajes de texto a un chat básico de audio-vídeo.

Como esta aplicación requerirá algo de código del lado del servidor, utilizaremos Glitch para facilitar la configuración. También puede descargar el código de este proyecto Glitch y desplegarlo en su propio servidor o plataforma de alojamiento de elección (probablemente puede requerir algunos ajustes de configuración en función de los requisitos de su plataforma).

No vamos a utilizar ningún marco de front-end para esta serie, sólo Javascript de vainilla para mantener el foco en la propia Video API. Al final de este tutorial, usted debe ser capaz de tener una aplicación de chat de audio-vídeo con funcionalidad de chat de texto. El chat de texto se implementa utilizando la API de señalización.

Screenshot of video chat with text chatScreenshot of video chat with text chat

El código final para esta aplicación se puede encontrar en este repositorio GitHub o en remezclado en Glitch.

Requisitos previos

Antes de comenzar, necesitarás una cuenta de Video API de Vonage, que puedes crear gratis aquí. También necesitarás Node.js instalado (si no estás usando Glitch).

Este tutorial se basa en el primer post introductorio de la serie: Creación de un Video Chat Básico. Si es la primera vez que utilizas la Video API, te recomendamos que lo leas porque cubre la siguiente configuración básica:

  • Crear un proyecto de Video API de Vonage

  • Instalación en Glitch

  • Estructura básica del proyecto

  • Iniciar una sesión

  • Conectarse a la sesión, suscribirse y publicar

  • Estilos básicos de diseño para un chat de vídeo

Conceptos básicos del chat de texto con Video API de Vonage

La implementación del chat de texto con la Video API se realiza a través de la API de señalización. Este mecanismo de señalización permite a los clientes conectados a una sesión enviarse texto y datos. Por ahora sólo nos centraremos en el texto.

La Video API Client SDK enviará un evento cuando el cliente reciba una señal. Para un chat de texto básico en el que los mensajes son visibles para todos los clientes conectados, utilizaremos el método signal() de la Sesión de la sesión. Los clientes participantes recibirán esa señal escuchando el evento signal enviado por el objeto Sesión de la sesión.

Para conocer en profundidad a qué se puede acceder a través de la función Sesión sus propiedades, métodos y eventos, consulte la Referencia del SDK para el objeto Session.

Configuración inicial

Como estamos construyendo un videochat básico, empieza por remezclar el proyecto del videochat básico construido en el tutorial anterior. Haga clic en el botón grande Remix abajo para hacer eso. 👇

remix this

Su estructura de carpetas debe parecerse a algo como esto:

Folder structure of the projectFolder structure of the project

Como mencionamos al principio, TokBox OpenTok es ahora Vonage Video API. No hemos hecho ningún cambio en los nombres de nuestros paquetes, por lo que seguirás haciendo referencia a OpenTok en tu código.

Si has remezclado el proyecto Glitch, tu archivo server.js debería tener este aspecto:

const express = require("express");
const app = express();
const OpenTok = require("opentok");
const OT = new OpenTok(process.env.API_KEY, process.env.API_SECRET);

let sessions = {};

app.use(express.static("public"));

app.get("/", (request, response) => {
  response.sendFile(__dirname + "/views/landing.html");
});

app.get("/session/:room", (request, response) => {
  response.sendFile(__dirname + "/views/index.html");
});

app.post("/session/:room", (request, response) => {
  const roomName = request.params.room;
  // Check if the session already exists
  if (sessions[roomName]) {
    // Generate the token
    generateToken(roomName, response);
  } else {
    // If the session does not exist, create one
    OT.createSession((error, session) => {
      if (error) {
        console.log("Error creating session:", error);
      } else {
        // Store the session in the sessions object
        sessions[roomName] = session.sessionId;
        // Generate the token
        generateToken(roomName, response);
      }
    });
  }
});

function generateToken(roomName, response) {
  // Configure token options
  const tokenOptions = {
    role: "publisher",
    data: `roomname=${roomName}`
  };
  // Generate token with the Video API Client SDK
  let token = OT.generateToken(
    sessions[roomName],
    tokenOptions
  );
  // Send the required credentials back to to the client
  // as a response from the fetch request
  response.status(200);
  response.send({
    sessionId: sessions[roomName],
    token: token,
    apiKey: process.env.API_KEY
  });
}

const listener = app.listen(process.env.PORT, () => {
  console.log("Your app is listening on port " + listener.address().port);
});

Para poner en marcha el chat de vídeo, vaya al archivo .env e introduce la clave API y el secreto de tu proyecto, que puedes encontrar en el panel de control. Una vez hecho esto, vamos a trabajar en el código del lado del cliente para obtener el chat de texto de trabajo antes de volver a visitar el server.js de nuevo.

Añadir el marcado obligatorio

Nuestra aplicación constará de 2 páginas. Una página de aterrizaje con dos entradas de texto. Una para que los usuarios creen una sesión, a la que llamaremos "Sala", para que los siguientes participantes puedan unirse a esa misma "Sala". La otra es para que los usuarios introduzcan un nombre que puedan utilizar para identificarse.

La página tendrá un simple elemento de formulario con dos campos de entrada para que los usuarios envíen su nombre de habitación y su nombre de usuario. Vamos a añadir el campo de nombre de usuario al formulario.

<form class="registration" id="registration">
  <label>
    <span>Room</span>
    <input
      type="text"
      name="room-name"
      placeholder="Enter room name"
      required
    />
  </label>

  <!-- Add the user name input field and label -->
  <label>
    <span>User name</span>
    <input
      type="text"
      name="user-name"
      placeholder="Enter your name"
      required
    />
  </label>
  <button>Enter</button>
</form>

También tenemos que añadir un chatbox a la página index.html página. Vamos a añadir el marcado para una ventana de chat con una cabecera, un área para mostrar mensajes y una entrada en la parte inferior para escribir y enviar mensajes. Para maximizar el espacio en pantalla, la ventana de chat estará fuera de la pantalla por defecto y sólo se activará cuando hagas clic en el botón de chat de la esquina inferior derecha de la página.

Chat button to trigger chat windowChat button to trigger chat window

Añade el siguiente marcado a tu página, añadiremos los estilos para que el botón tenga el aspecto del diseño anterior en la siguiente sección.

<button class="btn-chat" id="showChat" aria-label="Show chat">
  <svg viewBox="0 0 512 512">
    <path
      fill="white"
      d="m512 346.5c0-63.535156-36.449219-120.238281-91.039062-147.820312-1.695313 121.820312-100.460938 220.585937-222.28125 222.28125 27.582031 54.589843 84.285156 91.039062 147.820312 91.039062 29.789062 0 58.757812-7.933594 84.210938-23.007812l80.566406 22.285156-22.285156-80.566406c15.074218-25.453126 23.007812-54.421876 23.007812-84.210938zm0 0"
    />
    <path
      fill="white"
      d="m391 195.5c0-107.800781-87.699219-195.5-195.5-195.5s-195.5 87.699219-195.5 195.5c0 35.132812 9.351562 69.339844 27.109375 99.371094l-26.390625 95.40625 95.410156-26.386719c30.03125 17.757813 64.238282 27.109375 99.371094 27.109375 107.800781 0 195.5-87.699219 195.5-195.5zm-225.5-45.5h-30c0-33.085938 26.914062-60 60-60s60 26.914062 60 60c0 16.792969-7.109375 32.933594-19.511719 44.277344l-25.488281 23.328125v23.394531h-30v-36.605469l35.234375-32.25c6.296875-5.761719 9.765625-13.625 9.765625-22.144531 0-16.542969-13.457031-30-30-30s-30 13.457031-30 30zm15 121h30v30h-30zm0 0"
    />
  </svg>
</button>

También queremos añadir el marcado de la ventana de chat al archivo index.html archivo.

Bare bones chat window which will slide in from the rightBare bones chat window which will slide in from the right

<aside id="chatWindow">
  <header class="chat-header">
    <h1><span id="roomName"></span>Chat</h1>
    <button class="btn-close" id="closeChat" aria-label="Close chat">
      <svg viewBox="0 0 47.971 47.971" role="img">
        <path
          d="M28.228 23.986L47.092 5.122a2.998 2.998 0 000-4.242 2.998 2.998 0 00-4.242 0L23.986 19.744 5.121.88a2.998 2.998 0 00-4.242 0 2.998 2.998 0 000 4.242l18.865 18.864L.879 42.85a2.998 2.998 0 104.242 4.241l18.865-18.864L42.85 47.091c.586.586 1.354.879 2.121.879s1.535-.293 2.121-.879a2.998 2.998 0 000-4.242L28.228 23.986z"
        />
      </svg>
    </button>
  </header>

  <section id="messageArea" class="messages"></section>

  <form class="chat-form" id="chatForm">
    <input id="chatInput" type="text" />
    <input type="submit" value="Send" />
  </form>
</aside>

Estilizar los elementos relacionados con el chat

Nuestra interfaz de chat estará oculta hasta que sea necesario, ya que la función principal de esta aplicación es el chat de vídeo. Para activar la interfaz de chat, los usuarios harán clic en el botón de chat situado en la esquina inferior derecha de la página. Este botón tiene un icono SVG para indicar que activa el chat.

.btn-chat {
  height: 3.5em;
  width: 3.5em;
  background-color: black;
  border-radius: 50%;
  box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.2), 0 3px 6px 0 rgba(0, 0, 0, 0.19);
  position: fixed;
  right: 1em;
  bottom: 1em;
}

.btn-chat svg {
  height: 1.5em;
  width: 1.5em;
}

Estamos utilizando CSS para transformar la ventana de chat fuera de la ventana gráfica por defecto. Cuando alguien haga clic en el icono del chat, se activará una clase CSS que cambia el valor de modo que la ventana se deslice a la vista desde el lado derecho de la pantalla. translateX para que la ventana se deslice hacia la vista desde el lado derecho de la pantalla.

aside {
  position: fixed;
  top: 0;
  right: 0;
  transform: translateX(100%);
  display: flex;
  flex-direction: column;
  min-width: 20em;
  width: 25%;
  height: 100%;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.12), 0 1px 3px rgba(0, 0, 0, 0.24);
  transition: transform 0.5s ease;
}

aside.active {
  transform: translateX(0);
}

Añadamos también algunos estilos para la cabecera, el área de mensajes y el formulario de entrada de mensajes.

.chat-header {
  background-color: white;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.12), 0 1px 3px rgba(0, 0, 0, 0.24);
  display: flex;
  align-items: center;
}

.btn-close {
  margin-left: auto;
  height: 2em;
  width: 2em;
  background: transparent;
  border: none;
  cursor: pointer;
}

.btn-close svg {
  height: 1em;
  width: 1em;
}

.messages {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow-y: scroll;
  padding: 0.5em;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.12), 0 1px 3px rgba(0, 0, 0, 0.24);
  background-color: rgba(255, 255, 255, 0.75);
}

.messages p {
  margin-bottom: 0.5em;
  display: flex;
  word-break: break-word;
}

.chat-form {
  padding: 0.5em;
  background-color: white;
  display: flex;
  align-items: center;
}

.chat-form input[type="text"] {
  flex: 1;
}

.chat-form input[type="submit"] {
  margin-left: 0.5em;
  align-self: stretch;
}

Con estos estilos, deberías poder ver un icono de chat en la esquina inferior derecha del index.html después de entrar en la sala. Pero hacer clic en él no hace nada todavía.

Vamos a añadir un controlador de eventos para activar una clase CSS para deslizar la ventana de chat a la vista en el archivo client.js archivo. Así como uno en el icono de cierre de la ventana de chat para deslizarlo de nuevo hacia fuera.

const showChatBtn = document.getElementById("showChat");
showChatBtn.addEventListener(
  "click",
  event => {
    const chatWindow = document.getElementById("chatWindow");
    chatWindow.classList.toggle("active");
  },
  false
);

const closeChatBtn = document.getElementById("closeChat");
closeChatBtn.addEventListener(
  "click",
  event => {
    const chatWindow = document.getElementById("chatWindow");
    chatWindow.classList.remove("active");
  },
  false
);

Esta no es la única manera de diseñar una interfaz de chat, así que siéntete libre de cambiar las cosas para adaptarlas a tus necesidades.

Uso de la API Signal para el chat de texto

Hagamos algunos ajustes más al proyecto base y consigamos que funcione el chat de texto más básico.

Moveremos la variable session fuera de la función initializeSession y la declararemos globalmente en la parte superior del archivo client.js archivo.

let session;

function initializeSession(apiKey, sessionId, token) {
  // Create a session object with the sessionId
  session = OT.initSession(apiKey, sessionId);

  // All the rest of the code
}

Añade un escuchador de eventos al formulario de chat, que enviará una señal a todos los clientes conectados a la Sesión cuando se envíe el formulario.

const chat = document.getElementById("chatForm");
const msgTxt = document.getElementById("chatInput");
chat.addEventListener(
  "submit",
  event => {
    event.preventDefault();
    session.signal(
      {
        type: "msg",
        data: `${msgTxt.value}`
      },
      () => {
        msgTxt.value = "";
      }
    );
  },
  false
);

También necesitamos otro receptor de eventos para recibir ese mensaje escuchando el evento signal enviado por el objeto Session. La carga de datos de ese evento se utilizará para imprimir el mensaje en el área de mensajes de la ventana de chat.

function initializeSession(apiKey, sessionId, token) {
  // Create a session object with the sessionId
  session = OT.initSession(apiKey, sessionId);

  // All the rest of the code

  // Event listener for the msg signal
  session.on("signal:msg", event => {
    const content = event.data;
    updateChat(content);
  });
}

function updateChat(content) {
  const msgHistory = document.getElementById("messageArea");
  const msg = document.createElement("p");
  msg.textContent = content;
  msgHistory.appendChild(msg);
  msgHistory.scroll({
    top: msgHistory.scrollHeight,
    behavior: "smooth"
  });
}

Este es el mecanismo básico del chat de texto con la Video API. Si escribes algo en la ventana de chat y envías, debería aparecer en la pantalla.

Identificar a los participantes en el chat

Sin embargo, para que el chat sea más fácil de usar, también queremos añadir un medio para identificar quién dijo qué en el chat. Utilizaremos el nombre de usuario introducido en la página de destino para obtener esa información, pasándosela al servidor como cadena de consulta en la URL.

El siguiente script de la página landing.html pasa el nombre de la sala y el nombre de usuario introducidos a la página index.html página.

const form = document.getElementById("registration");
form.addEventListener("submit", event => {
  event.preventDefault();
  location.href = `/session/${form.elements["room-name"].value}?username=${form.elements["user-name"].value}`;
});

Cuando la página index.html página se cargue, se lanzará una POST a la ruta session/:name extrayendo el nombre de usuario enviado de la URL y pasándolo al servidor.

const url = new URL(window.location.href);
const roomName = url.pathname.split("/")[2];
const userName = url.searchParams.get("username");

fetch(location.pathname, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ username: userName })
})
  .then(res => {
    return res.json();
  })
  .then(res => {
    const apiKey = res.apiKey;
    const sessionId = res.sessionId;
    const token = res.token;
    const streamName = res.streamName;
    initializeSession(apiKey, sessionId, token, streamName);
  })
  .catch(handleCallback);

Ahora tendremos que modificar esa ruta en el archivo server.js para que gestione el nombre de la sala y el nombre de usuario y devuelva la información necesaria para la función initializeSession función. También tenemos que incluir un middleware para manejar la carga útil de la solicitud.

// Middleware to read the body of the request
app.use(express.json());

app.post("/session/:room", (request, response) => {
  const roomName = request.params.room;
  const streamName = request.body.username;
  // Check if the session already exists
  if (sessions[roomName]) {
    // Generate the token
    generateToken(roomName, streamName, response);
  } else {
    // If the session does not exist, create one
    OT.createSession((error, session) => {
      if (error) {
        console.log("Error creating session:", error);
      } else {
        // Store the session in the sessions object
        sessions[roomName] = session.sessionId;
        // Generate the token
        generateToken(roomName, streamName, response);
      }
    });
  }
});

function generateToken(roomName, streamName, response) {
  // Configure token options
  const tokenOptions = {
    role: "publisher",
    data: `roomname=${roomName}?streamname=${streamName}`
  };
  // Generate token with the Video API Client SDK
  let token = OT.generateToken(sessions[roomName], tokenOptions);
  // Send the required credentials back to to the client
  // as a response from the fetch request
  response.status(200);
  response.send({
    sessionId: sessions[roomName],
    token: token,
    apiKey: process.env.API_KEY
  });
}

En la pantalla client.jspodemos mostrar el nombre de usuario del participante que escribió el mensaje modificando la propiedad data del método session.signal() del método

session.signal(
  {
    type: "msg",
    data: `${session.connection.data.split("=")[2]}: ${msgTxt.value}`
  },
  () => {
    msgTxt.value = "";
  }
);

Ahora, cuando envíes un mensaje de texto, se le añadirá el nombre de usuario que utilizaste al entrar en la sala.

Configurar PouchDB como almacén de datos

Sin embargo, si actualizas la página, todos los mensajes de chat anteriores desaparecen. Esto se debe a que no hemos almacenado los mensajes, sino que nos hemos limitado a mostrarlos en la pantalla. Introduzcamos algún tipo de almacén de datos para los mensajes.

Utilizaremos PouchDB en el servidor en este tutorial, pero eres libre de sustituirlo por cualquier almacén de datos de tu elección.

Instale pouchdb-node con el siguiente comando (tenga en cuenta que el uso de pnpm es una cosa de Glitch):

pnpm install pouchdb-node --save

pouchdb-node installation on Glitchpouchdb-node installation on Glitch

Comprobemos que todo funciona según lo previsto.

const PouchDB = require("pouchdb-node");
const sessionDb = new PouchDB("sessionDb");

sessionDb.info().then(info => console.log(info));

Deberías ver lo siguiente en los registros de Glitch.

Database information printed to consoleDatabase information printed to console

PouchDB proporciona una API completamente asíncrona, aunque también incluye la opción de que los usuarios elijan entre el formato callback o el formato promise. Usaremos el formato de promesa para este tutorial, y el código lo reflejará como tal.

En lugar de almacenar la información de sesión en una variable de objeto, la almacenaremos en nuestra nueva base de datos. Elimine la siguiente línea del archivo server.js archivo.

// We no longer need this object
let sessions = {};

Hagamos más ajustes en la /session/:name ruta. Primero comprobamos la base de datos para verificar si existe una sesión, si existe, recuperamos la información asociada y generamos el token a partir de ella. Si la sesión no existe, crearemos una nueva sesión, la almacenaremos en la base de datos y generaremos el token a partir de ella.

app.post("/session/:room", (request, response) => {
  const roomName = request.params.room;
  const streamName = request.body.username;
  const isExistingSession = checkSession(roomName);

  isExistingSession.then(sessionExists => {
    if (sessionExists) {
      sessionDb
        .get(roomName)
        .then(sessionInfo => {
          generateToken(roomName, streamName, sessionInfo, response);
        })
        .catch(error => error);
    } else {
      OT.createSession((error, session) => {
        if (error) {
          console.log("Error creating session:", error);
        } else {
          const sessionInfo = {
            _id: roomName,
            sessionId: session.sessionId,
            messages: []
          };
          sessionDb.put(sessionInfo);
          generateToken(roomName, streamName, sessionInfo, response);
        }
      });
    }
  });
});

function checkSession(roomName) {
  return sessionDb
    .get(roomName)
    .then(() => {
      console.log(roomName + "exists");
      return Promise.resolve(true);
    })
    .catch(() => {
      console.log("Room does not exist");
      return Promise.resolve(false);
    });
}

function generateToken(roomName, streamName, sessionInfo, response) {
  const tokenOptions = {
    role: "publisher",
    data: `roomname=${roomName}?streamname=${streamName}`
  };
  let token = OT.generateToken(sessionInfo.sessionId, tokenOptions);
  response.status(200);
  response.send({
    sessionId: sessionInfo.sessionId,
    token: token,
    apiKey: process.env.API_KEY,
    streamName: streamName
  });
}

Añadir nombres de flujos a la interfaz de usuario

Podemos utilizar el nombre del flujo en la respuesta para etiquetar los flujos, de modo que los participantes puedan pasar el ratón por encima del flujo de vídeo de cada participante para ver el nombre. Tanto el método initPublisher() como el método subscribe() aceptan un argumento opcional properties que nos permite pasar opciones de personalización para el flujo.

function initializeSession(apiKey, sessionId, token, streamName) {
  // Create a session object with the sessionId
  session = OT.initSession(apiKey, sessionId);

  // Create a publisher
  const publisher = OT.initPublisher(
    "publisher",
    {
      insertMode: "append",
      width: "100%",
      height: "100%",
      name: streamName
    },
    handleCallback
  );

  // Subscribe to a newly created stream
  session.on("streamCreated", event => {
    session.subscribe(
      event.stream,
      "subscriber",
      {
        insertMode: "append",
        width: "100%",
        height: "100%",
        name: event.stream.name
      },
      handleCallback
    );
  });
}

Hover over a video stream to see the stream nameHover over a video stream to see the stream name

Guardar mensajes en la base de datos

Cuando los participantes envían mensajes de texto, queremos enviarlos al servidor para que se almacenen en la base de datos. Vamos a crear una función saveMessage() para ello.

function saveMessage(content) {
  const message = {
    _id: Date.now().toString(),
    content: content,
    roomname: name,
    user: username
  };

  fetch("/message", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(message)
  }).catch(handleCallback);
}

Modifique el receptor de eventos del formulario de chat para que active esta función cada vez que un participante envíe un mensaje de texto.

chat.addEventListener(
  "submit",
  event => {
    event.preventDefault();
    session.signal(
      {
        type: "msg",
        data: `${session.connection.data.split("=")[2]}: ${msgTxt.value}`
      },
      () => {
        saveMessage(msgTxt.value);
        msgTxt.value = "";
      }
    );
  },
  false
);

En el archivo server.js tenemos que añadir un gestor para esta POST solicitud.

app.post("/message", (request, response) => {
  const roomName = request.body.roomName;
  const message = {
    timeStamp: request.body._id,
    content: request.body.content,
    user: request.body.user
  };
  sessionDb
    .get(roomName)
    .then(result => {
      result.messages = [...result.messages, message];
      return sessionDb.put(result);
    })
    .then(() => {
      return sessionDb.get(roomName);
    })
    .then(result => {
      response.status(200);
      response.send({
        latestMessage: result.messages[result.messages.length - 1]
      });
    })
    .catch(error => console.log(error));
});

Ahora que nuestros mensajes están almacenados, queremos mostrarlos cada vez que se cargue la página. Añadiremos una función getChatHistory() en el lado del cliente que lance una petición GET para recuperar todos los mensajes almacenados para esa sesión y mostrarlos en la ventana de chat.

function getChatHistory() {
  fetch(`/messages/${roomName}`)
    .then(res => {
      return res.json();
    })
    .then(res => {
      const messageArea = document.getElementById("messageArea");
      res.messagesArray.forEach(message => {
        const msg = document.createElement("p");
        msg.textContent = `${message.user}: ${message.content}`;
        messageArea.appendChild(msg);
      });
      messageArea.scroll({
        top: messageArea.scrollHeight,
        behavior: "smooth"
      });
    })
    .catch(handleCallback);
}

Y la ruta correspondiente en el lado del servidor para pasar los mensajes como una matriz de vuelta al cliente.

app.get("/messages/:room", (request, response) => {
  const roomName = request.params.room;
  sessionDb
    .get(roomName)
    .then(result => {
      response.status(200);
      response.send({
        messagesArray: result.messages
      });
    })
    .catch(error => console.log(error));
});

Así que ahora, aunque actualices la página mientras la sesión sigue en curso, los mensajes seguirán ahí. Además, si introduces el mismo nombre de sala que en una sesión anterior con historial de chat almacenado, se mostrará ese historial de chat.

¿Y ahora qué?

El código final de Glitch y GitHub contiene todo lo que cubrimos en este post bastante largo, pero reorganizado para que el código sea más limpio y más fácil de mantener. Siéntete libre de remezclar o clonar el código y jugar con él.

Existen funcionalidades adicionales que podemos crear con la Video API de Vonage que se tratarán en futuros tutoriales, pero mientras tanto, puedes obtener más información en nuestro sitio de documentación integral. Si tienes algún problema o alguna pregunta, comunícate con nosotros en nuestra Slack de la comunidad. Gracias por leernos.

Compartir:

https://a.storyblok.com/f/270183/384x384/46621147f0/huijing.png
Hui Jing ChenAntiguos alumnos de Vonage

Hui Jing es defensora de los desarrolladores en Nexmo. Tiene un amor desmesurado por CSS y la tipografía, y en general es una apasionada de todo lo relacionado con la web.