在编辑器中为预设回复添加直接按钮

我经常使用“预设回复”插件。现在它们需要点击两次才能访问(齿轮图标 → 预设回复);是否有办法自定义我的编辑器工具栏,以便直接访问,同时仍能尊重用户权限?

两次点击其实不算什么麻烦事,但这正是一个绝佳的契机,可以提供一个通用的说明,文档化如何操作 Composer 按钮,并聚焦于你想要实现的目标。

向工具栏添加按钮是通过另一个 pluginAPI 方法完成的。

工具栏使用的是这个方法 plugin-api.js.es6#L375-L391,而弹出菜单使用的是这个方法 plugin-api.js.es6#L396-L411

预设回复插件使用的是弹出菜单方法,如下所示:

https://github.com/discourse/discourse-canned-replies/blob/master/assets/javascripts/initializers/add-canned-replies-ui-builder.js.es6#L18-L25

除非你 fork 该插件,否则无法移动按钮——我们非常不推荐这样做。

你可以做的是在工具栏中添加另一个执行相同操作的按钮,并隐藏旧的按钮。为了在工具栏中创建按钮,你需要参考其他按钮是如何添加的。

工具栏中有两种类型的按钮。第一种是处理格式化的按钮,例如 粗体斜体。由于它们与你的目标不太相似,我们先暂时忽略它们。

另一种是插入内容的按钮,例如日期和表情符号。让我们看看“日期 - 日历”按钮。

现在我们有了一个可以借鉴的示例。让我们尝试创建新按钮。

我们从这里开始:

api.onToolbarCreate(toolbar => {
  toolbar.addButton({
  
  });
});

然后逐个添加预设回复按钮的属性。作为参考,这些属性是:

id: "canned_replies_button",
icon: "far-clipboard",
action: "showCannedRepliesButton",
label: "canned_replies.composer_button_text"

id:这允许你为按钮添加 CSS 类——我们将使用 custom-canned-button

icon:按钮将使用的图标——我们将保持原样。

label:弹出按钮有文本,而 Composer 按钮没有,因此我们需要将其改为 title 并使用相同的值。

action:这是你定义按钮行为的地方。让我们把这些整合起来:

api.onToolbarCreate(toolbar => {
  toolbar.addButton({
    id: "custom-canned-replies",
    icon: "far-clipboard",
    action: "showCannedRepliesButton",
    title: "canned_replies.composer_button_text"
  });
});

如果你尝试这样做,你会在工具栏中看到按钮,但点击它不会有任何反应。这是因为 showCannedRepliesButton 动作未定义。这是由于不同的上下文——因为你是在主题中执行此操作。

如果你查看预设回复插件,你会发现该动作是在 Composer 控制器中定义的:

https://github.com/discourse/discourse-canned-replies/blob/master/assets/javascripts/initializers/add-canned-replies-ui-builder.js.es6#L5-L16

所以下一步是引用 Composer 控制器,以便在按钮点击时能够发送该动作。你可以这样做:

const composerController = api.container.lookup("controller:composer");

api.onToolbarCreate(toolbar => {
  toolbar.addButton({
    title: "canned_replies.composer_button_text",
    id: "custom-canned-replies",
    group: "extras",
    icon: "far-clipboard",
    sendAction: () => composerController.send("showCannedRepliesButton")
  });
});

请注意,我们使用了与 日历按钮 相同的 sendAction 模式。唯一的两个例外是:

我们使用 composerController.send 而不是 toolbar.context.send

并且我们没有传递事件,因为我认为这并不需要。

这应该能让你在工具栏中获得一个完全功能的按钮:

但我们还没有完成,因为这个按钮现在对所有成员都可见。使用权限仍然适用,如果无权用户尝试点击它,他们只会收到错误。然而,一个损坏的按钮并不好,所以让我们修复它。

使用预设回复的权限设置在这里:

https://github.com/discourse/discourse-canned-replies/blob/master/assets/javascripts/initializers/add-canned-replies-ui-builder.js.es6#L34-L38

因此,我们只需要将这些作为按钮添加的条件进行复制。类似于这样:

const currentUser = api.getCurrentUser();
const canUseCannedReplies = currentUser
  ? currentUser.can_use_canned_replies
  : false;

if (!canUseCannedReplies) return;

这将确保只有在拥有所需权限时才会显示该按钮。

那么,让我们把所有内容整合起来:

import { withPluginApi } from "discourse/lib/plugin-api";

export default {
  name: "move-canned-button",
  initialize() {
    withPluginApi("0.8.7", api => {
      const currentUser = api.getCurrentUser();
      const canUseCannedReplies = currentUser
        ? currentUser.can_use_canned_replies
        : false;

      if (!canUseCannedReplies) return;

      const composerController = api.container.lookup("controller:composer");

      api.onToolbarCreate(toolbar => {
        toolbar.addButton({
          title: "canned_replies.composer_button_text",
          id: "custom-canned-replies",
          group: "extras",
          icon: "far-clipboard",
          sendAction: () => composerController.send("showCannedRepliesButton")
        });
      });
    });
  }
};

这应该放入主题组件中的以下文件:

javascripts/discourse/initializers/move-canned-button.js.es6

如果你使用的是新功能 https://meta.discourse.org/t/splitting-up-theme-javascript-into-multiple-files/119369——非常推荐。

或者,如果你是在管理后台操作,只需将此脚本添加到主题的“头部”选项卡中即可。

旧语法
<script type="text/discourse-plugin"
        version="0.8">
const currentUser = api.getCurrentUser();
const canUseCannedReplies = currentUser
  ? currentUser.can_use_canned_replies
  : false;

if (!canUseCannedReplies) return;

const composerController = api.container.lookup("controller:composer");

api.onToolbarCreate(toolbar => {
  toolbar.addButton({
    title: "canned_replies.composer_button_text",
    id: "custom-canned-replies",
    group: "extras",
    icon: "far-clipboard",
    sendAction: () => composerController.send("showCannedRepliesButton")
  });
});
</script>

你最后需要做的就是隐藏旧按钮,你可以这样做:

.toolbar-popup-menu-options {
  [data-name="Canned replies"] {
    display: none;
  }
}

我完全没想到会得到如此详尽的回复,太棒了!今晚我需要一些时间来消化这些内容,谢谢 :folded_hands:

欢迎 @johani :tada: