Preciso de ajuda para descobrir por que a lista de tópicos não atualiza após clicar no alerta de novo tópico/resposta

No site do criador de temas, eu estava tentando remover tópicos de usuários ignorados da lista de tópicos, aqui está meu 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 no mobile quanto no desktop como esperado, mas descobri que também faz com que a lista de tópicos não atualize após clicar no alerta de novo tópico/resposta.

Tenho um screen record de como reproduzir este bug.

No vídeo, eu estava olhando a visualização mobile no meu desktop e criando uma nova resposta para ‘test new topic alert 3’ do meu celular, então clico no alerta no desktop, no caso normal, o tópico impulsionado deveria aparecer no topo dos tópicos, mas não aparece neste caso.

Pensei que o método que usei acima foi acionado erroneamente, mas ele não está sendo chamado ao clicar em um alerta de tópico.

Alguém tem alguma sugestão sobre por que esse bug aconteceu? Muito obrigado.

Atualização: Descobri que também faz com que o carregamento automático de mais tópicos ao rolar não funcione mais na visualização mobile.

2 curtidas

Se você remover o código acima que não mostra usuários ignorados, o problema é resolvido? Se for resolvido, minha suposição é que seu código está filtrando os tópicos de todos os usuários (ou de mais usuários do que deveria), o que faz com que eles não sejam exibidos.

Não sei de onde ignored é preenchido, mas pode estar sendo preenchido com o nome de usuário do usuário que criou o novo tópico que exibiu o alerta no topo. Talvez esteja sendo preenchido com usuários que não deveriam ser ignorados? Você pode registrar os valores presentes em ignored?

2 curtidas

Acho que não é o caso, já que esse problema só aconteceu no celular/visualização de celular. (não foi impulsionado e não pode carregar mais)

Defini manualmente const ignored = ['david', 'pekka_gaiser', 'sam']; para testar no site do criador de temas, já que assumi que ainda não atingi o nível de confiança que pode usar o recurso de ignorar ou que ele estava desativado.

E o tópico que impulsionei é um tópico que eu criei.

1 curtida

Estranho, a menos que o problema não esteja especificamente nesse código. Se você alterar o código para:

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

o problema persiste? Se persistir, o problema provavelmente está em outro lugar.

2 curtidas

Não, o bug desaparece se eu alterar o bloco para o que você fornecer. Pensei que talvez a função de filtro quebrasse o ciclo de atualização dos dados do tópico, mas este bug só acontece no celular, não consigo descobrir o que causa a diferença entre desktop e celular…

1 curtida

Então, a única coisa que consigo pensar é que a variável ignored está sendo preenchida incorretamente, como eu disse anteriormente. Talvez em outro lugar o valor esteja sendo alterado. Tente alterar o código para:

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)
        );
      }
});

No código acima, coloquei a variável ignored no mesmo escopo em que o filtro está sendo aplicado e alterei o nome da variável para evitar alterações não intencionais nos valores do array (por exemplo, se algum lugar chamar ignored.push(...), isso pode alterar seus valores em outro lugar).

1 curtida

Quando você faz algo como isto

this.topics = something_else

Você está redefinindo essa propriedade para outra coisa. Isso é bom se mais nada estiver observando essa propriedade, mas neste caso, várias coisas estão observando o array topics.

Por exemplo

Se você fizer isso em uma instalação de desenvolvimento, o Ember lançará erros no console como este.

.

Portanto, a principal conclusão aqui é fazer algo como isto em vez disso.

this.set("topics", something_else)

até que o decorador @tracked chegue ao Discourse.

Portanto, se você tentar algo como isto

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);
  }
});

Isso deve funcionar tanto no desktop quanto no mobile no criador de temas.

No entanto…

Se você tentar isso em uma instalação de desenvolvimento, verá outro erro.

Então isso deve lhe dizer algo. O array topics está sendo usado em muitos lugares diferentes. Portanto, não é uma boa ideia mexer nele - especialmente porque seu caso de uso é principalmente visual e não há implicações de segurança envolvidas.

Aqui está o que eu sugiro: Dê um passo para trás e tente uma abordagem diferente. Em vez de tentar modificar o array, faça algo muito mais simples. Se um usuário ignorado criou o tópico, adicione uma classe CSS e oculte-o com CSS.

Você faz isso com algo como isto - deixei alguns comentários se você quiser acompanhar, mas pode excluí-los quando estiver pronto para usá-lo.

Isso vai no arquivo inicializador:

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

export default apiInitializer("0.11.1", api => {
  // defina um id para suas modificações
  const PLUGIN_ID = "hide-ignored-op-topics";

  // O nome da classe que você deseja adicionar. O espaço no início é obrigatório
  const IGNORED_TOPIC_CLASS_STRING = " ignored-op-topic";

  // obter usuário atual
  const user = api.getCurrentUser();

  // não logado, saia
  if (!user) {
    return;
  }

  // obter uma lista de usuários ignorados
  const ignored = user.ignored_users;

  // função auxiliar para evitar duplicação de código
  const addIgnoredTopicClass = context => {
    // obter classes do core / outros plugins e temas
    let classList = context._super(...arguments);

    // crie sua condição
    const shouldAddClass = ignored.includes(
      context.topic.posters[0].user.username
    );

    // adicione a classe ignorada se a condição for verdadeira
    if (shouldAddClass) {
      classList += IGNORED_TOPIC_CLASS_STRING;
    }

    // retorne a classList mais as modificações, se houver
    return classList;
  };

  // adicione a classe à lista de tópicos padrão, como na página "mais recente"
  api.modifyClass("component:topic-list-item", {
    pluginId: PLUGIN_ID,
    @discourseComputed()
    unboundClassNames() {
      return addIgnoredTopicClass(this);
    }
  });

  // faça o mesmo para a lista de tópicos da página de categorias
  api.modifyClass("component:latest-topic-list-item", {
    pluginId: PLUGIN_ID,
    @discourseComputed()
    unboundClassNames() {
      return addIgnoredTopicClass(this);
    }
  });
});

e isso vai em /common/common.css

// não usamos display: none; aqui porque não queremos mexer com load-more
.ignored-op-topic {
  height: 0;
  width: 0;
  position: fixed;
  bottom: 0;
}
2 curtidas

Obrigado, o segundo método funciona.

É possível adicionar uma classe extra ao elemento filho do componente?

Por exemplo, gostaria de ocultar o avatar do usuário ignorado na lista de avatares da área de trabalho no lado direito do título do tópico, ao adicionar uma classe ao item da lista de tópicos, será possível adicionar uma classe a um componente de avatar?

O método unboundClassNames() que usamos acima é responsável pelas classes adicionadas ao elemento do componente. Em outras palavras, os elementos HTML .topic-list-item ou .latest-topic-list-item. Você pode ver isso aqui

e aqui

O Ember pega qualquer string que esse método retorna e a adiciona como o atributo class no próprio elemento.

Você terá que usar outra coisa se quiser adicionar classes aos filhos desse elemento.

Cada poster na propriedade posters tem uma propriedade chamada extras. Essa propriedade é usada como um indicador para classes extras a serem adicionadas ao avatar quando ele for renderizado.

Está definido aqui

e consumido aqui

Portanto, você pode adicionar classes aos avatares na lista de tópicos com base em uma condição se estender essa propriedade.

Você pode usar o decorador @on quando o componente receber seus atributos para chamar um método para fazer isso. Como já estamos modificando essas Classes de componentes, podemos incorporar esse novo comportamento ao código acima.

Aqui está o que obtemos

no 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);
    },
  });
});

Isso lhe dará uma classe CSS ignored-op-topic em itens da lista de tópicos iniciados por um usuário ignorado e uma classe CSS ignored-user-avatar em cada avatar de usuário ignorado na coluna de posters.

Já temos o CSS para .ignored-op-topic de cima.

// não usamos display: none; aqui porque não queremos mexer com load-more
.ignored-op-topic {
  height: 0;
  width: 0;
  position: fixed;
  bottom: 0;
}

Agora, você quer ocultar os avatares de usuários ignorados na coluna de posters.

Não faça isso. Isso criará muita confusão.

E se um usuário ignorado responder a um tópico, e ele for atualizado, mas você tiver o avatar dele oculto? Isso fará parecer que outra pessoa acabou de atualizar o tópico.

Além disso, há apenas um avatar na página de categorias ao lado dos títulos dos tópicos. O que acontece se a última resposta for de um usuário ignorado? Nenhum avatar?

Você pode ver como tais casos criariam uma experiência desagradável para seus usuários.

Em vez de ocultar os avatares de usuários ignorados, você pode substituí-los por um ícone SVG. Todos os usuários ignorados terão o mesmo avatar. Você pode fazer isso com 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'
  );
}

e renderizará assim

e o mesmo em latest-topic-list-item. Mude o SVG para qualquer ícone que você queira usar.

Com isso resolvido…

Eu respondi à sua pergunta porque é uma boa oportunidade para falar sobre a personalização da lista de tópicos e como você faria isso. No entanto, tenho muitas ressalvas sobre o seu caso de uso. A necessidade de ocultar os avatares de usuários ignorados indica um problema subjacente. É uma coisa dizer

“essa pessoa escreve sobre assuntos que não me interessam. Vou ignorá-la para reduzir o ruído.”

mas é uma coisa totalmente diferente dizer

“ver o avatar dessa pessoa me causa uma resposta emocional. Nunca mais quero ver o avatar dela.”

Você conhece sua comunidade melhor do que ninguém… mas provavelmente é algo que vale a pena investigar.

4 curtidas

Entendido. Eu estava tentando imitar a função de bloqueio como o Mastodon ou o Twitter o máximo possível, que é quando você ignora um usuário e nunca mais vê nenhum conteúdo dele. Concordo que a maioria das comunidades pode nunca precisar desse tipo de função. Como meus usuários estão pedindo isso, eu gostaria de fazer o meu melhor.

1 curtida