DEV: Bulk Actions Dropdown Plugin API (#26224)

Adds a plugin api for the bulk actions dropdown.

Co-authored-by: Martin Brennan <martin@discourse.org>
This commit is contained in:
Blake Erickson 2024-03-25 14:44:38 -06:00 committed by GitHub
parent df10c0b3d0
commit ce037f9962
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 81 additions and 5 deletions

View File

@ -4,7 +4,6 @@ import { Input } from "@ember/component";
import { action, computed } from "@ember/object";
import { service } from "@ember/service";
import { Promise } from "rsvp";
import ChangeTags from "discourse/components/bulk-actions/change-tags";
import ConditionalLoadingSection from "discourse/components/conditional-loading-section";
import DButton from "discourse/components/d-button";
import DModal from "discourse/components/d-modal";
@ -16,6 +15,12 @@ import i18n from "discourse-common/helpers/i18n";
import CategoryChooser from "select-kit/components/category-chooser";
import TagChooser from "select-kit/components/tag-chooser";
const _customActions = {};
export function addBulkDropdownAction(name, customAction) {
_customActions[name] = customAction;
}
export default class BulkTopicActions extends Component {
@service router;
@service toasts;
@ -32,7 +37,11 @@ export default class BulkTopicActions extends Component {
super(...arguments);
if (this.args.model.initialAction === "set-component") {
this.setComponent(ChangeTags);
if (this.args.model.initialActionLabel in _customActions) {
_customActions[this.args.model.initialActionLabel]({
setComponent: this.setComponent.bind(this),
});
}
}
}
@ -110,6 +119,11 @@ export default class BulkTopicActions extends Component {
this.activeComponent = component;
}
@action
registerCustomAction(customAction) {
this.customAction = customAction;
}
@action
performAction() {
this.loading = true;
@ -165,6 +179,14 @@ export default class BulkTopicActions extends Component {
(t) => t.set("category_id", this.categoryId)
);
break;
default:
// Plugins can register their own custom actions via onRegisterAction
// when the activeComponent is rendered.
if (this.customAction) {
this.customAction(this.performAndRefresh.bind(this));
} else {
_customActions[this.args.model.initialActionLabel](this);
}
}
}
@ -292,6 +314,13 @@ export default class BulkTopicActions extends Component {
@categoryId={{@categoryId}}
/></p>
{{/if}}
{{#if this.activeComponent}}
{{component
this.activeComponent
onRegisterAction=this.registerCustomAction
}}
{{/if}}
</ConditionalLoadingSection>
</:body>

View File

@ -141,6 +141,7 @@ import {
replaceIcon,
} from "discourse-common/lib/icon-library";
import { addImageWrapperButton } from "discourse-markdown-it/features/image-controls";
import { addBulkDropdownButton } from "select-kit/components/bulk-select-topics-dropdown";
import { CUSTOM_USER_SEARCH_OPTIONS } from "select-kit/components/user-chooser";
import { modifySelectKit } from "select-kit/mixins/plugin-api";
@ -2877,9 +2878,11 @@ class PluginApi {
* @param {string} opts.class
* @param {buttonVisibilityCallback} opts.visible
* @param {buttonAction} opts.action
* @param {string} opts.actionType - type of the action, either performanAndRefresh or setComponent
*/
addBulkActionButton(opts) {
_addBulkButton(opts);
addBulkDropdownButton(opts);
}
/**

View File

@ -1,9 +1,32 @@
import { action } from "@ember/object";
import { service } from "@ember/service";
import BulkTopicActions from "discourse/components/modal/bulk-topic-actions";
import BulkTopicActions, {
addBulkDropdownAction,
} from "discourse/components/modal/bulk-topic-actions";
import i18n from "discourse-common/helpers/i18n";
import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box";
const _customButtons = [];
const _customOnSelection = {};
export function addBulkDropdownButton(opts) {
_customButtons.push({
id: opts.label,
icon: opts.icon,
name: i18n(opts.label),
visible: opts.visible,
});
addBulkDropdownAction(opts.label, opts.action);
const actionOpts = {
label: opts.label,
setComponent: true,
};
if (opts.actionType === "performAndRefresh") {
actionOpts.setComponent = false;
}
_customOnSelection[opts.label] = actionOpts;
}
export default DropdownSelectBoxComponent.extend({
classNames: ["bulk-select-topics-dropdown"],
headerIcon: null,
@ -104,7 +127,7 @@ export default DropdownSelectBoxComponent.extend({
},
]);
return [...options].filter(({ visible }) => {
return [...options, ..._customButtons].filter(({ visible }) => {
if (visible) {
return visible({
topics: this.bulkSelectHelper.selected,
@ -119,16 +142,30 @@ export default DropdownSelectBoxComponent.extend({
showBulkTopicActionsModal(actionName, title, opts = {}) {
let allowSilent = false;
let initialAction = null;
let initialActionLabel = null;
if (opts.allowSilent === true) {
allowSilent = true;
}
if (opts.custom === true) {
title = i18n(_customOnSelection[actionName].label);
initialActionLabel = actionName;
if (opts.setComponent === true) {
initialAction = "set-component";
}
} else {
title = i18n(`topics.bulk.${title}`);
}
this.modal.show(BulkTopicActions, {
model: {
action: actionName,
title: i18n(`topics.bulk.${title}`),
title,
bulkSelectHelper: this.bulkSelectHelper,
refreshClosure: () => this.router.refresh(),
allowSilent,
initialAction,
initialActionLabel,
},
});
},
@ -174,6 +211,13 @@ export default DropdownSelectBoxComponent.extend({
case "defer":
this.showBulkTopicActionsModal(id, "defer");
break;
default:
if (_customOnSelection[id]) {
this.showBulkTopicActionsModal(id, _customOnSelection[id].label, {
custom: true,
setComponent: _customOnSelection[id].setComponent,
});
}
}
},
});