Si estás visitando /tags/category-slug/tag-name y haces clic en el botón Nuevo tema, el compositor tendrá la etiqueta predefinida, como se describe aquí:
Esto es increíble. Pero ahora yo (y al menos otra persona) queremos poder configurar este comportamiento con una etiqueta predeterminada al visitar /c/cat-slug/cat-id. Parece que un componente de tema debería poder apuntar a ese botón y modificarlo, o bien ocultarlo y agregar un nuevo botón (hay una salida de plugin justo allí que no logro encontrar ahora, pero vi hace un minuto).
¿Se supone que debe funcionar solo en una categoría en particular, o necesitarías que admitiera una “etiqueta predeterminada” para muchas categorías, donde esa etiqueta sea diferente para cada una de ellas?
Imagino que podría crear una configuración para tener una etiqueta predeterminada para varias categorías. Probablemente pueda hacerlo, pero no sé dónde ni cómo cambiar el botón “Crear tema” para que incluya la etiqueta predeterminada.
Los ids en los elementos HTML deben ser únicos; es decir, ningún dos elementos en la misma vista pueden compartir un atributo id de HTML. Por lo tanto, esto es suficiente para empezar.
Si busco "create-topic" en GitHub, esto es lo que veo…
Solo hay un resultado allí, así que tenemos suerte. Si hay más resultados, hay cosas que puedes hacer para reducir aún más la lista, pero eso está fuera del alcance de este tema.
Así que, revisemos ese archivo.
Entonces verás que la acción que tiene el botón se establece de la siguiente manera:
action=action
Bueno… eso no es muy útil… ¿Qué hacemos ahora?
Cuando ves action=action, significa que la acción se está pasando al componente desde una plantilla padre.
Intentemos ver qué plantillas tienen ese componente. Así que vamos a GitHub y buscamos el nombre del componente tal como se usaría en una plantilla. Para este ejemplo, usaríamos algo como esto: "{{create-topic-button"
Nota que solo agregué {{NOMBRE_COMPONENTE y omití el resto. No conocemos los otros argumentos que se le pasan, por lo que queremos una búsqueda genérica.
Obtenemos dos resultados… uno de ellos está en el plugin de estilo, así que simplemente lo ignoramos. El otro está en el núcleo. Así que veamos cómo se ve eso:
Ahh… nos estamos acercando. Ahora ves que la acción para el botón es:
action=(action "clickCreateTopicButton")
Ahora necesitamos averiguar qué hace esa acción. Así que buscamos el nombre de la acción. Luego filtramos a archivos .js porque ahora queremos ver la definición de esa acción en el archivo js del componente.
De nuevo, obtenemos solo un resultado, así que veámoslo.
Parece que la acción hace una de dos cosas. Si la categoría es de solo lectura y el usuario ya no tiene un borrador, muestra una alerta. De lo contrario, llama a un método createTopic().
Estamos interesados en este último, así que veámoslo.
Si buscas createTopic() en ese archivo (búsqueda en línea, no en GitHub)… notarás que solo hay una referencia para ello. ¿Qué pasa? ¿Cómo puede este componente llamar a un método que no está definido?
Bueno, la respuesta está más arriba en el archivo.
¿Qué significa esto?
No quiero perder mucho tiempo aquí, pero Ember usa Clases. Piensa en las clases como paquetes de código reutilizables. Toda la línea resaltada arriba significa:
Toma el paquete Component de Ember, agrega el paquete FilterModeMixin a él y déjame agregar más métodos, o sobrescribir algunos de los existentes, al resultado para crear un nuevo componente de Ember para mi aplicación.
Así que, ahora volvamos a la acción que estamos intentando rastrear.
Llama a this.createTopic(). Esto no es un método predeterminado de un componente de Ember. Es un método personalizado de Discourse, por lo que debe provenir de FilterModeMixin. ¿Qué es FilterModeMixin? Bueno… está definido en la parte superior del archivo.
import FilterModeMixin from "discourse/mixins/filter-mode";
Pausa un momento y haz una búsqueda en línea de createTopic() en ese archivo. En serio. Deja de leer y hazlo. Esperaré… no hagas trampa… tengo mis :ojos: puestos en ti.
OK. Buscaste y no hubo resultados. ¿Qué hacemos ahora?
Lo que describí arriba es solo un método para pasar cosas hacia abajo. Si no encuentras lo que buscas, da un paso atrás e intenta un enfoque diferente.
Así que, repasemos… ¿dónde estamos ahora? Antes de quedarnos atascados, estábamos viendo el archivo JS del componente d-navigation. Veamos su plantilla.
De nuevo, usamos "{{NOMBRE_COMPONENTE" y buscamos.
¿Importa esto? Quizás. ¿Importa para este caso? No. Solo estamos tratando de averiguar de dónde viene createTopic() o qué es. Así que simplemente usemos el primer resultado.
En serio, hablemos de las acciones de ruta. ¿Qué son? Bueno, son acciones de ruta… ¿acciones? Es decir, acciones definidas en la ruta. ¿Por qué son buenas? Porque las rutas en Discourse pueden estar anidadas.
Míralo de esta manera:
- ruta-1
- ruta-1-1
- ruta-1-2
- ruta-1-3
Si tengo un componente compartido que necesito usar en las rutas 111, 112 y 113 con diferentes parámetros, ¿no sería más fácil si simplemente definiera el mismo componente en todas ellas y pasara la misma acción? Luego lo modifico para cada ruta si es necesario.
Eso es lo que hacen las acciones de ruta.
OK, volvamos a la pregunta. Estábamos viendo:
createTopic=(route-action "createTopic")
en el componente navigation/default.
Ahora solo tenemos que averiguar cuál es la ruta para verificar qué hace esa acción de ruta.
Quieres modificar el comportamiento del botón de nuevo tema en las páginas /c/cat-slug/cat-id. Así que visitemos una de esas páginas. Por ejemplo: http://localhost:4200/c/meta/6
¿Cuál es esta ruta? A menos que estés muy familiarizado con Discourse, no podrías decirlo. ¿Qué hacemos ahora?
Aquí es donde la extensión de Ember para tu navegador resulta útil.
Instálala aquí si aún no la tienes. Esperaré.
(el enlace es un repositorio de GitHub, pero la descripción tiene los enlaces de la extensión para diferentes navegadores)
OK, ahora que la tienes instalada, visita esa página nuevamente /c/cat-slug/cat-id y mira la página de la extensión.
Una vez que se cargue, haz clic en Rutas y luego activa solo «Ruta actual».
Ahhh… mira eso. Ahora sabemos en qué ruta estamos. Estamos en discovery.category.
Pero esa no es toda la historia… es:
application > discovery > discovery.category
Recuerda, las rutas están anidadas. ¿Qué hacemos ahora?
Por lo general, empiezo desde la parte superior. En este caso, sería la ruta application. Encuentra el archivo para esa ruta y busca para ver si la acción está definida allí.
Parece que hace una de dos cosas. Si el usuario tiene un borrador, lo abre. Si no, llama a openComposer() con un parámetro. ¿Qué sigue? Bueno, ya deberías saber la respuesta. Necesitamos averiguar de dónde viene openComposer() o qué hace.
Así que buscamos en el archivo openComposer() y… por supuesto no obtenemos resultados. No hay ningún método en esa ruta llamado openComposer().
¿Qué sigue? Recuerda lo de las Clases de Ember. Intentémoslo.
Tenemos esto en la parte superior del archivo de ruta.
Esto significa que esta ruta hereda todos los métodos del paquete DiscourseRoute, así como los definidos en el paquete OpenComposer.
Es más probable que openComposer sea lo que queremos, así que veámoslo. Pero antes de hacerlo… necesitamos ver cómo se define openComposer en ese archivo.
import OpenComposer from "discourse/mixins/open-composer";
Mira la URL. No es un componente de Ember. No es una ruta; no es un modelo. Es un mixin. ¿Qué diablos es un mixin? La respuesta muy, muy corta… es un paquete de funciones reutilizables.
Definimos estos en tu mixin.
add(number) {
return number + 1
}
substract(number) {
return number - 1
}
luego agregamos el mixin a tu componente de Ember, y luego puedes hacer algo como esto:
// valor inicial es 1
myMethod () {
this.add(value) // devuelve 2
this.substract(value) // devuelve 0
}
Entonces, ¿cómo se relaciona esto con lo que estamos intentando hacer?
Bueno, open-composer aquí.
import OpenComposer from "discourse/mixins/open-composer";
es un mixin. Uno de los métodos en ese mixin es OpenComposer().
Está bien si te sientes confundido por esto. Comparten el mismo nombre, excepto que uno comienza con mayúscula, lo que indica que es una Clase.
Significan cosas diferentes.
Para entender esto, necesitarías saber que el nombre que le das a tus módulos importados no importa (en este caso particular), siempre y cuando se exporten como «default».
Explicar esto está un poco más allá del alcance de este tema. Todo lo que necesitas saber es que esto:
Id HTML del botón Nuevo tema < Acción del botón Nuevo tema < Acción del componente d-navigation < Acción de la ruta discovery < Mixin OpenComposer < método openComposer()
Así que… este es el método que finalmente se llama cuando haces clic en el botón + Nuevo Tema en esa ruta.
Hemos establecido cómo puedes averiguar la acción para ese botón en /c/cat-slug/cat-id, pero parece diferente de lo que sucede cuando visitas /tags/category-slug/tag-name, que es lo que quieres hacer.
¿Cuál es el siguiente paso? Veamos qué hace esa ruta para manejar la acción createTopic().
Bueno… notarás que maneja la acción de manera diferente.
createTopic() {
if (this.get("currentUser.has_topic_draft")) {
this.openTopicDraft();
} else {
const controller = this.controllerFor("tag.show");
const composerController = this.controllerFor("composer");
composerController
.open({
categoryId: controller.get("category.id"),
action: Composer.CREATE_TOPIC,
draftKey: Composer.NEW_TOPIC_KEY
})
.then(() => {
// Rellenar previamente el campo de entrada de etiquetas
if (composerController.canEditTags && controller.get("model.id")) {
const composerModel = this.controllerFor("composer").get("model");
composerModel.set(
"tags",
[
controller.get("model.id"),
...makeArray(controller.additionalTags)
].filter(Boolean)
);
}
});
}
}
Esta diferencia es básicamente lo que estás preguntando aquí.
Así que, todo lo que tienes que hacer es… modificar la acción createTopic() en la ruta discovery para que funcione como lo hace en la ruta tag-show. ¿Cómo lo haces?
Recuerda cómo hablamos de que Ember usa Clases? Sí, tendremos que volver a eso de nuevo.
La API de plugins te permite modificar clases de Ember mediante este método.
Así que, ¿qué estamos tratando de modificar aquí? La ruta discovery… porque… recuerda, ahí es donde se define createTopic() cuando estás en una página como /c/cat-slug/cat-id.
¿Qué hace eso? Rompe el botón + Nuevo Tema; sin embargo, nos dice que estamos en la dirección correcta. Si intentas agregar el fragmento anterior, notarás que al hacer clic en el botón ya no se abre el compositor. En su lugar, simplemente imprime un mensaje en la consola. Esto es bueno porque significa que hemos apuntado a la Clase correcta y a la acción correcta: route:discovery y createTopic().
¿Qué sigue? Bueno, recuerda que el botón en /tags/category-slug/tag-name hace exactamente lo que queremos. Así que copiemos el código de esa ruta y agreguemos las importaciones necesarias.
¿Funcionará eso? No, pero estamos un paso cerca. ¿Por qué no funciona? Porque las etiquetas que agrega cuando se abre el compositor no están definidas. ¿Por qué? Porque se cargan desde el controlador tag.show, que no es lo que queremos. Modifiquemos el código para que funcione con la ruta en la que estamos.
Antes de hacerlo, necesitamos algún tipo de índice para nuestras etiquetas predeterminadas deseadas. Usemos un nuevo objeto así:
// slug-de-categoría: [ARRAY_ETIQUETAS_POR_DEFECTO]
const defaultTagIndex = {
// slug de una sola palabra
meta: ["a", "b", "c"],
core: ["g", "h"],
// slug con un guion
["general-chat"]: ["d", "e", "f"]
};
Esto básicamente significa que si el compositor se abre en la página de la categoría meta, agrega las etiquetas «a, b, c».
Si el compositor se abre en la página de la categoría core, agrega las etiquetas «g, h», y así sucesivamente.
Ahora que tenemos eso, podemos modificar la acción para que se vea así.
He envuelto todo en un bloque try…catch. Si el código falla, ejecutamos this._super(...arguments).
Si estás familiarizado con Ember, sabrás qué hace this._super(...arguments). Si no, aquí tienes una explicación simple. Estamos sobrescribiendo createTopic(), por lo que si las sobrescrituras fallan debido a un error (quizás el núcleo se actualizó), entonces volvemos al método en el núcleo definido aquí.
Si el usuario tiene un borrador de nuevo tema, simplemente volvemos a this._super(...arguments) y dejamos que el núcleo haga su trabajo.
Eso debería ser suficiente. Todo lo que necesitas agregar ahora es una forma de crear el índice de etiquetas predeterminadas mediante la configuración del tema.