Cómo agregar HTML personalizado después de "post_1"

Me gustaría agregar un banner de cliente (con fines de afiliación) dentro de mis publicaciones. Específicamente, me gustaría insertar este bloque de HTML (de ejemplo) justo después de <article id="post_1...".

<div id="custom-ad">
    <a href="example.com">
        <img src="https://picsum.photos/id/74/750/90">
    </a>
</div>

Quedando algo así:

He tenido un éxito limitado con CSS :after. Por lo tanto, me pregunto si esto podría hacerse desde dentro de </HEAD> usando un <script> como este ejemplo.

EDIT: Después de probar un poco más, parece más limpio insertar el banner al final de <div class="topic-map"> en su lugar:

<div class="topic-map">
    <section class="map map-collapsed">...</section>
    AQUÍ EL HTML PERSONALIZADO
</div>

Los posts son widgets, lo que significa que lo que estás intentando hacer requerirá un poco más de trabajo que simplemente agregar HTML.

Los temas de Discourse tienen la capacidad de decorar widgets, así que puedes aprovechar eso.

La decoración de un widget se explica en el enlace anterior, así que centrémonos en lo que estás intentando hacer: agregar marcado después del primer post en cada tema.

Comienza agregando el marcado a todos los posts. Algo como esto:

<script type="text/discourse-plugin" version="0.8">
api.decorateWidget("post:after", helper => {
  return helper.h("div", "texto de prueba");
});
</script>

en la sección de encabezado de tu tema. Eso debería ser suficiente para agregar “texto de prueba” debajo de cada post.

Desglosemos el script anterior:

api.decorateWidget("post:after", helper => {

llama al método decorateWidget, siendo el widget objetivo post y la ubicación objetivo after. Es decir, después de un widget de post.

helper es un helper incorporado que te da acceso a un montón de cosas que explicaré más adelante.

return helper.h("div", "texto de prueba")

Este es el marcado adicional deseado que quieres agregar. Puedes notar que no hay HTML ahí y eso se debe a que los widgets de Discourse emiten nodos virtuales y no HTML crudo.

Explicar qué son los nodos virtuales o cómo funciona la sintaxis está fuera del alcance de este tema, así que lo omitiré. He añadido una nota para escribir una guía sobre cómo crear nodos virtuales, pero por ahora aquí tienes un par de ejemplos:

helper.h("div", "texto de prueba")

se renderiza como:

<div>texto de prueba</div>

y

return helper.h("div#custom-ad", [
  helper.h(
    "a.custom-ad-link",
    { href: "example.com" },
    helper.h("img", { src: "https://picsum.photos/id/74/750/90" })
  )
]);

se renderiza como:

<div id="custom-ad">
  <a href="example.com" class="custom-ad-link">
    <img src="https://picsum.photos/id/74/750/90">
  </a>
</div>

En pocas palabras, un nodo se ve así:

helper.h(selector, {propiedades}, hijos)

Explicaré esto con más detalle en la guía de nodos virtuales.

Así que, ahora que tienes los nodos listos, solo necesitas agregar todo el script a la sección de encabezado de tu tema, algo como esto:

<script type="text/discourse-plugin" version="0.8">
  api.decorateWidget("post:after", helper => {
    return helper.h("div#custom-ad", [
      helper.h(
        "a.custom-ad-link",
        { href: "example.com" },
        helper.h("img", { src: "https://picsum.photos/id/74/750/90" })
      )
    ]);
  });
</script>

Sin embargo, todavía hay un problema aquí: el anuncio se insertará debajo de cada post en el flujo, lo cual no es ideal.

Aquí es donde el helper resulta útil; los atributos del post se pasan al helper, así que puedes hacer un rápido:

console.log(helper)

Podrás ver todos los atributos del post disponibles para trabajar con ellos.

esos son solo ejemplos, hay más ahí.

Afortunadamente, el atributo firstPost está disponible para nosotros aquí, así que solo te queda incorporarlo en una condición que renderice el marcado del anuncio solo si realmente es el primer post; de lo contrario, no ocurre nada. Algo como esto:

<script type="text/discourse-plugin" version="0.8">
api.decorateWidget("post:after", helper => {
  const firstPost = helper.attrs.firstPost;
  const h = helper.h;
  if (firstPost) {
    return h("div#custom-ad", [
      h(
        "a.custom-ad-link",
        { href: "example.com" },
        h("img", { src: "https://picsum.photos/id/74/750/90" })
      )
    ]);
  }
});
</script>

y eso insertará tu anuncio solo después del primer post. Una cosa adicional a hacer aquí es agregar una altura a la imagen; de lo contrario, causará parpadeo mientras se carga. Como mencioné brevemente antes, el atributo height para la etiqueta de imagen es una propiedad, así que debes agregarla junto con src.

Con todo eso reunido, aquí está el código final para lo que estás intentando lograr:

<script type="text/discourse-plugin" version="0.8">
api.decorateWidget("post:after", helper => {
  const firstPost = helper.attrs.firstPost;
  const h = helper.h;
  if (firstPost) {
    return h("div#custom-ad", [
      h(
        "a.custom-ad-link",
        { href: "example.com" },
        h("img", { src: "https://picsum.photos/id/74/750/90", height: "90" })
      )
    ]);
  }
});
</script>

Una última nota que quiero destacar es que realmente puedes usar HTML crudo si los nodos virtuales resultan demasiado complicados, pero no se recomienda y es mucho mejor usar nodos virtuales. Así que el mismo script con HTML crudo se vería así:

<script type="text/discourse-plugin" version="0.8">
  const RawHtml = require("discourse/widgets/raw-html").default;
  api.decorateWidget("post:after", helper => {
    const firstPost = helper.attrs.firstPost;
    if (firstPost) {
      return [
        new RawHtml({
          html: `<div id="custom-ad">
                   <a href="example.com">
                     <img src="https://picsum.photos/id/74/750/90" height="90">
                   </a>
                 </div>`
        })
      ];
    }
  });
</script>

pero, de nuevo, esto no se recomienda.

Siempre aprecio las soluciones que explican cómo llegar de la A a la Z en lugar de solo dar una respuesta. Gracias.

Me gustó este post, pero solo quiero añadir: Esta es una explicación apasionada. Realmente aprecio posts como este.

Eso es porque @johani es :fire: :hot_pepper: :muscle: