diff --git a/app/assets/javascripts/discourse/controllers/user_topics_list_controller.js b/app/assets/javascripts/discourse/controllers/user_topics_list_controller.js
index 0d19c9925ba..98be7c7b295 100644
--- a/app/assets/javascripts/discourse/controllers/user_topics_list_controller.js
+++ b/app/assets/javascripts/discourse/controllers/user_topics_list_controller.js
@@ -8,6 +8,7 @@
**/
Discourse.UserTopicsListController = Discourse.ObjectController.extend({
hideCategory: false,
+ showParticipants: false,
actions: {
loadMore: function() {
diff --git a/app/assets/javascripts/discourse/models/topic_list.js b/app/assets/javascripts/discourse/models/topic_list.js
index dbbaa112f82..5b1647d92aa 100644
--- a/app/assets/javascripts/discourse/models/topic_list.js
+++ b/app/assets/javascripts/discourse/models/topic_list.js
@@ -163,6 +163,11 @@ Discourse.TopicList.reopenClass({
t.posters.forEach(function(p) {
p.user = users[p.user_id];
});
+ if (t.participants) {
+ t.participants.forEach(function(p) {
+ p.user = users[p.user_id];
+ });
+ }
return Discourse.Topic.create(t);
});
},
diff --git a/app/assets/javascripts/discourse/routes/user_topic_list_routes.js b/app/assets/javascripts/discourse/routes/user_topic_list_routes.js
index 47c1c8b38e6..0b3a5f1c34b 100644
--- a/app/assets/javascripts/discourse/routes/user_topic_list_routes.js
+++ b/app/assets/javascripts/discourse/routes/user_topic_list_routes.js
@@ -8,7 +8,8 @@ Discourse.UserTopicListRoute = Discourse.Route.extend({
this.controllerFor('user_activity').set('userActionType', this.get('userActionType'));
this.controllerFor('user_topics_list').setProperties({
model: model,
- hideCategory: false
+ hideCategory: false,
+ showParticipants: false
});
}
});
@@ -23,7 +24,10 @@ function createPMRoute(viewName, path) {
setupController: function() {
this._super.apply(this, arguments);
- this.controllerFor('user_topics_list').set('hideCategory', true);
+ this.controllerFor('user_topics_list').setProperties({
+ hideCategory: true,
+ showParticipants: true
+ });
this.controllerFor('user').setProperties({
pmView: viewName,
indexStream: false
diff --git a/app/assets/javascripts/discourse/templates/components/basic-topic-list.js.handlebars b/app/assets/javascripts/discourse/templates/components/basic-topic-list.js.handlebars
index 9be1e407e8c..55c562edabb 100644
--- a/app/assets/javascripts/discourse/templates/components/basic-topic-list.js.handlebars
+++ b/app/assets/javascripts/discourse/templates/components/basic-topic-list.js.handlebars
@@ -3,26 +3,18 @@
- {{#sortable-heading sortBy="default"}}
- {{i18n topic.title}}
- {{/sortable-heading}}
+ {{i18n topic.title}} |
{{#unless controller.hideCategory}}
- {{#sortable-heading sortBy="category"}}
- {{i18n category_title}}
- {{/sortable-heading}}
+ {{i18n category_title}} |
{{/unless}}
- {{#sortable-heading sortBy="posts" number=true}}
- {{i18n posts}}
- {{/sortable-heading}}
- {{#sortable-heading sortBy="likes" number=true}}
- {{i18n likes}}
- {{/sortable-heading}}
- {{#sortable-heading sortBy="views" number=true}}
- {{i18n views}}
- {{/sortable-heading}}
- {{#sortable-heading sortBy="activity" number=true colspan="2"}}
- {{i18n activity}}
- {{/sortable-heading}}
+ {{i18n posts}} |
+ {{#if controller.showParticipants}}
+ {{i18n users}} |
+ {{else}}
+ {{i18n likes}} |
+ {{/if}}
+ {{i18n views}} |
+ {{i18n activity}} |
@@ -44,20 +36,33 @@
{{#unless controller.hideCategory}}
-
- {{categoryLink topic.category showParent=true}}
- |
+
+ {{categoryLink topic.category showParent=true}}
+ |
{{/unless}}
- {{number topic.posts_count numberKey="posts_long"}} |
-
-
- {{#if topic.like_count}}
- {{unbound topic.like_count}}
- {{/if}}
+ |
+ {{number topic.posts_count numberKey="posts_long"}}
+ |
+
+ {{#if controller.showParticipants}}
+
+ {{#each topic.participants}}
+
+ {{/each}}
+ |
+ {{else}}
+
+ {{#if topic.like_count}}
+ {{unbound topic.like_count}}
+ {{/if}}
+ |
+ {{/if}}
+
+
+ {{number topic.views numberKey="views_long"}}
|
- {{number topic.views numberKey="views_long"}} |
{{#if topic.bumped}}
{{unboundAge topic.created_at}}
@@ -78,9 +83,11 @@
|
{{else}}
- {{i18n choose_topic.none_found}}
+ {{i18n choose_topic.none_found}}
{{/if}}
{{else}}
- {{i18n loading}}
+
+ {{i18n loading}}
+
{{/if}}
diff --git a/app/assets/javascripts/discourse/templates/list/user_topics_list.js.handlebars b/app/assets/javascripts/discourse/templates/list/user_topics_list.js.handlebars
index 05b91a64780..44836b7c277 100644
--- a/app/assets/javascripts/discourse/templates/list/user_topics_list.js.handlebars
+++ b/app/assets/javascripts/discourse/templates/list/user_topics_list.js.handlebars
@@ -1 +1 @@
-{{basic-topic-list topicList=model hideCategory=hideCategory}}
+{{basic-topic-list topicList=model hideCategory=hideCategory showParticipants=showParticipants}}
diff --git a/app/assets/javascripts/discourse/templates/mobile/components/basic-topic-list.js.handlebars b/app/assets/javascripts/discourse/templates/mobile/components/basic-topic-list.js.handlebars
index c1b1acfedb9..c84bedb98cb 100644
--- a/app/assets/javascripts/discourse/templates/mobile/components/basic-topic-list.js.handlebars
+++ b/app/assets/javascripts/discourse/templates/mobile/components/basic-topic-list.js.handlebars
@@ -29,9 +29,6 @@
{{/if}}
-
- {{categoryLink category showParent=true}}
-
+ {{#unless controller.hideCategory}}
+
+ {{categoryLink category showParent=true}}
+
+ {{/unless}}
+ {{#if controller.showParticipants}}
+
+ {{#each topic.participants}}
+
+ {{/each}}
+
+ {{/if}}
diff --git a/app/assets/stylesheets/desktop/topic-list.scss b/app/assets/stylesheets/desktop/topic-list.scss
index 4ad1ec802cc..2ceb8179dd8 100644
--- a/app/assets/stylesheets/desktop/topic-list.scss
+++ b/app/assets/stylesheets/desktop/topic-list.scss
@@ -183,6 +183,11 @@
}
.posters {
min-width: 150px;
+ }
+ .participants {
+ min-width: 85px;
+ }
+ .posters, .participants {
> a {
float: left;
margin-right: 4px;
diff --git a/app/models/topic.rb b/app/models/topic.rb
index fdb723be9bc..7afb405ca4e 100644
--- a/app/models/topic.rb
+++ b/app/models/topic.rb
@@ -22,6 +22,8 @@ class Topic < ActiveRecord::Base
def_delegator :notifier, :muted!, :notify_muted!
def_delegator :notifier, :toggle_mute, :toggle_mute
+ attr_accessor :allowed_user_ids
+
def self.max_sort_order
2**31 - 1
end
@@ -103,6 +105,7 @@ class Topic < ActiveRecord::Base
# When we want to temporarily attach some data to a forum topic (usually before serialization)
attr_accessor :user_data
attr_accessor :posters # TODO: can replace with posters_summary once we remove old list code
+ attr_accessor :participants
attr_accessor :topic_list
attr_accessor :meta_data
attr_accessor :include_last_poster
@@ -594,11 +597,14 @@ class Topic < ActiveRecord::Base
end
end
-
def posters_summary(options = {})
@posters_summary ||= TopicPostersSummary.new(self, options).summary
end
+ def participants_summary(options = {})
+ @participants_summary ||= TopicParticipantsSummary.new(self, options).summary
+ end
+
# Enable/disable the star on the topic
def toggle_star(user, starred)
Topic.transaction do
diff --git a/app/models/topic_list.rb b/app/models/topic_list.rb
index 54d847d3653..92c8e3f6602 100644
--- a/app/models/topic_list.rb
+++ b/app/models/topic_list.rb
@@ -20,6 +20,11 @@ class TopicList
def topics
return @topics if @topics.present?
+ # copy side-loaded data (allowed users) before dumping it with the .to_a
+ @topics_input.each do |t|
+ t.allowed_user_ids = t.allowed_users.map { |u| u.id }.to_a
+ end
+
@topics = @topics_input.to_a
# Attach some data for serialization to each topic
@@ -28,7 +33,7 @@ class TopicList
# Create a lookup for all the user ids we need
user_ids = []
@topics.each do |ft|
- user_ids << ft.user_id << ft.last_post_user_id << ft.featured_user_ids
+ user_ids << ft.user_id << ft.last_post_user_id << ft.featured_user_ids << ft.allowed_user_ids
end
avatar_lookup = AvatarLookup.new(user_ids)
@@ -36,10 +41,11 @@ class TopicList
@topics.each do |ft|
ft.user_data = @topic_lookup[ft.id] if @topic_lookup.present?
ft.posters = ft.posters_summary(avatar_lookup: avatar_lookup)
+ ft.participants = ft.participants_summary(avatar_lookup: avatar_lookup, user: @current_user)
ft.topic_list = self
end
- return @topics
+ @topics
end
def topic_ids
diff --git a/app/models/topic_participants_summary.rb b/app/models/topic_participants_summary.rb
new file mode 100644
index 00000000000..f72a5fb6cdd
--- /dev/null
+++ b/app/models/topic_participants_summary.rb
@@ -0,0 +1,37 @@
+class TopicParticipantsSummary
+ attr_reader :topic, :options
+
+ def initialize(topic, options = {})
+ @topic = topic
+ @options = options
+ @user = options[:user]
+ end
+
+ def summary
+ top_participants.compact.map(&method(:new_topic_poster_for))
+ end
+
+ def new_topic_poster_for(user)
+ TopicPoster.new.tap do |topic_poster|
+ topic_poster.user = user
+ topic_poster.extras = 'latest' if is_latest_poster?(user)
+ end
+ end
+
+ def is_latest_poster?(user)
+ topic.last_post_user_id == user.id
+ end
+
+ def top_participants
+ user_ids.map { |id| avatar_lookup[id] }.compact.uniq.take(3)
+ end
+
+ def user_ids
+ return [] unless @user
+ [topic.user_id] + topic.allowed_user_ids - [@user.id]
+ end
+
+ def avatar_lookup
+ @avatar_lookup ||= options[:avatar_lookup] || AvatarLookup.new(user_ids)
+ end
+end
diff --git a/app/models/topic_posters_summary.rb b/app/models/topic_posters_summary.rb
index 8ab031fd2df..f39c32b1e99 100644
--- a/app/models/topic_posters_summary.rb
+++ b/app/models/topic_posters_summary.rb
@@ -49,7 +49,7 @@ class TopicPostersSummary
:frequent_poster,
:frequent_poster,
:frequent_poster
- ].map { |description| I18n.t(description) })
+ ].map { |description| I18n.t(description) })
end
def last_poster_is_topic_creator?
diff --git a/app/serializers/topic_list_item_serializer.rb b/app/serializers/topic_list_item_serializer.rb
index 96e00be81a6..e0bfd1a697e 100644
--- a/app/serializers/topic_list_item_serializer.rb
+++ b/app/serializers/topic_list_item_serializer.rb
@@ -9,10 +9,12 @@ class TopicListItemSerializer < ListableTopicSerializer
:category_id
has_many :posters, serializer: TopicPosterSerializer, embed: :objects
+ has_many :participants, serializer: TopicPosterSerializer, embed: :objects
def starred
object.user_data.starred?
end
+
alias :include_starred? :has_user_data
def posters
@@ -23,4 +25,12 @@ class TopicListItemSerializer < ListableTopicSerializer
object.posters.find { |poster| poster.user.id == object.last_post_user_id }.try(:user).try(:username)
end
+ def participants
+ object.participants_summary || []
+ end
+
+ def include_participants?
+ object.private_message?
+ end
+
end
diff --git a/lib/avatar_lookup.rb b/lib/avatar_lookup.rb
index aef3196922a..147c4fe06df 100644
--- a/lib/avatar_lookup.rb
+++ b/lib/avatar_lookup.rb
@@ -13,11 +13,11 @@ class AvatarLookup
def self.lookup_columns
@lookup_columns ||= [:id,
- :email,
- :username,
- :use_uploaded_avatar,
- :uploaded_avatar_template,
- :uploaded_avatar_id]
+ :email,
+ :username,
+ :use_uploaded_avatar,
+ :uploaded_avatar_template,
+ :uploaded_avatar_id]
end
def users
@@ -27,12 +27,9 @@ class AvatarLookup
def user_lookup_hash
# adding tap here is a personal taste thing
hash = {}
- User
- .where(:id => @user_ids)
- .select(AvatarLookup.lookup_columns)
- .each{|user|
- hash[user.id] = user
- }
+ User.where(:id => @user_ids)
+ .select(AvatarLookup.lookup_columns)
+ .each{ |user| hash[user.id] = user }
hash
end
end
diff --git a/lib/topic_query.rb b/lib/topic_query.rb
index 6a14e37ee01..318c53d6ab9 100644
--- a/lib/topic_query.rb
+++ b/lib/topic_query.rb
@@ -159,7 +159,8 @@ class TopicQuery
options.reverse_merge!(per_page: SiteSetting.topics_per_page)
# Start with a list of all topics
- result = Topic.where("topics.id IN (SELECT topic_id FROM topic_allowed_users WHERE user_id = #{user.id.to_i})")
+ result = Topic.includes(:allowed_users)
+ .where("topics.id IN (SELECT topic_id FROM topic_allowed_users WHERE user_id = #{user.id.to_i})")
.joins("LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = #{user.id.to_i})")
.order(TopicQuerySQL.order_nocategory_basic_bumped)
.private_messages
diff --git a/spec/models/topic_participants_summary_spec.rb b/spec/models/topic_participants_summary_spec.rb
new file mode 100644
index 00000000000..58863e61421
--- /dev/null
+++ b/spec/models/topic_participants_summary_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+
+describe TopicParticipantsSummary do
+ describe '#summary' do
+ let(:summary) { described_class.new(topic, user: topic_creator).summary }
+
+ let(:topic) do
+ Fabricate(:topic,
+ user: topic_creator,
+ archetype: Archetype::private_message
+ )
+ end
+
+ let(:topic_creator) { Fabricate(:user) }
+ let(:user1) { Fabricate(:user) }
+ let(:user2) { Fabricate(:user) }
+ let(:user3) { Fabricate(:user) }
+ let(:user4) { Fabricate(:user) }
+
+ it "must never contains the user and at most 3 participants" do
+ topic.allowed_user_ids = [user1.id, user2.id, user3.id, user4.id]
+ expect(summary.map(&:user)).to eq([user1, user2, user3])
+ end
+
+ end
+end