FEATURE: Display PMs in assigned activity and differentiate them. (#27)
* FEATURE: Display PMs in assigned activity and differentiate them. * FEATURE: Unassign/Re-assign tasks from the activity view * Remove bulkAssign. Reuse assign/unassign with a service. Change PM icon position * Reuse ListItemDefaults instead of duplicating code. * Conditionally show/hide feature if list items defaults is present in core
This commit is contained in:
parent
f5a19501cc
commit
e3b2e5bdb6
|
@ -1,5 +1,4 @@
|
|||
import showModal from "discourse/lib/show-modal";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { getOwner } from "discourse-common/lib/get-owner";
|
||||
|
||||
export default {
|
||||
shouldRender(args, component) {
|
||||
|
@ -11,22 +10,18 @@ export default {
|
|||
);
|
||||
},
|
||||
|
||||
setupComponent(args, component) {
|
||||
const taskActions = getOwner(this).lookup("service:task-actions");
|
||||
component.set("taskActions", taskActions);
|
||||
},
|
||||
|
||||
actions: {
|
||||
unassign() {
|
||||
this.set("topic.assigned_to_user", null);
|
||||
|
||||
return ajax("/assign/unassign", {
|
||||
type: "PUT",
|
||||
data: { topic_id: this.get("topic.id") }
|
||||
});
|
||||
this.get("taskActions").unassign(this.get("topic.id"));
|
||||
},
|
||||
assign() {
|
||||
showModal("assign-user", {
|
||||
model: {
|
||||
topic: this.topic,
|
||||
username: this.topic.get("assigned_to_user.username")
|
||||
}
|
||||
});
|
||||
this.get("taskActions").assign(this.topic);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -44,7 +44,9 @@ export default Ember.Controller.extend({
|
|||
}
|
||||
})
|
||||
.then(() => {
|
||||
// done
|
||||
if (this.get("model.onSuccess")) {
|
||||
this.get("model.onSuccess")();
|
||||
}
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { ajax } from "discourse/lib/ajax";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import UserTopicsList from "discourse/controllers/user-topics-list";
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
export default UserTopicsList.extend({
|
||||
user: Ember.inject.controller(),
|
||||
taskActions: Ember.inject.service(),
|
||||
|
||||
@computed("model.topics")
|
||||
canUnassignAll(topics) {
|
||||
|
@ -21,10 +23,19 @@ export default Ember.Controller.extend({
|
|||
ajax("/assign/unassign-all", {
|
||||
type: "PUT",
|
||||
data: { user_id: user.get("id") }
|
||||
}).then(() => this.send("unassignedAll"));
|
||||
}).then(() => this.send("changeAssigned"));
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
unassign(topic) {
|
||||
this.get("taskActions")
|
||||
.unassign(topic.get("id"))
|
||||
.then(() => this.send("changeAssigned"));
|
||||
},
|
||||
reassign(topic) {
|
||||
const controller = this.get("taskActions").assign(topic);
|
||||
controller.set("model.onSuccess", () => this.send("changeAssigned"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -8,6 +8,9 @@ import { iconNode } from "discourse-common/lib/icon-library";
|
|||
import { h } from "virtual-dom";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
|
||||
// TODO: This has to be removed when 2.3 becomes the new stable version.
|
||||
import { ListItemDefaults } from "discourse/components/topic-list-item";
|
||||
|
||||
const ACTION_ID = "assign";
|
||||
|
||||
function modifySelectKit(api) {
|
||||
|
@ -138,9 +141,18 @@ function initialize(api) {
|
|||
const assignedTo = topic.get("assigned_to_user.username");
|
||||
if (assignedTo) {
|
||||
const assignedPath = topic.get("assignedToUserPath");
|
||||
return `<a data-auto-route='true' class='assigned-to discourse-tag simple' href='${assignedPath}'>${iconHTML(
|
||||
let assignLabels = `<a data-auto-route='true' class='assigned-to discourse-tag simple' href='${assignedPath}'>${iconHTML(
|
||||
"user-plus"
|
||||
)}${assignedTo}</a>`;
|
||||
|
||||
if (
|
||||
ListItemDefaults === undefined &&
|
||||
topic.get("archetype") === "private_message"
|
||||
) {
|
||||
assignLabels += `<div>${iconHTML("envelope")} Message</div>`;
|
||||
}
|
||||
|
||||
return assignLabels;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import UserTopicListRoute from "discourse/routes/user-topic-list";
|
||||
import { ListItemDefaults } from "discourse/components/topic-list-item";
|
||||
|
||||
export default UserTopicListRoute.extend({
|
||||
userActionType: 16,
|
||||
|
@ -6,9 +7,10 @@ export default UserTopicListRoute.extend({
|
|||
|
||||
model() {
|
||||
return this.store.findFiltered("topicList", {
|
||||
filter: "latest",
|
||||
filter: `topics/messages-assigned/${this.modelFor("user").get(
|
||||
"username_lower"
|
||||
)}`,
|
||||
params: {
|
||||
assigned: this.modelFor("user").get("username_lower"),
|
||||
// core is a bit odd here and is not sending an array, should be fixed
|
||||
exclude_category_ids: [-1]
|
||||
}
|
||||
|
@ -16,8 +18,12 @@ export default UserTopicListRoute.extend({
|
|||
},
|
||||
|
||||
renderTemplate() {
|
||||
// TODO: This has to be removed when 2.3 becomes the new stable version.
|
||||
const template = ListItemDefaults
|
||||
? "user-assigned-topics"
|
||||
: "user-topics-list";
|
||||
this.render("user-activity-assigned");
|
||||
this.render("user-topics-list", { into: "user-activity-assigned" });
|
||||
this.render(template, { into: "user-activity-assigned" });
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
|
@ -26,7 +32,7 @@ export default UserTopicListRoute.extend({
|
|||
},
|
||||
|
||||
actions: {
|
||||
unassignedAll() {
|
||||
changeAssigned() {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box";
|
||||
|
||||
export default DropdownSelectBoxComponent.extend({
|
||||
classNames: ["assign-actions-dropdown"],
|
||||
headerIcon: null,
|
||||
title: "...",
|
||||
allowInitialValueMutation: false,
|
||||
showFullTitle: true,
|
||||
|
||||
computeContent() {
|
||||
return [
|
||||
{
|
||||
id: "unassign",
|
||||
icon: "user-times",
|
||||
name: I18n.t("discourse_assign.unassign.title"),
|
||||
description: I18n.t("discourse_assign.unassign.help")
|
||||
},
|
||||
{
|
||||
id: "reassign",
|
||||
icon: "users",
|
||||
name: I18n.t("discourse_assign.reassign.title"),
|
||||
description: I18n.t("discourse_assign.reassign.help")
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
actions: {
|
||||
onSelect(id) {
|
||||
switch (id) {
|
||||
case "unassign":
|
||||
this.unassign(this.get("topic"), this.get("user"));
|
||||
break;
|
||||
case "reassign":
|
||||
this.reassign(this.get("topic"), this.get("user"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { ListItemDefaults } from "discourse/components/topic-list-item";
|
||||
|
||||
const privateMessageHelper = {
|
||||
@computed("topic.archetype")
|
||||
isPrivateMessage(archetype) {
|
||||
return archetype === "private_message";
|
||||
}
|
||||
};
|
||||
|
||||
export default Ember.Component.extend(ListItemDefaults, privateMessageHelper);
|
|
@ -0,0 +1,3 @@
|
|||
import TopicList from "discourse/components/topic-list";
|
||||
|
||||
export default TopicList;
|
|
@ -0,0 +1,3 @@
|
|||
import BasicTopicList from "discourse/components/basic-topic-list";
|
||||
|
||||
export default BasicTopicList;
|
|
@ -0,0 +1,20 @@
|
|||
import { ajax } from "discourse/lib/ajax";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
export default Ember.Service.extend({
|
||||
unassign(topicId) {
|
||||
return ajax("/assign/unassign", {
|
||||
type: "PUT",
|
||||
data: { topic_id: topicId }
|
||||
});
|
||||
},
|
||||
|
||||
assign(topic) {
|
||||
return showModal("assign-user", {
|
||||
model: {
|
||||
topic: topic,
|
||||
username: topic.get("assigned_to_user.username")
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,50 @@
|
|||
{{!--
|
||||
The `~` syntax strip spaces between the elements, making it produce
|
||||
`<a class=topic-post-badges>Some text</a><span class=topic-post-badges>`,
|
||||
with no space between them.
|
||||
This causes the topic-post-badge to be considered the same word as "text"
|
||||
at the end of the link, preventing it from line wrapping onto its own line.
|
||||
--}}
|
||||
<td class='main-link clearfix' colspan="1">
|
||||
<span class='link-top-line'>
|
||||
{{~raw "topic-status" topic=topic}}
|
||||
{{~#if isPrivateMessage}}
|
||||
{{~d-icon "envelope" class="private-message-icon"}}
|
||||
{{~/if}}
|
||||
{{~topic-link topic class="raw-link raw-topic-link"}}
|
||||
{{~#if topic.featured_link}}
|
||||
{{~topic-featured-link topic}}
|
||||
{{~/if}}
|
||||
{{~#if showTopicPostBadges}}
|
||||
{{~raw "topic-post-badges" unread=topic.unread newPosts=topic.displayNewPosts unseen=topic.unseen url=topic.lastUnreadUrl newDotText=newDotText}}
|
||||
{{~/if}}
|
||||
</span>
|
||||
<div class="link-bottom-line">
|
||||
{{#unless hideCategory}}
|
||||
{{#unless topic.isPinnedUncategorized}}
|
||||
{{category-link topic.category}}
|
||||
{{/unless}}
|
||||
{{/unless}}
|
||||
{{discourse-tags topic mode="list" tagsForUser=tagsForUser}}
|
||||
{{raw "list/action-list" topic=topic postNumbers=topic.liked_post_numbers className="likes" icon="heart"}}
|
||||
</div>
|
||||
{{#if expandPinned}}
|
||||
{{raw "list/topic-excerpt" topic=topic}}
|
||||
{{/if}}
|
||||
</td>
|
||||
|
||||
{{#if showPosters}}
|
||||
{{raw "list/posters-column" posters=topic.featuredUsers}}
|
||||
{{/if}}
|
||||
|
||||
{{raw "list/posts-count-column" topic=topic}}
|
||||
|
||||
<td class="num views {{topic.viewsHeat}}">{{number topic.views numberKey="views_long"}}</td>
|
||||
{{raw "list/activity-column" topic=topic class="num" tagName="td"}}
|
||||
|
||||
<td>
|
||||
{{assign-actions-dropdown topic=topic
|
||||
user=username
|
||||
unassign=unassign
|
||||
reassign=reassign}}
|
||||
</td>
|
|
@ -0,0 +1,34 @@
|
|||
{{#unless skipHeader}}
|
||||
<thead>
|
||||
{{raw "topic-list-header"
|
||||
toggleInTitle=toggleInTitle
|
||||
hideCategory=hideCategory
|
||||
showPosters=showPosters
|
||||
showLikes=showLikes
|
||||
showOpLikes=showOpLikes
|
||||
order=order
|
||||
ascending=ascending
|
||||
sortable=sortable
|
||||
listTitle=listTitle
|
||||
bulkSelectEnabled=bulkSelectEnabled}}
|
||||
</thead>
|
||||
{{/unless}}
|
||||
|
||||
<tbody>
|
||||
{{#each filteredTopics as |topic|}}
|
||||
{{assigned-topic-list-item topic=topic
|
||||
showTopicPostBadges=showTopicPostBadges
|
||||
hideCategory=hideCategory
|
||||
showPosters=showPosters
|
||||
showLikes=showLikes
|
||||
showOpLikes=showOpLikes
|
||||
expandGloballyPinned=expandGloballyPinned
|
||||
expandAllPinned=expandAllPinned
|
||||
lastVisitedTopic=lastVisitedTopic
|
||||
selected=selected
|
||||
tagsForUser=tagsForUser
|
||||
unassign=unassign
|
||||
reassign=reassign}}
|
||||
{{raw "list/visited-line" lastVisitedTopic=lastVisitedTopic topic=topic}}
|
||||
{{/each}}
|
||||
</tbody>
|
|
@ -0,0 +1,29 @@
|
|||
{{#conditional-loading-spinner condition=loading}}
|
||||
{{#if hasIncoming}}
|
||||
<div class="show-mores">
|
||||
<div class='alert alert-info clickable' {{action showInserted}}>
|
||||
<a tabindex="0" href="" {{action showInserted}}>
|
||||
{{count-i18n key="topic_count_" suffix="latest" count=incomingCount}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if topics}}
|
||||
{{assigned-topic-list showPosters=showPosters
|
||||
hideCategory=hideCategory
|
||||
topics=topics
|
||||
expandExcerpts=expandExcerpts
|
||||
selected=selected
|
||||
skipHeader=skipHeader
|
||||
tagsForUser=tagsForUser
|
||||
unassign=unassign
|
||||
reassign=reassign}}
|
||||
{{else}}
|
||||
{{#unless loadingMore}}
|
||||
<div class='alert alert-info'>
|
||||
{{i18n 'choose_topic.none_found'}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
{{/conditional-loading-spinner}}
|
|
@ -1,11 +1,4 @@
|
|||
<div class='assign-controls'>
|
||||
{{#if model.topic_list.assigned_messages_count}}
|
||||
{{#link-to 'userPrivateMessages.assigned' user.model class="btn btn-default assign-messages-assigned"}}
|
||||
{{d-icon "envelope" class="glyph"}}
|
||||
{{i18n 'user.messages.assigned_title' count=model.topic_list.assigned_messages_count}}
|
||||
{{/link-to}}
|
||||
{{/if}}
|
||||
|
||||
{{#if canUnassignAll}}
|
||||
{{d-button
|
||||
action=(action "unassignAll")
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
{{#load-more class="paginated-topics-list" selector=".paginated-topics-list .topic-list tr" action=(action "loadMore")}}
|
||||
{{basic-assigned-topic-list topicList=model
|
||||
hideCategory=hideCategory
|
||||
showPosters=showPosters
|
||||
bulkSelectEnabled=bulkSelectEnabled
|
||||
selected=selected
|
||||
hasIncoming=hasIncoming
|
||||
incomingCount=incomingCount
|
||||
showInserted=(action "showInserted")
|
||||
tagsForUser=tagsForUser
|
||||
unassign=(action 'unassign')
|
||||
reassign=(action 'reassign')}}
|
||||
|
||||
{{conditional-loading-spinner condition=model.loadingMore}}
|
||||
{{/load-more}}
|
||||
|
||||
|
|
@ -74,3 +74,7 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.private-message-icon {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,9 @@ en:
|
|||
assign:
|
||||
title: "Assign"
|
||||
help: "Assign Topic to User"
|
||||
reassign:
|
||||
title: "Re-assign"
|
||||
help: "Re-assign Topic to a different user"
|
||||
assign_modal:
|
||||
title: "Assign Topic"
|
||||
description: "Enter the username of the person you'd like to assign this topic"
|
||||
|
|
12
plugin.rb
12
plugin.rb
|
@ -18,6 +18,7 @@ load File.expand_path('../lib/discourse_assign/helpers.rb', __FILE__)
|
|||
Discourse::Application.routes.append do
|
||||
mount ::DiscourseAssign::Engine, at: "/assign"
|
||||
get "topics/private-messages-assigned/:username" => "list#private_messages_assigned", as: "topics_private_messages_assigned", constraints: { username: /[\w.\-]+?/ }
|
||||
get "topics/messages-assigned/:username" => "list#messages_assigned", as: "topics_messages_assigned", constraints: { username: /[\w.\-]+?/ }
|
||||
end
|
||||
|
||||
after_initialize do
|
||||
|
@ -139,6 +140,17 @@ after_initialize do
|
|||
require_dependency 'list_controller'
|
||||
class ::ListController
|
||||
generate_message_route(:private_messages_assigned)
|
||||
generate_message_route(:messages_assigned)
|
||||
end
|
||||
|
||||
add_to_class(:topic_query, :list_messages_assigned) do |user|
|
||||
list = joined_topic_user.where("
|
||||
topics.id IN (
|
||||
SELECT topic_id FROM topic_custom_fields
|
||||
WHERE name = 'assigned_to_id'
|
||||
AND value = ?)
|
||||
", user.id.to_s)
|
||||
create_list(:assigned, {}, list)
|
||||
end
|
||||
|
||||
add_to_class(:topic_query, :list_private_messages_assigned) do |user|
|
||||
|
|
|
@ -1,10 +1,36 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe TopicQuery do
|
||||
describe '#list_private_messages_assigned' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:user2) { Fabricate(:user) }
|
||||
before do
|
||||
SiteSetting.assign_enabled = true
|
||||
end
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:user2) { Fabricate(:user) }
|
||||
|
||||
describe '#list_messages_assigned' do
|
||||
before do
|
||||
@private_message = Fabricate(:private_message_topic, user: user)
|
||||
@topic = Fabricate(:topic, user: user)
|
||||
|
||||
assign_to(@private_message, user)
|
||||
assign_to(@topic, user)
|
||||
end
|
||||
|
||||
it 'Includes topics and PMs assigned to user' do
|
||||
assigned_messages = TopicQuery.new(user).list_messages_assigned(user).topics
|
||||
|
||||
expect(assigned_messages).to contain_exactly(@private_message, @topic)
|
||||
end
|
||||
|
||||
it 'Excludes topics and PMs not assigned to user' do
|
||||
assigned_messages = TopicQuery.new(user2).list_messages_assigned(user2).topics
|
||||
|
||||
expect(assigned_messages).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe '#list_private_messages_assigned' do
|
||||
let(:user_topic) do
|
||||
topic = Fabricate(:private_message_topic,
|
||||
topic_allowed_users: [
|
||||
|
@ -24,10 +50,7 @@ describe TopicQuery do
|
|||
Fabricate.build(:topic_allowed_user, user: user2)
|
||||
],
|
||||
)
|
||||
|
||||
topic.posts << Fabricate(:post)
|
||||
TopicAssigner.new(topic, user).assign(user)
|
||||
topic
|
||||
assign_to(topic, user)
|
||||
end
|
||||
|
||||
let(:group) { Fabricate(:group).add(user) }
|
||||
|
@ -42,13 +65,10 @@ describe TopicQuery do
|
|||
],
|
||||
)
|
||||
|
||||
topic.posts << Fabricate(:post)
|
||||
TopicAssigner.new(topic, user).assign(user)
|
||||
topic
|
||||
assign_to(topic, user)
|
||||
end
|
||||
|
||||
before do
|
||||
SiteSetting.assign_enabled = true
|
||||
user_topic
|
||||
assigned_topic
|
||||
group_assigned_topic
|
||||
|
@ -78,4 +98,11 @@ describe TopicQuery do
|
|||
).to contain_exactly(assigned_topic, group_assigned_topic)
|
||||
end
|
||||
end
|
||||
|
||||
def assign_to(topic, user)
|
||||
topic.tap do |t|
|
||||
t.posts << Fabricate(:post)
|
||||
TopicAssigner.new(t, user).assign(user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue