FEATURE: Reassign workflow (#231)

FEATURE: Reassign workflow (#231)

Build a new workflow that adds a dropdown in place of the old assign button with the ability to

Re-assign a new user / group to an assigned topic Re-assign yourself to an assigned topic Unassign a user / group from an assigned topic

diff --git a/app/controllers/discourse_assign/assign_controller.rb b/app/controllers/discourse_assign/assign_controller.rb
index 8dbcd7c..c9accfd 100644
--- a/app/controllers/discourse_assign/assign_controller.rb
+++ b/app/controllers/discourse_assign/assign_controller.rb
@@ -44,27 +44,11 @@ module DiscourseAssign
     end
 
     def assign
-      target_id = params.require(:target_id)
-      target_type = params.require(:target_type)
-      username = params.permit(:username)['username']
-      group_name = params.permit(:group_name)['group_name']
-
-      assign_to = username.present? ? User.find_by(username_lower: username.downcase) : Group.where("LOWER(name) = ?", group_name.downcase).first
-
-      raise Discourse::NotFound unless assign_to
-      raise Discourse::NotFound if !Assignment.valid_type?(target_type)
-      target = target_type.constantize.where(id: target_id).first
-      raise Discourse::NotFound unless target
-
-      # perhaps?
-      #Scheduler::Defer.later "assign topic" do
-      assign = Assigner.new(target, current_user).assign(assign_to)
+      reassign_or_assign_target(action: "assign")
+    end
 
-      if assign[:success]
-        render json: success_json
-      else
-        render json: translate_failure(assign[:reason], assign_to), status: 400
-      end
+    def reassign
+      reassign_or_assign_target(action: "reassign")
     end
 
     def assigned
@@ -185,5 +169,29 @@ module DiscourseAssign
     def ensure_assign_allowed
       raise Discourse::InvalidAccess.new unless current_user.can_assign?
     end
+
+    def reassign_or_assign_target(action:)
+      target_id = params.require(:target_id)
+      target_type = params.require(:target_type)
+      username = params.permit(:username)['username']
+      group_name = params.permit(:group_name)['group_name']
+
+      assign_to = username.present? ? User.find_by(username_lower: username.downcase) : Group.where("LOWER(name) = ?", group_name.downcase).first
+
+      raise Discourse::NotFound unless assign_to
+      raise Discourse::NotFound if !Assignment.valid_type?(target_type)
+      target = target_type.constantize.where(id: target_id).first
+      raise Discourse::NotFound unless target
+
+      # perhaps?
+      #Scheduler::Defer.later "assign topic" do
+      assign = Assigner.new(target, current_user).send(action, assign_to)
+
+      if assign[:success]
+        render json: success_json
+      else
+        render json: translate_failure(assign[:reason], assign_to), status: 400
+      end
+    end
   end
 end
diff --git a/assets/javascripts/discourse-assign/controllers/assign-user.js.es6 b/assets/javascripts/discourse-assign/controllers/assign-user.js.es6
index d89520b..841fef4 100644
--- a/assets/javascripts/discourse-assign/controllers/assign-user.js.es6
+++ b/assets/javascripts/discourse-assign/controllers/assign-user.js.es6
@@ -5,7 +5,6 @@ import { popupAjaxError } from "discourse/lib/ajax-error";
 import { not, or } from "@ember/object/computed";
 import { isEmpty } from "@ember/utils";
 import { action } from "@ember/object";
-import discourseComputed from "discourse-common/utils/decorators";
 
 export default Controller.extend({
   topicBulkActions: controller(),
@@ -39,14 +38,43 @@ export default Controller.extend({
     });
   },
 
-  @discourseComputed("model.targetType")
-  i18nSuffix(targetType) {
-    switch (targetType) {
-      case "Post":
-        return "_post_modal";
-      case "Topic":
-        return "_modal";
+  reassignOrAssignTarget(assign_action) {
+    if (this.isBulkAction) {
+      this.bulkAction(this.model.username);
+      return;
+    }
+    let path = "/assign/" + assign_action;
+
+    if (isEmpty(this.get("model.username"))) {
+      this.model.target.set("assigned_to_user", null);
+    }
+
+    if (isEmpty(this.get("model.group_name"))) {
+      this.model.target.set("assigned_to_group", null);
     }
+
+    if (
+      isEmpty(this.get("model.username")) &&
+      isEmpty(this.get("model.group_name"))
+    ) {
+      path = "/assign/unassign";
+    }
+
+    this.send("closeModal");
+
+    return ajax(path, {
+      type: "PUT",
+      data: {
+        username: this.get("model.username"),
+        group_name: this.get("model.group_name"),
+        target_id: this.get("model.target.id"),
+        target_type: this.get("model.targetType"),
+      },
+    })
+      .then(() => {
+        this.get("model.onSuccess")?.();
+      })
+      .catch(popupAjaxError);
   },
 
   @action
@@ -77,48 +105,47 @@ export default Controller.extend({
 
   @action
   assign() {
+    this.reassignOrAssignTarget("assign");
+  },
+
+  @action
+  reassign() {
+    this.reassignOrAssignTarget("reassign");
+  },
+
+  @action
+  reassignUser(name) {
     if (this.isBulkAction) {
-      this.bulkAction(this.model.username);
+      this.bulkAction(name);
       return;
     }
-    let path = "/assign/assign";
 
-    if (isEmpty(this.get("model.username"))) {
-      this.model.target.set("assigned_to_user", null);
-    }
-
-    if (isEmpty(this.get("model.group_name"))) {
-      this.model.target.set("assigned_to_group", null);
+    if (this.allowedGroupsForAssignment.includes(name)) {
+      this.setProperties({
+        "model.username": null,
+        "model.group_name": name,
+        "model.allowedGroups": this.taskActions.allowedGroups,
+      });
+    } else {
+      this.setProperties({
+        "model.username": name,
+        "model.group_name": null,
+        "model.allowedGroups": this.taskActions.allowedGroups,
+      });
     }
 
-    if (
-      isEmpty(this.get("model.username")) &&
-      isEmpty(this.get("model.group_name"))
-    ) {
-      path = "/assign/unassign";
+    if (name) {
+      return this.reassign();
     }
-
-    this.send("closeModal");
-
-    return ajax(path, {
-      type: "PUT",
-      data: {
-        username: this.get("model.username"),
-        group_name: this.get("model.group_name"),
-        target_id: this.get("model.target.id"),
-        target_type: this.get("model.targetType"),
-      },
-    })
-      .then(() => {
-        if (this.get("model.onSuccess")) {
-          this.get("model.onSuccess")();
-        }
-      })
-      .catch(popupAjaxError);
   },
 
   @action
   assignUsername(selected) {
     this.assignUser(selected.firstObject);
   },
+
+  @action
+  reassignUsername(selected) {
+    this.reassignUser(selected.firstObject);
+  },
 });
diff --git a/assets/javascripts/discourse-assign/initializers/extend-for-assigns.js.es6 b/assets/javascripts/discourse-assign/initializers/extend-for-assigns.js.es6
index c3f34ab..5268544 100644
--- a/assets/javascripts/discourse-assign/initializers/extend-for-assigns.js.es6
+++ b/assets/javascripts/discourse-assign/initializers/extend-for-assigns.js.es6
@@ -14,9 +14,17 @@ import TopicButtonAction, {
 import { inject } from "@ember/controller";
 import I18n from "I18n";
 import { isEmpty } from "@ember/utils";
+import { registerTopicFooterDropdown } from "discourse/lib/register-topic-footer-dropdown";
 
 const PLUGIN_ID = "discourse-assign";
 
+const DEPENDENT_KEYS = [
+  "topic.assigned_to_user",
+  "topic.assigned_to_group",
+  "currentUser.can_assign",
+  "topic.assigned_to_user.username",
+];
+
 function titleForState(name) {
   if (name) {
     return I18n.t("discourse_assign.unassign.help", {
@@ -27,14 +35,124 @@ function titleForState(name) {
   }
 }
 
+function defaultTitle(topic) {
+  return titleForState(
+    topic.get("topic.assigned_to_user.username") ||
+      topic.get("topic.assigned_to_group.name")
+  );
+}
+
+function includeIsAssignedOnTopic(api) {
+  api.modifyClass("model:topic", {
+    pluginId: PLUGIN_ID,
+    isAssigned() {
+      return this.assigned_to_user || this.assigned_to_group;
+    },
+  });
+}
+
 function registerTopicFooterButtons(api) {
+  registerTopicFooterDropdown({
+    id: "reassign",
+
+    action(id) {
+      if (!this.get("currentUser.can_assign")) {
+        return;
+      }
+

[... diff too long, it was truncated ...]

GitHub sha: 98103586b2621e315aea086bdbaf481b89abe8d8

This commit appears in #231 which was approved by jjaffeux. It was merged by janzenisaac.