FEATURE: badge grouping UI

FIX: not loading more badges on badge show page
This commit is contained in:
Sam 2014-07-18 15:46:36 +10:00
parent c47a70e390
commit c8284170ad
17 changed files with 177 additions and 67 deletions

View File

@ -1,4 +1,32 @@
export default Ember.ArrayController.extend({
sortProperties: ['displayName'],
sortAscending: true
export default Ember.Controller.extend({
badgeGroups: function(){
var sorted = _.sortBy(this.get('model'), function(badge){
var pos = badge.get('badge_grouping.position');
var type = badge.get('badge_type_id');
var name = badge.get('displayName');
return ("000" + pos).slice(-4) + (10-type) + name;
});
var grouped = [];
var group = [], groupId;
sorted.forEach(function(badge){
if(groupId !== badge.badge_grouping_id){
if(group && group.length > 0){
grouped.push({badges: group, badgeGrouping: group[0].badge_grouping});
}
group = [];
groupId = badge.badge_grouping_id;
}
group.push(badge);
});
if(group && group.length > 0){
grouped.push({badges: group, badgeGrouping: group[0].badge_grouping});
}
return grouped;
}.property('model')
});

View File

@ -7,6 +7,23 @@
@module Discourse
**/
export default Discourse.ObjectController.extend({
actions: {
loadMore: function() {
var self = this;
var userBadges = this.get('userBadges');
Discourse.UserBadge.findByBadgeId(this.get('model.id'), {
offset: userBadges.length
}).then(function(userBadges) {
self.get('userBadges').pushObjects(userBadges);
if(userBadges.length === 0){
self.set('noMoreBadges', true);
}
});
}
},
layoutClass: function(){
var ub = this.get("userBadges");
if(ub && ub[0] && ub[0].post_id){
@ -16,14 +33,15 @@ export default Discourse.ObjectController.extend({
}
}.property("userBadges"),
grantDates: Em.computed.mapBy('userBadges', 'grantedAt'),
minGrantedAt: Em.computed.min('grantDates'),
canLoadMore: function() {
if(this.get('noMoreBadges')) {
return false;
}
if (this.get('userBadges')) {
return this.get('model.grant_count') > this.get('userBadges.length');
} else {
return false;
}
}.property('model.grant_count', 'userBadges.length')
}.property('noMoreBadges', 'model.grant_count', 'userBadges.length')
});

View File

@ -165,6 +165,13 @@ Discourse.Badge.reopenClass({
});
}
var badgeGroupings = {};
if ('badge_groupings' in json) {
json.badge_groupings.forEach(function(badgeGroupingJson) {
badgeGroupings[badgeGroupingJson.id] = Discourse.BadgeGrouping.create(badgeGroupingJson);
});
}
// Create Badge objects.
var badges = [];
if ("badge" in json) {
@ -175,8 +182,10 @@ Discourse.Badge.reopenClass({
badges = badges.map(function(badgeJson) {
var badge = Discourse.Badge.create(badgeJson);
badge.set('badge_type', badgeTypes[badge.get('badge_type_id')]);
badge.set('badge_grouping', badgeGroupings[badge.get('badge_grouping_id')]);
return badge;
});
if ("badge" in json) {
return badges[0];
} else {

View File

@ -0,0 +1,10 @@
Discourse.BadgeGrouping= Discourse.Model.extend({
i18nNameKey: function() {
return this.get('name').toLowerCase().replace(/\s/g, '_');
}.property('name'),
displayName: function(){
var i18nKey = "badges.badge_grouping." + this.get('i18nNameKey') + ".name";
return I18n.t(i18nKey, {defaultValue: this.get('name')});
}.property()
});

View File

@ -111,11 +111,11 @@ Discourse.UserBadge.reopenClass({
**/
findByBadgeId: function(badgeId, options) {
if (!options) { options = {}; }
var url = "/user_badges.json?badge_id=" + badgeId;
if (options.granted_before) {
url = url + "&granted_before=" + encodeURIComponent(options.granted_before);
}
return Discourse.ajax(url).then(function(json) {
options.badge_id = badgeId;
return Discourse.ajax("/user_badges.json", {
data: options
}).then(function(json) {
return Discourse.UserBadge.createFromJson(json);
});
},

View File

@ -28,16 +28,5 @@ Discourse.BadgesShowRoute = Ember.Route.extend({
});
controller.set('model', model);
Discourse.set('title', model.get('displayName'));
},
actions: {
loadMore: function() {
var self = this;
Discourse.UserBadge.findByBadgeId(this.currentModel.get('id'), {
granted_before: this.get('controller.minGrantedAt') / 1000
}).then(function(userBadges) {
self.get('controller.userBadges').pushObjects(userBadges);
});
}
}
});

View File

@ -3,7 +3,11 @@
<table class='badges-listing'>
<tbody>
{{#each}}
{{#each badgeGroups}}
<tr class='title'>
<td colspan=4><h3>{{this.badgeGrouping.displayName}}</h3></td>
</tr>
{{#each this.badges}}
<tr>
<td class='granted'>{{#if this.has_badge}}<i class='fa fa-check'></i>{{/if}}</td>
<td class='badge'>{{user-badge badge=this}}</td>
@ -11,6 +15,7 @@
<td class='grant-count'>{{i18n badges.granted count=grant_count}}</td>
</tr>
{{/each}}
{{/each}}
</tbody>
</table>
</div>

View File

@ -75,6 +75,15 @@ table.badges-listing {
font-size: $base-font-size;
}
tr.title td {
padding-top: 30px;
padding-bottom: 15px;
}
tr.title {
border-top: 0px solid;
}
td {
padding: 10px 0px;
}

View File

@ -3,13 +3,21 @@ class BadgesController < ApplicationController
def index
badges = Badge.all
badges = badges.where(enabled: true, listable: true) if(params[:only_listable] == "true") || !request.xhr?
badges = badges.includes(:badge_grouping).to_a
if (params[:only_listable] == "true") || !request.xhr?
# NOTE: this is sorted client side if needed
badges = badges.includes(:badge_grouping)
.where(enabled: true, listable: true)
end
badges = badges.to_a
user_badges = nil
if current_user
user_badges = Set.new(current_user.user_badges.select('distinct badge_id').pluck(:badge_id))
end
serialized = MultiJson.dump(serialize_data(badges, BadgeSerializer, root: "badges", user_badges: user_badges, include_grouping: true))
serialized = MultiJson.dump(serialize_data(badges, BadgeIndexSerializer, root: "badges", user_badges: user_badges))
respond_to do |format|
format.html do
store_preloaded "badges", serialized

View File

@ -7,11 +7,11 @@ class UserBadgesController < ApplicationController
user_badges = user.user_badges
else
badge = fetch_badge_from_params
user_badges = badge.user_badges.order('granted_at DESC').limit(96)
user_badges = badge.user_badges.order('granted_at DESC, id DESC').limit(96)
end
if params[:granted_before]
user_badges = user_badges.where('granted_at < ?', Time.at(params[:granted_before].to_f))
if offset = params[:offset]
user_badges = user_badges.offset(offset.to_i)
end
user_badges = user_badges.includes(:user, :granted_by, badge: :badge_type, post: :topic)

View File

@ -1,6 +1,4 @@
class Badge < ActiveRecord::Base
belongs_to :badge_grouping
# badge ids
Welcome = 5
NicePost = 6
@ -145,6 +143,8 @@ SQL
end
belongs_to :badge_type
belongs_to :badge_grouping
has_many :user_badges, dependent: :destroy
validates :name, presence: true, uniqueness: true

View File

@ -1,12 +1,10 @@
class BadgeGrouping < ActiveRecord::Base
module Position
GettingStarted = 10
Community = 11
Posting = 12
TrustLevel = 13
Other = 14
end
GettingStarted = 1
Community = 2
Posting = 3
TrustLevel = 4
Other = 5
has_many :badges
end

View File

@ -1,3 +1,3 @@
class BadgeGroupingSerializer < ApplicationSerializer
attributes :id, :name, :description
attributes :id, :name, :description, :position
end

View File

@ -0,0 +1,12 @@
class BadgeIndexSerializer < BadgeSerializer
attributes :has_badge
has_one :badge_grouping
def include_has_badge?
@options[:user_badges]
end
def has_badge
@options[:user_badges].include?(object.id)
end
end

View File

@ -1962,6 +1962,17 @@ en:
other: "%{count} granted"
select_badge_for_title: Select a badge to use as your title
no_title: "<no title>"
badge_grouping:
getting_started:
name: Getting Started
community:
name: Community
trust_level:
name: Trust Level
other:
name: Other
posting:
name: Posting
badge:
editor:
name: Editor

View File

@ -1,34 +1,42 @@
BadgeGrouping.seed do |g|
g.id = 1
g.id = BadgeGrouping::GettingStarted
g.name = "Getting Started"
g.position = BadgeGrouping::Position::GettingStarted
g.position = 10
end
BadgeGrouping.seed do |g|
g.id = 2
g.id = BadgeGrouping::Community
g.name = "Community"
g.position = BadgeGrouping::Position::Community
g.position = 11
end
BadgeGrouping.seed do |g|
g.id = 3
g.id = BadgeGrouping::Posting
g.name = "Posting"
g.position = BadgeGrouping::Position::Posting
g.position = 12
end
BadgeGrouping.seed do |g|
g.id = 4
g.id = BadgeGrouping::TrustLevel
g.name = "Trust Level"
g.position = BadgeGrouping::Position::TrustLevel
g.position = 13
end
BadgeGrouping.seed do |g|
g.id = 5
g.id = BadgeGrouping::Other
g.name = "Other"
g.position = BadgeGrouping::Position::Other
g.position = 14
end
# BUGFIX
Badge.exec_sql 'UPDATE badges
SET badge_grouping_id = NULL
WHERE NOT EXISTS (
SELECT 1 FROM badge_groupings g
WHERE g.id = badge_grouping_id
)'
# Trust level system badges.
trust_level_badges = [
{id: 1, name: "Basic User", type: BadgeType::Bronze},
@ -43,7 +51,7 @@ trust_level_badges.each do |spec|
b.default_name = spec[:name]
b.badge_type_id = spec[:type]
b.query = Badge::Queries.trust_level(spec[:id])
b.default_badge_grouping_id = BadgeGrouping::Position::TrustLevel
b.default_badge_grouping_id = BadgeGrouping::TrustLevel
# allow title for leader and elder
b.allow_title = spec[:id] > 2
@ -57,7 +65,7 @@ Badge.seed do |b|
b.multiple_grant = false
b.target_posts = false
b.query = Badge::Queries::Reader
b.default_badge_grouping_id = BadgeGrouping::Position::GettingStarted
b.default_badge_grouping_id = BadgeGrouping::GettingStarted
b.auto_revoke = false
end
@ -68,7 +76,7 @@ Badge.seed do |b|
b.multiple_grant = false
b.target_posts = false
b.query = Badge::Queries::ReadGuidelines
b.default_badge_grouping_id = BadgeGrouping::Position::GettingStarted
b.default_badge_grouping_id = BadgeGrouping::GettingStarted
end
Badge.seed do |b|
@ -78,7 +86,7 @@ Badge.seed do |b|
b.multiple_grant = false
b.target_posts = true
b.query = Badge::Queries::FirstLink
b.default_badge_grouping_id = BadgeGrouping::Position::GettingStarted
b.default_badge_grouping_id = BadgeGrouping::GettingStarted
end
Badge.seed do |b|
@ -88,7 +96,7 @@ Badge.seed do |b|
b.multiple_grant = false
b.target_posts = true
b.query = Badge::Queries::FirstQuote
b.default_badge_grouping_id = BadgeGrouping::Position::GettingStarted
b.default_badge_grouping_id = BadgeGrouping::GettingStarted
end
Badge.seed do |b|
@ -98,7 +106,7 @@ Badge.seed do |b|
b.multiple_grant = false
b.target_posts = true
b.query = Badge::Queries::FirstLike
b.default_badge_grouping_id = BadgeGrouping::Position::GettingStarted
b.default_badge_grouping_id = BadgeGrouping::GettingStarted
end
Badge.seed do |b|
@ -108,7 +116,7 @@ Badge.seed do |b|
b.multiple_grant = false
b.target_posts = false
b.query = Badge::Queries::FirstFlag
b.default_badge_grouping_id = BadgeGrouping::Position::Community
b.default_badge_grouping_id = BadgeGrouping::Community
end
Badge.seed do |b|
@ -118,7 +126,7 @@ Badge.seed do |b|
b.multiple_grant = false
b.target_posts = true
b.query = Badge::Queries::FirstShare
b.default_badge_grouping_id = BadgeGrouping::Position::GettingStarted
b.default_badge_grouping_id = BadgeGrouping::GettingStarted
end
Badge.seed do |b|
@ -128,7 +136,7 @@ Badge.seed do |b|
b.multiple_grant = false
b.target_posts = true
b.query = Badge::Queries::Welcome
b.default_badge_grouping_id = BadgeGrouping::Position::Community
b.default_badge_grouping_id = BadgeGrouping::Community
end
Badge.seed do |b|
@ -137,7 +145,7 @@ Badge.seed do |b|
b.badge_type_id = BadgeType::Bronze
b.multiple_grant = false
b.query = Badge::Queries::Autobiographer
b.default_badge_grouping_id = BadgeGrouping::Position::GettingStarted
b.default_badge_grouping_id = BadgeGrouping::GettingStarted
end
Badge.seed do |b|
@ -146,7 +154,7 @@ Badge.seed do |b|
b.badge_type_id = BadgeType::Bronze
b.multiple_grant = false
b.query = Badge::Queries::Editor
b.default_badge_grouping_id = BadgeGrouping::Position::Community
b.default_badge_grouping_id = BadgeGrouping::Community
end
#
@ -165,6 +173,6 @@ like_badges.each do |spec|
b.multiple_grant = spec[:multiple]
b.target_posts = true
b.query = Badge::Queries.like_badge(Badge.like_badge_counts[spec[:id]])
b.default_badge_grouping_id = BadgeGrouping::Position::Posting
b.default_badge_grouping_id = BadgeGrouping::Posting
end
end

View File

@ -0,0 +1,5 @@
class SetDefaultBadgeGrouping < ActiveRecord::Migration
def change
change_column :badges, :badge_grouping_id, :integer, null: false, default: 5
end
end