diff --git a/assets/javascripts/discourse/components/assign-button.gjs b/assets/javascripts/discourse/components/assign-button.gjs
new file mode 100644
index 00000000..63c0915a
--- /dev/null
+++ b/assets/javascripts/discourse/components/assign-button.gjs
@@ -0,0 +1,66 @@
+import Component from "@glimmer/component";
+import { action } from "@ember/object";
+import { inject as service } from "@ember/service";
+import DButton from "discourse/components/d-button";
+
+export default class AssignButton extends Component {
+ static shouldRender(args) {
+ return !args.post.firstPost;
+ }
+
+ static hidden(args) {
+ return args.post.assigned_to_user?.id !== args.state.currentUser.id;
+ }
+
+ @service taskActions;
+
+ get icon() {
+ return this.isAssigned ? "user-times" : "user-plus";
+ }
+
+ get isAssigned() {
+ return this.args.post.assigned_to_user || this.args.post.assigned_to_group;
+ }
+
+ get title() {
+ return this.isAssigned
+ ? "discourse_assign.unassign_post.title"
+ : "discourse_assign.assign_post.title";
+ }
+
+ @action
+ acceptAnswer() {
+ if (this.isAssigned) {
+ unassignPost(this.args.post, this.taskActions);
+ } else {
+ assignPost(this.args.post, this.taskActions);
+ }
+ }
+
+
+
+
+}
+
+// TODO (glimmer-post-menu): Remove these exported functions and move the code into the button action after the widget code is removed
+export function assignPost(post, taskActions) {
+ taskActions.showAssignModal(post, {
+ isAssigned: false,
+ targetType: "Post",
+ });
+}
+
+export async function unassignPost(post, taskActions) {
+ await taskActions.unassign(post.id, "Post");
+ delete post.topic.indirectly_assigned_to[post.id];
+}
diff --git a/assets/javascripts/discourse/initializers/extend-for-assigns.js b/assets/javascripts/discourse/initializers/extend-for-assigns.js
index 2341fba5..35b62daf 100644
--- a/assets/javascripts/discourse/initializers/extend-for-assigns.js
+++ b/assets/javascripts/discourse/initializers/extend-for-assigns.js
@@ -10,10 +10,15 @@ import { registerTopicFooterDropdown } from "discourse/lib/register-topic-footer
import { escapeExpression } from "discourse/lib/utilities";
import RawHtml from "discourse/widgets/raw-html";
import RenderGlimmer from "discourse/widgets/render-glimmer";
+import { withSilencedDeprecations } from "discourse-common/lib/deprecated";
import getURL from "discourse-common/lib/get-url";
import { iconHTML, iconNode } from "discourse-common/lib/icon-library";
import discourseComputed from "discourse-common/utils/decorators";
import I18n from "I18n";
+import AssignButton, {
+ assignPost,
+ unassignPost,
+} from "../components/assign-button";
import BulkActionsAssignUser from "../components/bulk-actions/bulk-assign-user";
import EditTopicAssignments from "../components/modal/edit-topic-assignments";
import TopicLevelAssignMenu from "../components/topic-level-assign-menu";
@@ -314,47 +319,9 @@ function initialize(api) {
},
before: "top",
});
- if (api.getCurrentUser()?.can_assign) {
- api.addPostMenuButton("assign", (post) => {
- if (post.firstPost) {
- return;
- }
- if (post.assigned_to_user || post.assigned_to_group) {
- return {
- action: "unassignPost",
- icon: "user-times",
- className: "unassign-post",
- title: "discourse_assign.unassign_post.title",
- position:
- post.assigned_to_user?.id === api.getCurrentUser().id
- ? "first"
- : "second-last-hidden",
- };
- } else {
- return {
- action: "assignPost",
- icon: "user-plus",
- className: "assign-post",
- title: "discourse_assign.assign_post.title",
- position: "second-last-hidden",
- };
- }
- });
- api.attachWidgetAction("post", "assignPost", function () {
- const taskActions = getOwner(this).lookup("service:task-actions");
- taskActions.showAssignModal(this.model, {
- isAssigned: false,
- targetType: "Post",
- });
- });
-
- api.attachWidgetAction("post", "unassignPost", function () {
- const taskActions = getOwner(this).lookup("service:task-actions");
- taskActions.unassign(this.model.id, "Post").then(() => {
- delete this.model.topic.indirectly_assigned_to[this.model.id];
- });
- });
+ if (api.getCurrentUser()?.can_assign) {
+ customizePostMenu(api);
}
}
@@ -528,7 +495,9 @@ function initialize(api) {
return new RenderGlimmer(
this,
"p.assigned-to",
- hbs``,
+ hbs`
+ `,
{
assignedToUser: attrs.post.assigned_to_user,
assignedToGroup: attrs.post.assigned_to_group,
@@ -755,6 +724,76 @@ function initialize(api) {
api.addKeyboardShortcut("g a", "", { path: "/my/activity/assigned" });
}
+function customizePostMenu(api) {
+ const transformerRegistered = api.registerValueTransformer(
+ "post-menu-buttons",
+ ({
+ value: dag,
+ context: {
+ post,
+ state,
+ firstButtonKey,
+ lastHiddenButtonKey,
+ secondLastHiddenButtonKey,
+ },
+ }) => {
+ dag.add(
+ "assign",
+ AssignButton,
+ post.assigned_to_user?.id === state.currentUser.id
+ ? {
+ before: firstButtonKey,
+ }
+ : {
+ before: lastHiddenButtonKey,
+ after: secondLastHiddenButtonKey,
+ }
+ );
+ }
+ );
+
+ const silencedKey =
+ transformerRegistered && "discourse.post-menu-widget-overrides";
+
+ withSilencedDeprecations(silencedKey, () => customizeWidgetPostMenu(api));
+}
+
+function customizeWidgetPostMenu(api) {
+ api.addPostMenuButton("assign", (post) => {
+ if (post.firstPost) {
+ return;
+ }
+ if (post.assigned_to_user || post.assigned_to_group) {
+ return {
+ action: "unassignPost",
+ icon: "user-times",
+ className: "unassign-post",
+ title: "discourse_assign.unassign_post.title",
+ position:
+ post.assigned_to_user?.id === api.getCurrentUser().id
+ ? "first"
+ : "second-last-hidden",
+ };
+ } else {
+ return {
+ action: "assignPost",
+ icon: "user-plus",
+ className: "assign-post",
+ title: "discourse_assign.assign_post.title",
+ position: "second-last-hidden",
+ };
+ }
+ });
+
+ api.attachWidgetAction("post", "assignPost", function () {
+ assignPost(this.model, getOwner(this).lookup("service:task-actions"));
+ });
+
+ api.attachWidgetAction("post", "unassignPost", function () {
+ unassignPost(this.model, getOwner(this).lookup("service:task-actions"));
+ });
+}
+
const REGEXP_USERNAME_PREFIX = /^(assigned:)/gi;
export default {
@@ -794,7 +833,7 @@ export default {
});
}
- withPluginApi("0.13.0", (api) => {
+ withPluginApi("1.34.0", (api) => {
extendTopicModel(api, PLUGIN_ID);
initialize(api);
registerTopicFooterButtons(api);
diff --git a/test/javascripts/acceptance/assign-enabled-test.js b/test/javascripts/acceptance/assign-enabled-test.js
index 9c0d211b..9cdfed58 100644
--- a/test/javascripts/acceptance/assign-enabled-test.js
+++ b/test/javascripts/acceptance/assign-enabled-test.js
@@ -15,7 +15,7 @@ import { cloneJSON } from "discourse-common/lib/object";
acceptance("Discourse Assign | Assign mobile", function (needs) {
needs.user();
needs.mobileView();
- needs.settings({ assign_enabled: true });
+ needs.settings({ glimmer_post_menu_mode: "enabled", assign_enabled: true });
needs.pretender((server, helper) => {
server.get("/assign/suggestions", () => {
@@ -52,7 +52,7 @@ acceptance("Discourse Assign | Assign desktop", function (needs) {
needs.user({
can_assign: true,
});
- needs.settings({ assign_enabled: true });
+ needs.settings({ glimmer_post_menu_mode: "enabled", assign_enabled: true });
needs.pretender((server, helper) => {
server.get("/assign/suggestions", () => {
@@ -77,15 +77,15 @@ acceptance("Discourse Assign | Assign desktop", function (needs) {
await visit("/t/internationalization-localization/280");
assert
- .dom("#post_2 .extra-buttons .d-icon-user-plus")
+ .dom("#post_2 .post-action-menu__assign-post")
.doesNotExist("assign to post button is hidden");
await click("#post_2 button.show-more-actions");
assert
- .dom("#post_2 .extra-buttons .d-icon-user-plus")
+ .dom("#post_2 .post-action-menu__assign-post")
.exists("assign to post button exists");
- await click("#post_2 .extra-buttons .d-icon-user-plus");
+ await click("#post_2 .post-action-menu__assign-post");
assert.dom(".assign.d-modal").exists("assign modal opens");
const menu = selectKit(".assign.d-modal .user-chooser");
@@ -126,6 +126,7 @@ acceptance("Discourse Assign | Assign Status enabled", function (needs) {
can_assign: true,
});
needs.settings({
+ glimmer_post_menu_mode: "enabled",
assign_enabled: true,
enable_assign_status: true,
assign_statuses: "New|In Progress|Done",
@@ -187,7 +188,11 @@ acceptance("Discourse Assign | Assign Status disabled", function (needs) {
needs.user({
can_assign: true,
});
- needs.settings({ assign_enabled: true, enable_assign_status: false });
+ needs.settings({
+ glimmer_post_menu_mode: "enabled",
+ assign_enabled: true,
+ enable_assign_status: false,
+ });
needs.pretender((server, helper) => {
server.get("/assign/suggestions", () => {
@@ -245,6 +250,7 @@ const remindersFrequency = [
acceptance("Discourse Assign | User preferences", function (needs) {
needs.user({ can_assign: true, reminders_frequency: remindersFrequency });
needs.settings({
+ glimmer_post_menu_mode: "enabled",
assign_enabled: true,
remind_assigns_frequency: 43200,
});
@@ -291,6 +297,7 @@ acceptance(
function (needs) {
needs.user({ can_assign: true, reminders_frequency: remindersFrequency });
needs.settings({
+ glimmer_post_menu_mode: "enabled",
assign_enabled: true,
remind_assigns_frequency: 43200,
});
diff --git a/test/javascripts/acceptance/assign-enabled-widget-post-menu-test.js b/test/javascripts/acceptance/assign-enabled-widget-post-menu-test.js
new file mode 100644
index 00000000..e9eb76b4
--- /dev/null
+++ b/test/javascripts/acceptance/assign-enabled-widget-post-menu-test.js
@@ -0,0 +1,351 @@
+import { click, fillIn, visit } from "@ember/test-helpers";
+import { test } from "qunit";
+import userFixtures from "discourse/tests/fixtures/user-fixtures";
+import pretender, {
+ parsePostData,
+ response,
+} from "discourse/tests/helpers/create-pretender";
+import {
+ acceptance,
+ updateCurrentUser,
+} from "discourse/tests/helpers/qunit-helpers";
+import selectKit from "discourse/tests/helpers/select-kit-helper";
+import { cloneJSON } from "discourse-common/lib/object";
+
+// TODO (glimmer-post-menu): Remove this file when the post menu widget code is removed from core
+acceptance(
+ "Discourse Assign | Widget Post Menu | Assign mobile",
+ function (needs) {
+ needs.user();
+ needs.mobileView();
+ needs.settings({
+ glimmer_post_menu_mode: "disabled",
+ assign_enabled: true,
+ });
+
+ needs.pretender((server, helper) => {
+ server.get("/assign/suggestions", () => {
+ return helper.response({
+ success: true,
+ assign_allowed_groups: false,
+ assign_allowed_for_groups: [],
+ suggestions: [
+ {
+ id: 19,
+ username: "eviltrout",
+ name: "Robin Ward",
+ avatar_template:
+ "/user_avatar/meta.discourse.org/eviltrout/{size}/5275_2.png",
+ },
+ ],
+ });
+ });
+ });
+
+ test("Footer dropdown contains button", async function (assert) {
+ updateCurrentUser({ can_assign: true });
+ await visit("/t/internationalization-localization/280");
+ const menu = selectKit(".topic-footer-mobile-dropdown");
+ await menu.expand();
+
+ assert.true(menu.rowByValue("assign").exists());
+ await menu.selectRowByValue("assign");
+ assert.dom(".assign.d-modal").exists("assign modal opens");
+ });
+ }
+);
+
+acceptance(
+ "Discourse Assign | Widget Post Menu | Assign desktop",
+ function (needs) {
+ needs.user({
+ can_assign: true,
+ });
+ needs.settings({
+ glimmer_post_menu_mode: "disabled",
+ assign_enabled: true,
+ });
+
+ needs.pretender((server, helper) => {
+ server.get("/assign/suggestions", () => {
+ return helper.response({
+ success: true,
+ assign_allowed_groups: false,
+ assign_allowed_for_groups: [],
+ suggestions: [
+ {
+ id: 19,
+ username: "eviltrout",
+ name: "Robin Ward",
+ avatar_template:
+ "/user_avatar/meta.discourse.org/eviltrout/{size}/5275_2.png",
+ },
+ ],
+ });
+ });
+ });
+
+ test("Assigning user to a post", async function (assert) {
+ await visit("/t/internationalization-localization/280");
+
+ assert
+ .dom("#post_2 .extra-buttons .d-icon-user-plus")
+ .doesNotExist("assign to post button is hidden");
+
+ await click("#post_2 button.show-more-actions");
+ assert
+ .dom("#post_2 .extra-buttons .d-icon-user-plus")
+ .exists("assign to post button exists");
+
+ await click("#post_2 .extra-buttons .d-icon-user-plus");
+ assert.dom(".assign.d-modal").exists("assign modal opens");
+
+ const menu = selectKit(".assign.d-modal .user-chooser");
+ assert.true(menu.isExpanded(), "user selector is expanded");
+
+ await click(".assign.d-modal .btn-primary");
+ assert.dom(".error-label").includesText("Choose a user to assign");
+
+ await menu.expand();
+ await menu.selectRowByIndex(0);
+ assert.strictEqual(menu.header().value(), "eviltrout");
+ assert.dom(".error-label").doesNotExist();
+
+ pretender.put("/assign/assign", ({ requestBody }) => {
+ const body = parsePostData(requestBody);
+ assert.strictEqual(body.target_type, "Post");
+ assert.strictEqual(body.username, "eviltrout");
+ assert.strictEqual(body.note, "a note!");
+ return response({ success: true });
+ });
+
+ await fillIn("#assign-modal-note", "a note!");
+ await click(".assign.d-modal .btn-primary");
+
+ assert.dom(".assign.d-modal").doesNotExist("assign modal closes");
+ });
+
+ test("Footer dropdown contains button", async function (assert) {
+ await visit("/t/internationalization-localization/280");
+ await click("#topic-footer-button-assign");
+
+ assert.dom(".assign.d-modal").exists("assign modal opens");
+ });
+ }
+);
+
+acceptance(
+ "Discourse Assign | Widget Post Menu | Assign Status enabled",
+ function (needs) {
+ needs.user({
+ can_assign: true,
+ });
+ needs.settings({
+ glimmer_post_menu_mode: "disabled",
+ assign_enabled: true,
+ enable_assign_status: true,
+ assign_statuses: "New|In Progress|Done",
+ });
+
+ needs.pretender((server, helper) => {
+ server.get("/assign/suggestions", () => {
+ return helper.response({
+ success: true,
+ assign_allowed_groups: false,
+ assign_allowed_for_groups: [],
+ suggestions: [
+ {
+ id: 19,
+ username: "eviltrout",
+ name: "Robin Ward",
+ avatar_template:
+ "/user_avatar/meta.discourse.org/eviltrout/{size}/5275_2.png",
+ },
+ ],
+ });
+ });
+ });
+
+ test("Modal contains status dropdown", async function (assert) {
+ pretender.put("/assign/assign", ({ requestBody }) => {
+ const body = parsePostData(requestBody);
+ assert.strictEqual(body.target_type, "Topic");
+ assert.strictEqual(body.target_id, "280");
+ assert.strictEqual(body.username, "eviltrout");
+ assert.strictEqual(body.status, "In Progress");
+
+ return response({ success: true });
+ });
+
+ await visit("/t/internationalization-localization/280");
+ await click("#topic-footer-button-assign");
+
+ assert
+ .dom(".assign.d-modal #assign-status")
+ .exists("assign status dropdown exists");
+
+ const statusDropdown = selectKit("#assign-status");
+ assert.strictEqual(statusDropdown.header().value(), "New");
+
+ await statusDropdown.expand();
+ await statusDropdown.selectRowByValue("In Progress");
+ assert.strictEqual(statusDropdown.header().value(), "In Progress");
+
+ const menu = selectKit(".assign.d-modal .user-chooser");
+ await menu.expand();
+ await menu.selectRowByIndex(0);
+
+ await click(".assign.d-modal .btn-primary");
+ });
+ }
+);
+
+acceptance(
+ "Discourse Assign | Widget Post Menu | Assign Status disabled",
+ function (needs) {
+ needs.user({
+ can_assign: true,
+ });
+ needs.settings({
+ glimmer_post_menu_mode: "disabled",
+ assign_enabled: true,
+ enable_assign_status: false,
+ });
+
+ needs.pretender((server, helper) => {
+ server.get("/assign/suggestions", () => {
+ return helper.response({
+ success: true,
+ assign_allowed_groups: false,
+ assign_allowed_for_groups: [],
+ suggestions: [
+ {
+ id: 19,
+ username: "eviltrout",
+ name: "Robin Ward",
+ avatar_template:
+ "/user_avatar/meta.discourse.org/eviltrout/{size}/5275_2.png",
+ },
+ ],
+ });
+ });
+ });
+
+ test("Modal contains status dropdown", async function (assert) {
+ await visit("/t/internationalization-localization/280");
+ await click("#topic-footer-button-assign");
+
+ assert
+ .dom(".assign.d-modal #assign-status")
+ .doesNotExist("assign status dropdown doesn't exists");
+ });
+ }
+);
+
+// See RemindAssignsFrequencySiteSettings
+const remindersFrequency = [
+ {
+ name: "discourse_assign.reminders_frequency.never",
+ value: 0,
+ },
+ {
+ name: "discourse_assign.reminders_frequency.daily",
+ value: 1440,
+ },
+ {
+ name: "discourse_assign.reminders_frequency.weekly",
+ value: 10080,
+ },
+ {
+ name: "discourse_assign.reminders_frequency.monthly",
+ value: 43200,
+ },
+ {
+ name: "discourse_assign.reminders_frequency.quarterly",
+ value: 129600,
+ },
+];
+
+acceptance(
+ "Discourse Assign | Widget Post Menu | User preferences",
+ function (needs) {
+ needs.user({ can_assign: true, reminders_frequency: remindersFrequency });
+ needs.settings({
+ glimmer_post_menu_mode: "disabled",
+ assign_enabled: true,
+ remind_assigns_frequency: 43200,
+ });
+
+ test("The frequency for assigned topic reminders defaults to the site setting", async function (assert) {
+ await visit("/u/eviltrout/preferences/notifications");
+
+ assert.strictEqual(
+ selectKit("#remind-assigns-frequency").header().value(),
+ "43200",
+ "set frequency to default of Monthly"
+ );
+ });
+
+ test("The user can change the frequency to Never", async function (assert) {
+ await visit("/u/eviltrout/preferences/notifications");
+
+ await selectKit("#remind-assigns-frequency").expand();
+ await selectKit("#remind-assigns-frequency").selectRowByValue(0);
+
+ assert.strictEqual(
+ selectKit("#remind-assigns-frequency").header().value(),
+ "0",
+ "set frequency to Never"
+ );
+ });
+
+ test("The user can change the frequency to some other non-default value", async function (assert) {
+ await visit("/u/eviltrout/preferences/notifications");
+
+ await selectKit("#remind-assigns-frequency").expand();
+ await selectKit("#remind-assigns-frequency").selectRowByValue(10080); // weekly
+
+ assert.strictEqual(
+ selectKit("#remind-assigns-frequency").header().value(),
+ "10080",
+ "set frequency to Weekly"
+ );
+ });
+ }
+);
+
+acceptance(
+ "Discourse Assign | Widget Post Menu | User preferences | Pre-selected reminder frequency",
+ function (needs) {
+ needs.user({ can_assign: true, reminders_frequency: remindersFrequency });
+ needs.settings({
+ glimmer_post_menu_mode: "disabled",
+ assign_enabled: true,
+ remind_assigns_frequency: 43200,
+ });
+
+ needs.pretender((server, helper) => {
+ server.get("/u/eviltrout.json", () => {
+ let json = cloneJSON(userFixtures["/u/eviltrout.json"]);
+ json.user.custom_fields = { remind_assigns_frequency: 10080 };
+
+ // usually this is done automatically by this pretender but we
+ // have to do it manually here because we are overriding the
+ // pretender see app/assets/javascripts/discourse/tests/helpers/create-pretender.js
+ json.user.can_edit = true;
+
+ return helper.response(200, json);
+ });
+ });
+
+ test("The user's previously selected value is loaded", async function (assert) {
+ await visit("/u/eviltrout/preferences/notifications");
+
+ assert.strictEqual(
+ selectKit("#remind-assigns-frequency").header().value(),
+ "10080",
+ "frequency is pre-selected to Weekly"
+ );
+ });
+ }
+);