Achievements now popup when polled for by the client

This commit is contained in:
Ruben Vereecken 2014-05-24 20:45:53 +02:00
parent aa3fedeb02
commit d766a24e11
12 changed files with 106 additions and 13 deletions

View file

@ -3,4 +3,8 @@ CocoModel = require './CocoModel'
module.exports = class Achievement extends CocoModel
@className: 'Achievement'
@schema: require 'schemas/models/achievement'
urlRoot: '/db/achievement'
urlRoot: '/db/achievement'
initialize: (id) ->
super()
@set('_id', id) if id?

View file

@ -1,6 +1,10 @@
storage = require 'lib/storage'
deltasLib = require 'lib/deltas'
class NewAchievementCollection extends Backbone.Collection
initialize: (me = require('lib/auth').me) ->
@url = "/db/user/#{me.id}/achievements?notified=false"
class CocoModel extends Backbone.Model
idAttribute: "_id"
loaded: false
@ -86,6 +90,7 @@ class CocoModel extends Backbone.Model
success(@, res) if success
@markToRevert() if @_revertAttributes
@clearBackup()
CocoModel.pollAchievements()
options.error = (model, res) =>
error(@, res) if error
return unless @notyErrors
@ -266,4 +271,14 @@ class CocoModel extends Backbone.Model
getURL: ->
return if _.isString @url then @url else @url()
@pollAchievements: ->
achievements = new NewAchievementCollection
achievements.fetch(
success: (collection) ->
Backbone.Mediator.publish('achievements:new', collection) unless _.isEmpty(collection.models)
)
CocoModel.pollAchievements = _.debounce CocoModel.pollAchievements, 500
module.exports = CocoModel

View file

@ -21,12 +21,12 @@ MongoFindQuerySchema =
type: 'object'
patternProperties:
#'^[-a-zA-Z0-9_]*$':
'^[-a-zA-Z0-9]*$':
'^[-a-zA-Z0-9\.]*$':
oneOf: [
#{ $ref: '#/definitions/' + MongoQueryOperatorSchema.id},
{ type: 'string' }
]
additionalProperties: true
additionalProperties: true # TODO make Treema accept new pattern matched keys
definitions: {}
MongoFindQuerySchema.definitions[MongoQueryOperatorSchema.id] = MongoQueryOperatorSchema

19
app/styles/notify.sass Normal file
View file

@ -0,0 +1,19 @@
.notifyjs-achievement-base
opacity: 0.85
width: 200px
background: #F5F5F5
padding: 5px
border-radius: 10px
text-align: center
.achievement-title
font-weight: bold
.achievement-image
// pass
.achievement-name
// pass
.achievement-description
// pass

View file

@ -0,0 +1,6 @@
div
div.clearfix
div.achievement-title(data-notify-html="title")
div.achievement-image(data-notify-html="image")
div.achievement-name(data-notify-html="name")
div.achievement-description(data-notify-html="description")

View file

@ -12,7 +12,7 @@ body
a.navbar-brand(href='/')
img(src="/images/pages/base/logo.png", title="CodeCombat - Learn how to code by playing a game", alt="CodeCombat")
.collapse.navbar-collapse#collapsible-navbar
.collapse.navbar-collapse#collapsible-navbar
ul.nav.navbar-nav
li.play
a.header-font(href='/play', data-i18n="nav.play") Levels

View file

@ -6,6 +6,9 @@ CocoView = require './CocoView'
{logoutUser, me} = require('lib/auth')
locale = require 'locale/locale'
AchievementNotify = require '../../templates/achievement_notify'
Achievement = require '../../models/Achievement'
filterKeyboardEvents = (allowedEvents, func) ->
return (splat...) ->
e = splat[0]
@ -19,6 +22,41 @@ module.exports = class RootView extends CocoView
'click .toggle-fullscreen': 'toggleFullscreen'
'click .auth-button': 'onClickAuthbutton'
subscriptions:
'achievements:new': 'handleNewAchievements'
initialize: ->
$ ->
$.notify.addStyle 'achievement',
html: $(AchievementNotify())
showNewAchievement: (achievement) ->
imageURL = '/file/' + achievement.get('icon')
data =
title: 'Achievement Unlocked'
name: achievement.get('name')
image: $("<img src='#{imageURL}' />")
description: achievement.get('description')
options =
autoHideDelay: 15000
globalPosition: 'bottom right'
showDuration: 400
style: 'achievement'
autoHide: true
clickToHide: true
$.notify( data, options )
handleNewAchievements: (earnedAchievements) ->
# TODO performance?
_.each(earnedAchievements.models, (earnedAchievement) =>
achievement = new Achievement(earnedAchievement.get('achievement'))
achievement.fetch(
success: @showNewAchievement
)
)
logoutAccount: ->
logoutUser($('#login-email').val())

View file

@ -16,7 +16,7 @@ AchievementSchema.methods.objectifyQuery = () ->
try
@set('query', JSON.parse(@get('query'))) if typeof @get('query') == "string"
catch error
#log.error "Couldn't convert query string to object because of #{error}"
log.error "Couldn't convert query string to object because of #{error}"
@set('query', {})
AchievementSchema.methods.stringifyQuery = () ->

View file

@ -13,7 +13,7 @@ EarnedAchievementSchema = new mongoose.Schema({
default: false
}, {strict:false})
# Maybe consider indexing on changed: -1 as well?
EarnedAchievementSchema.index({user: 1, achievement: 1}, {unique: true, name: 'earned achievement index'})
EarnedAchievementSchema.index({user: 1, changed: -1}, {name: 'latest '})
module.exports = EarnedAchievement = mongoose.model('EarnedAchievement', EarnedAchievementSchema)

View file

@ -167,7 +167,7 @@ module.exports = class Handler
projection = PROJECT
else if req.query.project
if @modelClass.className is 'User'
projection = PROJECTION
projection = PROJECT
log.warn "Whoa, we haven't yet thought about public properties for User projection yet."
else
projection = {}

View file

@ -18,8 +18,6 @@ loadAchievements = ->
loadAchievements()
# TODO make a difference between '$userID' and '$userObjectID' ?
module.exports = AchievablePlugin = (schema, options) ->
checkForAchievement = (doc) ->
collectionName = doc.constructor.modelName
@ -43,9 +41,11 @@ module.exports = AchievablePlugin = (schema, options) ->
for achievement in achievements[category]
query = achievement.get('query')
isRepeatable = achievement.get('proportionalTo')?
console.log 'isRepeatable: ' + isRepeatable
alreadyAchieved = if isNew then false else LocalMongo.matchesQuery originalDocObj, query
newlyAchieved = LocalMongo.matchesQuery(docObj, query)
console.log 'isRepeatable: ' + isRepeatable
console.log 'alreadyAchieved: ' + alreadyAchieved
console.log 'newlyAchieved: ' + newlyAchieved
userObjectID = doc.get(achievement.get('userField'))
userID = if _.isObject userObjectID then userObjectID.toHexString() else userObjectID # Standardize! Use strings, not ObjectId's

View file

@ -21,6 +21,12 @@ candidateProperties = [
'jobProfile', 'jobProfileApproved', 'jobProfileNotes'
]
parseLiteral = (literalString) ->
return true if literalString is 'true'
return false if literalString is 'false'
return number if (number = Number(literalString)) isnt NaN
literalString
UserHandler = class UserHandler extends Handler
modelClass: User
@ -238,11 +244,16 @@ UserHandler = class UserHandler extends Handler
@sendSuccess(res, documents)
getEarnedAchievements: (req, res, userID) ->
query = EarnedAchievement.find(user: userID)
queryObject = {$query: {user: userID}, $orderby: {changed: -1}}
queryObject.$query[key] = parseLiteral(val) for key, val of req.query
query = EarnedAchievement.find(queryObject)
query.exec (err, documents) =>
return @sendDatabaseError(res, err) if err?
documents = (@formatEntity(req, doc) for doc in documents)
@sendSuccess(res, documents)
cleandocs = (@formatEntity(req, doc) for doc in documents)
for doc in documents # Maybe move this logic elsewhere
doc.set('notified', true)
doc.save()
@sendSuccess(res, cleandocs)
agreeToEmployerAgreement: (req, res) ->
userIsAnonymous = req.user?.get('anonymous')