Inserir widget dentro do texto em um tópico

Olá!! :slight_smile:

Espero que isso seja do interesse de alguém mais: estou tentando criar um tópico de “feed de redes sociais”, no qual quero incorporar os widgets de diferentes plataformas para que os usuários (e os administradores!) possam ter uma visão rápida de todos eles em apenas uma página. Assim, eles permanecem no fórum, evitando a sobrecarga de ter que alternar entre tantas plataformas sociais apenas para atualizações rápidas (considerando também que geralmente compartilham o mesmo conteúdo), aumentando, assim, a motivação para usar o fórum como um hub para o desenvolvimento da comunidade.

Encontrei um ótimo gerador de widgets chamado Woxo, que é limpo e simples o suficiente para o propósito. O problema agora é que não consigo descobrir como incorporar o widget no tópico. Estou tentando ver se há alguma solução alternativa com iframes ou algo assim, mas agora gostaria de perguntar se isso é possível de alguma forma.

Este é o código que recebo do Woxo para o feed do Instagram:

<div data-mc-src="f4b43a8f-c188-4f80-8206-36d9f7529f13#instagram"></div>
        
<script 
  src="https://cdn2.woxo.tech/a.js#616348fb53c1e8001686c619" 
  async data-usrc>
</script>

O que tentei até agora:

  • Colocar o <script> na seção <body>, <footer> e <header> (deixei no header)
  • Garantir que a URL de onde o script vem esteja na lista branca (https://cdn2.woxo.tech/ neste caso)
  • Adicionar defer não ajuda (estou mantendo apenas por precaução)

Se eu inspecionar a página, o script aparece no final da seção body (dentro dela) e, como a origem está na lista branca, deveria funcionar. Verifiquei se poderia ser o meu navegador, mas se eu executar o HTML aqui W3Schools Tryit Editor, funciona perfeitamente.

Reduzi o erro a uma função específica dentro do script JS. A seguinte chamada retorna um valor nulo. É o único erro de tempo de execução:

e=document.querySelector("div[data-mc-src]")
e is null

Esse div está escrito no tópico (a parte <div data-mc-src="f4b43a8f-c188-4f80-8206-36d9f7529f13#instagram"></div>). Ele permanece como código HTML puro, então deveria ser legível. Por algum motivo, o script não consegue localizá-lo.

Com o atributo defer e localizado no footer, o script não lança erro (o fato de ter lançado erro antes prova que a URL do arquivo JS está realmente na lista branca), então agora estou no escuro sobre por que não está fazendo nada.

Qualquer contribuição será mais do que apreciada, obrigado por seu tempo antecipadamente! :slight_smile:
Lisandro

EDIT: finalmente tive que desistir. Como apenas iframes são suportados, estou atualmente procurando um bom serviço web que possa fornecer um. A maioria dos serviços gratuitos é muito limitada, e as versões pagas custam mais do que o dobro do serviço de hospedagem do fórum. Desculpe pelo desabafo, precisei chorar em voz alta aqui por não conseguir apenas inserir o código HTML gratuito :cry:

O Discourse é um aplicativo de página única. O problema que você está enfrentando ocorre porque o script que você está usando não está ciente disso. Quando você visita a página inicial — ou qualquer outra página — no Discourse, você obtém algo assim.

<html>
  <head>
    conteúdo do head incluindo seu script
  </head>
  <body>
    <section id="main">
      conteúdo da página
    </section>
  </body>
</html>

Quando você navega para outra página, a única coisa que é recarregada é o conteúdo dentro de

<section id="main">

Portanto, o DOM mudou e seu script personalizado não é acionado novamente. Se você tentar visitar a página do tópico diretamente, verá que ela carrega normalmente.

.

Agora, a questão é como fazê-lo funcionar com o Discourse.

A plugin-api possui um método que você pode usar para “decorar” postagens.

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

Você pode usá-lo para acionar scripts de terceiros quando uma postagem for renderizada.

Aqui está o código que você precisará. Adicione isso à aba common > header do seu tema.

<script type="text/discourse-plugin" version="0.8">
const WOXO_SCRIPT_SRC = "https://cdn2.woxo.tech/a.js#616348fb53c1e8001686c619";
const PREVIEW_ICON = "heart";

const loadScript = require("discourse/lib/load-script").default;
const { iconHTML } = require("discourse-common/lib/icon-library");

const composerPreviewIcon = iconHTML(PREVIEW_ICON, {
  class: "woxo-preview-icon"
});

const previewMarkup = () => {
  const markup = `<div class="woxo-preview">${composerPreviewIcon}</div>`;
  return markup;
};

// cria um decorador de postagem
api.decorateCookedElement(
  post => {
    const woxoWidgets = post.querySelectorAll("div[data-mc-src]");

    if (woxoWidgets.length) {
      woxoWidgets.forEach(woxoWidget => {
        if (post.classList.contains("d-editor-preview")) {
          woxoWidget.innerHTML = previewMarkup();
          return;
        }

        loadScript(WOXO_SCRIPT_SRC).then(() => {
          const script = document.head.querySelector(
            `script[src*="cdn2.woxo.tech"]`
          );
          script.dataset.usrc = "";
          window.MC.Loader.init();
        });
      });
    }
  },
  { id: "render-woxo-widgets" }
);
</script>

Em seguida, você precisará adicionar alguns domínios para CSP. Adicione-os à sua

content_security_policy_script_src

configuração do site

https://*.woxo.tech/
https://us-central1-core-period-259421.cloudfunctions.net/availableComponentTracks

finalmente, você precisará adicionar um pouco de CSS para a pré-visualização estática do editor

Isso vai na aba common > CSS do seu tema.

.woxo-preview {
  height: 400px;
  width: 100%;
  background: var(--primary-low);
  display: flex;
  align-items: center;
  justify-content: center;
  .woxo-preview-icon {
    font-size: var(--font-up-4);
    color: var(--primary-high);
  }
}

Depois, basta adicionar

<div data-mc-src="f4b43a8f-c188-4f80-8206-36d9f7529f13#instagram"></div>

a qualquer postagem, e os widgets serão renderizados e totalmente funcionais.

Se você observar o JavaScript, notará que ele possui duas opções no topo.

const WOXO_SCRIPT_SRC = "https://cdn2.woxo.tech/a.js#616348fb53c1e8001686c619";
const PREVIEW_ICON = "heart";

Altere WOXO_SCRIPT_SRC para o src que o woxo fornece. Deve ser o mesmo para todos os embeds que você criar.

Altere PREVIEW_ICON para o nome do ícone que você deseja usar na pré-visualização do editor. Executar este código no editor é um pouco custoso, então o editor possui uma pré-visualização estática que se parece com isto.

O ícone que você escolher aparecerá no centro.

Aqui está uma cópia comentada do código, caso queira acompanhar o que está acontecendo

código comentado
<script type="text/discourse-plugin" version="0.8">
// opções
const WOXO_SCRIPT_SRC = "https://cdn2.woxo.tech/a.js#616348fb53c1e8001686c619";
const PREVIEW_ICON = "heart";

// usamos a biblioteca Discourse Load script para garantir que os scripts sejam carregados
// corretamente. Não se preocupe, isso é inteligente o suficiente para não duplicar o script
// se já estiver carregado
const loadScript = require("discourse/lib/load-script").default;

// carregamos a função Discourse Icon HTML para obter o svg do ícone
// que queremos usar na pré-visualização estática do editor
const { iconHTML } = require("discourse-common/lib/icon-library");

// configura o ícone de pré-visualização do editor
const composerPreviewIcon = iconHTML(PREVIEW_ICON, {
  class: "woxo-preview-icon"
});

// cria uma função auxiliar para o markup de pré-visualização do editor
const previewMarkup = () => {
  const markup = `<div class="woxo-preview">${composerPreviewIcon}</div>`;

  return markup;
};

// cria um decorador de postagem
api.decorateCookedElement(
  post => {
    // esta postagem tem widgets woxo?
    const woxoWidgets = post.querySelectorAll("div[data-mc-src]");

    // Sim, então vamos fazer algum trabalho.
    if (woxoWidgets.length) {
      // para cada widget woxo
      woxoWidgets.forEach(woxoWidget => {
        // se for um widget do editor, substitua-o por uma pré-visualização estática e
        // saia antecipadamente
        if (post.classList.contains("d-editor-preview")) {
          woxoWidget.innerHTML = previewMarkup();
          return;
        }

        // se não estiver no editor, carregue o script woxo.
        loadScript(WOXO_SCRIPT_SRC).then(() => {
          // O script woxo é muito estranho. Não funcionará a menos que a tag script
          // tenha um atributo data-usrc vazio. Então, vamos adicioná-lo
          const script = document.head.querySelector(
            `script[src*="cdn2.woxo.tech"]`
          );
          script.dataset.usrc = "";

          // tudo está pronto, vamos chamar o método init no script woxo
          window.MC.Loader.init();
        });
      });
    }
  },
  // adicione um id ao decorador para evitar vazamentos de memória
  { id: "render-woxo-widgets" }
);

</script>

Primeiramente: O-M-G OBRIGADO MUITÍSSIMO :exploding_head:
Fico atônito com o nível da resposta. Minha única consolação ao ver tanta gente trabalhando nisso é que é certo que isso será de grande ajuda para muitos. Isso abre muitas novas possibilidades, tudo em benefício do desenvolvimento de um fórum como um hub para uma comunidade.

Segundo: Peço desculpas por responder tão tarde, já que você respondeu tão rápido. Primeiro, não consegui acessar meu computador antes, e segundo, só queria responder depois de ter realmente analisado o código que você tão gentilmente comentou (linha por linha :flushed:, meu Deus, muito obrigado). Claro, tudo funcionou perfeitamente; o feed inteiro carrega tão rápido… Estou em dívida :pray:

Obrigado novamente, Johan. Realmente espero poder contribuir, da minha parte, com minha própria experiência :pray:

PD: Vou manter totalmente o coração para a pré-visualização estática.