diff --git a/assets/javascripts/discourse/components/assigned-to-post.gjs b/assets/javascripts/discourse/components/assigned-to-post.gjs
new file mode 100644
index 0000000..b0ad44b
--- /dev/null
+++ b/assets/javascripts/discourse/components/assigned-to-post.gjs
@@ -0,0 +1,64 @@
+import Component from "@glimmer/component";
+import { action } from "@ember/object";
+import { inject as service } from "@ember/service";
+import DButton from "discourse/components/d-button";
+import icon from "discourse-common/helpers/d-icon";
+import i18n from "discourse-common/helpers/i18n";
+import DMenu from "float-kit/components/d-menu";
+
+export default class AssignedToPost extends Component {
+ @service taskActions;
+
+ @action
+ unassign() {
+ this.taskActions.unassignPost(this.args.post);
+ }
+
+ @action
+ editAssignment() {
+ this.taskActions.showAssignPostModal(this.args.post);
+ }
+
+
+ {{#if @assignedToUser}}
+ {{icon "user-plus"}}
+ {{else}}
+ {{icon "group-plus"}}
+ {{/if}}
+
+
+ {{i18n "discourse_assign.assigned_to"}}
+
+
+
+ {{#if @assignedToUser}}
+ {{@assignedToUser.username}}
+ {{else}}
+ {{@assignedToGroup.name}}
+ {{/if}}
+
+
+
+
+
+
+}
diff --git a/assets/javascripts/discourse/initializers/extend-for-assigns.js b/assets/javascripts/discourse/initializers/extend-for-assigns.js
index ca14210..c5ca7d9 100644
--- a/assets/javascripts/discourse/initializers/extend-for-assigns.js
+++ b/assets/javascripts/discourse/initializers/extend-for-assigns.js
@@ -1,6 +1,7 @@
import { getOwner } from "@ember/application";
import { htmlSafe } from "@ember/template";
import { isEmpty } from "@ember/utils";
+import { hbs } from "ember-cli-htmlbars";
import { h } from "virtual-dom";
import SearchAdvancedOptions from "discourse/components/search-advanced-options";
import { renderAvatar } from "discourse/helpers/user-avatar";
@@ -8,6 +9,7 @@ import { withPluginApi } from "discourse/lib/plugin-api";
import { registerTopicFooterDropdown } from "discourse/lib/register-topic-footer-dropdown";
import { escapeExpression } from "discourse/lib/utilities";
import RawHtml from "discourse/widgets/raw-html";
+import RenderGlimmer from "discourse/widgets/render-glimmer";
import getURL from "discourse-common/lib/get-url";
import { iconHTML, iconNode } from "discourse-common/lib/icon-library";
import discourseComputed from "discourse-common/utils/decorators";
@@ -646,19 +648,19 @@ function initialize(api) {
}
});
- api.createWidget("assigned-to", {
+ api.createWidget("assigned-to-post", {
html(attrs) {
- let { assignedToUser, assignedToGroup, href } = attrs;
-
- return h("p.assigned-to", [
- assignedToUser ? iconNode("user-plus") : iconNode("group-plus"),
- h("span.assign-text", I18n.t("discourse_assign.assigned_to")),
- h(
- "a",
- { attributes: { class: "assigned-to-username", href } },
- assignedToUser ? assignedToUser.username : assignedToGroup.name
- ),
- ]);
+ return new RenderGlimmer(
+ this,
+ "p.assigned-to",
+ hbs``,
+ {
+ assignedToUser: attrs.post.assigned_to_user,
+ assignedToGroup: attrs.post.assigned_to_group,
+ href: attrs.href,
+ post: attrs.post,
+ }
+ );
},
});
@@ -792,6 +794,7 @@ function initialize(api) {
if (data.type === "unassigned") {
delete topic.indirectly_assigned_to[data.post_number];
}
+
this.appEvents.trigger("post-stream:refresh", {
id: topic.postStream.posts[0].id,
});
@@ -841,11 +844,13 @@ function initialize(api) {
? assignedToUserPath(assignedToUser)
: assignedToGroupPath(assignedToGroup);
}
+
if (href) {
- return dec.widget.attach("assigned-to", {
+ return dec.widget.attach("assigned-to-post", {
assignedToUser,
assignedToGroup,
href,
+ post: postModel,
});
}
}
diff --git a/assets/javascripts/discourse/services/task-actions.js b/assets/javascripts/discourse/services/task-actions.js
index 573de16..465d1ac 100644
--- a/assets/javascripts/discourse/services/task-actions.js
+++ b/assets/javascripts/discourse/services/task-actions.js
@@ -45,6 +45,11 @@ export default class TaskActions extends Service {
});
}
+ async unassignPost(post) {
+ await this.unassign(post.id, "Post");
+ delete post.topic.indirectly_assigned_to[post.id];
+ }
+
showAssignModal(
target,
{ isAssigned = false, targetType = "Topic", onSuccess }
@@ -62,6 +67,10 @@ export default class TaskActions extends Service {
});
}
+ showAssignPostModal(post) {
+ return this.showAssignModal(post, { targetType: "Post" });
+ }
+
reassignUserToTopic(user, target, targetType = "Topic") {
return ajax("/assign/assign", {
type: "PUT",
diff --git a/assets/stylesheets/assigns.scss b/assets/stylesheets/assigns.scss
index bc9db18..be0519e 100644
--- a/assets/stylesheets/assigns.scss
+++ b/assets/stylesheets/assigns.scss
@@ -26,6 +26,12 @@
.assignee:not(:last-child):after {
content: ", ";
}
+
+ .more-button {
+ padding-left: 0.3em;
+ padding-right: 0.3em;
+ vertical-align: middle;
+ }
}
.topic-body {
@@ -44,19 +50,6 @@
align-items: center;
}
-.assigned-to-user {
- display: flex;
- align-items: center;
-
- img.avatar {
- margin-right: 0.3em;
- }
-
- .unassign {
- margin-left: 0.5em;
- }
-}
-
.topic-assigned-to {
min-width: 15%;
width: 15%;
diff --git a/test/javascripts/acceptance/post-popup-menu-test.js b/test/javascripts/acceptance/post-popup-menu-test.js
new file mode 100644
index 0000000..10e7f60
--- /dev/null
+++ b/test/javascripts/acceptance/post-popup-menu-test.js
@@ -0,0 +1,124 @@
+import { click, fillIn, visit } from "@ember/test-helpers";
+import { test } from "qunit";
+import topicFixtures from "discourse/tests/fixtures/topic";
+import {
+ acceptance,
+ publishToMessageBus,
+ updateCurrentUser,
+} from "discourse/tests/helpers/qunit-helpers";
+import { cloneJSON } from "discourse-common/lib/object";
+
+const username = "eviltrout";
+const new_assignee_username = "new_assignee";
+
+function topicWithAssignedPostResponse() {
+ const topic = cloneJSON(topicFixtures["/t/28830/1.json"]);
+ const secondPost = topic.post_stream.posts[1];
+
+ topic["indirectly_assigned_to"] = {
+ [secondPost.id]: {
+ assigned_to: {
+ username,
+ },
+ post_number: 1,
+ },
+ };
+ secondPost["assigned_to_user"] = { username };
+
+ return topic;
+}
+
+const selectors = {
+ assignedTo: ".post-stream article#post_2 .assigned-to",
+ moreButton: ".post-stream .topic-post .more-button",
+ popupMenu: {
+ unassign: ".popup-menu .popup-menu-btn svg.d-icon-user-plus",
+ editAssignment: ".popup-menu .popup-menu-btn svg.d-icon-group-plus",
+ },
+ modal: {
+ assignee: ".modal-container .select-kit-header-wrapper",
+ assigneeInput: ".modal-container .filter-input",
+ assignButton: ".d-modal__footer .btn-primary",
+ },
+};
+
+const topic = topicWithAssignedPostResponse();
+const post = topic.post_stream.posts[1];
+
+acceptance("Discourse Assign | Post popup menu", function (needs) {
+ needs.user();
+ needs.settings({
+ assign_enabled: true,
+ });
+
+ needs.pretender((server, helper) => {
+ server.get("/t/44.json", () => helper.response(topic));
+
+ server.put("/assign/assign", () => {
+ return helper.response({ success: true });
+ });
+
+ server.put("/assign/unassign", () => {
+ return helper.response({ success: true });
+ });
+
+ server.get("/assign/suggestions", () =>
+ helper.response({
+ assign_allowed_for_groups: [],
+ suggestions: [{ username: new_assignee_username }],
+ })
+ );
+
+ server.get("/u/search/users", () =>
+ helper.response({ users: [{ username: new_assignee_username }] })
+ );
+ });
+
+ needs.hooks.beforeEach(() => {
+ updateCurrentUser({ can_assign: true });
+ });
+
+ test("Unassigns the post", async function (assert) {
+ await visit("/t/assignment-topic/44");
+ await click(selectors.moreButton);
+ await click(selectors.popupMenu.unassign);
+ await publishToMessageBus("/staff/topic-assignment", {
+ type: "unassigned",
+ topic_id: topic.id,
+ post_id: post.id,
+ assigned_type: "User",
+ });
+
+ assert.dom(".popup-menu").doesNotExist("The popup menu is closed");
+ assert.dom(selectors.assignedTo).doesNotExist("The post is unassigned");
+ });
+
+ test("Reassigns the post", async function (assert) {
+ await visit("/t/assignment-topic/44");
+ await click(selectors.moreButton);
+ await click(selectors.popupMenu.editAssignment);
+ await click(selectors.modal.assignee);
+ await fillIn(selectors.modal.assigneeInput, new_assignee_username);
+ await click(selectors.modal.assignButton);
+
+ await publishToMessageBus("/staff/topic-assignment", {
+ type: "assigned",
+ topic_id: topic.id,
+ post_id: post.id,
+ assigned_type: "User",
+ assigned_to: {
+ username: new_assignee_username,
+ },
+ });
+
+ // todo: we can skip this one for now, I can fix it in a core PR
+ // assert.dom(".popup-menu").doesNotExist("The popup menu is closed");
+
+ assert
+ .dom(`${selectors.assignedTo} .assigned-to-username`)
+ .hasText(
+ new_assignee_username,
+ "The post is assigned to the new assignee"
+ );
+ });
+});