https://d226lax1qjow5r.cloudfront.net/blog/blogposts/chat-pagination-with-infinite-scrolling-dr/E_Infinite-Scrolling_1200x600.png

Paginación del chat con desplazamiento infinito

Publicado el February 3, 2020

Tiempo de lectura: 25 minutos

Nota: Es posible que algunas de las herramientas o métodos descritos en este artículo ya no reciban soporte o no estén actualizados. Para obtener contenido actualizado o soporte, consulta nuestras últimas publicaciones o contáctanos en el Slack de la comunidad de Vonage

Siguiendo con el post anterior Crear una interfaz de usuario de mensajería simple con Bootstrapeste artículo te mostrará cómo cargar mensajes anteriores de la conversación usando el Client SDK de Vonage Conversation, que ahora se entrega paginado desde la Conversation API.

Requisitos previos

Vonage API Account

To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.

Nodo y NPM

Para empezar, vas a necesitar Node y NPM instalados. Esta guía utiliza Node 8 y NPM 6. Comprueba que están instalados y actualizados.

node --version npm --version

Tanto Node como NPM necesitan estar instalados y en la versión correcta. Vaya a nodejs.org e instale la versión correcta si no la tiene.

CLI de Vonage

Para configurar tu aplicación, necesitarás instalar Vonage CLI. Instálalo usando NPM en la terminal.

npm install @vonage/cli -g

Puede encontrar su clave y secreto API en el Panel que se utilizan para configurar la CLI de Vonage.

vonage config:set --apiKey=VONAGE_API_KEY --apiSecret=VONAGE_API_SECRET

La CLI de Vonage tiene complementos que, cuando se instalan, brindan capacidades adicionales. En este tutorial, trabajarás con Conversations, por lo que este es el comando para instalar su complemento:

vonage plugins:install @vonage/cli-plugin-conversations

Git (opcional)

Puede utilizar git para clonar la aplicación de demostración desde GitHub.

Para aquellos que no se sientan cómodos con los comandos git, no os preocupéis, os tengo cubiertos. Esta guía contiene instrucciones para descargar el proyecto como archivo ZIP.

Siga esta guía para instalar git.

Primeros pasos

Basado en la aplicación terminada del último tutorial, hay una nueva aplicación de demostración inicial. Clónala e instálala siguiendo estos pasos.

Obtenga la aplicación de demostración

git clone https://github.com/nexmo-community/infinite-scrolling-pagination.git

Para quienes no se sientan cómodos con los comandos git, pueden descargar la aplicación de demostración como archivo zip y descomprimirlo localmente.

Una vez clonado o descomprimido, cambie al nuevo directorio de aplicaciones de demostración.

cd infinite-scrolling-pagination

Instale las dependencias de npm.

npm install

Configure el puerto de la aplicación utilizando un archivo de entorno. Copie el archivo de ejemplo:

cp .env.example .env

Ahora, edita el archivo de entorno .env y establece el puerto en 3000 (o el puerto que necesites).

# app config PORT=3000

Entre otros paquetes instalados por su último comando, hay un paquete llamado nodemonque permite recargar la aplicación si se edita algún archivo automáticamente.

Para iniciar la aplicación de la forma estándar, ejecute:

npm start

Para iniciar la aplicación, pero con nodemon en su lugar, ejecute:

npm run dev

Consejo: Si está ejecutando la aplicación con nodemon durante el resto de este tutorial, siempre que te sugiera reiniciar la aplicación, no tendrás que hacerlo porque nodemon lo hace por ti. Sin embargo, si usted necesita volver a autenticarse con la aplicación, usted todavía tendrá que hacer eso, ya que la información de la sesión se almacena en la memoria y no está configurado para utilizar cualquier otro almacenamiento.

Configurar la aplicación de demostración

Para conectarte a Vonage y enviar o recibir mensajes del servicio, debes configurar la aplicación de demostración.

Crear una aplicación de Vonage

En primer lugar, crea una aplicación de Vonage con capacidades de RTC (comunicación en tiempo real). La URL del evento será un registro en vivo de los eventos que ocurren en el servicio de Vonage, como usuarios que se unen/abandonan, envían mensajes, habilitan el audio (si tienes ganas de habilitarlo).

vonage apps:create "Vonage RTC Chat" --rtc_event_url=http://example.com

Crear una conversación de Vonage

En segundo lugar, crea una Conversación de Vonage, que actúa como una sala de chat. O bien, un contenedor de mensajes y eventos.

vonage apps:conversations:create "Infinite Scrolling"

Cree su usuario

Ahora, crea un usuario para ti.

Nota: En esta demostración, no chatearás entre dos usuarios. Otras guías muestran cómo crear conversaciones entre varios usuarios. Esta guía se centra en estilizar la interfaz de usuario de los mensajes de una forma sencilla pero atractiva.

vonage apps:users:create USER_NAME --display_name=DISPLAY_NAME

Añadir el usuario a una conversación

A continuación, añade tu nuevo usuario a la conversación. Un usuario puede ser miembro de una aplicación, pero aún así tiene que unirse a la conversación.

vonage apps:conversations:members:add CONVERSATION_ID USER_ID

Generar un token de usuario

Por último, genere un token para su nuevo usuario. Este token representa al usuario cuando accede a la aplicación. Este token de acceso lo identifica, por lo que cualquiera que lo utilice asumirá que es el usuario correcto.

En la práctica, configurará la aplicación con este token. En producción, estos deben ser guardados, mantenidos en secreto, y muy cuidadosamente expuestos a la aplicación cliente, si acaso.

vonage jwt --key_file=./vonage_rtc_chat.key --acl='{"paths":{"/*/users/**":{},"/*/conversations/**":{},"/*/sessions/**":{},"/*/devices/**":{},"/*/image/**":{},"/*/media/**":{},/*/push/**":{},"/*/knocking/**":{},"/*/legs/**":{}}}' --subject=USER_NAME --app_id=APP_ID

Configurar la aplicación

Habiendo generado todas las partes que necesitarás, edita el archivo views/layout.hbs y encuentra el JavaScript que se muestra aquí.

<script>
      var userName = '';
      var displayName = '';
      var conversationId = '';
      var clientToken = '';
    </script>

Edita la configuración con los valores que has generado en los comandos anteriores.

<script>
      var userName = 'luke'; // <USER_NAME>
      var displayName = 'Luke Oliff'; // <DISPLAY_NAME>
      var conversationId = 'CON-123...y6346'; // <CONVERSATION_ID>
      var clientToken = 'eyJhbG9.eyJzdWIiO.Sfl5c'; // this will be much much longer
    </script>

Una vez configurada, inicie la aplicación y acceda a ella utilizando la URL por defecto de la aplicación.

Nota: Esto es sólo una demostración y no deberías codificar credenciales en ninguna aplicación, especialmente en una que las exponga al cliente.

Vonage Chat Simple Messaging UIVonage Chat Simple Messaging UI

Preparar un historial de mensajes

Dado que necesita más mensajes por los que desplazarse, cree un historial de mensajes enviando varios mensajes al cliente. El tamaño de página por defecto es de 20 elementos, así que crea más de 20 mensajes. Te recomiendo crear 60 mensajes de prueba para que puedas cargar 2 páginas enteras de historial.

Añadir paginación a la aplicación

La configuración por defecto de la aplicación sólo devuelve 20 elementos de los eventos pasados de la conversación. Ahora, es el momento de añadir paginación a la aplicación para que los usuarios puedan cargar eventos más antiguos.

¿Qué es la paginación?

La paginación es la forma en que una aplicación divide el contenido en varias páginas. Cuando se implementa en el diseño de una API, permite la entrega de colecciones manejables de resultados, que generalmente se pueden navegar mediante programación. Los SDK como el Client SDK de Vonage Conversation no son diferentes, y a menudo extienden la funcionalidad de paginación de las API en métodos amigables que hacen que la paginación sea más sencilla.

La experiencia del usuario

Algunas Applications ofrecen enlaces como "siguiente" o "anterior", o números de página. Pero eso no es lo que se implementará aquí. Como los mensajes en un canal de chat son un flujo continuo de conversación, esta aplicación permitirá a los usuarios simplemente desplazarse por los mensajes históricos. Esto se hace utilizando un concepto conocido como desplazamiento infinito. A medida que te desplaces por los mensajes antiguos y llegues al final, la aplicación solicitará la siguiente página del historial y los introducirá. En canales antiguos con mucho historial, esto dará la sensación de poder desplazarse eternamente o desplazamiento infinito.

El Código

Ahora, vas a escribir algo de código. Aquí, harás cambios para detectar la posición de desplazamiento de tu lista de mensajes, y cargar más mensajes cuando llegues al mensaje más antiguo. El mensaje más antiguo se mostrará en la parte superior de la ventana.

Desplazarse al principio

Para detectar cuando te desplazas a la parte superior, necesitas añadir un nuevo evento. Edite el archivo public/javascripts/chat.js y añade el siguiente código bajo el método setupUserEvents() método.

// public/javascripts/chat.js

// ...

  setupUserEvents() {

    // ...

    this.messageFeed.addEventListener("scroll", () => {
        alert('scrolling!');
    }
  }

// ...

Puedes probar esto en el navegador, donde descubrirás rápidamente por qué no es muy útil. Este código añade un receptor de eventos al elemento messageFeed lo que significa que cada vez que intentas desplazarte aparece una ventana emergente. No es lo que quieres.

Así que cámbialo ligeramente. Añade el siguiente código encima del método setupUserEvents() y modifica tu nuevo receptor de eventos como se muestra.

// public/javascripts/chat.js

// ...

  isFeedAtTop() {
    return 0 === this.messageFeed.scrollTop;
  }

  setupUserEvents() {

    // ...

    this.messageFeed.addEventListener("scroll", () => {
      if (this.isFeedAtTop()) {
        alert('scrolling!');
      }
    }
  }

// ...

Este nuevo cambio crea un nuevo método que detecta dónde se encuentra la posición de desplazamiento de la etiqueta messageFeed está en 0, cero, o el inicio en la parte superior del historial de mensajes. ¡Más útil! Ahora, usted sabe cuando alguien llega al mensaje más antiguo en la parte superior de la lista de mensajes.

Vonage Chat Alert When Scrolling to the TopVonage Chat Alert When Scrolling to the Top

Quién es usted

Para atribuir nuevos mensajes a un usuario cuando se cargan desde el historial de conversaciones, debes almacenar. Editando el archivo public/javascripts/chat.js añada la siguiente línea después de la línea this.conversation = conversation;.

// public/javascripts/chat.js

// ...

  setupConversationEvents(conversation, user) {
    // ...
    this.user = user;
    // ...
  }

// ...

Almacenar el contexto de la página

Para cargar más mensajes del historial de mensajes, es necesario saber qué página se cargó por última vez. Para ello, aún editando el archivo public/javascripts/chat.js cambie el archivo showConversationHistory como se muestra a continuación para almacenar la página de eventos más reciente de la aplicación.

// public/javascripts/chat.js

// ...

  showConversationHistory(conversation, user) {
    // ...
      .then((eventsPage) => {
        this.lastPage = eventsPage;
        var eventsHistory = "";
    // ...
  }

// ...

Si no está claro cómo debería quedar el método showConversationHistory después del cambio, aquí está el método completo con el cambio aplicado.

// public/javascripts/chat.js

// ...

  showConversationHistory(conversation, user) {
    conversation
      .getEvents({ page_size: 20, order: 'desc' })
      .then((eventsPage) => {
        this.lastPage = eventsPage;
        var eventsHistory = "";

        eventsPage.items.forEach((value, key) => {
          if (conversation.members.get(value.from)) {
            switch (value.type) {
              case 'text':
                eventsHistory = this.senderMessage(user, conversation.members.get(value.from), value) + eventsHistory;
                break;
              case 'member:joined':
                eventsHistory = this.memberJoined(conversation.members.get(value.from), value) + eventsHistory;
                break;
            }
          }
        });

        this.messageFeed.innerHTML = eventsHistory + this.messageFeed.innerHTML;

        this.scrollFeedToBottom();
      })
      .catch(this.errorLogger);
  }

// ...

La idea de este método es almacenar el EventsPage devuelto al llamar a getEventspara que la aplicación pueda volver a utilizarlo más adelante.

Con este cambio, la aplicación ya conoce la página más reciente.

Evite peticiones innecesarias

Un método del objeto EventsPage es hasNextque devuelve true si hay más eventos que cargar.

Con el método hasNext edite el evento de desplazamiento que añadió antes para añadir this.lastPage.hasNext() a la condición alrededor de nuestro alert.

// public/javascripts/chat.js

// ...

  setupUserEvents() {

    // ...

    this.messageFeed.addEventListener("scroll", () => {
      if (this.isFeedAtTop() && this.lastPage.hasNext()) {
        alert('scrolling!');
      }
    }
  }

// ...

Ahora, sólo recibirás una alerta si hay otra página de eventos que cargar.

Cargar la página siguiente

Para cargar la página siguiente, sustituya el alert en su receptor de eventos por el código que se muestra a continuación:

// public/javascripts/chat.js

// ...

        this.lastPage
          .getNext()
          .then((eventsPage) => {
            this.lastPage = eventsPage;
            var moreEvents = "";

            eventsPage.items.forEach((value, key) => {
              if (this.conversation.members.get(value.from)) {
                switch (value.type) {
                  case 'text':
                    moreEvents = this.senderMessage(this.user, this.conversation.members.get(value.from), value) + moreEvents;
                    break;
                  case 'member:joined':
                    moreEvents = this.memberJoined(this.conversation.members.get(value.from), value) + moreEvents;
                    break;
                }
              }
            });

            this.messageFeed.innerHTML = moreEvents + this.messageFeed.innerHTML;
          })
          .catch(this.errorLogger);

// ...

Este código utiliza this.lastPage que se almacenó en la aplicación anteriormente en el artículo, y solicita getNext que devuelve un nuevo archivo EventsPage.

El resto del código que se ve aquí sobrescribe this.LastPage con la última página, y realiza casi la misma función del método showConversationHistory que renderiza los mensajes históricos cuando se carga la página, añadiéndolos a la parte superior del archivo messageFeed.

Fijar la posición de desplazamiento

Con el desplazamiento infinito en su lugar, notarás que los nuevos mensajes se añaden a la parte superior, pero sigues viendo la parte superior del messageFeedperdiendo la posición en la que te encontrabas en el historial de mensajes del canal. Para arreglar esto, vas a reutilizar el método scrollTo que ya se encuentra dentro del archivo public/javascripts/chat.js archivo.

Anteriormente, scrollTo se utilizaba para desplazarse hasta la parte inferior de los mensajes, lo que se consigue con cualquier número mayor que la altura del campo messageFeed. Este equipo, es necesario desplazarse a un punto específico en el . messageFeed.

Si la posición era cuando la aplicación cargó nuevos mensajes fue 0 en la parte superior, entonces tendría sentido desplazarse a la diferencia entre la altura antes y después de que el mensaje messageFeed se actualizara.

Dentro de la condición que comprueba la posición de desplazamiento y hasNextpero antes de ejecutar el código the.lastPage.getNext() añada el código para almacenar el valor scrollHeightcomo se muestra aquí:

// public/javascripts/chat.js

// ...
      if (this.isFeedAtTop() && this.lastPage.hasNext()) {
        this.scrollHeight = this.messageFeed.scrollHeight;

        // ...
// ...

Ahora, en esta misma función, después de la línea que actualiza el archivo messageFeed.innerHTML con moreEventsañade también esta línea:

// public/javascripts/chat.js

// ...
            // ...

            this.scrollTo(this.messageFeed.scrollHeight-this.scrollHeight);
// ...

Si no está claro cómo debe quedar el "scroll" después del cambio, aquí está el código en su totalidad:

// public/javascripts/chat.js

// ...

    // ...

    this.messageFeed.addEventListener("scroll", () => {
      if (this.isFeedAtTop() && this.lastPage.hasNext()) {
        this.scrollHeight = this.messageFeed.scrollHeight;

        this.lastPage
          .getNext()
          .then((eventsPage) => {
            this.lastPage = eventsPage;
            var moreEvents = "";

            eventsPage.items.forEach((value, key) => {
              if (this.conversation.members.get(value.from)) {
                switch (value.type) {
                  case 'text':
                    moreEvents = this.senderMessage(this.user, this.conversation.members.get(value.from), value) + moreEvents;
                    break;
                  case 'member:joined':
                    moreEvents = this.memberJoined(this.conversation.members.get(value.from), value) + moreEvents;
                    break;
                }
              }
            });

            this.messageFeed.innerHTML = moreEvents + this.messageFeed.innerHTML;

            this.scrollTo(this.messageFeed.scrollHeight-this.scrollHeight);
          })
          .catch(this.errorLogger);
      }
    });

// ...

Con un poco de suerte, cuando lo pruebes, descubrirás que los mensajes se cargarán aparentemente por encima de tu posición de desplazamiento, permitiéndote desplazarte "hasta el infinito", o hasta arriba.

Vonage Chat Infinite Scrolling to the TopVonage Chat Infinite Scrolling to the Top

Fin

Este artículo es la continuación del anterior Crear una interfaz de usuario de mensajería sencilla con Bootstrapen el que se muestra cómo cargar mensajes antiguos al desplazarse por el historial de mensajes.

No olvides que si tienes alguna pregunta, comentario, consejo o idea que quieras compartir con la comunidad en general, no dudes en conectarte a nuestro canal de Slack de la comunidad o envíanos una respuesta 👇.

Compartir:

https://a.storyblok.com/f/270183/250x250/451101b4f0/lukeoliff.png
Luke OliffAntiguos alumnos de Vonage

Amable educador tecnológico, padre de familia, defensor de la diversidad, probablemente discuta demasiado. Anteriormente ingeniero de backend. Háblame de JavaScript (frontend o backend), el increíble Vue.js, DevOps, DevSecOps, cualquier cosa JamStack. Escritor en DEV.to