Próximos cambios en encabezados: preparando temas y plugins

Recientemente hemos estado trabajando en actualizar el encabezado de Discourse, pasando del sistema de renderizado heredado de «widgets» a los componentes modernos de Glimmer. Este cambio ya está disponible en el núcleo de Discourse mediante la configuración del sitio glimmer header mode.

:timer_clock: Cronograma aproximado

(estimaciones muy aproximadas; sujetas a cambios en cualquier dirección)

Primer trimestre de 2024:

  • :white_check_mark: implementación en el núcleo finalizada y habilitada en Meta

  • :white_check_mark: publicadas recomendaciones de actualización; habilitados mensajes de obsolescencia en la consola

  • :white_check_mark: iniciado el trabajo para actualizar todos los plugins y temas oficiales y de terceros

Segundo trimestre de 2024:

  • :white_check_mark: inicio de la activación por defecto de la nueva implementación del encabezado

  • :white_check_mark: temas y plugins oficiales y de terceros listos para la actualización

  • :white_check_mark: los mensajes de obsolescencia comienzan a mostrar una alerta de administrador en la interfaz para cualquier problema pendiente

Tercer trimestre de 2024:

  • :white_check_mark: publicado tema de anuncio para mayor visibilidad: Preparing your community for behind-the-scenes header changes

  • :white_check_mark: semana del 5 de agosto de 2024 (v3.4.0.beta1): el nuevo encabezado habilitado por defecto en todos los sitios. Los administradores podrán volver al encabezado antiguo activando la configuración del sitio glimmer header mode.

  • :white_check_mark: semana del 2 de septiembre de 2024: eliminación definitiva de la bandera de característica y del código heredado

:eyes: ¿Qué significa esto para mí?

Si tu plugin o tema utiliza alguna API de «widget» para personalizar el encabezado, deberás actualizarlo para que sea compatible con el nuevo encabezado.

:person_tipping_hand: ¿Cómo puedo probar el nuevo encabezado?

En la versión más reciente de Discourse, el nuevo encabezado se activa automáticamente cuando todos tus temas y plugins son compatibles.

Si tus temas o plugins no son compatibles, se seguirá utilizando el encabezado heredado y se mostrará una advertencia en la consola junto con los mensajes de obsolescencia existentes. También se mostrará una alerta a los administradores en la interfaz.

En el poco probable caso de que este sistema automático no funcione como se espera, puedes anular temporalmente esta «bandera de característica automática» mediante la configuración del sitio glimmer header mode. Si lo haces, por favor indícanos la razón en este tema.

:technologist: ¿Necesito actualizar mi plugin o tema?

Para determinar si tu personalización requiere actualización, verifica si utiliza decorateWidget, changeWidgetSetting, reopenWidget o attachWidgetAction en cualquiera de estos widgets:

  • header
  • site-header
  • header-contents
  • header-buttons
  • user-status-bubble
  • sidebar-toggle
  • header-icons
  • header-topic-info
  • header-notifications
  • home-logo
  • user-dropdown

o si utiliza alguno de estos métodos de la API de plugins:

  • addToHeaderIcons
  • addHeaderPanel

Todas estas acciones ahora generarán mensajes de obsolescencia en la consola. Los identificadores de obsolescencia son:

  • discourse.add-header-panel
  • discourse.header-widget-overrides

:warning: Si utilizas más de un tema en tu instancia, asegúrate de verificarlos a todos.

Aviso para administradores

A partir del 20 de junio de 2024, hemos habilitado el aviso para administradores sobre las obsolescencias mencionadas anteriormente.

Si tu instancia fue desplegada después de esta fecha y sus plugins, temas o componentes de tema actuales activan alguna de las advertencias de obsolescencia, se mostrará el siguiente mensaje únicamente para los administradores*:

Este mensaje solo sirve para alertar a los administradores de que deben actuar pronto para modernizar las personalizaciones afectadas: las personalizaciones antiguas seguirán funcionando hasta que eliminemos el código base heredado.

:twisted_rightwards_arrows: ¿Cuáles son las alternativas?

Cada tema o plugin es diferente, pero aquí tienes orientación para los casos de uso más comunes:

addToHeaderIcons

:information_source: Para iconos personalizados del encabezado, recomendamos eliminar tu código e instalar el componente oficial Custom Header Links (Icons) Theme Component. Si esto no cumple con tus requisitos, consulta a continuación los detalles sobre los cambios de código necesarios:

La API de plugins addToHeaderIcons ha sido obsoleta en favor de la nueva API headerIcons. Esta permite agregar, eliminar o reordenar iconos en el encabezado. Requiere pasar un Componente.

El componente puede pasarse de la siguiente manera:

Antes Después
api.addToHeaderIcons(“widget-foo”) api.headerIcons.add(“foo”, FooIcon)
api.decorateWidget(“header-icons:before”, () => return helper.h(“div”, “widget-foo”)) api.headerIcons.add(“foo”, FooIcon, { before: “search” })
api.decorateWidget(“header-icons:after”, () => return helper.h(“div”, “widget-foo”)) api.headerIcons.add(“foo”, FooComponent, { after: “search” })

Este ejemplo utiliza el Formato de etiquetas de plantilla (gjs) de Ember para definir un componente en línea y pasarlo a la API headerButtons.add:

// .../discourse/api-initializers/add-my-button.gjs

import DButton from "discourse/components/d-button";
import { apiInitializer } from "discourse/lib/api";

export default apiInitializer("1.0", (api) => {
  api.headerIcons.add("some-unique-name", <template>
    <li><DButton class="icon btn-flat" @href="/u" @icon="address-book" /></li>
  </template>);
});

O para un menú desplegable, podrías usar <DMenu en lugar de <DButton:

import DButton from "discourse/components/d-button";
import { apiInitializer } from "discourse/lib/api";
import DMenu from "float-kit/components/d-menu";

export default apiInitializer("1.0", (api) => {
  api.headerIcons.add("some-unique-name", <template>
    <li>
      <DMenu class="icon btn-flat" @icon="address-book">
        <DButton @translatedLabel="Usuario 1" @href="/u/user1" />
        <DButton @translatedLabel="Usuario 2" @href="/u/user2" />
        <DButton @translatedLabel="Usuario 3" @href="/u/user3" />
      </DMenu>
    </li>
  </template>);
});

Ejemplos de commits de actualización:

decorateWidget("header-buttons:*")

:information_source: Para enlaces personalizados del encabezado, recomendamos eliminar tu código e instalar el componente oficial Custom Header Links Theme Component. Si esto no cumple con tus requisitos, consulta a continuación los detalles sobre los cambios de código necesarios:

El widget header-buttons ha sido obsoleto y hemos introducido una API de plugins headerButtons. Esta permite agregar, eliminar o reordenar botones en el encabezado. Requiere pasar un Componente.

Antes Después
api.decorateWidget(“header-buttons:before”) api.headerButtons(“button-name”, ButtonComponent, { before: “auth” })
api.decorateWidget(“header-buttons:after”) api.headerButtons(“button-name”, ButtonComponent, { after: “auth” })

changeWidgetSetting(...) para los widgets del encabezado

:information_source: Los usos más comunes de changeWidgetSetting pueden lograrse mediante estos componentes de tema:

Si estos no se ajustan a tu caso de uso, continúa leyendo…

Algunas personalizaciones en los widgets del encabezado utilizaban la API changeWidgetSetting.

Aunque no existe un reemplazo directo para personalizaciones como la mencionada anteriormente, debido a cómo funcionan los campos de los componentes Glimmer, hemos introducido una nueva API de plugins en Discourse 3.3.0.beta3 para manejar algunos de estos casos.

registerValueTransformer puede usarse para anular valores marcados en el código fuente como anulables; este es un enfoque similar al de los outlets de plugins.

Ya hemos añadido dos transformadores para los casos de uso que encontramos más comunes en nuestra base de código fuente:

  • home-logo-href: puede usarse para anular la URL en el ancla del logotipo principal. Consulta la sección home-logo más abajo para ejemplos.

  • header-notifications-avatar-size: puede usarse para cambiar el tamaño de la imagen cargada para el avatar del usuario en el encabezado. Ejemplo:

El siguiente código:

api.changeWidgetSetting(
  "header-notifications",
  "avatarSize",
  settings.header_avatars_size
);

Se convertiría en:

api.registerValueTransformer(
  "header-notifications-avatar-size",
  () => settings.header_avatars_size
);

Estos transformadores deben agregarse al código fuente de Discourse. Si necesitas uno diferente, por favor indícanos tu caso de uso publicándolo a continuación.

Más detalles sobre las nuevas APIs de transformadores de valores se pueden encontrar aquí.

home-logo

Hemos introducido un outlet de plugins llamado home-logo en reemplazo de las decoraciones de widgets home-logo:before o home-logo:after. Puedes utilizar la nomenclatura automática __before y __after en tu archivo de conectores para especificar dónde debe colocarse tu contenido personalizado.

Más detalles sobre la nomenclatura de archivos de conectores antes/después se pueden encontrar aquí.

Antes Después
api.decorateWidget(“home-logo:before”) Mover contenido a /connectors/home-logo__before
api.decorateWidget(“header-buttons:after”) Mover contenido a /connectors/home-logo__after)

Modificar la URL del ancla del logotipo principal:

Una necesidad muy común es modificar la URL a la que enlaza el home-logo. Hemos introducido el transformador de valores home-logo-href para abordar esto. Ejemplos:

  • para cambiar el enlace a una URL estática

    api.registerValueTransformer("home-logo-href", () => "https://example.com");
    
  • para devolver una URL dinámica basada en el usuario actual

    api.registerValueTransformer("home-logo-href", () => {
      const currentUser = api.getCurrentUser();
      return `https://example.com/${currentUser.username}`;
    });
    
  • para devolver una URL basada en una configuración de componente de tema

    api.registerValueTransformer("home-logo-href", () => {
      return settings.example_logo_url_setting;
    });
    

:sos: ¿Qué pasa con otras personalizaciones?

Si tu personalización no puede lograrse mediante CSS, PluginOutlets o las nuevas APIs que hemos introducido, por favor indícanos creando un nuevo tema en Development para discutirlo.

:sparkles: ¿Cómo actualizo un tema o plugin para que soporte tanto el encabezado antiguo como el nuevo?

Todas las nuevas APIs y outlets de plugins mencionados en este documento son compatibles tanto con el encabezado nuevo como con el antiguo. Por lo tanto, solo necesitas realizar una actualización en tu tema o plugin ahora, y los usuarios estarán listos para el cambio.

Pero, ¿cómo defino un FooIcon?

En un plugin, intenté crear /assets/javascripts/discourse/components/server-link.js (como para otros componentes que uso en un archivo hbs)

import Component from "@ember/component";
import discourseComputed from "discourse-common/utils/decorators";

export default Component.extend({
// ¿tiene que ir algo aquí?
});

Y un assets/javascripts/discourse/templates/components/server-link.hbs con
esto es un enlace (Creo que puedo hacer que sea un enlace si consigo que funcione este “Hola, mundo”)

El ejemplo anterior tiene const IconWithDropdown = ..., pero ¿dónde iría eso? Intenté ponerlo en un inicializador (donde había estado api.decorateWidget), pero no me parece javascript válido ni para ember, según lo que entiendo.

Antes tenía un array headerlinks y solía

        headerLinks.push(
          h(
            `li.headerLink.no-servers`,
            h("a", anchorAttributes, I18n.t("pfaffmanager.no_servers_title"))
          )
        );

para añadir los enlaces que quería. Creo que si consigo que

      api.headerIcons.add("foo", ServerLink, { before: "search" });

funcione, entonces podré poner eso en el bucle que construyó ese array.

¡¡¡OMG!!! ¿Los componentes de Glimmer van en assets/javascripts/discourse/component y los componentes de Ember van en assets/javascripts/discourse/components?!?

Ahora tengo un server-link.gjs

import Component from "@ember/component";
export default class ServerLink extends Component {
  // Argumento requerido para la URL
  url = null;
  // Argumento opcional para el texto del enlace
  text = 'asdf';
  click() {
    console.log('ServerLink clicked!',this);

  }
  // Plantilla para el componente
  <template>
    {{log "my template" this}}
    LINK!
    <a href={{this.url}}>{{this.text}}</a>
  </template>
}

Y en mi inicializador esto:

      api.headerIcons.add("foo", ServerLink, { param: "url, yo", before: "search" });

Ahora tengo algo en la cabecera.

Pero, ¿cómo paso cosas a ServerLink? Necesito llamarlo varias veces con diferentes URLs y diferente texto para hacer clic. No puedo ver las cosas en ese {} en el componente.

Y en realidad no quieres poner JavaScript antes de la <template>, ¡ya que mi console.log("") no se analizará!

También intenté hacer:

      const x = new ServerLink({
        url: "mylink",
        text: "my-text",
        name: 'Bob',
        message: 'Generated from JavaScript',
      });

y luego pasar x en lugar de ServerLink, pero todavía no hay suerte.

¿Quieres decir que quieres varios botones en las cabeceras con diferentes iconos/texto/URL o el mismo botón, pero dependiendo del contexto, el texto/URL puede cambiar?

Sí, estás en una clase; ¡declararías variables, funciones o plantillas allí!

Sí. Los enlaces cambian para diferentes usuarios. El código antiguo enlazaba a través de una matriz de servers y los añadía a esta matriz:

            headerLinks.push(
              h(
                `li.headerLink${deviceClass}${newClass}`,
                h("a", anchorAttributes, linkText)
              )
            );

Y luego hizo esto:

      // api.decorateWidget("header-buttons:before", (helper) => {
      //   return helper.h("ul.pfaffmanager-header-links", headerLinks);
      // });

Así que tenía hasta 3 enlaces que se añadían a la cabecera, cada uno enlazando a una URL de servidor separada.

Ajá. Ahora lo entiendo.

No, no te preocupes, la convención sigue siendo /components/ :sweat_smile:

(Técnicamente, puedes definir y pasar componentes gjs como quieras, así que puedes elegir el nombre de directorio que prefieras. Pero nos ceñimos a /components/.)

Sí, esa es una pregunta justa. Trabajaré en la redacción de algunos documentos ‘desde cero’ sobre cómo añadir iconos a la cabecera para que tengamos un mejor punto de referencia.

Mientras tanto, podrías echar un vistazo a la actualización de discourse-icon-header-links para inspirarte. Lo bueno de usar GJS es que puedes definir componentes en cualquier lugar y tienen acceso a las variables del ámbito local.

Así que, si cambias el nombre de tu inicializador a .gjs, puedes hacer cosas como

servers.forEach((server) => {
  api.headerIcons.add(`server-${server.id}`, 
    <li><DButton @translatedLabel={{server.name}} @icon="server" /></li>
  );
});

O puedes definir un componente antes en el mismo archivo y usarlo como

class ServerButton extends Component {
  get icon(){
    // alguna lógica para decidir el icono
  }
  
    <li><DButton @translatedLabel={{@server.name}} @icon={{this.icon}} /></li>
  
}

...

servers.forEach((server) => {
  api.headerIcons.add(`server-${server.id}`, 
    <ServerButton @server={{server}} />
  );
});

O podrías mover la iteración dentro de la plantilla (¡útil si la lista de servidores es un TrackedArray que puede cambiar en tiempo de ejecución!)

api.headerIcons.add("server-buttons",  
  {{#each servers as |server|}}
    <ServerButton @server={{server}} />
  {{/each}}
);

Oh. Genial. Pensé que lo había probado en components y no había funcionado.

¡Gracias! Creo que puedo hacer que una de estas cosas funcione. El enlace a los enlaces de encabezado es de gran ayuda. Estoy bastante seguro de que cuando escribí mi código, tuve suficiente sentido como para mirar ese mismo componente para resolverlo entonces.

¡Viendo un rayo de esperanza!

¡Hola @david y @Arkshine! ¡Lo logré!

¿Quuuuuuuuuuuuuué? ¿Solo cambiarle el nombre? Eso es una locura. Pero, efectivamente. Lo hice, y ahora

          servers.filter(Boolean).map((server) => {
            const linkHref = `/pfaffmanager/servers/${server.id}`;
            const linkTitle = `click to configure server ${server.id}`;
            let host = String(server.hostname);
            const linkText = host.replace(
              /www.|community.|forums?.|talk.|discourse./,
              ""
            );
            const serverLink = <template>
              <li class="headerLink">
                <a class="btn-flat" href={{linkHref}} title={{linkTitle}}>
                  {{host}}
                </a>
              </li>
            </template>;
            const beforeIcon = ["chat", "search", "hamburger", "user-menu"];
            api.headerIcons.add(host, serverLink, { before: beforeIcon });
          });

¡Y está haciendo lo que hacía antes, que es lo que quería!

Sí, ahora eso suena muy bien, y aún más lo que quiero, pero esas cosas no cambian mucho, así que lo dejaré por hoy. Si cambian un nombre de host, tendrán que recargar la página para que el enlace cambie.

Supongo que borrarás mi código extra aquí cuando actualices lo anterior (o quizás no habría sido tan hablador en un tema de documentación…).

He actualizado el OP con algunos ejemplos completos de gjs e incluido un enlace a la documentación original de Ember. ¿Qué te parece @pfaffman? ¿Hay algo más que creas que valdría la pena añadir?

Es mejor porque tiene un ejemplo funcional. Pero, ¿entiendo correctamente que existen componentes de ember y componentes de glimmer? Y si es así, ¿deberías decir que se requiere un componente de glimmer, creo?

¿Y tal vez enlazar a la documentación de glimmer sobre cómo funcionan?

Parece que puedes tener componentes en línea como en tu ejemplo, y otro tipo donde lo asignas a algo como una variable en el mismo archivo o lo pones en otro archivo que pones en el directorio de componentes y luego incluyes? Creo que todo eso puede ser demasiado para este tema, pero me encantaría un tema dedicado a eso.

Son totalmente intercambiables: puedes usar un componente clásico de Ember o un componente de Glimmer. Y para cualquiera de ellos, puedes optar por crearlos utilizando el formato antiguo de .js/.hbs o el nuevo formato de .gjs.

Veré si puedo incluir algunos enlaces a la documentación de Ember :+1:

:mega: Hoy fusionamos este cambio, que habilitará automáticamente la nueva implementación de la cabecera para sitios con temas/plugins compatibles.

Si tus temas/plugins no son compatibles, se seguirá utilizando la cabecera heredada y se imprimirá una advertencia en la consola junto con los mensajes de deprecación existentes. En un futuro cercano, esta advertencia de consola se actualizará a un banner de advertencia en la interfaz de usuario.

En el improbable caso de que este cambio automático cause problemas, puedes anular temporalmente esta ‘bandera de función automática’ a través de la configuración del sitio glimmer header mode. Si lo haces, por favor, infórmanos el motivo en este tema.

No buscaba hacer ningún cambio, pero los avisos de depreciación me dicen lo contrario.

Entonces, ¿hay una opción y quizás una forma fácil de mantener el status quo?

o

¿Qué me estaría perdiendo al optar por mantener un encabezado antiguo? No entiendo qué significa el nuevo. Veo configuraciones de grupo, la personalización para diferentes grupos es intrigante, pero ¿qué se puede personalizar?

Esto es lo que encontré hoy:

No soy un gurú ni un genio con estos cambios, llevan tiempo y no los hago con la frecuencia suficiente como para desear aprender las técnicas que los usuarios aquí parecen comprender/conocer fácilmente.

De alguna manera me molesta tener que hacerlos en primer lugar, pero antes de gritar como un viejo que se quedó sin pudín, me gustaría saber, ¿qué, por qué y hacia dónde va esto?

Me dedico a esto profesionalmente y aun así encuentro que estas cosas de JavaScript están lejos de ser fáciles.

Soy un anciano y entiendo tu dolor.

Es solo el progreso, me temo. Esta actualización de Ember rompió un montón de cosas y aún no ha terminado.

“Lo pediste” cuando hiciste esa personalización. Apuesto a que en los últimos cinco años has comprado un teléfono o portátil nuevo.

Si yo fuera tú (y tú fueras como yo, sin el trabajo a tiempo completo en Discourse), publicaría en Marketplace. Si fuera yo, probablemente no respondería por menos de $300, pero hay una posibilidad razonable de que alguien más lo haga por $100 o $200. Supongo que no se volverá a romper en otros 5 años o más.

Creo que puedes deshacerte del selector de temas de hamburguesa y usar la barra lateral.

Respuesta honesta y agradable, la aprecio pero no hay mucho con lo que trabajar, quizás haya más por venir (espero)

Ni siquiera sabía que estábamos tratando con Java aquí :man_shrugging:

Tampoco quiero que nadie te quite tu pudín :face_with_hand_over_mouth:

Claro, pero ¿cuál es el objetivo deseado? Este software se dedica a tantas cosas que me pregunto quién ve qué final.

¿Es esto simplemente necesario por la actualización de Ember?

Tampoco sé por qué se hizo Ember, pero si funciona, ¿por qué arreglarlo? Estoy seguro de que hay una explicación larga y profunda que conduce al futuro de las cosas, pero ¿no hay una visión real que compartir?

Visito otros foros que usan software muy antiguo, personalmente veo Discourse como mucho mejor que cualquiera de ellos, pero no parecen sufrir en comparación, tienen los mismos problemas de crecimiento, la mayoría son personalidad vs software en mi opinión, demasiados veteranos que perdieron su pudín, me pregunto, ¿hay un futuro IOT que hará que todos esos foros queden obsoletos donde no funcionarán en absoluto y Discourse lo sabe y se está preparando?

Hay más de esa honestidad que proporcionas :grin: es cierto, y yo estaba más ansioso por aprender, más ambicioso, sentí que valía la pena más, he sido maltratado, atropellado y dejado por muerto desde entonces.

ok, aceptas, acepto esa apuesta y como ya has perdido me ayudas con esto, seremos amigos.

Entonces probablemente te habrías rendido hace mucho tiempo, lo de ser maltratado, atropellado y dejado por muerto fue una descripción un tanto eufemística, solo quedo yo al volante, supongo que cualquiera que quede está en algún panel tratando de arreglar el hiperimpulsor, no lo sé ya que no me comunico a menudo con otros, no tenemos financiación, nosotros (FULL30) fuimos des-plataformados de las redes sociales y también de Discourse, me pregunto cuántos otros clientes de pago cortó Discourse voluntariamente o cuántos otros fueron considerados tan ofensivos en sus creencias que Discourse públicamente invirtió dinero en su contra.

Sin embargo, mientras digo la verdad, no me ofendo, vive y deja vivir, sé que se acerca un futuro, lo que no sé es por qué sigo aquí y sigo intentándolo, pero lo hago, así que seguiré intentándolo, como AA, solo por hoy :hugs:

Pero estaba de moda cuando lo implementé :expressionless:

La barra lateral (aquí) se puede cerrar con un menú de hamburguesa, no hay mucha diferencia en la función, abre y cierra una ventana de navegación, pero ¿la mía no se puede guardar fácilmente?

Sí, me encantaría y preferiría pagarle a alguien para que limpie mi código personalizado y haga que las cosas funcionen bien, y pagaría felizmente, disfruto empleando a otros, compartiendo la riqueza, cuando sea mayor quiero ser un filántropo, pero hoy necesito un filántropo :innocent: y nuevamente aprecio cualquier ayuda que otros puedan ofrecer.

La otra forma de jugarlo es pedir ayuda a tu comunidad, dejar de hacer lo que sea que fuera la personalización, iniciar un nuevo tema que comparta tu código y pedir ayuda. He recibido mucha ayuda en tales asuntos recientemente.

Desafortunadamente no. La capacidad de mantener la ‘cabecera antigua’ es solo algo temporal durante el período de transición. Pronto, la nueva cabecera será la única opción.

¡Sí! Siempre estamos contentos de ayudar con preguntas en Dev. Además, compartir el código y las soluciones públicamente crea un recurso útil para otros.

uf, mi comunidad está más en sintonía con otros problemas

Seguramente podría compartir aquí, pero entonces va en sentido contrario, ¿a qué programador le interesa ayudarnos?

La ironía, la programación bien podría ser el arma de fuego del futuro, bien podría causar mucha más muerte y destrucción, me desvío

Muy bien, ¿qué significa eso exactamente para mí, crear un grupo de usuarios, público y no registrado, quizás?

Estas configuraciones de grupo, percibo que se basan en niveles de confianza en lugar de grupos reales diferentes, ¿como un grupo de caza y un grupo de pesca?

Primero necesito entender el objetivo y dónde mis esfuerzos como lobo solitario tendrán el mayor impacto, ahorrando tiempo y conservando la sensación personalizada de mi foro.


No deseo descarrilar el hilo de nadie, si se considera que esto debería ser su propio hilo, estoy de acuerdo

pero, ¿cómo habrá alguna vez una relación verdaderamente cohesiva cuando la gente sienta la necesidad de eliminar algo que le ofende?

Se necesita paciencia para entender a los demás, se eliminó el enlace, mostró un logotipo faltante después pero no mientras se publicaba, ¿otro problema de cabecera a discutir?

Era una publicación en mi foro escrita por un hombre que creo que tiene más de 80 años, podría preguntarle con seguridad pero se niega a hablar conmigo, ¿lo reprendo, lo prohíbo o lo evito?

No, ¿por qué? Porque hay una mejor manera, pero significa tolerar a los demás y cómo piensan, encuentro gente buena en áreas malas, gente buena que parece mala, y justo lo contrario en ambos casos.

Exactamente, acabo de encontrar los errores, deseo abordarlos pero no entiendo la causa raíz, aparte de que el futuro avanza, necesitamos una nueva cabecera, está bien, ¿cuál es el curso correcto para fijar mi objetivo, una simple puesta a punto, una corrección completa del rumbo?

¿Estamos discutiendo solo la necesidad de trabajar en estas tres áreas?

Tengo un uso mixto de componentes, comencé sin ninguno y luego aprendí que pueden ser beneficiosos, nunca fui completamente de componentes y tengo una mezcla.

Aquí está mi tema por lo que vale la pena sin los componentes
discourse-full30-ii.zip (10,1 KB)
También puedo publicar esos, algunos, los modales, ya no funcionan recientemente