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,
|
||||
} from "discourse-common/utils/decorators";
|
||||
import Component from "@ember/component";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import DiscourseURL, { groupPath } from "discourse/lib/url";
|
||||
import I18n from "I18n";
|
||||
import { RUNTIME_OPTIONS } from "discourse-common/lib/raw-handlebars-helpers";
|
||||
import { alias } from "@ember/object/computed";
|
||||
|
@ -115,6 +115,17 @@ export default Component.extend({
|
|||
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")
|
||||
unreadIndicatorChannel(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}}
|
||||
{{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"}}
|
||||
</div>
|
||||
{{#if expandPinned}}
|
||||
|
|
|
@ -263,6 +263,19 @@
|
|||
.discourse-tag.box {
|
||||
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 {
|
||||
|
|
|
@ -28,7 +28,7 @@ class Topic < ActiveRecord::Base
|
|||
def_delegator :notifier, :mute!, :notify_muted!
|
||||
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
|
||||
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 :participants
|
||||
attr_accessor :participant_groups
|
||||
attr_accessor :topic_list
|
||||
attr_accessor :meta_data
|
||||
attr_accessor :include_last_poster
|
||||
|
@ -1274,6 +1275,10 @@ class Topic < ActiveRecord::Base
|
|||
@participants_summary ||= TopicParticipantsSummary.new(self, options).summary
|
||||
end
|
||||
|
||||
def participant_groups_summary(options = {})
|
||||
@participant_groups_summary ||= TopicParticipantGroupsSummary.new(self, options).summary
|
||||
end
|
||||
|
||||
def make_banner!(user, bannered_until = nil)
|
||||
if bannered_until
|
||||
bannered_until =
|
||||
|
|
|
@ -97,12 +97,15 @@ class TopicList
|
|||
|
||||
# Create a lookup for all the user ids we need
|
||||
user_ids = []
|
||||
group_ids = []
|
||||
@topics.each do |ft|
|
||||
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
|
||||
|
||||
user_ids = TopicList.preload_user_ids(@topics, user_ids, self)
|
||||
user_lookup = UserLookup.new(user_ids)
|
||||
group_lookup = GroupLookup.new(group_ids)
|
||||
|
||||
@topics.each do |ft|
|
||||
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.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
|
||||
end
|
||||
|
||||
|
|
|
@ -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,
|
||||
:featured_link,
|
||||
:featured_link_root_domain,
|
||||
:allowed_user_count
|
||||
:allowed_user_count,
|
||||
:participant_groups
|
||||
|
||||
has_many :posters, serializer: TopicPosterSerializer, embed: :objects
|
||||
has_many :participants, serializer: TopicPosterSerializer, embed: :objects
|
||||
|
||||
def include_participant_groups?
|
||||
object.private_message?
|
||||
end
|
||||
|
||||
def posters
|
||||
object.posters || object.posters_summary || []
|
||||
end
|
||||
|
@ -44,6 +49,10 @@ class TopicListItemSerializer < ListableTopicSerializer
|
|||
object.participants_summary || []
|
||||
end
|
||||
|
||||
def participant_groups
|
||||
object.participant_groups_summary || []
|
||||
end
|
||||
|
||||
def include_liked_post_numbers?
|
||||
include_post_action? :like
|
||||
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: "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."
|
||||
participant_groups: "Participant groups"
|
||||
|
||||
# This string uses the ICU Message Format. See https://meta.discourse.org/t/7035 for translation guidelines.
|
||||
#
|
||||
|
|
|
@ -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
|
||||
|
||||
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
|
||||
|
||||
list = TopicList.new(filter, @user, topics, options.merge(@options))
|
||||
|
|
|
@ -71,7 +71,7 @@ class TopicQuery
|
|||
list = list.where("gm.id IS NULL")
|
||||
publish_read_state = !!group.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
|
||||
|
||||
def list_private_messages_group_archive(user)
|
||||
|
@ -84,7 +84,7 @@ class TopicQuery
|
|||
|
||||
publish_read_state = !!group.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
|
||||
|
||||
def list_private_messages_group_new(user)
|
||||
|
@ -92,14 +92,14 @@ class TopicQuery
|
|||
list = remove_dismissed(list, user)
|
||||
publish_read_state = !!group.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
|
||||
|
||||
def list_private_messages_group_unread(user)
|
||||
list = filter_private_messages_unread(user, :group)
|
||||
publish_read_state = !!group.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
|
||||
|
||||
def list_private_messages_warnings(user)
|
||||
|
@ -259,6 +259,7 @@ class TopicQuery
|
|||
Topic
|
||||
.private_messages
|
||||
.includes(:allowed_users)
|
||||
.includes(:allowed_groups)
|
||||
.joins(
|
||||
"LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = #{user.id.to_i})",
|
||||
)
|
||||
|
|
|
@ -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
|
|
@ -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