FEATURE: Show note in tooltip (#327)
This commit is contained in:
parent
1ac2dfbae2
commit
b314882a6b
|
@ -2,6 +2,7 @@ import { renderAvatar } from "discourse/helpers/user-avatar";
|
|||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { iconHTML, iconNode } from "discourse-common/lib/icon-library";
|
||||
import { escapeExpression } from "discourse/lib/utilities";
|
||||
import { h } from "virtual-dom";
|
||||
import { queryRegistry } from "discourse/widgets/widget";
|
||||
import { getOwner } from "discourse-common/lib/get-owner";
|
||||
|
@ -586,10 +587,19 @@ function initialize(api) {
|
|||
api.addDiscoveryQueryParam("assigned", { replace: true, refreshModel: true });
|
||||
|
||||
api.addTagsHtmlCallback((topic, params = {}) => {
|
||||
const [assignedToUser, assignedToGroup] = Object.values(
|
||||
topic.getProperties("assigned_to_user", "assigned_to_group")
|
||||
const [assignedToUser, assignedToGroup, topicNote] = Object.values(
|
||||
topic.getProperties(
|
||||
"assigned_to_user",
|
||||
"assigned_to_group",
|
||||
"assignment_note"
|
||||
)
|
||||
);
|
||||
|
||||
const topicAssignee = {
|
||||
assignee: assignedToUser || assignedToGroup,
|
||||
note: topicNote,
|
||||
};
|
||||
|
||||
let assignedToIndirectly;
|
||||
if (topic.get("indirectly_assigned_to")) {
|
||||
assignedToIndirectly = Object.entries(
|
||||
|
@ -603,17 +613,18 @@ function initialize(api) {
|
|||
}
|
||||
const assignedTo = []
|
||||
.concat(
|
||||
assignedToUser,
|
||||
assignedToGroup,
|
||||
assignedToIndirectly.map((assigned) => assigned.assigned_to)
|
||||
topicAssignee,
|
||||
assignedToIndirectly.map((assigned) => ({
|
||||
assignee: assigned.assigned_to,
|
||||
note: assigned.assignment_note,
|
||||
}))
|
||||
)
|
||||
.filter((element) => element)
|
||||
.flat()
|
||||
.uniqBy((assignee) => assignee.assign_path);
|
||||
.filter(({ assignee }) => assignee)
|
||||
.flat();
|
||||
|
||||
if (assignedTo) {
|
||||
return assignedTo
|
||||
.map((assignee) => {
|
||||
.map(({ assignee, note }) => {
|
||||
let assignedPath;
|
||||
if (assignee.assignedToPostId) {
|
||||
assignedPath = `/p/${assignee.assignedToPostId}`;
|
||||
|
@ -629,7 +640,7 @@ function initialize(api) {
|
|||
: "";
|
||||
return `<${tagName} class="assigned-to discourse-tag simple" ${href}>
|
||||
${icon}
|
||||
<span>${name}</span>
|
||||
<span title="${escapeExpression(note)}">${name}</span>
|
||||
</${tagName}>`;
|
||||
})
|
||||
.join("");
|
||||
|
@ -769,7 +780,7 @@ function initialize(api) {
|
|||
}
|
||||
const target = post || topic;
|
||||
|
||||
target.set("assignment_note", null);
|
||||
target.set("assignment_note", data.assignment_note);
|
||||
if (data.assigned_type === "User") {
|
||||
target.set(
|
||||
"assigned_to_user_id",
|
||||
|
|
|
@ -209,6 +209,8 @@ class ::Assigner
|
|||
action_code[:user] = topic.assignment.present? ? "reassigned" : "assigned"
|
||||
action_code[:group] = topic.assignment.present? ? "reassigned_group" : "assigned_group"
|
||||
|
||||
silent = silent || no_assignee_change?(assign_to)
|
||||
|
||||
@target.assignment&.destroy!
|
||||
|
||||
assignment = @target.create_assignment!(assigned_to_id: assign_to.id, assigned_to_type: type, assigned_by_user_id: @assigned_by.id, topic_id: topic.id, note: note)
|
||||
|
@ -233,7 +235,8 @@ class ::Assigner
|
|||
post_id: post_target? && @target.id,
|
||||
post_number: post_target? && @target.post_number,
|
||||
assigned_type: type,
|
||||
assigned_to: serializer.new(assign_to, scope: Guardian.new, root: false).as_json
|
||||
assigned_to: serializer.new(assign_to, scope: Guardian.new, root: false).as_json,
|
||||
assignment_note: note,
|
||||
},
|
||||
user_ids: allowed_user_ids
|
||||
)
|
||||
|
@ -385,7 +388,8 @@ class ::Assigner
|
|||
topic_id: topic.id,
|
||||
post_id: post_target? && @target.id,
|
||||
post_number: post_target? && @target.post_number,
|
||||
assigned_type: assignment.assigned_to.is_a?(User) ? "User" : "Group"
|
||||
assigned_type: assignment.assigned_to.is_a?(User) ? "User" : "Group",
|
||||
assignment_note: nil,
|
||||
},
|
||||
user_ids: allowed_user_ids
|
||||
)
|
||||
|
@ -429,4 +433,8 @@ class ::Assigner
|
|||
assignment&.note == note
|
||||
end
|
||||
end
|
||||
|
||||
def no_assignee_change?(assignee)
|
||||
@target.assignment&.assigned_to_id == assignee.id
|
||||
end
|
||||
end
|
||||
|
|
|
@ -30,12 +30,13 @@ module DiscourseAssign
|
|||
def self.build_indirectly_assigned_to(post_assignments, topic)
|
||||
post_assignments.map do |post_id, assigned_map|
|
||||
assigned_to = assigned_map[:assigned_to]
|
||||
note = assigned_map[:assignment_note]
|
||||
post_number = assigned_map[:post_number]
|
||||
|
||||
if (assigned_to.is_a?(User))
|
||||
[post_id, { assigned_to: build_assigned_to_user(assigned_to, topic), post_number: post_number }]
|
||||
[post_id, { assigned_to: build_assigned_to_user(assigned_to, topic), post_number: post_number, assignment_note: note }]
|
||||
elsif assigned_to.is_a?(Group)
|
||||
[post_id, { assigned_to: build_assigned_to_group(assigned_to, topic), post_number: post_number }]
|
||||
[post_id, { assigned_to: build_assigned_to_group(assigned_to, topic), post_number: post_number, assignment_note: note }]
|
||||
end
|
||||
end.to_h
|
||||
end
|
||||
|
|
|
@ -430,7 +430,7 @@ after_initialize do
|
|||
add_to_class(:topic, :indirectly_assigned_to) do
|
||||
return @indirectly_assigned_to if defined?(@indirectly_assigned_to)
|
||||
@indirectly_assigned_to = Assignment.where(topic_id: id, target_type: "Post", active: true).includes(:target).inject({}) do |acc, assignment|
|
||||
acc[assignment.target_id] = { assigned_to: assignment.assigned_to, post_number: assignment.target.post_number } if assignment.target
|
||||
acc[assignment.target_id] = { assigned_to: assignment.assigned_to, post_number: assignment.target.post_number, assignment_note: assignment.note } if assignment.target
|
||||
acc
|
||||
end
|
||||
end
|
||||
|
|
|
@ -47,13 +47,17 @@ describe Search do
|
|||
result = Search.execute('in:assigned', guardian: guardian)
|
||||
serializer = GroupedSearchResultSerializer.new(result, scope: guardian)
|
||||
indirectly_assigned_to = serializer.as_json[:topics].find { |topic| topic[:id] == post5.topic.id }[:indirectly_assigned_to]
|
||||
expect(indirectly_assigned_to).to eq(post5.id => { assigned_to: {
|
||||
assign_icon: "user-plus",
|
||||
assign_path: "/u/#{user.username}/activity/assigned",
|
||||
avatar_template: user.avatar_template,
|
||||
name: user.name,
|
||||
username: user.username
|
||||
}, post_number: post5.post_number })
|
||||
expect(indirectly_assigned_to).to eq(post5.id => {
|
||||
assigned_to: {
|
||||
assign_icon: "user-plus",
|
||||
assign_path: "/u/#{user.username}/activity/assigned",
|
||||
avatar_template: user.avatar_template,
|
||||
name: user.name,
|
||||
username: user.username,
|
||||
},
|
||||
post_number: post5.post_number,
|
||||
assignment_note: nil,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -52,7 +52,7 @@ RSpec.describe Assigner do
|
|||
it "publishes topic assignment after assign and unassign" do
|
||||
messages = MessageBus.track_publish('/staff/topic-assignment') do
|
||||
assigner = described_class.new(topic, moderator_2)
|
||||
assigner.assign(moderator)
|
||||
assigner.assign(moderator, note: "tomtom best mom")
|
||||
assigner.unassign
|
||||
end
|
||||
|
||||
|
@ -64,6 +64,7 @@ RSpec.describe Assigner do
|
|||
post_number: false,
|
||||
assigned_type: "User",
|
||||
assigned_to: BasicUserSerializer.new(moderator, scope: Guardian.new, root: false).as_json,
|
||||
assignment_note: "tomtom best mom"
|
||||
})
|
||||
|
||||
expect(messages[1].channel).to eq "/staff/topic-assignment"
|
||||
|
@ -73,6 +74,7 @@ RSpec.describe Assigner do
|
|||
post_id: false,
|
||||
post_number: false,
|
||||
assigned_type: "User",
|
||||
assignment_note: nil
|
||||
})
|
||||
end
|
||||
|
||||
|
|
|
@ -35,4 +35,10 @@ RSpec.describe TopicViewSerializer do
|
|||
serializer = TopicViewSerializer.new(TopicView.new(topic), scope: guardian)
|
||||
expect(serializer.as_json[:topic_view][:assignment_note]).to eq("note me down")
|
||||
end
|
||||
|
||||
it "includes indirectly_assigned_to notes in serializer" do
|
||||
Assigner.new(post, user).assign(user, note: "note me down")
|
||||
serializer = TopicViewSerializer.new(TopicView.new(topic), scope: guardian)
|
||||
expect(serializer.as_json[:topic_view][:indirectly_assigned_to][post.id][:assignment_note]).to eq("note me down")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,12 +20,14 @@ function assignCurrentUserToTopic(needs) {
|
|||
avatar_template:
|
||||
"/letter_avatar/eviltrout/{size}/3_f9720745f5ce6dfc2b5641fca999d934.png",
|
||||
};
|
||||
topic["assignment_note"] = "Shark Doododooo";
|
||||
topic["indirectly_assigned_to"] = {
|
||||
2: {
|
||||
assigned_to: {
|
||||
name: "Developers",
|
||||
},
|
||||
post_number: 2,
|
||||
assignment_note: '<script>alert("xss")</script>',
|
||||
},
|
||||
};
|
||||
return helper.response(topic);
|
||||
|
@ -98,6 +100,16 @@ acceptance("Discourse Assign | Assigned topic", function (needs) {
|
|||
"shows assignment and indirect assignments in the first post"
|
||||
);
|
||||
assert.ok(exists("#post_1 .assigned-to svg.d-icon-user-plus"));
|
||||
assert.equal(
|
||||
query(".discourse-tags .assigned-to[href='/t/28830'] span").title,
|
||||
"Shark Doododooo",
|
||||
"shows topic assign notes"
|
||||
);
|
||||
assert.equal(
|
||||
query(".discourse-tags .assigned-to[href='/p/2'] span").title,
|
||||
'<script>alert("xss")</script>',
|
||||
"shows indirect assign notes"
|
||||
);
|
||||
assert.ok(
|
||||
exists("#topic-footer-dropdown-reassign"),
|
||||
"shows reassign dropdown at the bottom of the topic"
|
||||
|
|
Loading…
Reference in New Issue