Incrustar comentarios de Discourse en tu aplicación de página única

Al intentar implementar [1] en tu aplicación de página única (SPA) que no proporciona páginas renderizadas del lado del servidor, eventualmente encontrarás problemas, consulta: [2]

Así que, después de un poco de experimentación, me gustaría presentar el siguiente enfoque. El ejemplo está en Vue.js, sin embargo, se puede adaptar fácilmente a otros frameworks/librerías.

Nota: Usaré el término entradas del blog donde se deba incrustar una sección de comentarios de Discourse. Pero, por supuesto, esto también puede significar páginas individuales en tu sitio.

1. Los problemas en [1]

1.1. javascripts/embed.js no puede funcionar con contenido renderizado del lado del cliente

El fragmento <script>...</script> que se te indica en [1] insertar en tu HTML, por lo tanto, no formará parte de la implementación que estamos abordando aquí. Utilizaremos algunas partes de javascripts/embed.js proporcionadas por tu instancia de Discourse como funciones dentro de nuestra SPA.

1.2. Discourse no puede rastrear contenido renderizado del lado del cliente

Discourse crea automáticamente temas para cada entrada del blog e intenta acceder a la URL original (de una entrada del blog) para determinar el título y el contenido. Esto falla con una SPA, porque Discourse obtendrá la parte sin JavaScript, por ejemplo, Lamentamos que este sitio no funcione correctamente sin JavaScript habilitado. Habilítelo para continuar.

Utilizaremos el plugin RSS Polling para proporcionar los datos necesarios y crear los temas por nosotros.

2. La implementación

2.1 RSS Polling y el feed RSS/Atom

Crea un endpoint en tu sitio que proporcione un feed RSS o Atom para el plugin RSS Polling. Este endpoint puede ser simplemente un archivo estático con formato XML o una función del lado del servidor que proporcione el contenido con formato XML, ejemplo:

URL: https://misitio.com/blog.atom

Contenido:

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title>Entradas del Blog de Mi Sitio</title>
  <link href="https://misitio.com/blog/"/>
  <updated>2022-07-03T09:02:48.721Z</updated>
  <id>urn:uuid:790c1857-b968-49cc-9fbd-bf7afe3552c2</id>

  <entry>
    <title>Un Artículo sobre Tecnología</title>
    <author>
      <name>Tu Nombre Aquí</name>
    </author>
    <link href="https://misitio.com/blog/un-articulo-sobre-tecnologia"/>
    <id>urn:uuid:f6cc13e4-d2eb-4385-af28-c867a94f48dc</id>
    <published>2022-07-03T00:00:00Z</published>
    <updated>2022-07-03T00:00:00Z</updated>
    <summary>Discutamos algo de tecnología en este artículo.</summary>
  </entry>

</feed>

Instala el plugin RSS Polling para Discourse según [3] (Discourse alojado) o [4] (autoalojado)

Configuraciones recomendadas para RSS Polling en Admin → Configuración → Plugins:

Clave Valor
rss polling enabled true
rss polling frequency 10 (es decir, 10 minutos)

Agrega un nuevo feed en la configuración del plugin RSS Polling en Admin → Plugins → RSS Polling

Configúralo según [3]:

Clave Valor
URL https://misitio.com/blog.atom
Category Filter <esto es opcional>
Author <define un autor de los temas generados automáticamente>
Category <define la/s categoría/s donde se publicarán los temas generados automáticamente>
Tags <esto es opcional>

2.2 Configuración del Router de la SPA

Discourse utiliza la última parte de la ruta de la URL como identificador para una entrada de blog individual.

Ejemplos:

https://misitio.com/blog/un-articulo-sobre-tecnologia
https://misitio.com/blog/otro-articulo-sobre-gatos

Configura tu router SPA en consecuencia, para que las entradas de blog individuales correspondan con URLs individuales.

Además: Discourse proporcionará un enlace de regreso a la entrada del blog individual, por lo que es una buena experiencia de usuario que, al hacer clic, tu sitio muestre el artículo real.

2.3 El Componente Artículo

Como se mencionó anteriormente, no podemos usar el enfoque con el <script></script> de [1]. Así que implementamos el iframe y algunas funciones de javascripts/embed.js en nuestro componente:

Article.vue

Cosas que deberías editar:

Ítem Descripción
#YOUR-DISCOURSE-URL# la URL de tu instancia de Discourse, por ejemplo, discourse.misitio.com)
#YOUR-SITE-URL# la URL de tu sitio, por ejemplo, misitio.com posiblemente también %2Fblog%2F si la ruta de las entradas de tu blog no es /blog/
<template>
  <div id="article">

    <!-- tu artículo formateado aquí -->

    <iframe
      v-if="slug"
      v-bind:src="`https://#YOUR-DISCOURSE-URL#/embed/comments?embed_url=https%3A%2F%2F#YOUR-SITE-URL#%2Fblog%2F${slug}%2F`"
      id="discourse-embed-frame"
      width="100%"
      v-bind:height="`${iframeHeight}px`"
      frameborder="0"
      scrolling="no"
      referrerpolicy="no-referrer-when-downgrade"
    />
  </div>
</template>

<script>
export default {
  data: () => ({
    slug: null,     // el slug de la entrada del blog, por ejemplo, "un-articulo-sobre-tecnologia" mientras la ruta es "https://misitio.com/blog/un-articulo-sobre-tecnologia"
    iframeHeight: 0 // Discourse nos dirá la altura exacta del iframe (ver: método receiveMessage)
  }),

  methods: {
    // comunicación iframe
    receiveMessage(event) {
      if (!event) {
        return;
      }
      if (!(event.origin || "").includes("#YOUR-DISCOURSE-URL#")) {
        return;
      }

      if (event.data) {
        if (event.data.type === "discourse-resize" && event.data.height) {
          this.iframeHeight = +event.data.height;
        }

        if (event.data.type === "discourse-scroll" && event.data.top) {
          // encontrar el offset del iframe
          const destY = this.findPosY(this.$refs["discourse-embed-frame"]) + event.data.top;
          window.scrollTo(0, destY);
        }
      }
    },

    // Gracias http://amendsoft-javascript.blogspot.ca/2010/04/find-x-and-y-coordinate-of-html-control.html
    findPosY(obj) {
      var top = 0;
      if (obj.offsetParent) {
        while (1) {
          top += obj.offsetTop;
          if (!obj.offsetParent) break;
          obj = obj.offsetParent;
        }
      } else if (obj.y) {
        top += obj.y;
      }
      return top;
    }
  },

  async created() {
    this.slug = this.$router.currentRoute.path.split("/")[2];
  },

  mounted() {
    window.addEventListener("message", this.receiveMessage);
  },
  beforeDestroy() {
    window.removeEventListener("message", this.receiveMessage);
  }
}
</script>

2.4 Configuración de Incrustación de Discourse

Ahora, con el sondeo del feed RSS/Atom en funcionamiento y la implementación en tu sitio, finalmente podemos configurar la incrustación en la instancia de Discourse.

Ve a Admin → Personalizar → Incrustación y agrega un host:

Clave Valor
Allowed Hosts la URL base de tu sitio> por ejemplo, “misitio.com
Class Name nombre de clase opcional para estilizar
Path Allowlist por ejemplo, “/blog/.*”
Post to Category la misma categoría que se configuró en la Categoría de RSS Polling

3. Comentarios Finales

El sondeo del feed RSS/Atom, así como Discourse mismo, crearán un nuevo tema si no existe para la entrada de blog individual. Asegúrate de que el sondeo del feed RSS/Atom ocurra primero (es decir, espera hasta que se cree el tema antes de visitar la entrada del blog en tu sitio).

Razón: Discourse no puede rastrear el título y el resumen, por lo que el tema sería misitio.com con el resumen siendo Lamentamos que este sitio no funcione sin javascript. :wink:

Si por alguna razón Discourse se adelantó, puedes simplemente eliminar el tema y esperar a que el feed RSS/Atom se active.

Saludos

– MK2k

2 Me gusta