diff --git a/app/assets/javascripts/discourse/app/components/reviewable-item.js b/app/assets/javascripts/discourse/app/components/reviewable-item.js index 1e373f2ea87..20553d66b9b 100644 --- a/app/assets/javascripts/discourse/app/components/reviewable-item.js +++ b/app/assets/javascripts/discourse/app/components/reviewable-item.js @@ -15,6 +15,7 @@ import ExplainReviewableModal from "discourse/components/modal/explain-reviewabl let _components = {}; const pluginReviewableParams = {}; +const actionModalClassMap = {}; export function addPluginReviewableParam(reviewableType, param) { pluginReviewableParams[reviewableType] @@ -22,6 +23,10 @@ export function addPluginReviewableParam(reviewableType, param) { : (pluginReviewableParams[reviewableType] = [param]); } +export function registerReviewableActionModal(actionName, modalClass) { + actionModalClassMap[actionName] = modalClass; +} + export default Component.extend({ adminTools: optionalService(), dialog: service(), @@ -273,8 +278,11 @@ export default Component.extend({ } const message = performableAction.get("confirm_message"); - let requireRejectReason = performableAction.get("require_reject_reason"); - let customModal = performableAction.get("custom_modal"); + const requireRejectReason = performableAction.get( + "require_reject_reason" + ); + const actionModalClass = actionModalClassMap[performableAction.id]; + if (message) { this.dialog.confirm({ message, @@ -288,13 +296,13 @@ export default Component.extend({ performConfirmed: this._performConfirmed, action: performableAction, }); - } else if (customModal) { - showModal(customModal, { - title: `review.${customModal}.title`, - model: this.reviewable, - }).setProperties({ - performConfirmed: this._performConfirmed, - action: performableAction, + } else if (actionModalClass) { + this.modal.show(actionModalClass, { + model: { + reviewable: this.reviewable, + performConfirmed: this._performConfirmed, + action: performableAction, + }, }); } else { return this._performConfirmed(performableAction); diff --git a/app/assets/javascripts/discourse/app/lib/plugin-api.js b/app/assets/javascripts/discourse/app/lib/plugin-api.js index d7d607b7ca1..4503549ef8d 100644 --- a/app/assets/javascripts/discourse/app/lib/plugin-api.js +++ b/app/assets/javascripts/discourse/app/lib/plugin-api.js @@ -55,7 +55,10 @@ import { addGlobalNotice } from "discourse/components/global-notice"; import { addNavItem } from "discourse/models/nav-item"; import { addPluginDocumentTitleCounter } from "discourse/components/d-document"; import { addPluginOutletDecorator } from "discourse/components/plugin-connector"; -import { addPluginReviewableParam } from "discourse/components/reviewable-item"; +import { + addPluginReviewableParam, + registerReviewableActionModal, +} from "discourse/components/reviewable-item"; import { addComposerSaveErrorCallback, addPopupMenuOptionsCallback, @@ -130,7 +133,7 @@ import { _addBulkButton } from "discourse/components/modal/topic-bulk-actions"; // based on Semantic Versioning 2.0.0. Please update the changelog at // docs/CHANGELOG-JAVASCRIPT-PLUGIN-API.md whenever you change the version // using the format described at https://keepachangelog.com/en/1.0.0/. -export const PLUGIN_API_VERSION = "1.9.0"; +export const PLUGIN_API_VERSION = "1.10.0"; // This helper prevents us from applying the same `modifyClass` over and over in test mode. function canModify(klass, type, resolverName, changes) { @@ -1648,10 +1651,44 @@ class PluginApi { addSaveableUserOptionField(fieldName) { addSaveableUserOptionField(fieldName); } + + /** + * Adds additional params to be sent to the reviewable/:id/perform/:action + * endpoint for a given reviewable type. This is so plugins can provide more + * complex reviewable actions that may depend on a custom modal. + * + * This is copied from the reviewable model instance when performing an action + * on the ReviewableItem component. + * + * ``` + * api.addPluginReviewableParam("ReviewablePluginType", "some_param"); + * ``` + **/ addPluginReviewableParam(reviewableType, param) { addPluginReviewableParam(reviewableType, param); } + /** + * Registers a mapping between a JavaScript modal component class and a server-side reviewable + * action, which is registered via `actions.add` and `build_actions`. + * + * For more information about modal classes, which are special Ember components used with + * the DModal API, see: + * + * https://meta.discourse.org/t/using-the-dmodal-api-to-render-modal-windows-aka-popups-dialogs-in-discourse/268304. + * + * @param {String} reviewableAction - The action name, as registered in the server-side. + * @param {Class} modalClass - The actual JavaScript class of the modal. + * + * @example + * ``` + * api.registerReviewableActionModal("approve_category_expert", ExpertGroupChooserModal); + * ``` + **/ + registerReviewableActionModal(reviewableType, modalClass) { + registerReviewableActionModal(reviewableType, modalClass); + } + /** * Change the default category background and text colors in the * category creation modal. diff --git a/app/serializers/reviewable_action_serializer.rb b/app/serializers/reviewable_action_serializer.rb index 469164bc26d..d4e09005619 100644 --- a/app/serializers/reviewable_action_serializer.rb +++ b/app/serializers/reviewable_action_serializer.rb @@ -8,8 +8,7 @@ class ReviewableActionSerializer < ApplicationSerializer :confirm_message, :description, :client_action, - :require_reject_reason, - :custom_modal + :require_reject_reason def label I18n.t(object.label) @@ -38,8 +37,4 @@ class ReviewableActionSerializer < ApplicationSerializer def include_require_reject_reason? object.require_reject_reason.present? end - - def include_custom_modal? - object.custom_modal.present? - end end diff --git a/docs/CHANGELOG-JAVASCRIPT-PLUGIN-API.md b/docs/CHANGELOG-JAVASCRIPT-PLUGIN-API.md index 0f3cdbfe782..176ea043469 100644 --- a/docs/CHANGELOG-JAVASCRIPT-PLUGIN-API.md +++ b/docs/CHANGELOG-JAVASCRIPT-PLUGIN-API.md @@ -7,6 +7,13 @@ in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.10.0] - 2023-08-25 + +### Added + +- Adds `registerReviewableActionModal` which allows core and plugins to register a modal component class + which is used to show a modal for certain reviewable actions. + ## [1.9.0] - 2023-08-09 ### Added