Tópico fixo: notificação de novo ou atualizado

Sim, adicionar uma URL causará navegação, já que não há lógica para interceptar o evento.

O link está chamando uma ação do Ember. Todas as ações no Discourse são extensíveis. Portanto, elas atuam como ganchos de personalização, de certa forma. Então, como você modifica uma ação? Bem, vamos verificar primeiro o que ela faz.

Pesquise no GitHub ou localmente pelo nome da ação. As ações são sempre definidas em arquivos JS e referenciadas no Handlebars. Queremos ver a definição, então restringimos a pesquisa aos arquivos JS.

Você obterá quatro arquivos. Qual deles você deve analisar? Você quer personalizar o

discovery/topics.hbs

template. Então

discovery/topics.js

é o que você deve analisar.

Algum desses códigos é útil? Não, mas agora sabemos onde a ação está definida. Então, vamos modificá-la.

discovery/topics.js é uma classe Ember. Você pode modificar classes Ember com um método na plugin-api chamado… modifyClass :stuck_out_tongue:

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

Já sabemos a Classe que queremos modificar. É discovery/topics. Precisamos saber que tipo de Classe é. Então, vamos verificar o diretório do arquivo.

discourse/app/controllers/discovery/topics.js

É um controlador.

Também sabemos que queremos modificar a ação showInserted nessa Classe. Então, começamos com isso.

api.modifyClass("controller:discovery/topics", {
  pluginId: 'sticky-new-topics-banner',
  actions: {
    showInserted() {
      // vamos fazer algum trabalho
    }
  }
});

Depois, você pode adicionar qualquer código que desejar para rolar a janela quando o usuário clicar no banner de “novos tópicos”. Eu optei por algo assim.

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

Você pode ler mais sobre scrollIntoView() aqui.

Em seguida, você adiciona isso ao método da plugin-api da seguinte forma.

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

Então, já terminamos? Não. Isso quebra o Discourse porque você está substituindo completamente a ação. Ao clicar no link, agora ele rolará até o elemento list-controls, mas não carregará os novos tópicos. Por quê? Porque o código principal não está mais sendo aplicado. Quero dizer, essas coisas

Então, como você corrige isso? Com esta linha simples

this._super(...arguments);

Você não precisa copiar nenhum código do núcleo se quiser apenas adicioná-lo. Faça seu trabalho e depois adicione essa linha. Tudo o que ela faz é garantir que o código do núcleo seja aplicado.

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

Se você testar isso, verá que quase tudo funciona muito bem, exceto… o cabeçalho está sobrepondo o list-controls. Por quê? Porque o cabeçalho está definido como fixo (sticky).

Você pode corrigir isso de várias formas em JS — calcular a altura, obter o deslocamento, importar um auxiliar do Discourse… etc. Não entrarei nesses detalhes.

A maneira mais fácil é com CSS usando scroll-margin-top, sobre o qual você pode ler aqui.

Então, adicionamos isso

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

Em português: Quando o link for clicado, role até o topo do list-controls - 2 * a altura do cabeçalho, para que não haja sobreposição e haja um pouco de espaço abaixo dele.

Então, vamos reunir tudo isso.

aba de cabeçalho comum

<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 comum

#list-area {
  // mobile tem um layout diferente
  .alert-info,
  .show-more.has-topics {
    position: sticky;
    // safari às vezes é exigente sem o prefixo
    position: -webkit-sticky;
    top: var(--header-offset);
    // banner deve estar acima do conteúdo
    z-index: z("header");
  }
}

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