FEATURE: you can not drill down and see why you have badges

Clicking on badges filters down the list to a particular user.
This commit is contained in:
Sam 2016-01-18 17:59:07 +11:00
parent a055c37939
commit ca3e2b4da3
17 changed files with 188 additions and 27 deletions

View file

@ -3,5 +3,13 @@ export default Ember.Component.extend({
showGrantCount: function() {
return this.get('count') && this.get('count') > 1;
}.property('count')
}.property('count'),
badgeUrl: function(){
// NOTE: I tried using a link-to helper here but the queryParams mean it fails
var username = this.get('user.username_lower') || '';
username = username !== '' ? "?username=" + username : '';
return this.get('badge.url') + username;
}.property("badge", "user")
});

View file

@ -1,17 +1,33 @@
import UserBadge from 'discourse/models/user-badge';
export default Ember.Controller.extend({
queryParams: ['username'],
noMoreBadges: false,
userBadges: null,
needs: ["application"],
user: function(){
if (this.get("username")) {
return this.get('userBadges')[0].get('user');
}
}.property("username"),
grantCount: function() {
if (this.get("username")) {
return this.get('userBadges.grant_count');
} else {
return this.get('model.grant_count');
}
}.property('username', 'model', 'userBadges'),
actions: {
loadMore() {
const self = this;
const userBadges = this.get('userBadges');
UserBadge.findByBadgeId(this.get('model.id'), {
offset: userBadges.length
offset: userBadges.length,
username: this.get('username'),
}).then(function(result) {
userBadges.pushObjects(result);
if(userBadges.length === 0){
@ -22,11 +38,12 @@ export default Ember.Controller.extend({
},
layoutClass: function(){
var user = this.get("user") ? " single-user" : "";
var ub = this.get("userBadges");
if(ub && ub[0] && ub[0].post_id){
return "user-badge-with-posts";
return "user-badge-with-posts" + user;
} else {
return "user-badge-no-posts";
return "user-badge-no-posts" + user;
}
}.property("userBadges"),
@ -34,7 +51,7 @@ export default Ember.Controller.extend({
if (this.get('noMoreBadges')) { return false; }
if (this.get('userBadges')) {
return this.get('model.grant_count') > this.get('userBadges.length');
return this.get('grantCount') > this.get('userBadges.length');
} else {
return false;
}

View file

@ -1,4 +1,6 @@
export default Ember.ArrayController.extend({
needs: ["user"],
user: Em.computed.alias("controllers.user.model"),
sortProperties: ['badge.badge_type.sort_order', 'badge.name'],
orderBy: function(ub1, ub2){
var sr1 = ub1.get('badge.badge_type.sort_order');

View file

@ -5,6 +5,10 @@ const Badge = RestModel.extend({
newBadge: Em.computed.none('id'),
url: function() {
return Discourse.getURL(`/badges/${this.get('id')}/${this.get('slug')}`);
}.property(),
/**
@private

View file

@ -48,7 +48,7 @@ UserBadge.reopenClass({
if ("user_badge" in json) {
userBadges = [json.user_badge];
} else {
userBadges = json.user_badges;
userBadges = (json.user_badge_info && json.user_badge_info.user_badges) || json.user_badges;
}
userBadges = userBadges.map(function(userBadgeJson) {
@ -73,6 +73,10 @@ UserBadge.reopenClass({
if ("user_badge" in json) {
return userBadges[0];
} else {
if (json.user_badge_info) {
userBadges.grant_count = json.user_badge_info.grant_count;
userBadges.username = json.user_badge_info.username;
}
return userBadges;
}
},

View file

@ -2,6 +2,11 @@ import UserBadge from 'discourse/models/user-badge';
import Badge from 'discourse/models/badge';
export default Discourse.Route.extend({
queryParams: {
username: {
refreshModel: true
}
},
actions: {
didTransition() {
this.controllerFor("badges/show")._showFooter();
@ -24,10 +29,13 @@ export default Discourse.Route.extend({
}
},
afterModel(model) {
return UserBadge.findByBadgeId(model.get("id")).then(userBadges => {
afterModel(model,transition) {
const username = transition.queryParams && transition.queryParams.username;
return UserBadge.findByBadgeId(model.get("id"), {username}).then(userBadges => {
this.userBadges = userBadges;
});
},
titleToken() {

View file

@ -9,7 +9,9 @@
<div class='row'>
<div class='badge'>{{user-badge badge=model}}</div>
<div class='description'>{{{model.displayDescriptionHtml}}}</div>
<div class='grant-count'>{{i18n 'badges.granted' count=model.grant_count}}</div>
{{#unless user}}
<div class='grant-count'>{{i18n 'badges.granted' count=grantCount}}</div>
{{/unless}}
<div class='info'>{{i18n 'badges.allow_title'}} {{{view.allowTitle}}}<br>{{i18n 'badges.multiple_grant'}} {{{view.multipleGrant}}}
</div>
</div>
@ -22,10 +24,27 @@
</div>
{{/if}}
{{#if user}}
<div class='badge-user-info'>
{{#link-to 'user' user}}
{{avatar user imageSize="extra_large"}}
<div class="details clearfix">
{{poster-name post=user}}
</div>
{{/link-to}}
<div class='earned'>
{{i18n 'badges.earned_n_times' count=grantCount}}
</div>
</div>
{{/if}}
{{#if userBadges}}
<div class={{unbound layoutClass}}>
{{#each ub in userBadges}}
<div class="badge-user">
{{#if user}}
{{format-date ub.granted_at}}
{{else}}
{{#link-to 'user' ub.user classNames="badge-info"}}
{{avatar ub.user imageSize="large"}}
<div class="details">
@ -33,12 +52,20 @@
{{format-date ub.granted_at}}
</div>
{{/link-to}}
{{/if}}
{{#if ub.post_number}}
<a class="post-link" href="{{unbound ub.topic.url}}/{{unbound ub.post_number}}">{{{ub.topic.fancyTitle}}}</a>
{{/if}}
</div>
{{/each}}
{{#unless canLoadMore}}
{{#if user}}
<a class='load-more' href='{{model.url}}'>{{i18n 'badges.more_with_badge'}}</a>
{{/if}}
{{/unless}}
</div>
{{conditional-loading-spinner condition=canLoadMore}}

View file

@ -1,7 +1,7 @@
{{#link-to 'badges.show' badge}}
<a href="{{badgeUrl}}">
{{#badge-button badge=badge}}
{{#if showGrantCount}}
<span class="count">(&times;&nbsp;{{count}})</span>
{{/if}}
{{/badge-button}}
{{/link-to}}
</a>

View file

@ -68,7 +68,7 @@
{{#if showBadges}}
<div class="badge-section">
{{#each ub in user.featured_user_badges}}
{{user-badge badge=ub.badge}}
{{user-badge badge=ub.badge user=user}}
{{/each}}
{{#if showMoreBadges}}
{{#link-to 'user.badges' user class="btn more-user-badges"}}

View file

@ -1,5 +1,5 @@
<section class='user-content user-badges-list'>
{{#each ub in controller}}
{{user-badge badge=ub.badge count=ub.count}}
{{user-badge badge=ub.badge count=ub.count user=user}}
{{/each}}
</section>

View file

@ -187,6 +187,58 @@
}
}
.show-badge .badge-user-info {
margin-left: 2%;
.earned {
margin-top: 15px;
font-size: 1.3em;
}
.username {
margin-top: 5px;
display: block;
color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%));
}
}
.show-badge .single-user {
margin-left: 2%;
padding-bottom: 20px;
.load-more {
padding-top: 30px;
display: block;
font-size: 1.2em;
}
}
.show-badge .single-user .badge-user {
padding-left: 0;
text-align: left;
display: block;
margin: 20px 0;
.badge-info {
display: none;
}
.date {
display: inline-block;
font-size: 1.1em;
margin-left: 10px;
}
.post-link {
font-size: 1.3em;
width: 500px;
margin: 0;
padding: 0;
}
width: 800px;
&:after {
content: "";
clear: both;
display: table;
}
}
.long-description.banner {
width: 88%;
margin-bottom: 20px;

View file

@ -1,16 +1,28 @@
class UserBadgesController < ApplicationController
def index
params.permit [:granted_before, :offset]
params.permit [:granted_before, :offset, :username]
badge = fetch_badge_from_params
user_badges = badge.user_badges.order('granted_at DESC, id DESC').limit(96)
user_badges = user_badges.includes(:user, :granted_by, badge: :badge_type, post: :topic)
grant_count = nil
if params[:username]
user_id = User.where(username_lower: params[:username].downcase).pluck(:id).first
user_badges = user_badges.where(user_id: user_id) if user_id
grant_count = user_badges.count
end
if offset = params[:offset]
user_badges = user_badges.offset(offset.to_i)
end
render_serialized(user_badges, UserBadgeSerializer, root: "user_badges", include_long_description: true)
user_badges = UserBadges.new(user_badges: user_badges,
username: params[:username],
grant_count: grant_count)
render_serialized(user_badges, UserBadgesSerializer, root: :user_badge_info, include_long_description: true)
end
def username
@ -28,7 +40,7 @@ class UserBadgesController < ApplicationController
.includes(post: :topic)
.includes(:granted_by)
render_serialized(user_badges, DetailedUserBadgeSerializer, root: "user_badges")
render_serialized(user_badges, DetailedUserBadgeSerializer, root: :user_badges)
end
def create

12
app/models/user_badges.rb Normal file
View file

@ -0,0 +1,12 @@
# view model for user badges
class UserBadges
alias :read_attribute_for_serialization :send
attr_accessor :user_badges, :username, :grant_count
def initialize(opts={})
@user_badges = opts[:user_badges]
@username = opts[:username]
@grant_count = opts[:grant_count]
end
end

View file

@ -1,9 +1,14 @@
class UserBadgeSerializer < ApplicationSerializer
class UserSerializer < BasicUserSerializer
attributes :name, :moderator, :admin
end
attributes :id, :granted_at, :count, :post_id, :post_number
has_one :badge
has_one :user, serializer: BasicUserSerializer, root: :users
has_one :granted_by, serializer: BasicUserSerializer, root: :users
has_one :user, serializer: UserSerializer, root: :users
has_one :granted_by, serializer: UserSerializer, root: :users
has_one :topic, serializer: BasicTopicSerializer
def include_count?

View file

@ -0,0 +1,4 @@
class UserBadgesSerializer < ApplicationSerializer
has_many :user_badges, embed: :objects
attributes :grant_count, :username
end

View file

@ -2746,6 +2746,10 @@ en:
mark_watching: '<b>m</b>, <b>w</b> Watch topic'
badges:
earned_n_times:
one: "Earned this badge 1 time"
other: "Earned this badge %{count} times"
more_with_badge: "Others with this badge"
title: Badges
allow_title: "can be used as a title"
multiple_grant: "can be awarded multiple times"

View file

@ -12,9 +12,11 @@ describe UserBadgesController do
xhr :get, :index, badge_id: badge.id
expect(response.status).to eq(200)
parsed = JSON.parse(response.body)
expect(parsed["topics"]).to eq(nil)
expect(parsed["user_badges"][0]["post_id"]).to eq(nil)
expect(parsed["badges"].length).to eq(1)
expect(parsed["user_badge_info"]["user_badges"][0]["post_id"]).to eq(nil)
end
end
@ -38,7 +40,7 @@ describe UserBadgesController do
expect(response.status).to eq(200)
parsed = JSON.parse(response.body)
expect(parsed["user_badges"].length).to eq(1)
expect(parsed["user_badge_info"]["user_badges"].length).to eq(1)
end
it 'includes counts when passed the aggregate argument' do