mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-19 07:37:25 +01:00
between client & server. If the op is called on the client, it updates the user & then POSTs to the server with op of the same name. If called on server, it updates the user and user.save()s
122 lines
3.7 KiB
JavaScript
122 lines
3.7 KiB
JavaScript
var mongoose = require("mongoose");
|
|
var Schema = mongoose.Schema;
|
|
var shared = require('habitrpg-shared');
|
|
var _ = require('lodash');
|
|
var TaskSchemas = require('./task');
|
|
var Group = require('./group').model;
|
|
|
|
var ChallengeSchema = new Schema({
|
|
_id: {type: String, 'default': shared.uuid},
|
|
name: String,
|
|
shortName: String,
|
|
description: String,
|
|
habits: [TaskSchemas.HabitSchema],
|
|
dailys: [TaskSchemas.DailySchema],
|
|
todos: [TaskSchemas.TodoSchema],
|
|
rewards: [TaskSchemas.RewardSchema],
|
|
leader: {type: String, ref: 'User'},
|
|
group: {type: String, ref: 'Group'},
|
|
timestamp: {type: Date, 'default': Date.now},
|
|
members: [{type: String, ref: 'User'}],
|
|
memberCount: {type: Number, 'default': 0},
|
|
prize: {type: Number, 'default': 0}
|
|
});
|
|
|
|
ChallengeSchema.virtual('tasks').get(function () {
|
|
var tasks = this.habits.concat(this.dailys).concat(this.todos).concat(this.rewards);
|
|
var tasks = _.object(_.pluck(tasks,'id'), tasks);
|
|
return tasks;
|
|
});
|
|
|
|
// FIXME this isn't always triggered, since we sometimes use update() or findByIdAndUpdate()
|
|
// @see https://github.com/LearnBoost/mongoose/issues/964
|
|
ChallengeSchema.pre('save', function(next){
|
|
this.memberCount = _.size(this.members);
|
|
next()
|
|
})
|
|
|
|
ChallengeSchema.methods.toJSON = function(){
|
|
var doc = this.toObject();
|
|
doc.memberCount = doc.members ? _.size(doc.members) : doc.memberCount; // @see pre('save') comment above
|
|
doc._isMember = this._isMember;
|
|
return doc;
|
|
}
|
|
|
|
// --------------
|
|
// Syncing logic
|
|
// --------------
|
|
|
|
function syncableAttrs(task) {
|
|
var t = (task.toObject) ? task.toObject() : task; // lodash doesn't seem to like _.omit on EmbeddedDocument
|
|
// only sync/compare important attrs
|
|
var omitAttrs = 'history tags completed streak'.split(' ');
|
|
if (t.type != 'reward') omitAttrs.push('value');
|
|
return _.omit(t, omitAttrs);
|
|
}
|
|
|
|
/**
|
|
* Compare whether any changes have been made to tasks. If so, we'll want to sync those changes to subscribers
|
|
*/
|
|
function comparableData(obj) {
|
|
return (
|
|
_.chain(obj.habits.concat(obj.dailys).concat(obj.todos).concat(obj.rewards))
|
|
.sortBy('id') // we don't want to update if they're sort-order is different
|
|
.transform(function(result, task){
|
|
result.push(syncableAttrs(task));
|
|
}))
|
|
.toString(); // for comparing arrays easily
|
|
}
|
|
|
|
ChallengeSchema.methods.isOutdated = function(newData) {
|
|
return comparableData(this) !== comparableData(newData);
|
|
}
|
|
|
|
/**
|
|
* Syncs all new tasks, deleted tasks, etc to the user object
|
|
* @param user
|
|
* @return nothing, user is modified directly. REMEMBER to save the user!
|
|
*/
|
|
ChallengeSchema.methods.syncToUser = function(user, cb) {
|
|
if (!user) return;
|
|
var self = this;
|
|
self.shortName = self.shortName || self.name;
|
|
|
|
// Sync tags
|
|
var tags = user.tags || [];
|
|
var i = _.findIndex(tags, {id: self._id})
|
|
if (~i) {
|
|
if (tags[i].name !== self.shortName) {
|
|
// update the name - it's been changed since
|
|
user.tags[i].name = self.shortName;
|
|
}
|
|
} else {
|
|
user.tags.push({
|
|
id: self._id,
|
|
name: self.shortName,
|
|
challenge: true
|
|
});
|
|
}
|
|
|
|
// Sync new tasks and updated tasks
|
|
_.each(self.tasks, function(task){
|
|
var list = user[task.type+'s'];
|
|
var userTask = user.tasks[task.id] || (list.push(syncableAttrs(task)), list[list.length-1]);
|
|
userTask.challenge = {id:self._id};
|
|
userTask.tags = userTask.tags || {};
|
|
userTask.tags[self._id] = true;
|
|
_.merge(userTask, syncableAttrs(task));
|
|
})
|
|
|
|
// Flag deleted tasks as "broken"
|
|
_.each(user.tasks, function(task){
|
|
if (task.challenge && task.challenge.id==self._id && !self.tasks[task.id]) {
|
|
task.challenge.broken = 'TASK_DELETED';
|
|
}
|
|
})
|
|
|
|
user.save(cb);
|
|
};
|
|
|
|
|
|
module.exports.schema = ChallengeSchema;
|
|
module.exports.model = mongoose.model("Challenge", ChallengeSchema); |