Notificación de tema nuevo o actualizado fijo

Sí, agregar una URL causará navegación ya que no hay lógica para interceptar el evento.

El enlace está llamando a una acción de Ember. Todas las acciones en Discourse son extensibles. Por lo tanto, actúan como ganchos de personalización de cierta manera. Entonces, ¿cómo modificas una acción? Bueno, primero veamos qué hace.

Busca en GitHub o localmente por el nombre de la acción. Las acciones siempre se definen en archivos JS y se referencian en Handlebars. Queremos ver la definición, así que reducimos la búsqueda a archivos JS.

Repository search results · GitHub

Obtienes cuatro archivos. ¿Cuál deberías revisar? Quieres personalizar la plantilla

discovery/topics.hbs

Así que

discovery/topics.js

es lo que quieres revisar.

¿Alguna parte de este código es útil? No, pero ahora sabemos dónde se define la acción. Así que, modifiquémosla.

discovery/topics.js es una clase de Ember. Puedes modificar las clases de Ember con un método en la API de plugins llamado… modifyClass :stuck_out_tongue:

https://github.com/discourse/discourse/blob/main/app/assets/javascripts/discourse/app/lib/plugin-api.js#L166-L195

Ya sabemos la clase que queremos modificar. Es discovery/topics. Necesitamos saber qué tipo de clase es. Así que, revisemos el directorio de archivos.

discourse/app/controllers/discovery/topics.js

Es un controlador.

También sabemos que queremos modificar la acción showInserted en esa clase. Así que, comenzamos con esto.

api.modifyClass("controller:discovery/topics", {
  pluginId: 'sticky-new-topics-banner',
  actions: {
    showInserted() {
      // hagamos algo
    }
  }
});

Luego puedes agregar cualquier código que desees para hacer scroll en la ventana cuando el usuario hace clic en el banner de “nuevos temas”. Yo opté por algo así.

const listControls = document.querySelector(".list-controls");
listControls.scrollIntoView();

Puedes leer más sobre scrollIntoView() aquí.

Luego agregas eso al método de la API de plugins de la siguiente manera.

api.modifyClass("controller:discovery/topics", {
  pluginId: 'sticky-new-topics-banner',
  actions: {
    showInserted() {
+     const listControls = document.querySelector(".list-controls");
+     listControls.scrollIntoView();
    }
  }
});

Entonces, ¿ya terminamos? No. Esto rompe Discourse porque estás sobrescribiendo completamente la acción. Al hacer clic en el enlace ahora se hará scroll hasta el elemento list-controls, pero no se cargarán los nuevos temas. ¿Por qué? Porque el código principal ya no se está aplicando. Me refiero a esto

Entonces, ¿cómo lo arreglas? Con esta línea simple

this._super(...arguments);

No necesitas copiar ningún código del núcleo si solo quieres agregarle algo. Haz tu trabajo y luego agrega esa línea. Todo lo que hace es asegurarse de que el código del núcleo se aplique.

api.modifyClass("controller:discovery/topics", {
  pluginId: 'sticky-new-topics-banner',
  actions: {
    showInserted() {
     const listControls = document.querySelector(".list-controls");
     listControls.scrollIntoView();
+
+    this._super(...arguments);
    }
  }
});

Si pruebas esto, verás que casi todo funciona muy bien, excepto… el encabezado se superpone con list-controls. ¿Por qué? Porque el encabezado está configurado como sticky.

Puedes solucionarlo de varias maneras en JS: calcular la altura, obtener el desplazamiento, importar un helper de Discourse, etc. No entraré en esos detalles.

La forma más fácil es con CSS usando scroll-margin-top, sobre lo cual puedes leer aquí.

Así que, agregamos esto

.list-controls {
  scroll-margin-top: calc(var(--header-offset) * 2);
}

En inglés: Cuando se hace clic en el enlace, haz scroll hasta la parte superior de list-controls menos 2 veces la altura del encabezado, para que no se superponga y tenga un poco de espacio debajo.

Así que, pongamos todo esto junto.

Pestaña de encabezado común

<script type="text/discourse-plugin" version="0.8">
api.modifyClass("controller:discovery/topics", {
  pluginId: "sticky-new-topics-banner",
  actions: {
    showInserted() {
      const listControls = document.querySelector(".list-controls");
      listControls.scrollIntoView();
      this._super(...arguments);
    }
  }
});
</script>

CSS común

#list-area {
  // móvil tiene un diseño diferente
  .alert-info,
  .show-more.has-topics {
    position: sticky;
    // safari a veces es exigente sin el prefijo
    position: -webkit-sticky;
    top: var(--header-offset);
    // el banner debe estar encima del contenido
    z-index: z("header");
  }
}

.list-controls {
  scroll-margin-top: calc(var(--header-offset) * 2);
}