mirror of
https://github.com/discourse/discourse.git
synced 2025-06-07 16:54:42 +08:00
FEATURE: additional "related messages" section
This splits out previous message correspondence from suggeted and instead has a dedicated section called "related messages"
This commit is contained in:
@ -0,0 +1,17 @@
|
|||||||
|
import computed from "ember-addons/ember-computed-decorators";
|
||||||
|
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
elementId: "related-messages",
|
||||||
|
classNames: ["suggested-topics"],
|
||||||
|
|
||||||
|
@computed("topic")
|
||||||
|
relatedTitle(topic) {
|
||||||
|
const href = this.currentUser && this.currentUser.pmPath(topic);
|
||||||
|
return href
|
||||||
|
? `<a href="${href}">${iconHTML("envelope", {
|
||||||
|
class: "private-message-glyph"
|
||||||
|
})}</a><span>${I18n.t("related_messages.title")}</span>`
|
||||||
|
: I18n.t("related_messages.title");
|
||||||
|
}
|
||||||
|
});
|
@ -4,6 +4,7 @@ import { iconHTML } from "discourse-common/lib/icon-library";
|
|||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
elementId: "suggested-topics",
|
elementId: "suggested-topics",
|
||||||
|
classNames: ["suggested-topics"],
|
||||||
|
|
||||||
@computed("topic")
|
@computed("topic")
|
||||||
suggestedTitle(topic) {
|
suggestedTitle(topic) {
|
||||||
|
@ -124,6 +124,20 @@ const Topic = RestModel.extend({
|
|||||||
return newTags;
|
return newTags;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@computed("related_messages")
|
||||||
|
relatedMessages(relatedMessages) {
|
||||||
|
if (relatedMessages) {
|
||||||
|
const store = this.store;
|
||||||
|
|
||||||
|
return this.set(
|
||||||
|
"related_messages",
|
||||||
|
relatedMessages.map(st => {
|
||||||
|
return store.createRecord("topic", st);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
@computed("suggested_topics")
|
@computed("suggested_topics")
|
||||||
suggestedTopics(suggestedTopics) {
|
suggestedTopics(suggestedTopics) {
|
||||||
if (suggestedTopics) {
|
if (suggestedTopics) {
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
<h3 class="suggested-topics-title">{{{relatedTitle}}}</h3>
|
||||||
|
<div class="topics">
|
||||||
|
{{basic-topic-list
|
||||||
|
hideCategory="true"
|
||||||
|
showPosters="true"
|
||||||
|
topics=topic.relatedMessages}}
|
||||||
|
</div>
|
@ -1,5 +1,4 @@
|
|||||||
<h3 class="suggested-topics-title">{{{suggestedTitle}}}</h3>
|
<h3 class="suggested-topics-title">{{{suggestedTitle}}}</h3>
|
||||||
|
|
||||||
<div class="topics">
|
<div class="topics">
|
||||||
{{#if topic.isPrivateMessage}}
|
{{#if topic.isPrivateMessage}}
|
||||||
{{basic-topic-list
|
{{basic-topic-list
|
||||||
|
@ -284,6 +284,9 @@
|
|||||||
|
|
||||||
{{plugin-outlet name="topic-above-suggested" args=(hash model=model)}}
|
{{plugin-outlet name="topic-above-suggested" args=(hash model=model)}}
|
||||||
|
|
||||||
|
{{#if model.relatedMessages.length}}
|
||||||
|
{{related-messages topic=model}}
|
||||||
|
{{/if}}
|
||||||
{{#if model.suggestedTopics.length}}
|
{{#if model.suggestedTopics.length}}
|
||||||
{{suggested-topics topic=model}}
|
{{suggested-topics topic=model}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -726,7 +726,7 @@ a.mention-group {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#suggested-topics {
|
.suggested-topics {
|
||||||
.topics {
|
.topics {
|
||||||
padding-bottom: 15px;
|
padding-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
@ -168,31 +168,31 @@ a.badge-category {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Target the .badge-category text, the bullet icon needs to maintain `display: block`
|
// Target the .badge-category text, the bullet icon needs to maintain `display: block`
|
||||||
#suggested-topics h3 .badge-wrapper.bullet span.badge-category,
|
.suggested-topics h3 .badge-wrapper.bullet span.badge-category,
|
||||||
#suggested-topics h3 .badge-wrapper.box span,
|
.suggested-topics h3 .badge-wrapper.box span,
|
||||||
#suggested-topics h3 .badge-wrapper.bar span {
|
.suggested-topics h3 .badge-wrapper.bar span {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
#suggested-topics h3 .badge-wrapper.bullet span.badge-category {
|
.suggested-topics h3 .badge-wrapper.bullet span.badge-category {
|
||||||
// Override vertical-align: text-top from `badges.css.scss`
|
// Override vertical-align: text-top from `badges.css.scss`
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
line-height: $line-height-medium;
|
line-height: $line-height-medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
#suggested-topics h3 .badge-wrapper.bullet,
|
.suggested-topics h3 .badge-wrapper.bullet,
|
||||||
#suggested-topics h3 .badge-wrapper.bullet span.badge-category-parent-bg,
|
.suggested-topics h3 .badge-wrapper.bullet span.badge-category-parent-bg,
|
||||||
#suggested-topics h3 .badge-wrapper.bullet span.badge-category-bg {
|
.suggested-topics h3 .badge-wrapper.bullet span.badge-category-bg {
|
||||||
// Top of bullet aligns with top of line - adjust line height to vertically align bullet.
|
// Top of bullet aligns with top of line - adjust line height to vertically align bullet.
|
||||||
line-height: 0.8;
|
line-height: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
#suggested-topics .badge-wrapper.bullet span.badge-category,
|
.suggested-topics .badge-wrapper.bullet span.badge-category,
|
||||||
#suggested-topics .badge-wrapper.bar span.badge-category {
|
.suggested-topics .badge-wrapper.bar span.badge-category {
|
||||||
max-width: 150px;
|
max-width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#suggested-topics .suggested-topics-title {
|
.suggested-topics .suggested-topics-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
.topic-map,
|
.topic-map,
|
||||||
.post-menu-area.clearfix,
|
.post-menu-area.clearfix,
|
||||||
div#topic-footer-buttons,
|
div#topic-footer-buttons,
|
||||||
div#suggested-topics,
|
div.suggested-topics,
|
||||||
div#progress-topic-wrapper,
|
div#progress-topic-wrapper,
|
||||||
#topic-progress-wrapper,
|
#topic-progress-wrapper,
|
||||||
div.nums,
|
div.nums,
|
||||||
|
@ -447,7 +447,7 @@ nav.post-controls {
|
|||||||
width: 757px;
|
width: 757px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#suggested-topics {
|
.suggested-topics {
|
||||||
clear: left;
|
clear: left;
|
||||||
padding: 20px 0 15px 0;
|
padding: 20px 0 15px 0;
|
||||||
table {
|
table {
|
||||||
@ -459,7 +459,7 @@ nav.post-controls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#suggested-topics .topic-statuses .topic-status {
|
.suggested-topics .topic-statuses .topic-status {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
i {
|
i {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
|
@ -259,7 +259,7 @@ a.reply-to-tab {
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#suggested-topics {
|
.suggested-topics {
|
||||||
clear: left;
|
clear: left;
|
||||||
padding: 20px 0 15px 0;
|
padding: 20px 0 15px 0;
|
||||||
th.views,
|
th.views,
|
||||||
|
@ -1,12 +1,23 @@
|
|||||||
module SuggestedTopicsMixin
|
module SuggestedTopicsMixin
|
||||||
def self.included(klass)
|
def self.included(klass)
|
||||||
|
klass.attributes :related_messages
|
||||||
klass.attributes :suggested_topics
|
klass.attributes :suggested_topics
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def include_related_messages?
|
||||||
|
object.next_page.nil? && object.related_messages&.topics.present?
|
||||||
|
end
|
||||||
|
|
||||||
def include_suggested_topics?
|
def include_suggested_topics?
|
||||||
object.next_page.nil? && object.suggested_topics&.topics.present?
|
object.next_page.nil? && object.suggested_topics&.topics.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def related_messages
|
||||||
|
object.related_messages.topics.map do |t|
|
||||||
|
SuggestedTopicSerializer.new(t, scope: scope, root: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def suggested_topics
|
def suggested_topics
|
||||||
object.suggested_topics.topics.map do |t|
|
object.suggested_topics.topics.map do |t|
|
||||||
SuggestedTopicSerializer.new(t, scope: scope, root: false)
|
SuggestedTopicSerializer.new(t, scope: scope, root: false)
|
||||||
|
@ -252,6 +252,9 @@ en:
|
|||||||
one: "{{count}} character"
|
one: "{{count}} character"
|
||||||
other: "{{count}} characters"
|
other: "{{count}} characters"
|
||||||
|
|
||||||
|
related_messages:
|
||||||
|
title: "Related Messages"
|
||||||
|
|
||||||
suggested_topics:
|
suggested_topics:
|
||||||
title: "Suggested Topics"
|
title: "Suggested Topics"
|
||||||
pm_title: "Suggested Messages"
|
pm_title: "Suggested Messages"
|
||||||
|
@ -135,8 +135,71 @@ class TopicQuery
|
|||||||
(list || Topic).joins("LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = #{@user.id.to_i})")
|
(list || Topic).joins("LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = #{@user.id.to_i})")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_pm_params(topic)
|
||||||
|
if topic.private_message?
|
||||||
|
|
||||||
|
my_group_ids = topic.topic_allowed_groups
|
||||||
|
.joins("
|
||||||
|
LEFT JOIN group_users gu
|
||||||
|
ON topic_allowed_groups.group_id = gu.group_id
|
||||||
|
AND gu.user_id = #{@user.id.to_i}
|
||||||
|
")
|
||||||
|
.where("gu.group_id IS NOT NULL")
|
||||||
|
.pluck(:group_id)
|
||||||
|
|
||||||
|
target_group_ids = topic.topic_allowed_groups.pluck(:group_id)
|
||||||
|
|
||||||
|
target_users = topic
|
||||||
|
.topic_allowed_users
|
||||||
|
|
||||||
|
if my_group_ids.present?
|
||||||
|
|
||||||
|
# strip out users in groups you already belong to
|
||||||
|
target_users = target_users
|
||||||
|
.joins("LEFT JOIN group_users gu ON gu.user_id = topic_allowed_users.user_id AND gu.group_id IN (#{sanitize_sql_array(my_group_ids)})")
|
||||||
|
.where('gu.group_id IS NULL')
|
||||||
|
end
|
||||||
|
|
||||||
|
target_user_ids = target_users
|
||||||
|
.where('NOT topic_allowed_users.user_id = ?', @user.id)
|
||||||
|
.pluck(:user_id)
|
||||||
|
|
||||||
|
{
|
||||||
|
topic: topic,
|
||||||
|
my_group_ids: my_group_ids,
|
||||||
|
target_group_ids: target_group_ids,
|
||||||
|
target_user_ids: target_user_ids
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_related_for(topic, pm_params: nil)
|
||||||
|
return if !topic.private_message?
|
||||||
|
return if @user.blank?
|
||||||
|
return if !SiteSetting.enable_personal_messages?
|
||||||
|
|
||||||
|
builder = SuggestedTopicsBuilder.new(topic)
|
||||||
|
pm_params = pm_params || get_pm_params(topic)
|
||||||
|
|
||||||
|
if pm_params[:my_group_ids].present?
|
||||||
|
builder.add_results(related_messages_group(
|
||||||
|
pm_params.merge(count: [6, builder.results_left].max,
|
||||||
|
exclude: builder.excluded_topic_ids)
|
||||||
|
))
|
||||||
|
else
|
||||||
|
builder.add_results(related_messages_user(
|
||||||
|
pm_params.merge(count: [6, builder.results_left].max,
|
||||||
|
exclude: builder.excluded_topic_ids)
|
||||||
|
))
|
||||||
|
end
|
||||||
|
|
||||||
|
params = { unordered: true }
|
||||||
|
params[:preload_posters] = true
|
||||||
|
create_list(:suggested, params, builder.results)
|
||||||
|
end
|
||||||
|
|
||||||
# Return a list of suggested topics for a topic
|
# Return a list of suggested topics for a topic
|
||||||
def list_suggested_for(topic)
|
def list_suggested_for(topic, pm_params: nil)
|
||||||
|
|
||||||
# Don't suggest messages unless we have a user, and private messages are
|
# Don't suggest messages unless we have a user, and private messages are
|
||||||
# enabled.
|
# enabled.
|
||||||
@ -145,42 +208,7 @@ class TopicQuery
|
|||||||
|
|
||||||
builder = SuggestedTopicsBuilder.new(topic)
|
builder = SuggestedTopicsBuilder.new(topic)
|
||||||
|
|
||||||
pm_params =
|
pm_params = pm_params || get_pm_params(topic)
|
||||||
if topic.private_message?
|
|
||||||
|
|
||||||
my_group_ids = topic.topic_allowed_groups
|
|
||||||
.joins("
|
|
||||||
LEFT JOIN group_users gu
|
|
||||||
ON topic_allowed_groups.group_id = gu.group_id
|
|
||||||
AND gu.user_id = #{@user.id.to_i}
|
|
||||||
")
|
|
||||||
.where("gu.group_id IS NOT NULL")
|
|
||||||
.pluck(:group_id)
|
|
||||||
|
|
||||||
target_group_ids = topic.topic_allowed_groups.pluck(:group_id)
|
|
||||||
|
|
||||||
target_users = topic
|
|
||||||
.topic_allowed_users
|
|
||||||
|
|
||||||
if my_group_ids.present?
|
|
||||||
|
|
||||||
# strip out users in groups you already belong to
|
|
||||||
target_users = target_users
|
|
||||||
.joins("LEFT JOIN group_users gu ON gu.user_id = topic_allowed_users.user_id AND gu.group_id IN (#{sanitize_sql_array(my_group_ids)})")
|
|
||||||
.where('gu.group_id IS NULL')
|
|
||||||
end
|
|
||||||
|
|
||||||
target_user_ids = target_users
|
|
||||||
.where('NOT topic_allowed_users.user_id = ?', @user.id)
|
|
||||||
.pluck(:user_id)
|
|
||||||
|
|
||||||
{
|
|
||||||
topic: topic,
|
|
||||||
my_group_ids: my_group_ids,
|
|
||||||
target_group_ids: target_group_ids,
|
|
||||||
target_user_ids: target_user_ids
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
# When logged in we start with different results
|
# When logged in we start with different results
|
||||||
if @user
|
if @user
|
||||||
@ -194,18 +222,6 @@ class TopicQuery
|
|||||||
pm_params.merge(count: builder.results_left)
|
pm_params.merge(count: builder.results_left)
|
||||||
)) unless builder.full?
|
)) unless builder.full?
|
||||||
|
|
||||||
if pm_params[:my_group_ids].present?
|
|
||||||
builder.add_results(related_messages_group(
|
|
||||||
pm_params.merge(count: [3, builder.results_left].max,
|
|
||||||
exclude: builder.excluded_topic_ids)
|
|
||||||
), :ultra_high)
|
|
||||||
else
|
|
||||||
builder.add_results(related_messages_user(
|
|
||||||
pm_params.merge(count: [3, builder.results_left].max,
|
|
||||||
exclude: builder.excluded_topic_ids)
|
|
||||||
), :ultra_high)
|
|
||||||
end
|
|
||||||
|
|
||||||
else
|
else
|
||||||
builder.add_results(unread_results(topic: topic, per_page: builder.results_left), :high)
|
builder.add_results(unread_results(topic: topic, per_page: builder.results_left), :high)
|
||||||
builder.add_results(new_results(topic: topic, per_page: builder.category_results_left)) unless builder.full?
|
builder.add_results(new_results(topic: topic, per_page: builder.category_results_left)) unless builder.full?
|
||||||
|
@ -402,8 +402,16 @@ class TopicView
|
|||||||
@initial_load
|
@initial_load
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pm_params
|
||||||
|
@pm_params ||= TopicQuery.new(@user).get_pm_params(topic)
|
||||||
|
end
|
||||||
|
|
||||||
def suggested_topics
|
def suggested_topics
|
||||||
@suggested_topics ||= TopicQuery.new(@user).list_suggested_for(topic)
|
@suggested_topics ||= TopicQuery.new(@user).list_suggested_for(topic, pm_params: pm_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def related_messages
|
||||||
|
@related_messages ||= TopicQuery.new(@user).list_related_for(topic, pm_params: pm_params)
|
||||||
end
|
end
|
||||||
|
|
||||||
# This is pending a larger refactor, that allows custom orders
|
# This is pending a larger refactor, that allows custom orders
|
||||||
|
@ -644,7 +644,7 @@ describe TopicQuery do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'suggested_for message do' do
|
context 'list_related_for do' do
|
||||||
|
|
||||||
let(:user) do
|
let(:user) do
|
||||||
Fabricate(:admin)
|
Fabricate(:admin)
|
||||||
@ -679,11 +679,6 @@ describe TopicQuery do
|
|||||||
pm_to_group = create_pm(sender, target_group_names: [group_with_user.name])
|
pm_to_group = create_pm(sender, target_group_names: [group_with_user.name])
|
||||||
pm_to_user = create_pm(sender, target_usernames: [user.username])
|
pm_to_user = create_pm(sender, target_usernames: [user.username])
|
||||||
|
|
||||||
new_pm = create_pm(target_usernames: [user.username])
|
|
||||||
|
|
||||||
unread_pm = create_pm(target_usernames: [user.username])
|
|
||||||
read(user, unread_pm, 0)
|
|
||||||
|
|
||||||
old_unrelated_pm = create_pm(target_usernames: [user.username])
|
old_unrelated_pm = create_pm(target_usernames: [user.username])
|
||||||
read(user, old_unrelated_pm, 1)
|
read(user, old_unrelated_pm, 1)
|
||||||
|
|
||||||
@ -693,17 +688,17 @@ describe TopicQuery do
|
|||||||
related_by_group_pm = create_pm(sender, target_group_names: [group_with_user.name])
|
related_by_group_pm = create_pm(sender, target_group_names: [group_with_user.name])
|
||||||
read(user, related_by_group_pm, 1)
|
read(user, related_by_group_pm, 1)
|
||||||
|
|
||||||
expect(TopicQuery.new(user).list_suggested_for(pm_to_group).topics.map(&:id)).to(
|
expect(TopicQuery.new(user).list_related_for(pm_to_group).topics.map(&:id)).to(
|
||||||
eq([related_by_group_pm.id])
|
eq([related_by_group_pm.id])
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(TopicQuery.new(user).list_suggested_for(pm_to_user).topics.map(&:id)).to(
|
expect(TopicQuery.new(user).list_related_for(pm_to_user).topics.map(&:id)).to(
|
||||||
eq([related_by_user_pm.id, new_pm.id, unread_pm.id])
|
eq([related_by_user_pm.id])
|
||||||
)
|
)
|
||||||
|
|
||||||
SiteSetting.enable_personal_messages = false
|
SiteSetting.enable_personal_messages = false
|
||||||
expect(TopicQuery.new(user).list_suggested_for(pm_to_group)).to be_blank
|
expect(TopicQuery.new(user).list_related_for(pm_to_group)).to be_blank
|
||||||
expect(TopicQuery.new(user).list_suggested_for(pm_to_user)).to be_blank
|
expect(TopicQuery.new(user).list_related_for(pm_to_user)).to be_blank
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user