Necesito ayuda para encontrar por qué la lista de temas no se actualiza después de hacer clic en la alerta de nuevo tema/respuesta

En el sitio del creador de temas, estaba intentando eliminar temas de usuarios ignorados de la lista de temas, aquí está mi código

api.modifyClass("component:topic-list", {
      @on("didReceiveAttrs")
      removeIgnoredUsers() {
        this.topics = this.topics.filter(
          topic => !ignored.includes(topic.posters[0].user.username)
        );
      }
});

Funciona tanto en móvil como en escritorio como se esperaba, pero también causa que la lista de temas no se actualice después de hacer clic en la alerta de nuevo tema/respuesta.

Tengo una grabación de pantalla de cómo reproducir este error.

En el video, estaba mirando la vista móvil en mi escritorio y creando una nueva respuesta a ‘test new topic alert 3’ desde mi teléfono celular, luego hago clic en la alerta en el escritorio, en el caso normal, el tema impulsado debería aparecer en la parte superior de los temas, pero no lo hace en este caso.

Pensé que el método que usé anteriormente se activaba por error, pero no se llama al hacer clic en una alerta de tema.

¿Alguien tiene alguna sugerencia sobre por qué ocurrió este error? Muchas gracias.

Actualización: Descubrí que también causa que la carga automática de más temas al desplazarse ya no funcione en la vista móvil.

2 Me gusta

¿Si quitas el código anterior que no muestra a los usuarios ignorados, se soluciona el problema? Si se soluciona, supongo que tu código está filtrando los temas de todos los usuarios (o de más usuarios de los que debería), lo que hace que no se muestren.

No sé de dónde se rellena ignored, pero podría rellenarse con el nombre de usuario del usuario que creó el nuevo tema que mostró la alerta en la parte superior. ¿Quizás se está rellenando con usuarios que no deberían ser ignorados? ¿Puedes registrar los valores presentes en ignored?

2 Me gusta

Creo que no es el caso ya que este problema solo ocurrió en la vista móvil/móvil. (no se impulsó y no se puede cargar más)

Establecí manualmente const ignored = ['david', 'pekka_gaiser', 'sam']; para probar en el sitio del creador de temas, ya que supuse que aún no había alcanzado el nivel de confianza que puede usar la función de ignorar o que estaba deshabilitada.

Y el tema que impulsé es un tema que creé.

1 me gusta

Extraño, a menos que el problema no esté específicamente en ese código. Si cambias el código a:

api.modifyClass("component:topic-list", {
      @on("didReceiveAttrs")
      removeIgnoredUsers() {
        this.topics = this.topics.filter(() => true);
      }
});

¿el problema persiste? Si persiste, el problema probablemente esté en otro lugar.

2 Me gusta

No, el error desaparece si cambio el bloque a lo que proporcionas. Pensé que tal vez la función de filtro rompía el ciclo de actualización de los datos del tema, pero este error solo ocurre en dispositivos móviles, no puedo entender qué causa la diferencia entre el escritorio y el móvil…

1 me gusta

Entonces, lo único que se me ocurre es que la variable ignored se está poblando incorrectamente, como dije anteriormente. Quizás en otro lugar se esté cambiando el valor. Intenta cambiar el código a:

api.modifyClass("component:topic-list", {
      @on("didReceiveAttrs")
      removeIgnoredUsers() {
        const ignored2 = ['david', 'pekka_gaiser', 'sam'];
        this.topics = this.topics.filter(
          topic => !ignored2.includes(topic.posters[0].user.username)
        );
      }
});

En el código anterior, he puesto la variable ignored en el mismo ámbito en el que se está aplicando el filtro, y he cambiado el nombre de la variable para evitar cambios no intencionados en los valores de la matriz (por ejemplo, si algún lugar llama a ignored.push(...), podría cambiar sus valores en otro lugar).

1 me gusta

Cuando haces algo como esto

this.topics = something_else

Estás redefiniendo esa propiedad a otra cosa. Esto está bien si nada más está observando esa propiedad, pero en este caso, varias cosas están observando el array topics.

Por ejemplo

Si haces esto en una instalación de desarrollo, Ember lanzará errores en la consola como estos.

.

Entonces, la conclusión clave aquí es hacer algo como esto en su lugar.

this.set("topics", something_else)

hasta que el decorador @tracked aterrice en Discourse.

Entonces, si intentas algo como esto

api.modifyClass("component:topic-list", {
  @on("didReceiveAttrs")
  removeIgnoredUsers() {
    const filtered = this.topics.filter(
      topic => !ignored.includes(topic.posters[0].user.username)
    );
    this.set("topics", filtered);
  }
});

Debería funcionar tanto en escritorio como en móvil en el creador de temas.

Sin embargo…

Si intentas eso en una instalación de desarrollo, verás otro error.

Así que esto debería decirte algo. El array topics se está utilizando en muchos lugares diferentes. Por lo tanto, no es una buena idea manipularlo, especialmente porque tu caso de uso es principalmente visual y no hay implicaciones de seguridad involucradas.

Aquí tienes lo que sugiero: da un paso atrás y prueba un enfoque diferente. En lugar de intentar modificar el array, haz algo mucho más simple. Si un usuario ignorado creó el tema, agrega una clase CSS y ocúltalo con CSS.

Lo haces con algo como esto: dejé algunos comentarios si quieres seguirlo, pero puedes eliminarlos cuando estés listo para usarlo.

Esto va en el archivo inicializador:

import { apiInitializer } from "discourse/lib/api";
import discourseComputed from "discourse-common/utils/decorators";

export default apiInitializer("0.11.1", api => {
  // establece un id para tus modificaciones
  const PLUGIN_ID = "hide-ignored-op-topics";

  // El nombre de la clase que quieres añadir. El espacio al principio es obligatorio
  const IGNORED_TOPIC_CLASS_STRING = " ignored-op-topic";

  // obtener el usuario actual
  const user = api.getCurrentUser();

  // no está conectado, sal
  if (!user) {
    return;
  }

  // obtener una lista de usuarios ignorados
  const ignored = user.ignored_users;

  // función auxiliar para evitar duplicar código
  const addIgnoredTopicClass = context => {
    // obtener clases del núcleo / otros plugins y temas
    let classList = context._super(...arguments);

    // crea tu condición
    const shouldAddClass = ignored.includes(
      context.topic.posters[0].user.username
    );

    // añade la clase ignorada si la condición es verdadera
    if (shouldAddClass) {
      classList += IGNORED_TOPIC_CLASS_STRING;
    }

    // devuelve la lista de clases más las modificaciones si las hay
    return classList;
  };

  // añade la clase a la lista de temas predeterminada, como en la página "latest"
  api.modifyClass("component:topic-list-item", {
    pluginId: PLUGIN_ID,
    @discourseComputed()
    unboundClassNames() {
      return addIgnoredTopicClass(this);
    }
  });

  // haz lo mismo para la lista de temas de la página de categorías
  api.modifyClass("component:latest-topic-list-item", {
    pluginId: PLUGIN_ID,
    @discourseComputed()
    unboundClassNames() {
      return addIgnoredTopicClass(this);
    }
  });
});

y esto va en /common/common.css

// no usamos display: none; aquí porque no queremos interferir con load-more
.ignored-op-topic {
  height: 0;
  width: 0;
  position: fixed;
  bottom: 0;
}
2 Me gusta

Gracias, el segundo método funciona.

¿Es posible añadir una clase adicional al elemento hijo del componente?

Por ejemplo, me gustaría ocultar el avatar del usuario ignorado en la lista de avatares de escritorio en el lado derecho del título del tema, ¿al añadir una clase al elemento de la lista de temas, se podrá añadir una clase al componente del avatar?

El método unboundClassNames() que usamos anteriormente es responsable de las clases añadidas al elemento del componente. En otras palabras, los elementos HTML .topic-list-item o .latest-topic-list-item. Puedes verlo aquí

y aquí

Ember toma cualquier cadena que devuelva ese método y la añade como el atributo class en el propio elemento.

Tendrás que usar algo más si quieres añadir clases a los hijos de ese elemento.

Cada publicador en la propiedad posters tiene una propiedad llamada extras. Esta propiedad se utiliza como un indicador para clases adicionales que se añadirán al avatar cuando se renderice.

Se establece aquí

y se consume aquí

Por lo tanto, puedes añadir clases a los avatares en la lista de temas basándote en una condición si extiendes esa propiedad.

Puedes usar el decorador @on cuando el componente reciba sus atributos para llamar a un método que haga eso. Dado que ya estamos modificando esas Clases de componente, podemos incorporar ese nuevo comportamiento al código anterior.

Aquí está lo que obtenemos

en el inicializador

import { apiInitializer } from "discourse/lib/api";
import discourseComputed, { on } from "discourse-common/utils/decorators";

export default apiInitializer("0.11.1", (api) => {
  const PLUGIN_ID = "hide-ignored-op-topics";
  const IGNORED_TOPIC_CLASS_STRING = " ignored-op-topic";
  const IGNORED_AVATAR_CLASS_STRING = " ignored-user-avatar";

  const user = api.getCurrentUser();

  if (!user) {
    return;
  }

  const ignoredUsers = user.ignored_users;

  function isIgnoredUser(poster) {
    return ignoredUsers.includes(poster.user.username);
  }

  function addIgnoredTopicClass() {
    let classList = this._super(...arguments);
    
    const topicCreator = this.topic.posters[0];

    if (isIgnoredUser(topicCreator)) {
      classList += IGNORED_TOPIC_CLASS_STRING;
    }

    return classList;
  }

  function addIgnoredAvatarClass() {
    this.topic.posters.forEach((poster) => {
      if (isIgnoredUser(poster)) {
        // default raw topic-lists
        poster.extras += IGNORED_AVATAR_CLASS_STRING;

        // categories page topic lists
        poster.user.set("extras", IGNORED_AVATAR_CLASS_STRING);
      }
    });
  }

  api.modifyClass("component:topic-list-item", {
    pluginId: PLUGIN_ID,

    @discourseComputed()
    unboundClassNames() {
      return addIgnoredTopicClass.call(this);
    },

    @on("didReceiveAttrs")
    ignoredAvatarClass() {
      addIgnoredAvatarClass.call(this);
    },
  });

  api.modifyClass("component:latest-topic-list-item", {
    pluginId: PLUGIN_ID,

    @discourseComputed()
    unboundClassNames() {
      return addIgnoredTopicClass.call(this);
    },

    @on("didReceiveAttrs")
    ignoredAvatarClass() {
      addIgnoredAvatarClass.call(this);
    },
  });
});

Esto te dará una clase CSS ignored-op-topic en los elementos de la lista de temas iniciados por un usuario ignorado y una clase CSS ignored-user-avatar en cada avatar de usuario ignorado en la columna de publicadores.

Ya tenemos el CSS para .ignored-op-topic de arriba.

// no usamos display: none; aquí porque no queremos interferir con load-more
.ignored-op-topic {
  height: 0;
  width: 0;
  position: fixed;
  bottom: 0;
}

Ahora, quieres ocultar los avatares de los usuarios ignorados en la columna de publicadores.

No hagas eso. Esto creará mucha confusión.

¿Qué pasa si un usuario ignorado responde a un tema, y este se actualiza, pero tienes su avatar oculto? Parecerá que alguien más ha actualizado el tema.

Además, solo hay un avatar en la página de categorías junto a los títulos de los temas. ¿Qué pasa si la última respuesta es de un usuario ignorado? ¿Sin avatar?

Puedes ver cómo tales casos crearían una experiencia desagradable para tus usuarios.

En lugar de ocultar los avatares de los usuarios ignorados, puedes sustituirlos por un icono SVG. Todos los usuarios ignorados tendrán el mismo avatar. Puedes hacerlo con CSS

.ignored-user-avatar {
  background: white;
  border: 1px solid transparent;
  box-sizing: border-box;
  opacity: 0.5;
  content: svg-uri(
    '\u003csvg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"\u003e\u003cpath d="m256 0c141.385 0 256 114.615 256 256s-114.615 256-256 256-256-114.615-256-256 114.615-256 256-256zm0 105c-83.262 0-151 67.74-151 151s67.737 151 151 151 151-67.736 151-151-67.74-151-151-151zm-52.816 130.621a22.119 22.119 0 1 0 0-44.237 22.119 22.119 0 0 0 0 44.237zm127.749-22.121a22.116 22.116 0 0 0 -22.12-22.12 22.119 22.119 0 1 0 22.12 22.12zm-40.233 70.79a9.439 9.439 0 0 0 -13.35-13.347l-21.35 21.357-21.352-21.357a9.438 9.438 0 1 0 -13.348 13.347l21.352 21.352-21.352 21.358a9.438 9.438 0 1 0 13.347 13.347l21.353-21.355 21.351 21.351a9.439 9.439 0 0 0 13.349-13.343l-21.352-21.354z"/\u003e\u003c/svg\u003e'\n  );
}

y se renderizará así

y lo mismo en latest-topic-list-item. Cambia el SVG por cualquier icono que quieras usar.

Con eso fuera de camino…

Respondí a tu pregunta porque es una buena oportunidad para hablar sobre la personalización de la lista de temas y cómo lo harías. Sin embargo, tengo muchas reservas sobre tu caso de uso. La necesidad de ocultar los avatares de los usuarios ignorados indica un problema subyacente. Una cosa es decir

“esta persona escribe sobre temas que no me interesan. La ignoraré para reducir el ruido.”

pero es algo completamente diferente decir

“incluso ver el avatar de esta persona me provoca una respuesta emocional. Nunca más quiero volver a ver su avatar.”

Conoces tu comunidad más que nadie… pero probablemente sea algo que valga la pena investigar.

4 Me gusta

Entendido. Estaba tratando de imitar la función de bloqueo como Mastodon o Twitter tanto como fuera posible, de esa manera, cuando ignoras a un usuario, nunca volverás a ver contenido de él. Estoy de acuerdo en que la mayoría de las comunidades pueden no necesitar este tipo de función. Ya que mis usuarios lo están pidiendo, me gustaría hacer mi mejor esfuerzo.

1 me gusta