User Summary improvements

Added "Top Links" list
Added "Most Liked By" list

Added "Bookmark count" stat

UX: Use fa heart icon instead of "like" text in stats

Change the order of the user stats
This commit is contained in:
Régis Hanol 2016-04-13 23:02:51 +02:00
parent b0803f7035
commit e808f7f41e
6 changed files with 143 additions and 23 deletions

View File

@ -1,7 +1,7 @@
import computed from 'ember-addons/ember-computed-decorators'; import computed from 'ember-addons/ember-computed-decorators';
// should be kept in sync with 'UserSummary::MAX_TOPICS' // should be kept in sync with 'UserSummary::MAX_SUMMARY_RESULTS'
const MAX_TOPICS = 6; const MAX_SUMMARY_RESULTS = 6;
// should be kept in sync with 'UserSummary::MAX_BADGES' // should be kept in sync with 'UserSummary::MAX_BADGES'
const MAX_BADGES = 6; const MAX_BADGES = 6;
@ -10,10 +10,10 @@ export default Ember.Controller.extend({
user: Ember.computed.alias('controllers.user.model'), user: Ember.computed.alias('controllers.user.model'),
@computed("model.topics.length") @computed("model.topics.length")
moreTopics(topicsLength) { return topicsLength >= MAX_TOPICS; }, moreTopics(topicsLength) { return topicsLength >= MAX_SUMMARY_RESULTS; },
@computed("model.replies.length") @computed("model.replies.length")
moreReplies(repliesLength) { return repliesLength >= MAX_TOPICS; }, moreReplies(repliesLength) { return repliesLength >= MAX_SUMMARY_RESULTS; },
@computed("model.badges.length") @computed("model.badges.length")
moreBadges(badgesLength) { return badgesLength >= MAX_BADGES; }, moreBadges(badgesLength) { return badgesLength >= MAX_BADGES; },

View File

@ -1,21 +1,22 @@
<div class='top-section stats-section'> <div class='top-section stats-section'>
<h3 class='stats-title'>{{i18n "user.summary.stats"}}</h3> <h3 class='stats-title'>{{i18n "user.summary.stats"}}</h3>
<ul> <ul>
<li>{{user-stat value=model.days_visited label="user.summary.days_visited"}}</li>
<li> <li>
<span class='value'>{{model.time_read}}</span> <span class='value'>{{model.time_read}}</span>
<span class='label'>{{{i18n "user.summary.time_read"}}}</span> <span class='label'>{{{i18n "user.summary.time_read"}}}</span>
</li> </li>
<li>{{user-stat value=model.posts_read_count label="user.summary.posts_read"}}</li>
<li>{{user-stat value=model.likes_given label="user.summary.likes_given"}}</li>
<li>{{user-stat value=model.bookmark_count label="user.summary.bookmark_count"}}</li>
<li>{{user-stat value=model.topic_count label="user.summary.topic_count"}}</li> <li>{{user-stat value=model.topic_count label="user.summary.topic_count"}}</li>
<li>{{user-stat value=model.post_count label="user.summary.post_count"}}</li> <li>{{user-stat value=model.post_count label="user.summary.post_count"}}</li>
<li>{{user-stat value=model.likes_received label="user.summary.likes_received"}}</li> <li>{{user-stat value=model.likes_received label="user.summary.likes_received"}}</li>
<li>{{user-stat value=model.likes_given label="user.summary.likes_given"}}</li>
<li>{{user-stat value=model.days_visited label="user.summary.days_visited"}}</li>
<li>{{user-stat value=model.posts_read_count label="user.summary.posts_read"}}</li>
</ul> </ul>
</div> </div>
<div class='top-section'> <div class='top-section'>
<div class='replies-section'> <div class='replies-section pull-left'>
<h3 class='stats-title'>{{i18n "user.summary.top_replies"}}</h3> <h3 class='stats-title'>{{i18n "user.summary.top_replies"}}</h3>
{{#if model.replies.length}} {{#if model.replies.length}}
<ul> <ul>
@ -42,7 +43,7 @@
<p>{{i18n "user.summary.no_replies"}}</p> <p>{{i18n "user.summary.no_replies"}}</p>
{{/if}} {{/if}}
</div> </div>
<div class='topics-section'> <div class='topics-section pull-right'>
<h3 class='stats-title'>{{i18n "user.summary.top_topics"}}</h3> <h3 class='stats-title'>{{i18n "user.summary.top_topics"}}</h3>
{{#if model.topics.length}} {{#if model.topics.length}}
<ul> <ul>
@ -71,6 +72,51 @@
</div> </div>
</div> </div>
<div class='top-section'>
<div class='links-section pull-left'>
<h3 class='stats-title'>{{i18n "user.summary.top_links"}}</h3>
{{#if model.links.length}}
<ul>
{{#each link in model.links}}
<li>
<span class='badge badge-notification clicks' title='{{i18n 'topic_map.clicks' count=link.clicks}}'>{{link.clicks}}</span>
<span class='domain'>{{unbound link.domain}}</span>
<br>
<a href="{{unbound link.url}}" target="_blank" title="{{unbound link.url}}">
{{#if link.title}}
{{link.title}}
{{else}}
{{shorten-url link.url}}
{{/if}}
</a>
</li>
{{/each}}
</ul>
{{else}}
<p>{{i18n "user.summary.no_links"}}</p>
{{/if}}
</div>
<div class='likes-section pull-right'>
<h3 class='stats-title'>{{i18n "user.summary.most_liked_by"}}</h3>
{{#if model.most_liked_by_users.length}}
<ul>
{{#each user in model.most_liked_by_users}}
<li>
<span>
{{#user-info user=user}}
{{fa-icon "heart"}}
<span class='likes'>{{user.likes}}</span>
{{/user-info}}
</span>
</li>
{{/each}}
</ul>
{{else}}
<p>{{i18n "user.summary.no_likes"}}</p>
{{/if}}
</div>
</div>
<div class='top-section badges-section'> <div class='top-section badges-section'>
<h3 class='stats-title'>{{i18n "user.summary.top_badges"}}</h3> <h3 class='stats-title'>{{i18n "user.summary.top_badges"}}</h3>
{{#each badge in model.badges}} {{#each badge in model.badges}}

View File

@ -199,7 +199,9 @@
.top-section, .top-section,
.replies-section, .replies-section,
.topics-section { .topics-section,
.links-section,
.likes-section {
margin-bottom: 20px; margin-bottom: 20px;
} }
@ -235,7 +237,9 @@
} }
.replies-section, .replies-section,
.topics-section { .topics-section,
.links-section,
.likes-section {
width: 50%; width: 50%;
ul { ul {
@ -253,12 +257,17 @@
} }
} }
.replies-section { .links-section {
float: left; .domain {
font-size: 0.714em;
color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 40%));
}
} }
.topics-section { .likes-section {
float: right; li {
height: 40px;
}
} }
@media all @media all

View File

@ -2,8 +2,8 @@
class UserSummary class UserSummary
MAX_SUMMARY_RESULTS = 6
MAX_BADGES = 6 MAX_BADGES = 6
MAX_TOPICS = 6
alias :read_attribute_for_serialization :send alias :read_attribute_for_serialization :send
@ -20,7 +20,7 @@ class UserSummary
.where(user: @user) .where(user: @user)
.order('like_count DESC, created_at ASC') .order('like_count DESC, created_at ASC')
.includes(:user, :category) .includes(:user, :category)
.limit(MAX_TOPICS) .limit(MAX_SUMMARY_RESULTS)
end end
def replies def replies
@ -33,7 +33,47 @@ class UserSummary
.where('post_number > 1') .where('post_number > 1')
.where('topics.archetype <> ?', Archetype.private_message) .where('topics.archetype <> ?', Archetype.private_message)
.order('posts.like_count DESC, posts.created_at ASC') .order('posts.like_count DESC, posts.created_at ASC')
.limit(MAX_TOPICS) .limit(MAX_SUMMARY_RESULTS)
end
def links
TopicLink
.where(user: @user)
.where(internal: false)
.where(reflection: false)
.order('clicks DESC, created_at ASC')
.limit(MAX_SUMMARY_RESULTS)
end
class LikedByUser < OpenStruct
include ActiveModel::SerializerSupport
end
def most_liked_by_users
likers_ids = []
counts = []
UserAction.where(user: @user)
.where(action_type: UserAction::WAS_LIKED)
.group(:acting_user_id)
.order("COUNT(*) DESC")
.limit(MAX_SUMMARY_RESULTS)
.pluck("acting_user_id, COUNT(*)")
.each do |i|
likers_ids << i[0]
counts << i[1]
end
User.where(id: likers_ids)
.pluck(:id, :username, :name, :uploaded_avatar_id)
.map.with_index do |u, i|
LikedByUser.new(
id: u[0],
username: u[1],
name: u[2],
avatar_template: User.avatar_template(u[1], u[3]),
likes: counts[i]
)
end
end end
def badges def badges
@ -44,6 +84,13 @@ class UserSummary
@user.user_stat @user.user_stat
end end
def bookmark_count
UserAction
.where(user: @user)
.where(action_type: UserAction::BOOKMARK)
.count
end
delegate :likes_given, delegate :likes_given,
:likes_received, :likes_received,
:days_visited, :days_visited,

View File

@ -8,9 +8,19 @@ class UserSummarySerializer < ApplicationSerializer
has_one :topic, serializer: TopicSerializer has_one :topic, serializer: TopicSerializer
end end
class LinkSerializer < ApplicationSerializer
attributes :url, :title, :clicks, :domain
end
class MostLikedByUserSerializer < BasicUserSerializer
attributes :likes, :name
end
has_many :topics, serializer: TopicSerializer has_many :topics, serializer: TopicSerializer
has_many :replies, serializer: ReplySerializer, embed: :object has_many :replies, serializer: ReplySerializer, embed: :object
has_many :badges, serializer: UserBadgeSerializer, embed: :object has_many :badges, serializer: UserBadgeSerializer, embed: :object
has_many :links, serializer: LinkSerializer, embed: :object
has_many :most_liked_by_users, serializer: MostLikedByUserSerializer, embed: :object
attributes :likes_given, attributes :likes_given,
:likes_received, :likes_received,
@ -18,7 +28,8 @@ class UserSummarySerializer < ApplicationSerializer
:days_visited, :days_visited,
:topic_count, :topic_count,
:post_count, :post_count,
:time_read :time_read,
:bookmark_count
def include_badges? def include_badges?
SiteSetting.enable_badges SiteSetting.enable_badges

View File

@ -742,17 +742,20 @@ en:
one: "post created" one: "post created"
other: "posts created" other: "posts created"
likes_given: likes_given:
one: "like given" one: "<i class='fa fa-heart'></i> given"
other: "likes given" other: "<i class='fa fa-heart'></i> given"
likes_received: likes_received:
one: "like received" one: "<i class='fa fa-heart'></i> received"
other: "likes received" other: "<i class='fa fa-heart'></i> received"
days_visited: days_visited:
one: "day visited" one: "day visited"
other: "days visited" other: "days visited"
posts_read: posts_read:
one: "post read" one: "post read"
other: "posts read" other: "posts read"
bookmark_count:
one: "bookmark"
other: "bookmarks"
top_replies: "Top Replies" top_replies: "Top Replies"
no_replies: "No replies yet." no_replies: "No replies yet."
more_replies: "More Replies" more_replies: "More Replies"
@ -762,6 +765,10 @@ en:
top_badges: "Top Badges" top_badges: "Top Badges"
no_badges: "No badges yet." no_badges: "No badges yet."
more_badges: "More Badges" more_badges: "More Badges"
top_links: "Top Links"
no_links: "No links yet."
most_liked_by: "Most Liked By"
no_likes: "No likes yet."
associated_accounts: "Logins" associated_accounts: "Logins"