Merge pull request #2212 from vikhyat/badge-system

Show badges in the poster expansion card
This commit is contained in:
Sam 2014-04-14 10:54:43 +10:00
commit d3810ba3bc
9 changed files with 169 additions and 11 deletions

View File

@ -57,7 +57,10 @@ Discourse.AdminUserBadgesController = Ember.ArrayController.extend({
self.pushObject(userBadge);
Ember.run.next(function() {
// Update the selected badge ID after the combobox has re-rendered.
self.set('selectedBadgeId', self.get('grantableBadges')[0].get('id'));
var newSelectedBadge = self.get('grantableBadges')[0];
if (newSelectedBadge) {
self.set('selectedBadgeId', newSelectedBadge.get('id'));
}
});
}, function() {
// Failure

View File

@ -20,6 +20,16 @@ Discourse.PosterExpansionController = Discourse.ObjectController.extend({
hasUserFilters: Em.computed.gt('postStream.userFilters.length', 0),
showBadges: function() {
return Discourse.SiteSettings.enable_badges;
}.property(),
moreBadgesCount: function() {
return this.get('user.badge_count') - this.get('user.featured_user_badges.length');
}.property('user.badge_count', 'user.featured_user_badges.@each'),
showMoreBadges: Em.computed.gt('moreBadgesCount', 0),
show: function(post) {
// Don't show on mobile

View File

@ -312,10 +312,21 @@ Discourse.User = Discourse.Model.extend({
return Discourse.Group.create(g);
});
}
if (json.user.invited_by) {
json.user.invited_by = Discourse.User.create(json.user.invited_by);
}
if (!Em.isEmpty(json.user.featured_user_badge_ids)) {
var userBadgesMap = {};
Discourse.UserBadge.createFromJson(json).forEach(function(userBadge) {
userBadgesMap[ userBadge.get('id') ] = userBadge;
});
json.user.featured_user_badges = json.user.featured_user_badge_ids.map(function(id) {
return userBadgesMap[id];
});
}
user.setProperties(json.user);
return user;
});

View File

@ -1,16 +1,31 @@
{{#if model}}
{{#link-to 'user' user}}{{boundAvatar model imageSize="huge"}}{{/link-to}}
<h1 {{bind-attr class="staff new_user"}}><a {{bind-attr href="usernameUrl"}}>{{username}}</a></h1>
{{#if showName}}
<h2><a {{bind-attr href="usernameUrl"}}>{{name}}</a></h2>
{{/if}}
<div class="names">
<h1 {{bind-attr class="staff new_user"}}><a {{bind-attr href="usernameUrl"}}>{{username}}</a></h1>
{{#if showName}}
<h2><a {{bind-attr href="usernameUrl"}}>{{name}}</a></h2>
{{/if}}
</div>
{{#if user}}
<h3>{{i18n last_post}} {{unboundDate path="user.last_posted_at" leaveAgo="true"}}</h3>
<h3>{{i18n joined}} {{unboundDate path="user.created_at" leaveAgo="true"}}</h3>
<div class="metadata">
<h3>{{i18n last_post}} {{unboundDate path="user.last_posted_at" leaveAgo="true"}}</h3>
<h3>{{i18n joined}} {{unboundDate path="user.created_at" leaveAgo="true"}}</h3>
{{groups-list groups=user.custom_groups}}
</div>
{{groups-list groups=user.custom_groups}}
{{#if showBadges}}
<div class="badge-section">
<h3>{{i18n badges.badge_count count=user.badge_count}}</h3>
{{#each user.featured_user_badges}}
<span class="badge">{{badge.name}}</span>
{{/each}}
{{#if showMoreBadges}}
<span class="btn more-badges">{{i18n badges.more_badges count=moreBadgesCount}}</span>
{{/if}}
</div>
{{/if}}
<div class='bottom'>
{{#if user.bio_cooked}}<div class='bio'>{{{user.bio_cooked}}}</div>{{/if}}

View File

@ -10,7 +10,7 @@ var clickOutsideEventName = "mousedown.outside-poster-expansion";
Discourse.PosterExpansionView = Discourse.View.extend({
elementId: 'poster-expansion',
classNameBindings: ['controller.visible::hidden'],
classNameBindings: ['controller.visible::hidden', 'controller.showBadges'],
// Position the expansion when the post changes
_visibleChanged: function() {

View File

@ -65,7 +65,7 @@
}
p.loading {
margin-top: 30px;
margin-top: 45px;
color: $secondary_text_color;
}
@ -76,4 +76,47 @@
.new-user a {
color: $secondary_text_color;
}
&.show-badges {
width: 560px;
.names {
width: 250px;
float: left;
}
.metadata {
float: right;
max-width: 180px;
}
h2 {
line-height: 15px;
}
.badge-section {
h3 {
line-height: 43px;
color: $primary_text_color;
font-size: 14px;
margin-bottom: -8px;
}
.badge, .more-badges {
font-size: 12px;
margin: 0;
line-height: 16px;
display: inline-block;
}
.badge {
padding: 3px 8px;
border: 1px solid $secondary-border-color;
}
.more-badges {
padding: 4px 8px;
}
}
}
}

View File

@ -21,10 +21,12 @@ class UserSerializer < BasicUserSerializer
:admin,
:title,
:suspend_reason,
:suspended_till
:suspended_till,
:badge_count
has_one :invited_by, embed: :object, serializer: BasicUserSerializer
has_many :custom_groups, embed: :object, serializer: BasicGroupSerializer
has_many :featured_user_badges, embed: :ids, serializer: UserBadgeSerializer, root: :user_badges
def self.private_attributes(*attrs)
attributes(*attrs)
@ -128,4 +130,13 @@ class UserSerializer < BasicUserSerializer
def watched_category_ids
CategoryUser.lookup(object, :watching).pluck(:category_id)
end
def badge_count
object.user_badges.count
end
def featured_user_badges
# The three rarest badges this user has received should be featured.
object.user_badges.joins(:badge).order('badges.grant_count ASC').includes(:granted_by, badge: :badge_type).limit(3)
end
end

View File

@ -1765,6 +1765,12 @@ en:
mark_watching: '<b>m</b> then <b>w</b> Mark topic as watching'
badges:
badge_count:
one: "1 Badge"
other: "%{count} Badges"
more_badges:
one: "+1 More"
other: "+%{count} More"
example_badge:
name: Example Badge
description: This is a generic example badge.

View File

@ -0,0 +1,59 @@
# Just ignore included associations that are to be embedded in the root instead of
# throwing an exception in AMS 0.8.x.
#
# The 0.9.0 branch does exactly this, see:
# https://github.com/rails-api/active_model_serializers/issues/377
module ActiveModel
class Serializer
# This method is copied over verbatim from the AMS version, except for silently
# ignoring associations that cannot be embedded without a root instead of
# raising an exception.
def include!(name, options={})
unique_values =
if hash = options[:hash]
if @options[:hash] == hash
@options[:unique_values] ||= {}
else
{}
end
else
hash = @options[:hash]
@options[:unique_values] ||= {}
end
node = options[:node] ||= @node
value = options[:value]
if options[:include] == nil
if @options.key?(:include)
options[:include] = @options[:include].include?(name)
elsif @options.include?(:exclude)
options[:include] = !@options[:exclude].include?(name)
end
end
association_class =
if klass = _associations[name]
klass
elsif value.respond_to?(:to_ary)
Associations::HasMany
else
Associations::HasOne
end
association = association_class.new(name, self, options)
if association.embed_ids?
node[association.key] = association.serialize_ids
if association.embed_in_root? && hash.nil?
# Don't raise an error!
elsif association.embed_in_root? && association.embeddable?
merge_association hash, association.root, association.serializables, unique_values
end
elsif association.embed_objects?
node[association.key] = association.serialize
end
end
end
end