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:
Vinoth Kannan 2023-05-31 19:32:06 +05:30 committed by GitHub
parent c01580298e
commit d4bfd441ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 157 additions and 8 deletions

View File

@ -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}`;

View File

@ -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>

View File

@ -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}}

View File

@ -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 {

View File

@ -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 =

View File

@ -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

View 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

View File

@ -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

View File

@ -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
View 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

View File

@ -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))

View File

@ -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})",
) )

View 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

View 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