Вставить компонент Glimmer после первого сообщения

У меня есть компонент Glimmer. Он работает. Но мне нужно, чтобы он размещался после первого поста в теме. Там нет выхода плагина.

Я понял, как вставить текст в innerHTML тега <article>, и это помещает текст туда, куда мне нужно, но, похоже, нет способа сделать то же самое с компонентом.

Не упускаю ли я что-то?

Я изучил, как плагин рекламы вставляет рекламу между постами, но не до конца разобрался в этом.

export default {
  name: "initialize-ad-plugin",
  initialize(container) {
    registerWidgetShim(
      "after-post-ad",
      "div.widget-connector",
      hbs`<PostBottomAd @model={{@data}} />`
    );

    withPluginApi("0.1", (api) => {
      api.decorateWidget("post:after", (helper) => {
        return helper.attach("after-post-ad", helper.widget.model);
      });
    });

....

Давайте добавим его! Где бы вы хотели его разместить?

(существуют устаревшие способы достижения того, что вы делаете, но они перестанут работать, когда мы модернизируем просмотр тем. Если мы добавим выход для плагинов, это будет гораздо более устойчивым к будущим изменениям)

Этот слот будет соответствовать функциональности decorateWidget("post:after"), которую использует плагин объявлений:

Это здорово! Жалуйся — и получишь!

На самом деле я “знаю”, что добавить точки расширения плагина “легко”, но когда я в общих чертах понимал, как это сделать, это было в файле hbs.

Размещать это будет после каждого поста? Значит, мне нужно придумать какой-то хитрый способ, чтобы оно появлялось только после первого?

article:after — это то, что я придумал в своих предыдущих попытках.

Пост передается как один из аргументов выхода, поэтому вы можете проверить post.post_number == 1

Ха-ха! Всегда рад помочь с такими вопросами. Гораздо лучше для всех, если темы и плагины будут использовать стандартные API, как в этом случае, а не обходные пути через виджеты (которые будут устаревшими в ближайшем будущем).

Да, это справедливо. Добавление их внутри виджетов — головная боль на стороне ядра. Но со стороны темы или плагина это должно быть очень чисто :crossed_fingers:


Я постараюсь разобраться с этими сбоями тестов и завтра объединю PR. (Похоже, лишний HTML-элемент мешает некоторым хрупким селекторам в тестах QUnit)

Ага! @outletArgs={{hash post=@data.post}}. У меня есть все шансы разобраться, как это сделать. :slight_smile:

Ещё раз спасибо!

Боюсь, я, возможно, был немного излишне оптимистичен здесь, @pfaffman, извините! Мой PR привел бы к появлению нового обертки <div> между каждым сообщением, даже если outlet не используется. Это не то, что мы хотим делать.

Возможно, есть способы избежать создания обертки… но ничего простого, что мы могли бы сделать немедленно.

Поэтому я думаю, что лучшее немедленное решение для вас — скопировать реализацию плагина adplugin, на которую вы сослались в исходном посте.

По сути:

  1. Создайте компонент (Glimmer или классический, не имеет значения), который рендерит то, что вам нужно.

  2. Используйте registerWidgetShim, чтобы сделать этот компонент доступным как виджет. Пример с adplugin создает виджет с именем “after-post-ad”, который рендерит компонент PostBottomAd. Он передает все атрибуты виджета (@data) в аргумент @model компонента.

  3. Используйте api.decorateWidget, чтобы отрендерить ваш новый виджет-обертку в позиции post:after. В вашем случае, поскольку вам нужно это только для первого сообщения, вы можете сделать что-то вроде:

    api.decorateWidget("post:after", (helper) => {
      if(helper.widget.model.post_number === 1){
        return helper.attach("my-widget-shim");
      }
    });
    

Когда мы в конечном итоге переведем страницу темы на Glimmer, вам нужно будет удалить обертку-виджет/декорацию и заменить её на плагин-аутлет. Это должно быть довольно просто, так как вся логика отображения в вашем компоненте будет переиспользована в плагин-аутлете.

Дайте нам знать, как у вас получится! Мы рады помочь с любыми последующими вопросами — знаю, что здесь много движущихся частей.

Я почти добрался до цели!

Единственная проблема в том, что компоненту нужен currentUser, и я не могу понять, как его передать. Вот что не работает.

import { hbs } from "ember-cli-htmlbars";
import { withPluginApi } from "discourse/lib/plugin-api";
import Site from "discourse/models/site";
import { registerWidgetShim } from "discourse/widgets/render-glimmer";
import Banner from "../components/banner";

export default {
  name: "initialize-banner-widget",
  initialize(container) {
    registerWidgetShim(
      "geo-banner-widget",
      "div.widget-connector",
      hbs`<Banner @currentUser={{currentUser}} />`
    );

    // withPluginApi("0.1", (api) => {
    //   console.log("doing plugin");
    //   api.decorateWidget("post:after", (helper) => {
    //     return helper.attach("geo-banner-widget", helper.widget.model);
    //   });
    // });

    withPluginApi("0.1", (api) => {
      const currentUser = container.lookup("service:current-user");
      api.decorateWidget("post:after", (helper) => {
        console.log(`decorate widget ${currentUser.username}`, );
        if(helper.widget.model.post_number === 1){
          return helper.attach("geo-banner-widget", { currentUser });
        }
      });
    });
  },
};

В вашем компоненте Banner вы можете внедрить сервис currentUser следующим образом:

class Banner extends Component {
  @service currentUser;
}

Тогда вам не потребуется передавать его из виджета.

Я знал, что передавать currentUser — глупо, но не мог понять этого!

Ещё один момент, на случай, если кому-то ещё это пригодится:

import Service, { inject as service } from "@ember/service";
import { hbs } from "ember-cli-htmlbars";
import { withPluginApi } from "discourse/lib/plugin-api";
import { registerWidgetShim } from "discourse/widgets/render-glimmer";

export default {
  name: "initialize-banner-widget",
  initialize(container) {
    registerWidgetShim(
      "geo-banner-widget",
      "div.widget-connector",
      hbs`<Banner/>`
    );

    withPluginApi("0.1", (api) => {
      api.decorateWidget("post:after", (helper) => {
        const currentUser = container.lookup("service:current-user");
        if(helper.widget.model.post_number === 1){
          return helper.attach("geo-banner-widget");
        }
      });
    });
  },
};

Да!

В данном случае, думаю, вам не нужно импортировать класс Service. Это потребуется только если вы создаёте собственный сервис. Вам нужен только декоратор внедрения service.

В последней версии Discourse/Ember это можно упростить ещё больше, чтобы не использовать псевдоним inject as. Ember теперь предоставляет декоратор внедрения напрямую как service.

import { service } from "@ember/service";

(но старый вариант { inject as service } всё ещё работает, и мне неизвестно о каких-либо планах по его устареванию в Ember)

РЕДАКТИРОВАНИЕ: Но, возможно, я могу использовать decorateWidget вместо выходного порта плагина…

Линтер тоже так считает :slight_smile:

Мне этот вариант нравится больше. Плагин «Домашняя реклама» всё ещё использует старый способ. :slight_smile: