Встраивание виджета в текст темы

Discourse — это одностраничное приложение. Проблема, с которой вы столкнулись, возникает из-за того, что используемый вами скрипт не учитывает эту особенность. Когда вы переходите на главную страницу — или на любую другую страницу — в Discourse, вы видите что-то вроде этого.

<html>
  <head>
    содержимое head, включая ваш скрипт
  </head>
  <body>
    <section id="main">
      содержимое страницы
    </section>
  </body>
</html>

При переходе на другую страницу перезагружается только содержимое внутри

<section id="main">

Таким образом, DOM изменяется, и ваш пользовательский скрипт не срабатывает повторно. Если вы попытаетесь перейти на страницу темы напрямую, вы увидите, что она загружается корректно.

.

Итак, теперь вопрос в том, как заставить это работать с Discourse.

В плагин-API есть метод, который можно использовать для «декорирования» постов.

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

Вы можете использовать его для запуска сторонних скриптов при отображении поста.

Вот код, который вам понадобится. Добавьте его во вкладку common > header вашей темы.

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

// создаем декоратор постов
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>

Затем вам нужно добавить несколько доменов для CSP. Добавьте их в настройку сайта

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

Наконец, вам нужно добавить немного CSS для статического предпросмотра в редакторе.

Это помещается во вкладку common > CSS вашей темы.

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

Затем вы можете просто добавить

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

в любой пост, и виджеты будут отображаться и работать в полном объеме.

Если вы посмотрите на JavaScript, то заметите, что в самом верху у него есть два варианта.

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

Измените WOXO_SCRIPT_SRC на src, который предоставляет Woxo. Он должен быть одинаковым для всех создаваемых вами встраиваний.

Измените PREVIEW_ICON на имя иконки, которую вы хотите использовать в предпросмотре редактора. Запуск этого кода в редакторе довольно ресурсоемок, поэтому редактор имеет статический предпросмотр, который выглядит так.

Выбранная вами иконка будет отображаться в центре.

Вот версия кода с комментариями, если вы хотите следить за происходящим

код с комментариями
<script type="text/discourse-plugin" version="0.8">
// опции
const WOXO_SCRIPT_SRC = "https://cdn2.woxo.tech/a.js#616348fb53c1e8001686c619";
const PREVIEW_ICON = "heart";

// мы используем библиотеку Discourse Load script для корректной загрузки скриптов.
// Не беспокойтесь, она достаточно умна, чтобы не дублировать скрипт,
// если он уже загружен
const loadScript = require("discourse/lib/load-script").default;

// мы загружаем функцию Discourse Icon HTML для получения SVG иконки,
// которую хотим использовать в статическом предпросмотре редактора
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;
};

// создаем декоратор постов
api.decorateCookedElement(
  post => {
    // есть ли в этом посте виджеты woxo?
    const woxoWidgets = post.querySelectorAll("div[data-mc-src]");

    // Да, давайте выполним некоторые действия.
    if (woxoWidgets.length) {
      // для каждого виджета woxo
      woxoWidgets.forEach(woxoWidget => {
        // если это виджет редактора, замените его статическим предпросмотром и
        // завершите работу
        if (post.classList.contains("d-editor-preview")) {
          woxoWidget.innerHTML = previewMarkup();
          return;
        }

        // если это не в редакторе, загрузите скрипт woxo.
        loadScript(WOXO_SCRIPT_SRC).then(() => {
          // Скрипт woxo очень странный. Он не будет работать, если тег
          // скрипта не имеет пустого атрибута data-usrc. Давайте добавим его
          const script = document.head.querySelector(
            `script[src*="cdn2.woxo.tech"]`
          );
          script.dataset.usrc = "";

          // всё готово, вызываем метод init в скрипте woxo
          window.MC.Loader.init();
        });
      });
    }
  },
  // добавляем id декоратору, чтобы избежать утечек памяти
  { id: "render-woxo-widgets" }
);

</script>