From 1b8b2a3a3227c919a6d11c92ba31691488af0949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Saquetim?= Date: Fri, 28 Mar 2025 19:31:33 -0300 Subject: [PATCH 1/4] DEV: Add compatibility with the Glimmer Post Stream --- .../components/assigned-to-first-post.gjs | 95 +++++++ .../discourse/components/assigned-to-post.gjs | 11 +- .../components/post-assignments-display.gjs | 49 ++++ .../initializers/extend-for-assigns.js | 231 ++++++++++++++---- assets/javascripts/discourse/lib/url.js | 19 ++ 5 files changed, 343 insertions(+), 62 deletions(-) create mode 100644 assets/javascripts/discourse/components/assigned-to-first-post.gjs create mode 100644 assets/javascripts/discourse/components/post-assignments-display.gjs create mode 100644 assets/javascripts/discourse/lib/url.js diff --git a/assets/javascripts/discourse/components/assigned-to-first-post.gjs b/assets/javascripts/discourse/components/assigned-to-first-post.gjs new file mode 100644 index 00000000..d1a292d7 --- /dev/null +++ b/assets/javascripts/discourse/components/assigned-to-first-post.gjs @@ -0,0 +1,95 @@ +import Component from "@glimmer/component"; +import { concat } from "@ember/helper"; +import icon from "discourse/helpers/d-icon"; +import userPrioritizedName from "discourse/helpers/user-prioritized-name"; +import { i18n } from "discourse-i18n"; +import { assignedToGroupPath, assignedToUserPath } from "../lib/url"; + +export default class AssignedToFirstPost extends Component { + get assignedToUser() { + return this.args.post?.topic?.assigned_to_user; + } + + get assignedToGroup() { + return this.args.post?.topic?.assigned_to_group; + } + + get icon() { + return this.assignedToUser ? "user-plus" : "group-plus"; + } + + get indirectlyAssignedTo() { + return this.args.post?.topic?.indirectly_assigned_to; + } + + get indirectAssignments() { + if (!this.indirectlyAssignedTo) { + return null; + } + + return Object.keys(this.indirectlyAssignedTo).map((postId) => { + const postNumber = this.indirectlyAssignedTo[postId].post_number; + + return { + postId, + assignee: this.indirectlyAssignedTo[postId].assigned_to, + postNumber, + url: `${this.args.post.topic.url}/${postNumber}`, + }; + }); + } + + get isAssigned() { + return !!( + this.assignedToUser || + this.assignedToGroup || + this.args.post?.topic?.indirectly_assigned_to + ); + } + + +} diff --git a/assets/javascripts/discourse/components/assigned-to-post.gjs b/assets/javascripts/discourse/components/assigned-to-post.gjs index 6c17144c..80800a6b 100644 --- a/assets/javascripts/discourse/components/assigned-to-post.gjs +++ b/assets/javascripts/discourse/components/assigned-to-post.gjs @@ -4,6 +4,7 @@ import { service } from "@ember/service"; import DButton from "discourse/components/d-button"; import DropdownMenu from "discourse/components/dropdown-menu"; import icon from "discourse/helpers/d-icon"; +import userPrioritizedName from "discourse/helpers/user-prioritized-name"; import { i18n } from "discourse-i18n"; import DMenu from "float-kit/components/d-menu"; @@ -11,14 +12,6 @@ export default class AssignedToPost extends Component { @service taskActions; @service siteSettings; - get nameOrUsername() { - if (this.siteSettings.prioritize_full_name_in_ux) { - return this.args.assignedToUser.name || this.args.assignedToUser.username; - } else { - return this.args.assignedToUser.username; - } - } - @action unassign() { this.taskActions.unassignPost(this.args.post); @@ -42,7 +35,7 @@ export default class AssignedToPost extends Component { {{#if @assignedToUser}} - {{this.nameOrUsername}} + {{userPrioritizedName @assignedToUser}} {{else}} {{@assignedToGroup.name}} {{/if}} diff --git a/assets/javascripts/discourse/components/post-assignments-display.gjs b/assets/javascripts/discourse/components/post-assignments-display.gjs new file mode 100644 index 00000000..c078c890 --- /dev/null +++ b/assets/javascripts/discourse/components/post-assignments-display.gjs @@ -0,0 +1,49 @@ +import Component from "@glimmer/component"; +import { assignedToGroupPath, assignedToUserPath } from "../lib/url"; +import AssignedFirstPost from "./assigned-to-first-post"; +import AssignedToPost from "./assigned-to-post"; + +export default class PostAssignmentsDisplay extends Component { + static shouldRender(args) { + return args.post; + } + + get post() { + return this.args.outletArgs.post; + } + + get assignedTo() { + return this.post.topic?.indirectly_assigned_to?.[this.post.id]?.assigned_to; + } + + get assignedToUser() { + return this.assignedTo.username ? this.assignedTo : null; + } + + get assignedToGroup() { + return !this.assignedToUser && this.assignedTo.name + ? this.assignedTo + : null; + } + + get assignedHref() { + return this.assignedToUser + ? assignedToUserPath(this.assignedToUser) + : assignedToGroupPath(this.assignedToGroup); + } + + +} diff --git a/assets/javascripts/discourse/initializers/extend-for-assigns.js b/assets/javascripts/discourse/initializers/extend-for-assigns.js index 96cfbd8e..9cf25212 100644 --- a/assets/javascripts/discourse/initializers/extend-for-assigns.js +++ b/assets/javascripts/discourse/initializers/extend-for-assigns.js @@ -6,6 +6,7 @@ import { hbs } from "ember-cli-htmlbars"; import { h } from "virtual-dom"; import { renderAvatar } from "discourse/helpers/user-avatar"; import discourseComputed from "discourse/lib/decorators"; +import { withSilencedDeprecations } from "discourse/lib/deprecated"; import getURL from "discourse/lib/get-url"; import { iconHTML, iconNode } from "discourse/lib/icon-library"; import { withPluginApi } from "discourse/lib/plugin-api"; @@ -18,7 +19,9 @@ import { i18n } from "discourse-i18n"; import AssignButton from "../components/assign-button"; import BulkActionsAssignUser from "../components/bulk-actions/bulk-assign-user"; import EditTopicAssignments from "../components/modal/edit-topic-assignments"; +import PostAssignmentsDisplay from "../components/post-assignments-display"; import TopicLevelAssignMenu from "../components/topic-level-assign-menu"; +import { assignedToGroupPath, assignedToUserPath } from "../lib/url"; import { extendTopicModel } from "../models/topic"; const DEPENDENT_KEYS = [ @@ -322,7 +325,10 @@ function initialize(api) { } api.addPostSmallActionClassesCallback((post) => { - if (post.actionCode.includes("assigned") && !siteSettings.assigns_public) { + // TODO (glimmer-post-stream): only check for .action_code once the widget code is removed + const actionCode = post.action_code || post.actionCode; + + if (actionCode.includes("assigned") && !siteSettings.assigns_public) { return ["private-assign"]; } }); @@ -344,19 +350,6 @@ function initialize(api) { : {} ); - function assignedToUserPath(assignedToUser) { - return getURL( - siteSettings.assigns_user_url_path.replace( - "{username}", - assignedToUser.username - ) - ); - } - - function assignedToGroupPath(assignedToGroup) { - return getURL(`/g/${assignedToGroup.name}/assigned/everyone`); - } - api.modifyClass( "model:bookmark", (Superclass) => @@ -403,29 +396,6 @@ function initialize(api) { api.addPostSmallActionIcon("reassigned", "user-plus"); api.addPostSmallActionIcon("reassigned_group", "group-plus"); - api.addPostTransformCallback((transformed) => { - if ( - [ - "assigned", - "unassigned", - "reassigned", - "assigned_group", - "unassigned_group", - "reassigned_group", - "assigned_to_post", - "assigned_group_to_post", - "unassigned_from_post", - "unassigned_group_from_post", - "details_change", - "note_change", - "status_change", - ].includes(transformed.actionCode) - ) { - transformed.isSmallAction = true; - transformed.canEdit = true; - } - }); - api.addDiscoveryQueryParam("assigned", { replace: true, refreshModel: true }); api.addTagsHtmlCallback((topic, params = {}) => { @@ -717,6 +687,42 @@ function initialize(api) { } ); + customizePost(api); + + api.replaceIcon("notification.assigned", "user-plus"); + + api.replaceIcon( + "notification.discourse_assign.assign_group_notification", + "group-plus" + ); + + api.modifyClass( + "controller:preferences/notifications", + (Superclass) => + class extends Superclass { + @action + save() { + this.saveAttrNames.push("custom_fields"); + super.save(...arguments); + } + } + ); + + api.addKeyboardShortcut("g a", "", { path: "/my/activity/assigned" }); +} + +function customizePost(api) { + api.renderAfterWrapperOutlet( + "post-content-cooked-html", + PostAssignmentsDisplay + ); + + withSilencedDeprecations("discourse.post-stream-widget-overrides", () => + customizeWidgetPost(api) + ); +} + +function customizeWidgetPost(api) { api.decorateWidget("post-contents:after-cooked", (dec) => { const postModel = dec.getModel(); if (postModel) { @@ -752,26 +758,145 @@ function initialize(api) { } }); - api.replaceIcon("notification.assigned", "user-plus"); + api.createWidget("assigned-to-post", { + html(attrs) { + 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, + } + ); + }, + }); - api.replaceIcon( - "notification.discourse_assign.assign_group_notification", - "group-plus" - ); + api.createWidget("assigned-to-first-post", { + html(attrs) { + const topic = attrs.topic; + const [assignedToUser, assignedToGroup, indirectlyAssignedTo] = [ + topic.assigned_to_user, + topic.assigned_to_group, + topic.indirectly_assigned_to, + ]; + const assigneeElements = []; - api.modifyClass( - "controller:preferences/notifications", - (Superclass) => - class extends Superclass { - @action - save() { - this.saveAttrNames.push("custom_fields"); - super.save(...arguments); - } + const assignedHtml = (username, path, type) => { + return `${htmlSafe( + i18n("discourse_assign.assigned_topic_to", { + username, + path, + }) + )}`; + }; + + let displayedName = ""; + if (assignedToUser) { + displayedName = !this.siteSettings.prioritize_username_in_ux + ? assignedToUser.name || assignedToUser.username + : assignedToUser.username; + + assigneeElements.push( + h( + "span.assignee", + new RawHtml({ + html: assignedHtml( + displayedName, + assignedToUserPath(assignedToUser), + "user" + ), + }) + ) + ); } - ); - api.addKeyboardShortcut("g a", "", { path: "/my/activity/assigned" }); + if (assignedToGroup) { + assigneeElements.push( + h( + "span.assignee", + new RawHtml({ + html: assignedHtml( + assignedToGroup.name, + assignedToGroupPath(assignedToGroup), + "group" + ), + }) + ) + ); + } + + if (indirectlyAssignedTo) { + Object.keys(indirectlyAssignedTo).map((postId) => { + const assignee = indirectlyAssignedTo[postId].assigned_to; + const postNumber = indirectlyAssignedTo[postId].post_number; + + displayedName = + !this.siteSettings.prioritize_username_in_ux || !assignee.username + ? assignee.name || assignee.username + : assignee.username; + + assigneeElements.push( + h("span.assignee", [ + h( + "a", + { + attributes: { + class: "assigned-indirectly", + href: `${topic.url}/${postNumber}`, + }, + }, + i18n("discourse_assign.assign_post_to_multiple", { + post_number: postNumber, + username: displayedName, + }) + ), + ]) + ); + }); + } + + if (!isEmpty(assigneeElements)) { + return h("p.assigned-to", [ + assignedToUser ? iconNode("user-plus") : iconNode("group-plus"), + assignedToUser || assignedToGroup + ? "" + : h("span.assign-text", i18n("discourse_assign.assigned")), + assigneeElements, + ]); + } + }, + }); + + // This won't have a direct translation in the Glimmer API as it uses can_edit from the model + // TODO (glimmer-post-stream): check the post small action component and introduce a transformer to override the + // canEdit behavior there + api.addPostTransformCallback((transformed) => { + if ( + [ + "assigned", + "unassigned", + "reassigned", + "assigned_group", + "unassigned_group", + "reassigned_group", + "assigned_to_post", + "assigned_group_to_post", + "unassigned_from_post", + "unassigned_group_from_post", + "details_change", + "note_change", + "status_change", + ].includes(transformed.actionCode) + ) { + transformed.isSmallAction = true; + transformed.canEdit = true; + } + }); } function customizePostMenu(api) { diff --git a/assets/javascripts/discourse/lib/url.js b/assets/javascripts/discourse/lib/url.js new file mode 100644 index 00000000..208cecf0 --- /dev/null +++ b/assets/javascripts/discourse/lib/url.js @@ -0,0 +1,19 @@ +import { getOwnerWithFallback } from "discourse/lib/get-owner"; +import getURL from "discourse/lib/get-url"; + +export function assignedToUserPath(assignedToUser) { + const siteSettings = getOwnerWithFallback(this).lookup( + "service:site-settings" + ); + + return getURL( + siteSettings.assigns_user_url_path.replace( + "{username}", + assignedToUser.username + ) + ); +} + +export function assignedToGroupPath(assignedToGroup) { + return getURL(`/g/${assignedToGroup.name}/assigned/everyone`); +} From e8cb94931e5ac391c99993e3506d1c0f8576f11f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Saquetim?= Date: Thu, 24 Apr 2025 22:57:12 -0300 Subject: [PATCH 2/4] Use `modifyClass("model:post", ...)` as an alternative to `addPostTransformCallback` --- .../initializers/extend-for-assigns.js | 66 +++++++++++++------ 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/assets/javascripts/discourse/initializers/extend-for-assigns.js b/assets/javascripts/discourse/initializers/extend-for-assigns.js index 9cf25212..320ddc0b 100644 --- a/assets/javascripts/discourse/initializers/extend-for-assigns.js +++ b/assets/javascripts/discourse/initializers/extend-for-assigns.js @@ -392,7 +392,6 @@ function initialize(api) { api.addPostSmallActionIcon("unassigned_group", "group-times"); api.addPostSmallActionIcon("unassigned_from_post", "user-xmark"); api.addPostSmallActionIcon("unassigned_group_from_post", "group-times"); - api.includePostAttributes("assigned_to_user", "assigned_to_group"); api.addPostSmallActionIcon("reassigned", "user-plus"); api.addPostSmallActionIcon("reassigned_group", "group-plus"); @@ -712,6 +711,30 @@ function initialize(api) { } function customizePost(api) { + api.addTrackedPostProperties("assigned_to_user", "assigned_to_group"); + + api.modifyClass( + "model:post", + (Superclass) => + class extends Superclass { + get can_edit() { + return isAssignSmallAction(this.action_code) ? true : super.can_edit; + } + + // overriding tracked properties requires overriding both the getter and the setter. + // otherwise the superclass will throw an error when the application sets the field value + set can_edit(value) { + super.can_edit = value; + } + + get isSmallAction() { + return isAssignSmallAction(this.action_code) + ? true + : super.isSmallAction; + } + } + ); + api.renderAfterWrapperOutlet( "post-content-cooked-html", PostAssignmentsDisplay @@ -872,33 +895,34 @@ function customizeWidgetPost(api) { }, }); - // This won't have a direct translation in the Glimmer API as it uses can_edit from the model - // TODO (glimmer-post-stream): check the post small action component and introduce a transformer to override the - // canEdit behavior there + // `addPostTransformCallback` doesn't have a direct translation in the new Glimmer API. + // We need to use a modify class in the post model instead api.addPostTransformCallback((transformed) => { - if ( - [ - "assigned", - "unassigned", - "reassigned", - "assigned_group", - "unassigned_group", - "reassigned_group", - "assigned_to_post", - "assigned_group_to_post", - "unassigned_from_post", - "unassigned_group_from_post", - "details_change", - "note_change", - "status_change", - ].includes(transformed.actionCode) - ) { + if (isAssignSmallAction(transformed.actionCode)) { transformed.isSmallAction = true; transformed.canEdit = true; } }); } +function isAssignSmallAction(actionCode) { + return [ + "assigned", + "unassigned", + "reassigned", + "assigned_group", + "unassigned_group", + "reassigned_group", + "assigned_to_post", + "assigned_group_to_post", + "unassigned_from_post", + "unassigned_group_from_post", + "details_change", + "note_change", + "status_change", + ].includes(actionCode); +} + function customizePostMenu(api) { api.registerValueTransformer( "post-menu-buttons", From 76d3bc1b17525b24bf7028f7d6123263bacbad59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Saquetim?= Date: Wed, 30 Apr 2025 02:02:53 -0300 Subject: [PATCH 3/4] Fixed markup issues and changed test scenarios to also use the Glimmer Post Stream --- .../components/assigned-to-first-post.gjs | 71 +++- .../discourse/components/assigned-to-post.gjs | 11 +- spec/system/assign_topic_spec.rb | 248 ++++++------ .../acceptance/assign-disabled-test.js | 24 +- .../acceptance/assign-enabled-test.js | 298 ++++++++------- .../acceptance/assigned-topic-test.js | 360 ++++++++++-------- 6 files changed, 552 insertions(+), 460 deletions(-) diff --git a/assets/javascripts/discourse/components/assigned-to-first-post.gjs b/assets/javascripts/discourse/components/assigned-to-first-post.gjs index d1a292d7..5a91ae2b 100644 --- a/assets/javascripts/discourse/components/assigned-to-first-post.gjs +++ b/assets/javascripts/discourse/components/assigned-to-first-post.gjs @@ -1,11 +1,14 @@ import Component from "@glimmer/component"; -import { concat } from "@ember/helper"; +import { service } from "@ember/service"; +import { htmlSafe } from "@ember/template"; import icon from "discourse/helpers/d-icon"; -import userPrioritizedName from "discourse/helpers/user-prioritized-name"; +import { bind } from "discourse/lib/decorators"; import { i18n } from "discourse-i18n"; import { assignedToGroupPath, assignedToUserPath } from "../lib/url"; export default class AssignedToFirstPost extends Component { + @service siteSettings; + get assignedToUser() { return this.args.post?.topic?.assigned_to_user; } @@ -47,6 +50,19 @@ export default class AssignedToFirstPost extends Component { ); } + get hasOnlyIndirectAssignments() { + return !this.assignedToUser && !this.assignedToGroup; + } + + @bind + prioritizedAssigneeName(assignee) { + // if this code is ever replaced to use `prioritize_username_in_ux`, remove this function and use the helper + // userPrioritizedName instead + return this.siteSettings.prioritize_full_name_in_ux || !assignee.username + ? assignee.name || assignee.username + : assignee.username; + } + diff --git a/assets/javascripts/discourse/components/assigned-to-post.gjs b/assets/javascripts/discourse/components/assigned-to-post.gjs index 80800a6b..6c17144c 100644 --- a/assets/javascripts/discourse/components/assigned-to-post.gjs +++ b/assets/javascripts/discourse/components/assigned-to-post.gjs @@ -4,7 +4,6 @@ import { service } from "@ember/service"; import DButton from "discourse/components/d-button"; import DropdownMenu from "discourse/components/dropdown-menu"; import icon from "discourse/helpers/d-icon"; -import userPrioritizedName from "discourse/helpers/user-prioritized-name"; import { i18n } from "discourse-i18n"; import DMenu from "float-kit/components/d-menu"; @@ -12,6 +11,14 @@ export default class AssignedToPost extends Component { @service taskActions; @service siteSettings; + get nameOrUsername() { + if (this.siteSettings.prioritize_full_name_in_ux) { + return this.args.assignedToUser.name || this.args.assignedToUser.username; + } else { + return this.args.assignedToUser.username; + } + } + @action unassign() { this.taskActions.unassignPost(this.args.post); @@ -35,7 +42,7 @@ export default class AssignedToPost extends Component { {{#if @assignedToUser}} - {{userPrioritizedName @assignedToUser}} + {{this.nameOrUsername}} {{else}} {{@assignedToGroup.name}} {{/if}} diff --git a/spec/system/assign_topic_spec.rb b/spec/system/assign_topic_spec.rb index 16032e39..05cd7f02 100644 --- a/spec/system/assign_topic_spec.rb +++ b/spec/system/assign_topic_spec.rb @@ -17,162 +17,168 @@ sign_in(admin) end - describe "with open topic" do - it "can assign and unassign" do - visit "/t/#{topic.id}" + %w[enabled disabled].each do |value| + before { SiteSetting.glimmer_post_stream_mode = value } - topic_page.click_assign_topic - assign_modal.assignee = staff_user - assign_modal.confirm - - expect(assign_modal).to be_closed - expect(topic_page).to have_assigned(user: staff_user, at_post: 2) - expect(find("#topic .assigned-to")).to have_content(staff_user.username) - - topic_page.click_unassign_topic - - expect(topic_page).to have_unassigned(user: staff_user, at_post: 3) - expect(page).to have_no_css("#topic .assigned-to") - end + context "when glimmer_post_stream_mode=#{value}" do + describe "with open topic" do + it "can assign and unassign" do + visit "/t/#{topic.id}" - it "can submit form with shortcut from texatea" do - visit "/t/#{topic.id}" + topic_page.click_assign_topic + assign_modal.assignee = staff_user + assign_modal.confirm - topic_page.click_assign_topic - assign_modal.assignee = staff_user + expect(assign_modal).to be_closed + expect(topic_page).to have_assigned(user: staff_user, at_post: 2) + expect(find("#topic .assigned-to")).to have_content(staff_user.username) - find("body").send_keys(:tab) - find("body").send_keys(:control, :enter) + topic_page.click_unassign_topic - expect(assign_modal).to be_closed - expect(topic_page).to have_assigned(user: staff_user, at_post: 2) - expect(find("#topic .assigned-to")).to have_content(staff_user.username) - end + expect(topic_page).to have_unassigned(user: staff_user, at_post: 3) + expect(page).to have_no_css("#topic .assigned-to") + end - context "when prioritize_full_name_in_ux setting is enabled" do - before { SiteSetting.prioritize_full_name_in_ux = true } + it "can submit form with shortcut from texatea" do + visit "/t/#{topic.id}" - it "shows the user's name after assign" do - visit "/t/#{topic.id}" + topic_page.click_assign_topic + assign_modal.assignee = staff_user - topic_page.click_assign_topic - assign_modal.assignee = staff_user - assign_modal.confirm - expect(find("#topic .assigned-to")).to have_content(staff_user.name) - end + find("body").send_keys(:tab) + find("body").send_keys(:control, :enter) - it "show the user's username if there is no name" do - visit "/t/#{topic.id}" - staff_user.name = nil - staff_user.save - staff_user.reload + expect(assign_modal).to be_closed + expect(topic_page).to have_assigned(user: staff_user, at_post: 2) + expect(find("#topic .assigned-to")).to have_content(staff_user.username) + end - topic_page.click_assign_topic - assign_modal.assignee = staff_user - assign_modal.confirm - expect(find("#topic .assigned-to")).to have_content(staff_user.name) - end - end + context "when prioritize_full_name_in_ux setting is enabled" do + before { SiteSetting.prioritize_full_name_in_ux = true } + + it "shows the user's name after assign" do + visit "/t/#{topic.id}" + + topic_page.click_assign_topic + assign_modal.assignee = staff_user + assign_modal.confirm + expect(find("#topic .assigned-to")).to have_content(staff_user.name) + end + + it "show the user's username if there is no name" do + visit "/t/#{topic.id}" + staff_user.name = nil + staff_user.save + staff_user.reload + + topic_page.click_assign_topic + assign_modal.assignee = staff_user + assign_modal.confirm + expect(find("#topic .assigned-to")).to have_content(staff_user.name) + end + end - context "when assigns are not public" do - before { SiteSetting.assigns_public = false } + context "when assigns are not public" do + before { SiteSetting.assigns_public = false } - it "assigned small action post has 'private-assign' in class attribute" do - visit "/t/#{topic.id}" + it "assigned small action post has 'private-assign' in class attribute" do + visit "/t/#{topic.id}" - topic_page.click_assign_topic - assign_modal.assignee = staff_user - assign_modal.confirm + topic_page.click_assign_topic + assign_modal.assignee = staff_user + assign_modal.confirm - expect(assign_modal).to be_closed - expect(topic_page).to have_assigned( - user: staff_user, - at_post: 2, - class_attribute: ".private-assign", - ) - end - end + expect(assign_modal).to be_closed + expect(topic_page).to have_assigned( + user: staff_user, + at_post: 2, + class_attribute: ".private-assign", + ) + end + end - context "when unassign_on_close is set to true" do - before { SiteSetting.unassign_on_close = true } + context "when unassign_on_close is set to true" do + before { SiteSetting.unassign_on_close = true } - it "unassigns the topic on close" do - visit "/t/#{topic.id}" + it "unassigns the topic on close" do + visit "/t/#{topic.id}" - topic_page.click_assign_topic - assign_modal.assignee = staff_user - assign_modal.confirm + topic_page.click_assign_topic + assign_modal.assignee = staff_user + assign_modal.confirm - expect(assign_modal).to be_closed - expect(topic_page).to have_assigned(user: staff_user, at_post: 2) + expect(assign_modal).to be_closed + expect(topic_page).to have_assigned(user: staff_user, at_post: 2) - find(".timeline-controls .toggle-admin-menu").click - find(".topic-admin-close").click + find(".timeline-controls .toggle-admin-menu").click + find(".topic-admin-close").click - expect(find("#post_3")).to have_content( - I18n.t("js.action_codes.closed.enabled", when: "just now"), - ) - expect(page).to have_no_css("#post_4") - expect(page).to have_no_css("#topic .assigned-to") - end + expect(find("#post_3")).to have_content( + I18n.t("js.action_codes.closed.enabled", when: "just now"), + ) + expect(page).to have_no_css("#post_4") + expect(page).to have_no_css("#topic .assigned-to") + end - it "can assign the previous assignee" do - visit "/t/#{topic.id}" + it "can assign the previous assignee" do + visit "/t/#{topic.id}" - topic_page.click_assign_topic - assign_modal.assignee = staff_user - assign_modal.confirm + topic_page.click_assign_topic + assign_modal.assignee = staff_user + assign_modal.confirm - expect(assign_modal).to be_closed - expect(topic_page).to have_assigned(user: staff_user, at_post: 2) + expect(assign_modal).to be_closed + expect(topic_page).to have_assigned(user: staff_user, at_post: 2) - find(".timeline-controls .toggle-admin-menu").click - find(".topic-admin-close").click + find(".timeline-controls .toggle-admin-menu").click + find(".topic-admin-close").click - expect(find("#post_3")).to have_content( - I18n.t("js.action_codes.closed.enabled", when: "just now"), - ) - expect(page).to have_no_css("#post_4") - expect(page).to have_no_css("#topic .assigned-to") + expect(find("#post_3")).to have_content( + I18n.t("js.action_codes.closed.enabled", when: "just now"), + ) + expect(page).to have_no_css("#post_4") + expect(page).to have_no_css("#topic .assigned-to") - topic_page.click_assign_topic - assign_modal.assignee = staff_user - assign_modal.confirm + topic_page.click_assign_topic + assign_modal.assignee = staff_user + assign_modal.confirm - expect(page).to have_no_css("#post_4") - expect(find("#topic .assigned-to")).to have_content(staff_user.username) - end + expect(page).to have_no_css("#post_4") + expect(find("#topic .assigned-to")).to have_content(staff_user.username) + end - context "when reassign_on_open is set to true" do - before { SiteSetting.reassign_on_open = true } + context "when reassign_on_open is set to true" do + before { SiteSetting.reassign_on_open = true } - it "reassigns the topic on open" do - visit "/t/#{topic.id}" + it "reassigns the topic on open" do + visit "/t/#{topic.id}" - topic_page.click_assign_topic - assign_modal.assignee = staff_user - assign_modal.confirm + topic_page.click_assign_topic + assign_modal.assignee = staff_user + assign_modal.confirm - expect(assign_modal).to be_closed - expect(topic_page).to have_assigned(user: staff_user, at_post: 2) + expect(assign_modal).to be_closed + expect(topic_page).to have_assigned(user: staff_user, at_post: 2) - find(".timeline-controls .toggle-admin-menu").click - find(".topic-admin-close").click + find(".timeline-controls .toggle-admin-menu").click + find(".topic-admin-close").click - expect(find("#post_3")).to have_content( - I18n.t("js.action_codes.closed.enabled", when: "just now"), - ) - expect(page).to have_no_css("#post_4") - expect(page).to have_no_css("#topic .assigned-to") + expect(find("#post_3")).to have_content( + I18n.t("js.action_codes.closed.enabled", when: "just now"), + ) + expect(page).to have_no_css("#post_4") + expect(page).to have_no_css("#topic .assigned-to") - find(".timeline-controls .toggle-admin-menu").click - find(".topic-admin-open").click + find(".timeline-controls .toggle-admin-menu").click + find(".topic-admin-open").click - expect(find("#post_4")).to have_content( - I18n.t("js.action_codes.closed.disabled", when: "just now"), - ) - expect(page).to have_no_css("#post_5") - expect(find("#topic .assigned-to")).to have_content(staff_user.username) + expect(find("#post_4")).to have_content( + I18n.t("js.action_codes.closed.disabled", when: "just now"), + ) + expect(page).to have_no_css("#post_5") + expect(find("#topic .assigned-to")).to have_content(staff_user.username) + end + end end end end diff --git a/test/javascripts/acceptance/assign-disabled-test.js b/test/javascripts/acceptance/assign-disabled-test.js index d379210e..95a03353 100644 --- a/test/javascripts/acceptance/assign-disabled-test.js +++ b/test/javascripts/acceptance/assign-disabled-test.js @@ -2,13 +2,21 @@ import { visit } from "@ember/test-helpers"; import { test } from "qunit"; import { acceptance } from "discourse/tests/helpers/qunit-helpers"; -acceptance("Discourse Assign | Assign disabled mobile", function (needs) { - needs.user({ can_assign: true }); - needs.mobileView(); - needs.settings({ assign_enabled: false }); +["enabled", "disabled"].forEach((postStreamMode) => { + acceptance( + `Discourse Assign | Assign disabled mobile (glimmer_post_stream_mode = ${postStreamMode})`, + function (needs) { + needs.user({ can_assign: true }); + needs.mobileView(); + needs.settings({ + assign_enabled: false, + glimmer_post_stream_mode: postStreamMode, + }); - test("Footer dropdown does not contain button", async function (assert) { - await visit("/t/internationalization-localization/280"); - assert.dom(".assign").doesNotExist(); - }); + test("Footer dropdown does not contain button", async function (assert) { + await visit("/t/internationalization-localization/280"); + assert.dom(".assign").doesNotExist(); + }); + } + ); }); diff --git a/test/javascripts/acceptance/assign-enabled-test.js b/test/javascripts/acceptance/assign-enabled-test.js index 987857d7..0af5a39e 100644 --- a/test/javascripts/acceptance/assign-enabled-test.js +++ b/test/javascripts/acceptance/assign-enabled-test.js @@ -45,175 +45,191 @@ acceptance("Discourse Assign | Assign mobile", function (needs) { }); }); -acceptance("Discourse Assign | Assign desktop", function (needs) { - needs.user({ can_assign: true }); - needs.settings({ assign_enabled: true }); +["enabled", "disabled"].forEach((postStreamMode) => { + acceptance( + `Discourse Assign | Assign desktop (glimmer_post_stream_mode = ${postStreamMode})`, + function (needs) { + needs.user({ can_assign: true }); + needs.settings({ + assign_enabled: true, + glimmer_post_stream_mode: postStreamMode, + }); - 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", - }, - ], + 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"); + test("Assigning user to a post", async function (assert) { + await visit("/t/internationalization-localization/280"); - assert - .dom("#post_2 .post-action-menu__assign-post") - .doesNotExist("assign to post button is hidden"); + assert + .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 .post-action-menu__assign-post") - .exists("assign to post button exists"); + await click("#post_2 button.show-more-actions"); + assert + .dom("#post_2 .post-action-menu__assign-post") + .exists("assign to post button exists"); - await click("#post_2 .post-action-menu__assign-post"); - assert.dom(".assign.d-modal").exists("assign modal opens"); + 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"); - assert.true(menu.isExpanded(), "user selector is expanded"); + 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 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(); + 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 }); - }); + 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"); + await fillIn("#assign-modal-note", "a note!"); + await click(".assign.d-modal .btn-primary"); - assert.dom(".assign.d-modal").doesNotExist("assign modal closes"); - }); + 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"); + 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 | Assign Status enabled", function (needs) { - needs.user({ - can_assign: true, - }); - needs.settings({ - 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", - }, - ], + assert.dom(".assign.d-modal").exists("assign modal opens"); + }); + } + ); + + acceptance( + `Discourse Assign | Assign Status enabled (glimmer_post_stream_mode = ${postStreamMode})`, + function (needs) { + needs.user({ + can_assign: true, + }); + needs.settings({ + assign_enabled: true, + enable_assign_status: true, + assign_statuses: "New|In Progress|Done", + glimmer_post_stream_mode: postStreamMode, }); - }); - }); - 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"); + 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", + }, + ], + }); + }); + }); - return response({ success: true }); - }); + 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"); - await visit("/t/internationalization-localization/280"); - await click("#topic-footer-button-assign"); + return response({ success: true }); + }); - assert - .dom(".assign.d-modal #assign-status") - .exists("assign status dropdown exists"); + await visit("/t/internationalization-localization/280"); + await click("#topic-footer-button-assign"); - const statusDropdown = selectKit("#assign-status"); - assert.strictEqual(statusDropdown.header().value(), "New"); + assert + .dom(".assign.d-modal #assign-status") + .exists("assign status dropdown exists"); - await statusDropdown.expand(); - await statusDropdown.selectRowByValue("In Progress"); - assert.strictEqual(statusDropdown.header().value(), "In Progress"); + const statusDropdown = selectKit("#assign-status"); + assert.strictEqual(statusDropdown.header().value(), "New"); - const menu = selectKit(".assign.d-modal .user-chooser"); - await menu.expand(); - await menu.selectRowByIndex(0); + await statusDropdown.expand(); + await statusDropdown.selectRowByValue("In Progress"); + assert.strictEqual(statusDropdown.header().value(), "In Progress"); - await click(".assign.d-modal .btn-primary"); - }); -}); + const menu = selectKit(".assign.d-modal .user-chooser"); + await menu.expand(); + await menu.selectRowByIndex(0); -acceptance("Discourse Assign | Assign Status disabled", function (needs) { - needs.user({ - can_assign: true, - }); - needs.settings({ - assign_enabled: true, - enable_assign_status: false, - }); + await click(".assign.d-modal .btn-primary"); + }); + } + ); + + acceptance( + `Discourse Assign | Assign Status disabled (glimmer_post_stream_mode = ${postStreamMode})`, + function (needs) { + needs.user({ + can_assign: true, + }); + needs.settings({ + assign_enabled: true, + enable_assign_status: false, + glimmer_post_stream_mode: postStreamMode, + }); - 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", - }, - ], + 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"); + 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"); - }); + assert + .dom(".assign.d-modal #assign-status") + .doesNotExist("assign status dropdown doesn't exists"); + }); + } + ); }); // See RemindAssignsFrequencySiteSettings diff --git a/test/javascripts/acceptance/assigned-topic-test.js b/test/javascripts/acceptance/assigned-topic-test.js index 0d65101c..3788ff16 100644 --- a/test/javascripts/acceptance/assigned-topic-test.js +++ b/test/javascripts/acceptance/assigned-topic-test.js @@ -82,171 +82,197 @@ function assignNewUserToTopic(needs) { }); } -acceptance("Discourse Assign | Assigned topic", function (needs) { - needs.user(); - needs.settings({ - assign_enabled: true, - tagging_enabled: true, - assigns_user_url_path: "/", - assigns_public: true, - enable_assign_status: true, - }); - - assignCurrentUserToTopic(needs); - - test("Shows user assignment info", async function (assert) { - updateCurrentUser({ can_assign: true }); - await visit("/t/assignment-topic/44"); - - assert - .dom("#topic-title .assigned-to") - .hasText("eviltrout", "shows assignment in the header"); - assert - .dom("#post_1 .assigned-to") - .hasText( - "Assigned topic to eviltrout#2 to Developers", - "shows assignment and indirect assignments in the first post" - ); - assert.dom("#post_1 .assigned-to svg.d-icon-user-plus").exists(); - assert.dom("#post_1 .assigned-to a[href='/']").exists(); - assert - .dom(".discourse-tags .assigned-to[href='/t/28830'] span") - .hasAttribute("title", "Shark Doododooo", "shows topic assign notes"); - assert - .dom(".discourse-tags .assigned-to[href='/p/2'] span") - .hasAttribute( - "title", - '', - "shows indirect assign notes" - ); - assert - .dom("#topic-footer-dropdown-reassign") - .exists("shows reassign dropdown at the bottom of the topic"); - }); - - test("Shows group assignment info", async function (assert) { - updateCurrentUser({ can_assign: true }); - await visit("/t/assignment-topic/45"); - - assert - .dom("#topic-title .assigned-to") - .hasText("Developers", "shows assignment in the header"); - assert - .dom("#post_1 .assigned-to--group") - .hasText( - "Assigned topic to Developers", - "shows assignment in the first post" - ); - assert.dom("#post_1 .assigned-to svg.d-icon-group-plus").exists(); - assert - .dom("#post_1 .assigned-to a[href='/g/Developers/assigned/everyone']") - .exists(); - assert - .dom("#topic-footer-dropdown-reassign") - .exists("shows reassign dropdown at the bottom of the topic"); - }); - - test("User without assign ability cannot see footer button", async function (assert) { - updateCurrentUser({ can_assign: false, admin: false, moderator: false }); - await visit("/t/assignment-topic/45"); - - assert - .dom("#topic-footer-dropdown-reassign") - .doesNotExist( - "does not show reassign dropdown at the bottom of the topic" - ); - }); - - test("Shows assignment notification", async function (assert) { - updateCurrentUser({ can_assign: true }); - - await visit("/u/eviltrout/notifications"); - - const notification = query( - "section.user-content .user-notifications-list li.notification" - ); - - assert.true( - notification.classList.contains("assigned"), - "with correct assigned class" - ); - - assert.strictEqual( - notification.querySelector("a").title, - i18n("notifications.titles.assigned"), - "with correct title" - ); - assert.strictEqual( - notification.querySelector("svg use").href["baseVal"], - "#user-plus", - "with correct icon" - ); - }); -}); - -acceptance("Discourse Assign | Reassign topic", function (needs) { - needs.user(); - needs.settings({ - assign_enabled: true, - tagging_enabled: true, - assigns_user_url_path: "/", - }); - - assignNewUserToTopic(needs); - - test("Reassign Footer dropdown contains reassign buttons", async function (assert) { - updateCurrentUser({ can_assign: true }); - const menu = selectKit("#topic-footer-dropdown-reassign"); - - await visit("/t/assignment-topic/44"); - await menu.expand(); - - assert.true(menu.rowByValue("unassign").exists()); - assert.true(menu.rowByValue("reassign").exists()); - assert.true(menu.rowByValue("reassign-self").exists()); - }); -}); - -acceptance("Discourse Assign | Reassign topic | mobile", function (needs) { - needs.user(); - needs.mobileView(); - needs.settings({ - assign_enabled: true, - tagging_enabled: true, - assigns_user_url_path: "/", - }); - - assignNewUserToTopic(needs); - - test("Mobile Footer dropdown contains reassign buttons", async function (assert) { - updateCurrentUser({ can_assign: true }); - - await visit("/t/assignment-topic/44"); - await click(".topic-footer-mobile-dropdown-trigger"); - - assert.dom("#topic-footer-button-unassign-mobile").exists(); - assert.dom("#topic-footer-button-reassign-self-mobile").exists(); - assert.dom("#topic-footer-button-reassign-mobile").exists(); - }); -}); - -acceptance("Discourse Assign | Reassign topic conditionals", function (needs) { - needs.user(); - needs.settings({ - assign_enabled: true, - tagging_enabled: true, - assigns_user_url_path: "/", - }); - - assignCurrentUserToTopic(needs); - - test("Reassign Footer dropdown won't display reassign-to-self button when already assigned to current user", async function (assert) { - updateCurrentUser({ can_assign: true }); - const menu = selectKit("#topic-footer-dropdown-reassign"); - - await visit("/t/assignment-topic/44"); - await menu.expand(); - - assert.false(menu.rowByValue("reassign-self").exists()); - }); +["enabled", "disabled"].forEach((postStreamMode) => { + acceptance( + `Discourse Assign | Assigned topic (glimmer_post_stream_mode = ${postStreamMode})`, + function (needs) { + needs.user(); + needs.settings({ + assign_enabled: true, + tagging_enabled: true, + assigns_user_url_path: "/", + assigns_public: true, + enable_assign_status: true, + glimmer_post_stream_mode: postStreamMode, + }); + + assignCurrentUserToTopic(needs); + + test("Shows user assignment info", async function (assert) { + updateCurrentUser({ can_assign: true }); + await visit("/t/assignment-topic/44"); + + assert + .dom("#topic-title .assigned-to") + .hasText("eviltrout", "shows assignment in the header"); + + assert + .dom("#post_1 .assigned-to") + .includesText( + "Assigned topic to eviltrout", + "shows assignment in the first post" + ); + assert + .dom("#post_1 .assigned-to") + .includesText("#2 to Developers", "Also shows indirects assignments"); + assert.dom("#post_1 .assigned-to svg.d-icon-user-plus").exists(); + assert.dom("#post_1 .assigned-to a[href='/']").exists(); + assert + .dom(".discourse-tags .assigned-to[href='/t/28830'] span") + .hasAttribute("title", "Shark Doododooo", "shows topic assign notes"); + assert + .dom(".discourse-tags .assigned-to[href='/p/2'] span") + .hasAttribute( + "title", + '', + "shows indirect assign notes" + ); + assert + .dom("#topic-footer-dropdown-reassign") + .exists("shows reassign dropdown at the bottom of the topic"); + }); + + test("Shows group assignment info", async function (assert) { + updateCurrentUser({ can_assign: true }); + await visit("/t/assignment-topic/45"); + + assert + .dom("#topic-title .assigned-to") + .hasText("Developers", "shows assignment in the header"); + assert + .dom("#post_1 .assigned-to--group") + .hasText( + "Assigned topic to Developers", + "shows assignment in the first post" + ); + assert.dom("#post_1 .assigned-to svg.d-icon-group-plus").exists(); + assert + .dom("#post_1 .assigned-to a[href='/g/Developers/assigned/everyone']") + .exists(); + assert + .dom("#topic-footer-dropdown-reassign") + .exists("shows reassign dropdown at the bottom of the topic"); + }); + + test("User without assign ability cannot see footer button", async function (assert) { + updateCurrentUser({ + can_assign: false, + admin: false, + moderator: false, + }); + await visit("/t/assignment-topic/45"); + + assert + .dom("#topic-footer-dropdown-reassign") + .doesNotExist( + "does not show reassign dropdown at the bottom of the topic" + ); + }); + + test("Shows assignment notification", async function (assert) { + updateCurrentUser({ can_assign: true }); + + await visit("/u/eviltrout/notifications"); + + const notification = query( + "section.user-content .user-notifications-list li.notification" + ); + + assert.true( + notification.classList.contains("assigned"), + "with correct assigned class" + ); + + assert.strictEqual( + notification.querySelector("a").title, + i18n("notifications.titles.assigned"), + "with correct title" + ); + assert.strictEqual( + notification.querySelector("svg use").href["baseVal"], + "#user-plus", + "with correct icon" + ); + }); + } + ); + + acceptance( + `Discourse Assign | Reassign topic (glimmer_post_stream_mode = ${postStreamMode})`, + function (needs) { + needs.user(); + needs.settings({ + assign_enabled: true, + tagging_enabled: true, + assigns_user_url_path: "/", + glimmer_post_stream_mode: postStreamMode, + }); + + assignNewUserToTopic(needs); + + test("Reassign Footer dropdown contains reassign buttons", async function (assert) { + updateCurrentUser({ can_assign: true }); + const menu = selectKit("#topic-footer-dropdown-reassign"); + + await visit("/t/assignment-topic/44"); + await menu.expand(); + + assert.true(menu.rowByValue("unassign").exists()); + assert.true(menu.rowByValue("reassign").exists()); + assert.true(menu.rowByValue("reassign-self").exists()); + }); + } + ); + + acceptance( + `Discourse Assign | Reassign topic | mobile (glimmer_post_stream_mode = ${postStreamMode})`, + function (needs) { + needs.user(); + needs.mobileView(); + needs.settings({ + assign_enabled: true, + tagging_enabled: true, + assigns_user_url_path: "/", + glimmer_post_stream_mode: postStreamMode, + }); + + assignNewUserToTopic(needs); + + test("Mobile Footer dropdown contains reassign buttons", async function (assert) { + updateCurrentUser({ can_assign: true }); + + await visit("/t/assignment-topic/44"); + await click(".topic-footer-mobile-dropdown-trigger"); + + assert.dom("#topic-footer-button-unassign-mobile").exists(); + assert.dom("#topic-footer-button-reassign-self-mobile").exists(); + assert.dom("#topic-footer-button-reassign-mobile").exists(); + }); + } + ); + + acceptance( + `Discourse Assign | Reassign topic conditionals (glimmer_post_stream_mode = ${postStreamMode})`, + function (needs) { + needs.user(); + needs.settings({ + assign_enabled: true, + tagging_enabled: true, + assigns_user_url_path: "/", + glimmer_post_stream_mode: postStreamMode, + }); + + assignCurrentUserToTopic(needs); + + test("Reassign Footer dropdown won't display reassign-to-self button when already assigned to current user", async function (assert) { + updateCurrentUser({ can_assign: true }); + const menu = selectKit("#topic-footer-dropdown-reassign"); + + await visit("/t/assignment-topic/44"); + await menu.expand(); + + assert.false(menu.rowByValue("reassign-self").exists()); + }); + } + ); }); From 456c253c279d9ef5c89ec043c816dcbb6074e1c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Saquetim?= Date: Wed, 30 Apr 2025 12:51:37 -0300 Subject: [PATCH 4/4] Fix failing test because of prioritize_full_name_in_ux --- .../initializers/extend-for-assigns.js | 159 +++--------------- 1 file changed, 23 insertions(+), 136 deletions(-) diff --git a/assets/javascripts/discourse/initializers/extend-for-assigns.js b/assets/javascripts/discourse/initializers/extend-for-assigns.js index 320ddc0b..c02fa37f 100644 --- a/assets/javascripts/discourse/initializers/extend-for-assigns.js +++ b/assets/javascripts/discourse/initializers/extend-for-assigns.js @@ -324,15 +324,6 @@ function initialize(api) { } } - api.addPostSmallActionClassesCallback((post) => { - // TODO (glimmer-post-stream): only check for .action_code once the widget code is removed - const actionCode = post.action_code || post.actionCode; - - if (actionCode.includes("assigned") && !siteSettings.assigns_public) { - return ["private-assign"]; - } - }); - api.addAdvancedSearchOptions( api.getCurrentUser()?.can_assign ? { @@ -384,17 +375,6 @@ function initialize(api) { } ); - api.addPostSmallActionIcon("assigned", "user-plus"); - api.addPostSmallActionIcon("assigned_to_post", "user-plus"); - api.addPostSmallActionIcon("assigned_group", "group-plus"); - api.addPostSmallActionIcon("assigned_group_to_post", "group-plus"); - api.addPostSmallActionIcon("unassigned", "user-xmark"); - api.addPostSmallActionIcon("unassigned_group", "group-times"); - api.addPostSmallActionIcon("unassigned_from_post", "user-xmark"); - api.addPostSmallActionIcon("unassigned_group_from_post", "group-times"); - api.addPostSmallActionIcon("reassigned", "user-plus"); - api.addPostSmallActionIcon("reassigned_group", "group-plus"); - api.addDiscoveryQueryParam("assigned", { replace: true, refreshModel: true }); api.addTagsHtmlCallback((topic, params = {}) => { @@ -487,119 +467,6 @@ function initialize(api) { return result; }); - api.createWidget("assigned-to-post", { - html(attrs) { - 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, - } - ); - }, - }); - - api.createWidget("assigned-to-first-post", { - html(attrs) { - const topic = attrs.topic; - const [assignedToUser, assignedToGroup, indirectlyAssignedTo] = [ - topic.assigned_to_user, - topic.assigned_to_group, - topic.indirectly_assigned_to, - ]; - const assigneeElements = []; - - const assignedHtml = (username, path, type) => { - return `${htmlSafe( - i18n("discourse_assign.assigned_topic_to", { - username, - path, - }) - )}`; - }; - - let displayedName = ""; - if (assignedToUser) { - displayedName = this.siteSettings.prioritize_full_name_in_ux - ? assignedToUser.name || assignedToUser.username - : assignedToUser.username; - - assigneeElements.push( - h( - "span.assignee", - new RawHtml({ - html: assignedHtml( - displayedName, - assignedToUserPath(assignedToUser), - "user" - ), - }) - ) - ); - } - if (assignedToGroup) { - assigneeElements.push( - h( - "span.assignee", - new RawHtml({ - html: assignedHtml( - assignedToGroup.name, - assignedToGroupPath(assignedToGroup), - "group" - ), - }) - ) - ); - } - - if (indirectlyAssignedTo) { - Object.keys(indirectlyAssignedTo).map((postId) => { - const assignee = indirectlyAssignedTo[postId].assigned_to; - const postNumber = indirectlyAssignedTo[postId].post_number; - - displayedName = - this.siteSettings.prioritize_full_name_in_ux || !assignee.username - ? assignee.name || assignee.username - : assignee.username; - - assigneeElements.push( - h("span.assignee", [ - h( - "a", - { - attributes: { - class: "assigned-indirectly", - href: `${topic.url}/${postNumber}`, - }, - }, - i18n("discourse_assign.assign_post_to_multiple", { - post_number: postNumber, - username: displayedName, - }) - ), - ]) - ); - }); - } - - if (!isEmpty(assigneeElements)) { - return h("p.assigned-to", [ - assignedToUser ? iconNode("user-plus") : iconNode("group-plus"), - assignedToUser || assignedToGroup - ? "" - : h("span.assign-text", i18n("discourse_assign.assigned")), - assigneeElements, - ]); - } - }, - }); - api.modifyClass( "model:group", (Superclass) => @@ -686,7 +553,7 @@ function initialize(api) { } ); - customizePost(api); + customizePost(api, siteSettings); api.replaceIcon("notification.assigned", "user-plus"); @@ -710,7 +577,7 @@ function initialize(api) { api.addKeyboardShortcut("g a", "", { path: "/my/activity/assigned" }); } -function customizePost(api) { +function customizePost(api, siteSettings) { api.addTrackedPostProperties("assigned_to_user", "assigned_to_group"); api.modifyClass( @@ -740,6 +607,26 @@ function customizePost(api) { PostAssignmentsDisplay ); + api.addPostSmallActionClassesCallback((post) => { + // TODO (glimmer-post-stream): only check for .action_code once the widget code is removed + const actionCode = post.action_code || post.actionCode; + + if (actionCode.includes("assigned") && !siteSettings.assigns_public) { + return ["private-assign"]; + } + }); + + api.addPostSmallActionIcon("assigned", "user-plus"); + api.addPostSmallActionIcon("assigned_to_post", "user-plus"); + api.addPostSmallActionIcon("assigned_group", "group-plus"); + api.addPostSmallActionIcon("assigned_group_to_post", "group-plus"); + api.addPostSmallActionIcon("unassigned", "user-xmark"); + api.addPostSmallActionIcon("unassigned_group", "group-times"); + api.addPostSmallActionIcon("unassigned_from_post", "user-xmark"); + api.addPostSmallActionIcon("unassigned_group_from_post", "group-times"); + api.addPostSmallActionIcon("reassigned", "user-plus"); + api.addPostSmallActionIcon("reassigned_group", "group-plus"); + withSilencedDeprecations("discourse.post-stream-widget-overrides", () => customizeWidgetPost(api) ); @@ -820,7 +707,7 @@ function customizeWidgetPost(api) { let displayedName = ""; if (assignedToUser) { - displayedName = !this.siteSettings.prioritize_username_in_ux + displayedName = this.siteSettings.prioritize_full_name_in_ux ? assignedToUser.name || assignedToUser.username : assignedToUser.username;