Измените страницы групп по умолчанию на /messages

Здравствуйте,

При доступе к группам со страницы /g можно ли настроить так, чтобы ссылка на группу вела на /g/group-slug/messages, а не на /g/group-slug/members?

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

Я знаю, что можно сделать это через прямую ссылку, но мне нужно, чтобы это работало со страницы /g?type=my, которая удобно динамична и ориентирована на пользователя.

Это не обычные ссылки. Они обрабатываются компонентом Ember link-to, и здесь задействована небольшая логика маршрутизации.

Самый простой способ реализовать это — перехватить клик по этому элементу и выполнить необходимые действия. Этот код размещается в секции common > header темы:

<script
  type="text/x-handlebars"
  data-template-name="/connectors/group-index-box-after/link-to-group-inbox"
></script>

<script type="text/discourse-plugin" version="0.8">
  const DiscourseURL = require("discourse/lib/url").default;
  api.registerConnectorClass('group-index-box-after', 'link-to-group-inbox', {
    setupComponent(args) {
      const group = args.model;

       if (group.is_group_user && group.has_messages) {
         const groupLink = document.querySelector(`.group-box.${group.name}`);
         const newHref = `/g/${group.name}/messages`;

         // Обновляет заголовок браузера и обрабатывает открытие в новой вкладке
         groupLink.href = newHref;

         // Перенаправляет ссылку
         groupLink.addEventListener("click", (event) => {
            DiscourseURL.routeTo(newHref);
            event.preventDefault();
         }, {once: true})
       }
     }
  });
</script>

Если пользователь является участником группы и у группы есть сообщения, он будет перенаправлен во входящие. В противном случае будет использована страница по умолчанию /members.

Вау — это просто потрясающе!!! Всё работает как по маслу. Спасибо.


позже…

У меня возникла ещё одна проблема, с которой мне, извините, нужна небольшая помощь!

Несколько моих групп по-прежнему отображают /g/group-slug/messages, хотя у них нет сообщений.

При более внимательном рассмотрении выяснилось, что у них раньше были сообщения, которые были преобразованы в темы или удалены. Data Explorer показывает, что для этих групп в таблице Groups значение has_messages равно True.

Я пытался изменить это для данных групп через Rails, но у меня не получается:

[2] pry(main)> Group.find_by_name(“group-slug”).has_messages
=> true
[3] pry(main)> Group.find_by_name(“group-slug”).has_messages = false
=> false
[4] pry(main)> Group.find_by_name(“group-slug”).has_messages
=> true

Есть ли какие-либо предложения, кроме удаления групп и их создания заново (к чему я совсем не стремлюсь)?

Вам нужно выполнить только эту строку в консоли Rails. Это должно обновить все группы.

Group.refresh_has_messages!

Это сработало для всех, кроме двух — спасибо. Эти последние назойливые два, думаю, мне придётся просто удалить и создать заново!

При этом я обнаружил причину — это небольшой #баг.

При преобразовании личных сообщений группы в темы информация об участниках приглашения сохраняется. Хотя это удобно, если тему снова преобразовать обратно, это означает, что они ошибочно учитываются при выполнении Group.refresh_has_messages!.

Можно ли также перехватить ссылки, которые появляются при клике на упомянутую группу (@mentioned group) и последующем переходе в эту группу? Мне кажется, это единственные два способа попасть в группы, не прибегая к прямым URL-адресам.

Теперь я хочу (жаден, не так ли?) добиться того, чтобы все ссылки на группы вели себя так же, как и те, что ведут на страницу /g, благодаря вашему коду.

@Johani, это реализуемо? Я немного покопался, и это явно выше моего уровня компетенции.

Конечно, вы можете попробовать что-то вроде этого во вкладке «Header» вашей темы или компонента. Я оставил несколько комментариев в сниппете, чтобы вы могли следить за процессом. Вы можете удалить их, когда будете готовы использовать код.

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

<script type="text/discourse-plugin" version="0.8">
  // Часть 1: изменение пути для ссылок в карточках групп. Это гарантирует, что
  // заголовок ссылки отражает её назначение, а также обрабатывает открытие ссылки в новой вкладке.
  const discourseComputed = require("discourse-common/utils/decorators").default;
  
  api.modifyClass("component:group-card-contents", {
    // Свойство groupPath используется для генерации href для ссылок на группы
    // в карточке группы. Давайте изменим его
    @discourseComputed("group")
    groupPath(group) {
      // получаем значение по умолчанию из ядра
      let groupURL = this._super(...arguments);

      // если пользователь соответствует нашим требованиям, изменяем URL, чтобы он вел в входящие
      if (group.is_group_user && group.has_messages) {
        groupURL = `${groupURL}/messages/`;
      }

      // возвращаем либо значение по умолчанию, либо изменённое
      return groupURL;
    }
  });

  // Часть 2: изменение маршрутизации внутри приложения. Это гарантирует, что обычная навигация
  // внутри приложения обрабатывается корректно и направляет пользователя в входящие сообщения группы,
  // если он соответствует требованиям
  
  const DiscourseURL = require("discourse/lib/url").default;
  const { groupPath } = require("discourse/lib/url");
  const { action } = require("@ember/object");

  api.modifyClass("controller:user-card", {

    // showGroup здесь — это действие, принадлежащее контроллеру user-card.
    // Именно оно вызывается при клике на имя группы / аватарку внутри карточек групп
    @action
    showGroup(group) {
      // вызываем super, чтобы убедиться, что код из ядра загружен первым
      this._super(...arguments);

      // groupPath — это встроенная функция-помощник URL в Discourse
      let groupURL = groupPath(group.name);

      // если пользователь соответствует требованиям, изменяем URL
      if (group.is_group_user && group.has_messages) {
        groupURL = `${groupURL}/messages/`;
      }

      // вызываем routeTo() с либо значением по умолчанию, либо изменённым groupURL
      DiscourseURL.routeTo(groupURL);
    }
  });
</script>

Вы молодец! Это отлично работает для любого случая с использованием карточек.

Теперь остался один тип ссылки, который не имеет направления: ссылка на группу в частном сообщении группы. Это на самом деле очень удобно для перехода к группе (и, следовательно, к входящим сообщениям) из сообщения группы.

Можем ли мы тоже поймать это в сеть, как вы думаете?

(Кстати, я упакую это в TC и создам тему для этого, как только мы закончим, чтобы это было очень доступно для других)

Эти ссылки используют несколько иной сериализатор, так как там не нужны все данные о группах.

Тем не менее, вы можете сохранить отфильтрованный список групп текущего пользователя при первоначальной загрузке страницы и использовать его как справочник для проверки членства. Эта информация уже доступна в начальном пакете данных, поэтому дополнительных запросов или иных накладных расходов не возникает. Что-то вроде этого:

<script type="text/discourse-plugin" version="0.8">
  const user = api.getCurrentUser();

  if (!user) {
    return;
  }

  const userGroups = user.groups.map(group => group.name);

  api.reopenWidget("pm-map-user-group", {
    transform(attrs) {

      const group = attrs.group;
      const isGroupUser = Object.values(userGroups).includes(group.name);

      // {href: "/g/foo"};
      let groupURL = this._super(...arguments);

      if (isGroupUser && group.has_messages) {
        groupURL.href = `${groupURL.href}/messages/`;
      }

      return groupURL;
    }
  });
</script>

Разместите этот фрагмент в отдельных тегах скрипта и добавьте его в заголовок вместе с остальными фрагментами. Разделение необходимо, потому что код быстро завершает выполнение, если пользователь не авторизован.

:partying_face: Мы добрались! Всё работает отлично, даже на мобильных устройствах.

Спасибо, Джо, за твою невероятную настойчивость. Мне пора выполнить свою часть сделки и взяться за работу над тем самым #theme-component, да?

позже…
Вот оно:

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

Сейчас я пытаюсь модифицировать Pavilion, чтобы она тоже поддерживала такое поведение, используя простую логику из ваших трёх замечательных сниппетов. Однако у меня ничего не получается из-за моего неумения работать с JavaScript. Вот что я пытаюсь изменить:

createWidget('layouts-group-link', {
  tagName: 'li.layouts-group-link',
  buildKey: (attrs) => `layouts-group-link-${attrs.id}`,

  getGroupTitle(group) {
    return h('span.group-title', group.name);
  },

  isOwner(group) {
    if (group.owner) {
      return h('span.group-owner-icon', iconNode('shield-alt'));
    }
  },

  html(attrs) {
    const contents = [this.getGroupTitle(attrs), this.isOwner(attrs)];
    return contents;
  },

  click() {
      DiscourseURL.routeTo(`/g/${this.attrs.name}/messages`);
  },
});

Как применить код ниже к этому? Я перепробовал всё, что мог придумать, в части click(){...}, но безуспешно. В итоге ссылка полностью ломается.

      if (group.is_group_user && group.has_messages) {
        groupURL = `${groupURL}/messages/`;
      }

Неважно!!! @keegan уже решил это:

Для некоторых групп, где существуют «призраки сообщений», это не работает. Эти группы можно идентифицировать с помощью следующего запроса (при условии, что в настройках взаимодействия группы ограничена возможность отправки сообщений):

SELECT id, name, has_messages, messageable_level
From groups
Where messageable_level < 10
AND messageable_level <> 3
AND has_messages = true
ORDER by id

Вы можете вручную изменить таблицу groups, чтобы принудительно сбросить это значение (конечно, в консоли Rails, предварительно сделав резервную копию!).

 Group.where(id: XXX).update_all(has_messages: false)

Если это не сработает или изменения отменятся, возможно, вам также потребуется очистить таблицы topic_groups и topic_allowed_groups:

 TopicGroup.where(group_id: XXX).destroy_all
 TopicAllowedGroup.where(group_id: XXX).destroy_all