mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 05:21:50 +08:00
FEATURE: display PM participant group names in the topics list. (#21677)
After this change, we can view all participant group names on the topic list page. Co-authored-by: Régis Hanol <regis@hanol.fr>
This commit is contained in:
parent
c01580298e
commit
d4bfd441ba
|
@ -3,7 +3,7 @@ import discourseComputed, {
|
||||||
observes,
|
observes,
|
||||||
} from "discourse-common/utils/decorators";
|
} from "discourse-common/utils/decorators";
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import DiscourseURL from "discourse/lib/url";
|
import DiscourseURL, { groupPath } from "discourse/lib/url";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { RUNTIME_OPTIONS } from "discourse-common/lib/raw-handlebars-helpers";
|
import { RUNTIME_OPTIONS } from "discourse-common/lib/raw-handlebars-helpers";
|
||||||
import { alias } from "@ember/object/computed";
|
import { alias } from "@ember/object/computed";
|
||||||
|
@ -115,6 +115,17 @@ export default Component.extend({
|
||||||
nodeClassList.toggle("read", !data.show_indicator);
|
nodeClassList.toggle("read", !data.show_indicator);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@discourseComputed("topic.participant_groups")
|
||||||
|
participantGroups(groupNames) {
|
||||||
|
if (!groupNames) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return groupNames.map((name) => {
|
||||||
|
return { name, url: groupPath(name) };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
@discourseComputed("topic.id")
|
@discourseComputed("topic.id")
|
||||||
unreadIndicatorChannel(topicId) {
|
unreadIndicatorChannel(topicId) {
|
||||||
return `/private-messages/unread-indicator/${topicId}`;
|
return `/private-messages/unread-indicator/${topicId}`;
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<div class="participant-groups" role="list" aria-label="{{i18n 'topic.participant_groups'}}">
|
||||||
|
{{#each groups as |group|}}
|
||||||
|
<a class="user-group trigger-group-card" href="{{group.url}}" data-group-card="{{group.name}}">
|
||||||
|
{{d-icon 'users'}}
|
||||||
|
{{group.name}}
|
||||||
|
</a>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
|
@ -40,6 +40,9 @@
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
{{discourse-tags topic mode="list" tagsForUser=tagsForUser}}
|
{{discourse-tags topic mode="list" tagsForUser=tagsForUser}}
|
||||||
|
{{#if participantGroups}}
|
||||||
|
{{raw "list/participant-groups" groups=participantGroups}}
|
||||||
|
{{/if}}
|
||||||
{{raw "list/action-list" topic=topic postNumbers=topic.liked_post_numbers className="likes" icon="heart"}}
|
{{raw "list/action-list" topic=topic postNumbers=topic.liked_post_numbers className="likes" icon="heart"}}
|
||||||
</div>
|
</div>
|
||||||
{{#if expandPinned}}
|
{{#if expandPinned}}
|
||||||
|
|
|
@ -263,6 +263,19 @@
|
||||||
.discourse-tag.box {
|
.discourse-tag.box {
|
||||||
margin-right: 0.25em;
|
margin-right: 0.25em;
|
||||||
}
|
}
|
||||||
|
.participant-groups {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: var(--font-down-1);
|
||||||
|
|
||||||
|
> a {
|
||||||
|
color: var(--primary-medium);
|
||||||
|
border: solid 1px var(--primary-low);
|
||||||
|
padding: 0 0.3em 0.07em;
|
||||||
|
border-radius: 0.6em;
|
||||||
|
margin-right: 0.25em;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.topic-featured-link {
|
.topic-featured-link {
|
||||||
|
|
|
@ -28,7 +28,7 @@ class Topic < ActiveRecord::Base
|
||||||
def_delegator :notifier, :mute!, :notify_muted!
|
def_delegator :notifier, :mute!, :notify_muted!
|
||||||
def_delegator :notifier, :toggle_mute, :toggle_mute
|
def_delegator :notifier, :toggle_mute, :toggle_mute
|
||||||
|
|
||||||
attr_accessor :allowed_user_ids, :tags_changed, :includes_destination_category
|
attr_accessor :allowed_user_ids, :allowed_group_ids, :tags_changed, :includes_destination_category
|
||||||
|
|
||||||
def self.max_fancy_title_length
|
def self.max_fancy_title_length
|
||||||
400
|
400
|
||||||
|
@ -293,6 +293,7 @@ class Topic < ActiveRecord::Base
|
||||||
|
|
||||||
attr_accessor :posters # TODO: can replace with posters_summary once we remove old list code
|
attr_accessor :posters # TODO: can replace with posters_summary once we remove old list code
|
||||||
attr_accessor :participants
|
attr_accessor :participants
|
||||||
|
attr_accessor :participant_groups
|
||||||
attr_accessor :topic_list
|
attr_accessor :topic_list
|
||||||
attr_accessor :meta_data
|
attr_accessor :meta_data
|
||||||
attr_accessor :include_last_poster
|
attr_accessor :include_last_poster
|
||||||
|
@ -1274,6 +1275,10 @@ class Topic < ActiveRecord::Base
|
||||||
@participants_summary ||= TopicParticipantsSummary.new(self, options).summary
|
@participants_summary ||= TopicParticipantsSummary.new(self, options).summary
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def participant_groups_summary(options = {})
|
||||||
|
@participant_groups_summary ||= TopicParticipantGroupsSummary.new(self, options).summary
|
||||||
|
end
|
||||||
|
|
||||||
def make_banner!(user, bannered_until = nil)
|
def make_banner!(user, bannered_until = nil)
|
||||||
if bannered_until
|
if bannered_until
|
||||||
bannered_until =
|
bannered_until =
|
||||||
|
|
|
@ -97,12 +97,15 @@ class TopicList
|
||||||
|
|
||||||
# Create a lookup for all the user ids we need
|
# Create a lookup for all the user ids we need
|
||||||
user_ids = []
|
user_ids = []
|
||||||
|
group_ids = []
|
||||||
@topics.each do |ft|
|
@topics.each do |ft|
|
||||||
user_ids << ft.user_id << ft.last_post_user_id << ft.featured_user_ids << ft.allowed_user_ids
|
user_ids << ft.user_id << ft.last_post_user_id << ft.featured_user_ids << ft.allowed_user_ids
|
||||||
|
group_ids |= (ft.allowed_group_ids || [])
|
||||||
end
|
end
|
||||||
|
|
||||||
user_ids = TopicList.preload_user_ids(@topics, user_ids, self)
|
user_ids = TopicList.preload_user_ids(@topics, user_ids, self)
|
||||||
user_lookup = UserLookup.new(user_ids)
|
user_lookup = UserLookup.new(user_ids)
|
||||||
|
group_lookup = GroupLookup.new(group_ids)
|
||||||
|
|
||||||
@topics.each do |ft|
|
@topics.each do |ft|
|
||||||
ft.user_data = @topic_lookup[ft.id] if @topic_lookup.present?
|
ft.user_data = @topic_lookup[ft.id] if @topic_lookup.present?
|
||||||
|
@ -120,6 +123,8 @@ class TopicList
|
||||||
ft.posters = ft.posters_summary(user_lookup: user_lookup)
|
ft.posters = ft.posters_summary(user_lookup: user_lookup)
|
||||||
|
|
||||||
ft.participants = ft.participants_summary(user_lookup: user_lookup, user: @current_user)
|
ft.participants = ft.participants_summary(user_lookup: user_lookup, user: @current_user)
|
||||||
|
ft.participant_groups =
|
||||||
|
ft.participant_groups_summary(group_lookup: group_lookup, group: @opts[:group])
|
||||||
ft.topic_list = self
|
ft.topic_list = self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
31
app/models/topic_participant_groups_summary.rb
Normal file
31
app/models/topic_participant_groups_summary.rb
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# This is used on a topic page
|
||||||
|
class TopicParticipantGroupsSummary
|
||||||
|
attr_reader :topic, :options
|
||||||
|
|
||||||
|
def initialize(topic, options = {})
|
||||||
|
@topic = topic
|
||||||
|
@options = options
|
||||||
|
@group = options[:group]
|
||||||
|
end
|
||||||
|
|
||||||
|
def summary
|
||||||
|
group_participants.compact
|
||||||
|
end
|
||||||
|
|
||||||
|
def group_participants
|
||||||
|
return [] if group_ids.blank?
|
||||||
|
group_ids.map { |id| group_lookup[id] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def group_ids
|
||||||
|
ids = topic.allowed_group_ids
|
||||||
|
ids = ids - [@group.id] if @group.present?
|
||||||
|
ids
|
||||||
|
end
|
||||||
|
|
||||||
|
def group_lookup
|
||||||
|
@group_lookup ||= options[:group_lookup] || GroupLookup.new(group_ids)
|
||||||
|
end
|
||||||
|
end
|
|
@ -14,11 +14,16 @@ class TopicListItemSerializer < ListableTopicSerializer
|
||||||
:liked_post_numbers,
|
:liked_post_numbers,
|
||||||
:featured_link,
|
:featured_link,
|
||||||
:featured_link_root_domain,
|
:featured_link_root_domain,
|
||||||
:allowed_user_count
|
:allowed_user_count,
|
||||||
|
:participant_groups
|
||||||
|
|
||||||
has_many :posters, serializer: TopicPosterSerializer, embed: :objects
|
has_many :posters, serializer: TopicPosterSerializer, embed: :objects
|
||||||
has_many :participants, serializer: TopicPosterSerializer, embed: :objects
|
has_many :participants, serializer: TopicPosterSerializer, embed: :objects
|
||||||
|
|
||||||
|
def include_participant_groups?
|
||||||
|
object.private_message?
|
||||||
|
end
|
||||||
|
|
||||||
def posters
|
def posters
|
||||||
object.posters || object.posters_summary || []
|
object.posters || object.posters_summary || []
|
||||||
end
|
end
|
||||||
|
@ -44,6 +49,10 @@ class TopicListItemSerializer < ListableTopicSerializer
|
||||||
object.participants_summary || []
|
object.participants_summary || []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def participant_groups
|
||||||
|
object.participant_groups_summary || []
|
||||||
|
end
|
||||||
|
|
||||||
def include_liked_post_numbers?
|
def include_liked_post_numbers?
|
||||||
include_post_action? :like
|
include_post_action? :like
|
||||||
end
|
end
|
||||||
|
|
|
@ -2968,6 +2968,7 @@ en:
|
||||||
read_more_in_category: "Want to read more? Browse other topics in %{categoryLink} or <a href='%{latestLink}'>view latest topics</a>."
|
read_more_in_category: "Want to read more? Browse other topics in %{categoryLink} or <a href='%{latestLink}'>view latest topics</a>."
|
||||||
read_more: "Want to read more? <a href='%{categoryLink}'>Browse all categories</a> or <a href='%{latestLink}'>view latest topics</a>."
|
read_more: "Want to read more? <a href='%{categoryLink}'>Browse all categories</a> or <a href='%{latestLink}'>view latest topics</a>."
|
||||||
unread_indicator: "No member has read the last post of this topic yet."
|
unread_indicator: "No member has read the last post of this topic yet."
|
||||||
|
participant_groups: "Participant groups"
|
||||||
|
|
||||||
# This string uses the ICU Message Format. See https://meta.discourse.org/t/7035 for translation guidelines.
|
# This string uses the ICU Message Format. See https://meta.discourse.org/t/7035 for translation guidelines.
|
||||||
#
|
#
|
||||||
|
|
18
lib/group_lookup.rb
Normal file
18
lib/group_lookup.rb
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class GroupLookup
|
||||||
|
def initialize(group_ids = [])
|
||||||
|
@group_ids = group_ids.flatten.compact.uniq
|
||||||
|
end
|
||||||
|
|
||||||
|
# Lookup a group by id
|
||||||
|
def [](group_id)
|
||||||
|
group_names[group_id]
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def group_names
|
||||||
|
@group_names ||= Group.where(id: @group_ids).pluck(:id, :name).to_h
|
||||||
|
end
|
||||||
|
end
|
|
@ -498,7 +498,13 @@ class TopicQuery
|
||||||
end
|
end
|
||||||
|
|
||||||
topics.each do |t|
|
topics.each do |t|
|
||||||
t.allowed_user_ids = filter == :private_messages ? t.allowed_users.map { |u| u.id } : []
|
if filter == :private_messages
|
||||||
|
t.allowed_user_ids = t.allowed_users.map { |u| u.id }
|
||||||
|
t.allowed_group_ids = t.allowed_groups.map { |g| g.id }
|
||||||
|
else
|
||||||
|
t.allowed_user_ids = []
|
||||||
|
t.allowed_group_ids = []
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
list = TopicList.new(filter, @user, topics, options.merge(@options))
|
list = TopicList.new(filter, @user, topics, options.merge(@options))
|
||||||
|
|
|
@ -71,7 +71,7 @@ class TopicQuery
|
||||||
list = list.where("gm.id IS NULL")
|
list = list.where("gm.id IS NULL")
|
||||||
publish_read_state = !!group.publish_read_state
|
publish_read_state = !!group.publish_read_state
|
||||||
list = append_read_state(list, group) if publish_read_state
|
list = append_read_state(list, group) if publish_read_state
|
||||||
create_list(:private_messages, { publish_read_state: publish_read_state }, list)
|
create_list(:private_messages, { publish_read_state: publish_read_state, group: group }, list)
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_private_messages_group_archive(user)
|
def list_private_messages_group_archive(user)
|
||||||
|
@ -84,7 +84,7 @@ class TopicQuery
|
||||||
|
|
||||||
publish_read_state = !!group.publish_read_state
|
publish_read_state = !!group.publish_read_state
|
||||||
list = append_read_state(list, group) if publish_read_state
|
list = append_read_state(list, group) if publish_read_state
|
||||||
create_list(:private_messages, { publish_read_state: publish_read_state }, list)
|
create_list(:private_messages, { publish_read_state: publish_read_state, group: group }, list)
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_private_messages_group_new(user)
|
def list_private_messages_group_new(user)
|
||||||
|
@ -92,14 +92,14 @@ class TopicQuery
|
||||||
list = remove_dismissed(list, user)
|
list = remove_dismissed(list, user)
|
||||||
publish_read_state = !!group.publish_read_state
|
publish_read_state = !!group.publish_read_state
|
||||||
list = append_read_state(list, group) if publish_read_state
|
list = append_read_state(list, group) if publish_read_state
|
||||||
create_list(:private_messages, { publish_read_state: publish_read_state }, list)
|
create_list(:private_messages, { publish_read_state: publish_read_state, group: group }, list)
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_private_messages_group_unread(user)
|
def list_private_messages_group_unread(user)
|
||||||
list = filter_private_messages_unread(user, :group)
|
list = filter_private_messages_unread(user, :group)
|
||||||
publish_read_state = !!group.publish_read_state
|
publish_read_state = !!group.publish_read_state
|
||||||
list = append_read_state(list, group) if publish_read_state
|
list = append_read_state(list, group) if publish_read_state
|
||||||
create_list(:private_messages, { publish_read_state: publish_read_state }, list)
|
create_list(:private_messages, { publish_read_state: publish_read_state, group: group }, list)
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_private_messages_warnings(user)
|
def list_private_messages_warnings(user)
|
||||||
|
@ -259,6 +259,7 @@ class TopicQuery
|
||||||
Topic
|
Topic
|
||||||
.private_messages
|
.private_messages
|
||||||
.includes(:allowed_users)
|
.includes(:allowed_users)
|
||||||
|
.includes(:allowed_groups)
|
||||||
.joins(
|
.joins(
|
||||||
"LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = #{user.id.to_i})",
|
"LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = #{user.id.to_i})",
|
||||||
)
|
)
|
||||||
|
|
21
spec/lib/group_lookup_spec.rb
Normal file
21
spec/lib/group_lookup_spec.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec.describe GroupLookup do
|
||||||
|
fab!(:group) { Fabricate(:group) }
|
||||||
|
|
||||||
|
describe "#[]" do
|
||||||
|
before { @group_lookup = GroupLookup.new([group.id, nil]) }
|
||||||
|
|
||||||
|
it "returns nil if group_id does not exists" do
|
||||||
|
expect(@group_lookup[0]).to eq(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns nil if group_id is nil" do
|
||||||
|
expect(@group_lookup[nil]).to eq(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns name if group_id exists" do
|
||||||
|
expect(@group_lookup[group.id]).to eq(group.name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
17
spec/models/topic_participant_groups_summary_spec.rb
Normal file
17
spec/models/topic_participant_groups_summary_spec.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec.describe TopicParticipantGroupsSummary do
|
||||||
|
describe "#summary" do
|
||||||
|
fab!(:group1) { Fabricate(:group) }
|
||||||
|
fab!(:group2) { Fabricate(:group) }
|
||||||
|
fab!(:group3) { Fabricate(:group) }
|
||||||
|
|
||||||
|
let(:topic) { Fabricate(:private_message_topic) }
|
||||||
|
|
||||||
|
it "must contain the name of allowed groups" do
|
||||||
|
topic.allowed_group_ids = [group1.id, group2.id, group3.id]
|
||||||
|
expect(described_class.new(topic, group: group1).summary).to eq([group2.name, group3.name])
|
||||||
|
expect(described_class.new(topic, group: group2).summary).to eq([group1.name, group3.name])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user