UX: improved user summary page

This commit is contained in:
Régis Hanol 2016-03-30 18:05:16 +02:00
parent b55d3b240b
commit bf579174c1
9 changed files with 219 additions and 131 deletions
app
assets
javascripts/discourse
controllers
templates
stylesheets/common
models
serializers
config/locales

View file

@ -1,13 +1,20 @@
import computed from 'ember-addons/ember-computed-decorators';
// should be kept in sync with 'UserSummary::MAX_TOPICS'
const MAX_TOPICS = 6;
// should be kept in sync with 'UserSummary::MAX_BADGES'
const MAX_BADGES = 6;
export default Ember.Controller.extend({ export default Ember.Controller.extend({
needs: ['user'], needs: ['user'],
user: Em.computed.alias('controllers.user.model'), user: Ember.computed.alias('controllers.user.model'),
moreTopics: function(){
return this.get('model.topics').length > 5; @computed("model.topics.length")
}.property('model'), moreTopics(topicsLength) { return topicsLength >= MAX_TOPICS; },
moreReplies: function(){
return this.get('model.replies').length > 5; @computed("model.replies.length")
}.property('model'), moreReplies(repliesLength) { return repliesLength >= MAX_TOPICS; },
moreBadges: function(){
return this.get('model.badges').length > 5; @computed("model.badges.length")
}.property('model') moreBadges(badgesLength) { return badgesLength >= MAX_BADGES; },
}); });

View file

@ -2,4 +2,4 @@
{{#if icon}}{{fa-icon icon}}{{/if}} {{#if icon}}{{fa-icon icon}}{{/if}}
{{number value}} {{number value}}
</span> </span>
<span class='label'>{{i18n label}}</span> <span class='label'>{{{i18n label count=value}}}</span>

View file

@ -1,62 +1,84 @@
{{#if model.replies.length}}
<div class='top-section'>
<h3>{{i18n "user.summary.top_replies"}}</h3>
{{#each reply in model.replies}}
<ul>
<li>
<a href="{{reply.url}}">{{reply.topic.title}}</a> {{#if reply.like_count}}<span class='like-count'>{{reply.like_count}}<i class='fa fa-heart'></i></span>{{/if}} {{format-date reply.createdAt format="tiny" noTitle="true"}}
</li>
</ul>
{{/each}}
{{#if moreReplies}}
{{#link-to "userActivity.replies" user class="more"}}{{i18n "user.summary.more_replies"}}{{/link-to}}
{{/if}}
</div>
{{/if}}
{{#if model.topics.length}}
<div class='top-section'>
<h3>{{i18n "user.summary.top_topics"}}</h3>
{{#each topic in model.topics}}
<ul>
<li>
<a href="{{topic.url}}">{{topic.title}}</a> {{#if topic.like_count}}<span class='like-count'>{{topic.like_count}}<i class='fa fa-heart'></i></span>{{/if}} {{format-date topic.createdAt format="tiny" noTitle="true"}}
</li>
</ul>
{{/each}}
{{#if moreTopics}}
{{#link-to "userActivity.topics" user class="more"}}{{i18n "user.summary.more_topics"}}{{/link-to}}
{{/if}}
</div>
{{/if}}
<div class='top-section stats-section'> <div class='top-section stats-section'>
<h3>{{i18n "user.summary.stats"}}</h3> <h3 class='stats-title'>{{i18n "user.summary.stats"}}</h3>
<dl> <ul>
<dt>{{i18n "user.summary.topic_count"}}</dt> <li>
<dd>{{model.topic_count}}</dd> <span class='value'>{{model.time_read}}</span>
<dt>{{i18n "user.summary.post_count"}}</dt> <span class='label'>{{{i18n "user.summary.time_read"}}}</span>
<dd>{{model.post_count}}</dd> </li>
<dt>{{i18n "user.summary.likes_given"}}</dt> <li>{{user-stat value=model.topic_count label="user.summary.topic_count"}}</li>
<dd>{{model.likes_given}}</dd> <li>{{user-stat value=model.post_count label="user.summary.post_count"}}</li>
<dt>{{i18n "user.summary.likes_received"}}</dt> <li>{{user-stat value=model.likes_received label="user.summary.likes_received"}}</li>
<dd>{{model.likes_received}}</dd> <li>{{user-stat value=model.likes_given label="user.summary.likes_given"}}</li>
<dt>{{i18n "user.summary.days_visited"}}</dt> <li>{{user-stat value=model.days_visited label="user.summary.days_visited"}}</li>
<dd>{{model.days_visited}}</dd> <li>{{user-stat value=model.posts_read_count label="user.summary.posts_read"}}</li>
<dt>{{i18n "user.summary.posts_read_count"}}</dt> </ul>
<dd>{{model.posts_read_count}}</dd> </div>
</dl>
<div class='top-section'>
<div class='replies-section'>
<h3 class='stats-title'>{{i18n "user.summary.top_replies"}}</h3>
{{#if model.replies.length}}
<ul>
{{#each reply in model.replies}}
<li>
<span class='topic-info'>
{{format-date reply.createdAt format="tiny" noTitle="true"}}
{{#if reply.like_count}}
&middot;
<span class='like-count'>{{number reply.like_count}}&nbsp;{{fa-icon 'heart'}}</span>
{{/if}}
</span>
<br>
<span>
<a href="{{reply.url}}">{{{reply.topic.fancyTitle}}}</a>
</span>
</li>
{{/each}}
</ul>
{{#if moreReplies}}
<p>{{#link-to "userActivity.replies" user class="more"}}{{i18n "user.summary.more_replies"}}{{/link-to}}</p>
{{/if}}
{{else}}
<p>{{i18n "user.summary.no_replies"}}</p>
{{/if}}
</div>
<div class='topics-section'>
<h3 class='stats-title'>{{i18n "user.summary.top_topics"}}</h3>
{{#if model.topics.length}}
<ul>
{{#each topic in model.topics}}
<li>
<span class='topic-info'>
{{format-date topic.createdAt format="tiny" noTitle="true"}}
{{#if topic.like_count}}
&middot;
<span class='like-count'>{{number topic.like_count}}&nbsp;{{fa-icon 'heart'}}</span>
{{/if}}
</span>
<br>
<span>
<a href="{{topic.url}}">{{{topic.fancyTitle}}}</a>
</span>
</li>
{{/each}}
</ul>
{{#if moreTopics}}
<p>{{#link-to "userActivity.topics" user class="more"}}{{i18n "user.summary.more_topics"}}{{/link-to}}</p>
{{/if}}
{{else}}
<p>{{i18n "user.summary.no_topics"}}</p>
{{/if}}
</div>
</div> </div>
{{#if model.badges.length}}
<div class='top-section badges-section'> <div class='top-section badges-section'>
<h3>{{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}}
{{user-badge badge=badge count=badge.count user=user}} {{badge-card badge=badge count=badge.count navigateOnClick="true" username=user.username_lower}}
{{/each}} {{else}}
{{#if moreBadges}} <p>{{i18n "user.summary.no_badges"}}</p>
{{#link-to "user.badges" user class="more"}}{{i18n "user.summary.more_badges"}}{{/link-to}} {{/each}}
{{/if}} {{#if moreBadges}}
<p>{{#link-to "user.badges" user class="more"}}{{i18n "user.summary.more_badges"}}{{/link-to}}</p>
{{/if}}
</div> </div>
{{/if}}

View file

@ -57,6 +57,7 @@
} }
} }
} }
.user-info.medium.badge-info { .user-info.medium.badge-info {
min-height: 80px; min-height: 80px;
@ -194,6 +195,12 @@
vertical-align: top; vertical-align: top;
} }
@media all and (max-width: 320px) {
.badge-card.medium {
width: 100%;
}
}
.badge-card.large { .badge-card.large {
width: 750px; width: 750px;
} }

View file

@ -190,59 +190,82 @@
} }
.top-section { .top-section {
display: inline-block;
width: 45%;
max-width: 500px;
padding-right: 20px;
vertical-align: top;
margin-bottom: 30px;
.more {
display: block;
margin-top: 10px;
color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%));
}
h3 {
margin-bottom: 15px;
}
.relative-date {
color: lighten($primary, 40%);
font-size: 0.8em;
margin-left: 5px;
}
.like-count {
margin-left: 5px;
}
ul { ul {
list-style-type: none; list-style: none;
padding: 0;
margin: 0; margin: 0;
li { }
margin: 0; }
padding: 8px 0;
.fa-heart { .top-section,
margin-left: 3px; .replies-section,
} .topics-section {
} margin-bottom: 20px;
}
.stats-title {
text-transform: uppercase;
margin-bottom: 10px;
}
.stats-section {
ul {
margin: 10px 0;
} }
dt,dd { li {
float:left; display: inline-block;
padding: 10px 14px;
margin: 0 5px 10px 0;
background: dark-light-diff($primary, $secondary, 90%, -65%);
} }
dd {
min-width: 80px; li:last-of-type {
text-align: right; margin: 0;
} }
dt {
clear: left; .value {
min-width: 100px; font-weight: bold;
color: dark-light-choose(scale-color($primary, $lightness: 25%), scale-color($secondary, $lightness: 75%)); font-size: 1.2em;
} }
} }
.replies-section,
.topics-section {
width: 50%;
ul {
max-width: 95%;
}
li {
border-left: dark-light-diff($primary, $secondary, 90%, -65%) solid 2px;
padding: 5px 8px;
margin: 10px 0;
}
.topic-info {
color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 40%));
}
}
.replies-section {
float: left;
}
.topics-section {
float: right;
}
.badges-section {
clear: both;
}
@media all @media all
and (max-width : 600px) { and (max-width : 600px) {
.top-section { .replies-section,
width: 90%; .topics-section {
float: none;
width: 100%;
} }
} }

View file

@ -53,7 +53,7 @@
} }
&:hover { &:hover {
background: dark-light-diff($primary, $secondary, 65%, -75%); background: dark-light-diff($primary, $secondary, 65%, -75%);
color: #fff; color: $secondary;
} }
&[disabled], &.disabled { &[disabled], &.disabled {
background: dark-light-diff($primary, $secondary, 90%, -60%); background: dark-light-diff($primary, $secondary, 90%, -60%);
@ -80,7 +80,7 @@
} }
&:active { &:active {
@include linear-gradient(scale-color($tertiary, $lightness: -20%), scale-color($tertiary, $lightness: -10%)); @include linear-gradient(scale-color($tertiary, $lightness: -20%), scale-color($tertiary, $lightness: -10%));
color: #fff; color: $secondary;
} }
&[disabled], &.disabled { &[disabled], &.disabled {
background: $tertiary; background: $tertiary;

View file

@ -2,7 +2,7 @@
class UserSummary class UserSummary
MAX_FEATURED_BADGES = 10 MAX_BADGES = 6
MAX_TOPICS = 6 MAX_TOPICS = 6
alias :read_attribute_for_serialization :send alias :read_attribute_for_serialization :send
@ -18,7 +18,7 @@ class UserSummary
.listable_topics .listable_topics
.visible .visible
.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_TOPICS)
end end
@ -26,26 +26,31 @@ class UserSummary
def replies def replies
Post Post
.secured(@guardian) .secured(@guardian)
.includes(:user, {topic: :category}) .includes(:user, topic: :category)
.references(:topic) .references(:topic)
.merge(Topic.listable_topics.visible.secured(@guardian)) .merge(Topic.listable_topics.visible.secured(@guardian))
.where(user: @user) .where(user: @user)
.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_TOPICS)
end end
def badges def badges
@user.featured_user_badges(MAX_FEATURED_BADGES) @user.featured_user_badges(MAX_BADGES)
end end
def user_stat def user_stat
@user.user_stat @user.user_stat
end end
delegate :likes_given, :likes_received, :days_visited, delegate :likes_given,
:posts_read_count, :topic_count, :post_count, :likes_received,
:days_visited,
:posts_read_count,
:topic_count,
:post_count,
:time_read,
to: :user_stat to: :user_stat
end end

View file

@ -12,11 +12,19 @@ class UserSummarySerializer < ApplicationSerializer
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
attributes :likes_given, :likes_received, :posts_read_count, attributes :likes_given,
:days_visited, :topic_count, :post_count :likes_received,
:posts_read_count,
:days_visited,
:topic_count,
:post_count,
:time_read
def include_badges? def include_badges?
SiteSetting.enable_badges SiteSetting.enable_badges
end end
def time_read
AgeWords.age_words(object.time_read)
end
end end

View file

@ -734,17 +734,33 @@ en:
summary: summary:
title: "Summary" title: "Summary"
stats: "Stats" stats: "Stats"
topic_count: "Topics Created" time_read: "read time"
post_count: "Posts Created" topic_count:
likes_given: "Likes Given" one: "topic created"
likes_received: "Likes Received" other: "topics created"
days_visited: "Days Visited" post_count:
posts_read_count: "Posts Read" one: "post created"
other: "posts created"
likes_given:
one: "like given"
other: "likes given"
likes_received:
one: "like received"
other: "likes received"
days_visited:
one: "day visited"
other: "days visited"
posts_read:
one: "post read"
other: "posts read"
top_replies: "Top Replies" top_replies: "Top Replies"
top_topics: "Top Topics" no_replies: "No replies yet."
top_badges: "Top Badges"
more_topics: "More Topics"
more_replies: "More Replies" more_replies: "More Replies"
top_topics: "Top Topics"
no_topics: "No topics yet."
more_topics: "More Topics"
top_badges: "Top Badges"
no_badges: "No badges yet."
more_badges: "More Badges" more_badges: "More Badges"