diff --git a/src/app/misc.coffee b/src/app/misc.coffee index ce14ca9f2a..ee3ad2c173 100644 --- a/src/app/misc.coffee +++ b/src/app/misc.coffee @@ -4,7 +4,7 @@ items = require('habitrpg-shared/script/items').items helpers = require('habitrpg-shared/script/helpers') module.exports.batchTxn = batchTxn = (model, cb, options) -> - user = model.at("_user") + user = options?.user or model.at("_user") uObj = hydrate(user.get()) # see https://github.com/codeparty/racer/issues/116 batch = set: (k,v) -> helpers.dotSet(k,v,uObj); paths[k] = true @@ -19,6 +19,7 @@ module.exports.batchTxn = batchTxn = (model, cb, options) -> unless _.isEmpty paths setOps = _.reduce paths, ((m,v,k)-> m[k] = helpers.dotGet(k,uObj);m), {} user.set "update__", setOps, options?.done + else options?.done?() ret #TODO put this in habitrpg-shared diff --git a/src/server/api.coffee b/src/server/api.coffee index 2539a5bdbd..4d8bd79c4f 100644 --- a/src/server/api.coffee +++ b/src/server/api.coffee @@ -12,10 +12,23 @@ utils = require 'derby-auth/utils' NO_TOKEN_OR_UID = err: "You must include a token and uid (user id) in your request" NO_USER_FOUND = err: "No user found." -addTask = (user, task) -> +addTask = (user, task, cb) -> task.type ?= 'habit' - tid = user.add "tasks", task - user.push "#{task.type}Ids", tid + tid = user.add "tasks", task, -> + user.push "#{task.type}Ids", tid, cb + +deleteTask = (user, task, cb) -> + user.del "tasks.#{task.id}", -> + taskIds = user.get "#{task.type}Ids" + user.remove "#{task.type}Ids", taskIds.indexOf(task.id), 1, cb + +score = (model, user, taskId, direction, cb) -> + delta = 0 + misc.batchTxn model, (uObj, paths) -> + tObj = uObj.tasks[taskId] + delta = algos.score(uObj, tObj, direction, {paths}) + , {user, cb} + delta # ---------- /api/v1 API ------------ # Every url added beneath router is prefaced by /api/v1 @@ -202,12 +215,7 @@ router.put '/user/task/:id', auth, validateTask, (req, res) -> DELETE /user/task/:id ### router.delete '/user/task/:id', auth, validateTask, (req, res) -> - taskIds = req.user.get "#{req.task.type}Ids" - - req.user.del "tasks.#{req.task.id}" - # Remove one id from array of typeIds - req.user.remove "#{req.task.type}Ids", taskIds.indexOf(req.task.id), 1 - + deleteTask user, req.task.type, req.task.id res.send 204 ### @@ -297,15 +305,10 @@ scoreTask = (req, res, next) -> addTask user, task # TODO - could modify batchTxn to conform to this better - uObj = req.user.get() - tObj = uObj.tasks[taskId] - paths = {} - delta = algos.score(uObj, tObj, direction, {paths}) - _.each paths, (v,k) -> user.set(k,helpers.dotGet(k, uObj));true - - result = uObj.stats - result.delta = delta - res.json result + delta = score model, req.user, taskId, direction, -> + result = user.get('stats') + result.delta = delta + res.json result ### POST /user/tasks/:taskId/:direction @@ -318,3 +321,6 @@ module.exports.auth = auth module.exports.scoreTask = scoreTask # export so deprecated can call it module.exports.NO_TOKEN_OR_UID = NO_TOKEN_OR_UID module.exports.NO_USER_FOUND = NO_USER_FOUND +module.exports.addTask = addTask +module.exports.score = score +module.exports.deleteTask = deleteTask diff --git a/src/server/apiv2.coffee b/src/server/apiv2.coffee index 84ebd0de2d..33d2db0108 100644 --- a/src/server/apiv2.coffee +++ b/src/server/apiv2.coffee @@ -1,6 +1,6 @@ express = require 'express' router = new express.Router() -util = require('util') +util = require 'util' _ = require 'lodash' algos = require 'habitrpg-shared/script/algos' @@ -23,71 +23,65 @@ router.get '/status', (req, res) -> ### POST new actions ### -router.post '/', api.auth, (req, res) -> +router.post '/', api.auth, (req, res, next) -> model = req.getModel() - user = req.user + {user} = req actions = req.body - console.log util.inspect req.body + #console.log util.inspect req.body + doneCount = 0 + done = (err) -> + return next(err) if err + if --doneCount is 0 + uObj = misc.hydrate user.get() + #transform user structure FROM user.tasks{} + user.habitIds[] TO user.habits[] + user.todos[] etc. + _.each ['habit','daily','todo','reward'], (type) -> + uObj["#{type}s"] = _.where(uObj.tasks, {type}); true + delete uObj["#{type}Ids"] + delete uObj.tasks + res.json 200, uObj + console.log "Reply sent" misc.batchTxn model, (uObj, paths) -> - # habitrpg-shared/algos requires uObj.habits, uObj.dailys etc instead of uObj.tasks + doneCount++ + # habitrpg-shared/algos requires uObj.habits, uObj.dailys etc instead of uObj.tasks _.each ['habit','daily','todo','reward'], (type) -> uObj["#{type}s"] = _.where(uObj.tasks, {type}); true algos.cron uObj, {paths} - ,{cron:true} - - - _.each ['habit', 'daily', 'todo', 'reward'], (type) -> - model.refList "_#{type}List", "_user.tasks", "_user.#{type}Ids" + , {user, done, cron:true} if _.isArray actions actions.forEach (action)-> - task = {} - if action.task? then task = action.task + doneCount++ - if action.op == "score" - if task.type == "daily" || task.type == "todo" -# switch completed state. Since checkbox is not binded to model unlike when you click through Derby website. - completed = if action.dir == "up" then true else false - user.set("tasks.#{task.id}.completed", completed) - misc.score(model, task.id, action.dir, true) + task = action.task ? {} - if action.op == "sortTask" - path = action.task.type + "Ids" - a=user.get(path) - a.splice(action.to, 0, a.splice(action.from, 1)[0]) - user.set(path, a) + switch action.op + when "score" + if task.type in ["daily","todo"] + # switch completed state. Since checkbox is not binded to model unlike when you click through Derby website. + completed = if action.dir is "up" then true else false + user.set "tasks.#{task.id}.completed", completed, done + doneCount++ + api.score model, user, task.id, action.dir, done - if action.op == "addTask" - model.unshift "_#{task.type}List", task + when "sortTask" + path = action.task.type + "Ids" + a = user.get(path) + a.splice(action.to, 0, a.splice(action.from, 1)[0]) + user.set(path, a) - if action.op == "delTask" -# to make sure we update DOM on Derby client - ids = user.get(task.type + 'Ids') - ids.splice(ids.indexOf(task.id), 1); - user.set(task.type + 'Ids', ids) + when "addTask" + api.addTask user, task, done - # Actually delete the task - user.del ("tasks." + task.id) + when "delTask" + api.deleteTask user, task, done - - # this API is only working with string or number variables. It should return error if object given or object is at the path. - if action.op == "set" - oldValue = user.get(action.path); - if (typeof action.value == "number" || typeof action.value == "string") - if (typeof oldValue == "number" || typeof oldValue == "string") - user.set(action.path, action.value) - - user = misc.hydrate user.get() - - #transform user structure FROM user.tasks{} + user.habitIds[] TO user.habits[] + user.todos[] etc. - ["habit", "daily", "todo", "reward"].forEach (type) -> - user[type + 's'] = [] - user[type + 'Ids'].forEach (id)-> - user[type + 's'].push(user.tasks[id]) - delete user[type + 'Ids'] - delete user.tasks - res.json 200, user - console.log "Reply sent" + # this API is only working with string or number variables. It should return error if object given or object is at the path. + when "set" + oldValue = user.get(action.path) + if _.isObject(action.value) or _.isObject(oldValue) + console.error "action.value was an object, which isn't currently supported. Tyler - double check this" + else + user.set action.path, action.value, done module.exports = router \ No newline at end of file