mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-05-08 15:16:33 +00:00
Beginnings of toggling days-of-week for repeatable dailies
Conflicts: lib/app/index.js package.json src/app/index.coffee
This commit is contained in:
parent
33c285c398
commit
9c58a7809f
11 changed files with 128 additions and 37 deletions
|
|
@ -1,4 +1,7 @@
|
|||
// Generated by CoffeeScript 1.3.3
|
||||
var moment;
|
||||
|
||||
moment = require('moment');
|
||||
|
||||
module.exports.daysBetween = function(a, b) {
|
||||
var DAY;
|
||||
|
|
@ -9,10 +12,20 @@ module.exports.daysBetween = function(a, b) {
|
|||
};
|
||||
|
||||
module.exports.viewHelpers = function(view) {
|
||||
view.fn('taskClasses', function(type, completed, value) {
|
||||
var classes;
|
||||
view.fn('taskClasses', function(type, completed, value, repeat) {
|
||||
var classes, dayMapping;
|
||||
classes = type;
|
||||
if (completed) {
|
||||
dayMapping = {
|
||||
0: 'su',
|
||||
1: 'm',
|
||||
2: 't',
|
||||
3: 'w',
|
||||
4: 'th',
|
||||
5: 'f',
|
||||
6: 's',
|
||||
7: 'su'
|
||||
};
|
||||
if (completed || (repeat && repeat[dayMapping[moment().day()]] === false)) {
|
||||
classes += " completed";
|
||||
}
|
||||
switch (false) {
|
||||
|
|
|
|||
|
|
@ -178,6 +178,22 @@ ready(function(model) {
|
|||
value: 20
|
||||
});
|
||||
case 'daily':
|
||||
return list.push({
|
||||
type: type,
|
||||
text: text,
|
||||
notes: '',
|
||||
value: 0,
|
||||
repeat: {
|
||||
su: true,
|
||||
m: true,
|
||||
t: true,
|
||||
w: true,
|
||||
th: true,
|
||||
f: true,
|
||||
s: true
|
||||
},
|
||||
completed: false
|
||||
});
|
||||
case 'todo':
|
||||
return list.push({
|
||||
type: type,
|
||||
|
|
@ -222,6 +238,15 @@ ready(function(model) {
|
|||
return model.set('_user.completedIds', []);
|
||||
});
|
||||
};
|
||||
exports.toggleDay = function(e, el) {
|
||||
var task;
|
||||
task = model.at(e.target);
|
||||
if (/active/.test($(el).attr('class'))) {
|
||||
return task.set('repeat.' + $(el).attr('data-day'), false);
|
||||
} else {
|
||||
return task.set('repeat.' + $(el).attr('data-day'), true);
|
||||
}
|
||||
};
|
||||
exports.toggleTaskEdit = function(e, el) {
|
||||
var hideId, toggleId;
|
||||
hideId = $(el).attr('data-hide-id');
|
||||
|
|
@ -324,8 +349,10 @@ ready(function(model) {
|
|||
daysPassed = helpers.daysBetween(lastCron, today);
|
||||
if (daysPassed > 0) {
|
||||
model.set('_user.lastCron', today);
|
||||
return _(daysPassed).times(function() {
|
||||
return scoring.tally(model.at('_user'));
|
||||
return _(daysPassed).times(function(n) {
|
||||
var tallyFor;
|
||||
tallyFor = moment(lastCron).add('d', n);
|
||||
return scoring.tally(model.at('_user'), tallyFor);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -335,10 +362,7 @@ ready(function(model) {
|
|||
setInterval(function() {
|
||||
return poormanscron();
|
||||
}, 3600000);
|
||||
exports.endOfDayTally = function(e, el) {
|
||||
return exports.endOfDayTally = function(e, el) {
|
||||
return scoring.tally(model);
|
||||
};
|
||||
return exports.updateSchema = function(e, el) {
|
||||
return schema.updateSchema(model);
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ module.exports.userSchema = userSchema = {
|
|||
};
|
||||
|
||||
module.exports.updateSchema = function(model) {
|
||||
return;
|
||||
return model.fetch('users', function(err, users) {
|
||||
return _.each(users.get(), function(userObj) {
|
||||
var daysOld, sameTasks, user, userPath;
|
||||
|
|
|
|||
|
|
@ -150,28 +150,40 @@ module.exports.score = score = function(spec) {
|
|||
return delta;
|
||||
};
|
||||
|
||||
module.exports.tally = function(user) {
|
||||
module.exports.tally = function(user, momentDate) {
|
||||
var expTally, lvl, todoTally;
|
||||
todoTally = 0;
|
||||
_.each(user.get('tasks'), function(taskObj, taskId, list) {
|
||||
var absVal, completed, task, type, value, _ref;
|
||||
var absVal, completed, dayMapping, repeat, task, type, value, _ref;
|
||||
if (taskObj.id == null) {
|
||||
return;
|
||||
}
|
||||
_ref = [taskObj.type, taskObj.value, taskObj.completed], type = _ref[0], value = _ref[1], completed = _ref[2];
|
||||
_ref = [taskObj.type, taskObj.value, taskObj.completed, taskObj.repeat], type = _ref[0], value = _ref[1], completed = _ref[2], repeat = _ref[3];
|
||||
task = user.at("tasks." + taskId);
|
||||
if (type === 'todo' || type === 'daily') {
|
||||
if (!completed) {
|
||||
score({
|
||||
user: user,
|
||||
task: task,
|
||||
direction: 'down',
|
||||
cron: true
|
||||
});
|
||||
dayMapping = {
|
||||
0: 'su',
|
||||
1: 'm',
|
||||
2: 't',
|
||||
3: 'w',
|
||||
4: 'th',
|
||||
5: 'f',
|
||||
6: 's',
|
||||
7: 'su'
|
||||
};
|
||||
if (repeat && repeat[dayMapping[momentDate.day()]] === true) {
|
||||
score({
|
||||
user: user,
|
||||
task: task,
|
||||
direction: 'down',
|
||||
cron: true
|
||||
});
|
||||
}
|
||||
}
|
||||
if (type === 'daily') {
|
||||
task.push("history", {
|
||||
date: new Date(),
|
||||
date: new Date(momentDate),
|
||||
value: value
|
||||
});
|
||||
} else {
|
||||
|
|
@ -186,7 +198,7 @@ module.exports.tally = function(user) {
|
|||
}
|
||||
});
|
||||
user.push('history.todos', {
|
||||
date: new Date(),
|
||||
date: new Date(momentDate),
|
||||
value: todoTally
|
||||
});
|
||||
expTally = user.get('stats.exp');
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@
|
|||
"connect-mongo": ">=0.1.9",
|
||||
"derby-ui-boot": "*",
|
||||
"guid": "*",
|
||||
"node.extend": "*"
|
||||
"node.extend": "*",
|
||||
"moment": "*"
|
||||
},
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
|
|
|
|||
6
public/js/moment.min.js
vendored
Normal file
6
public/js/moment.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,5 +1,7 @@
|
|||
moment = require('moment')
|
||||
|
||||
module.exports.daysBetween = (a, b) ->
|
||||
#TODO replace this function with moment().diff()?
|
||||
DAY = 1000 * 60 * 60 * 24
|
||||
# calculate as midnight
|
||||
a = new Date( (new Date(a)).toDateString() )
|
||||
|
|
@ -7,10 +9,14 @@ module.exports.daysBetween = (a, b) ->
|
|||
return Math.abs(Math.floor((a.getTime() - b.getTime()) / DAY))
|
||||
|
||||
module.exports.viewHelpers = (view) ->
|
||||
view.fn 'taskClasses', (type, completed, value) ->
|
||||
view.fn 'taskClasses', (type, completed, value, repeat) ->
|
||||
#TODO figure out how to just pass in the task model, so i can access all these properties from one object
|
||||
classes = type
|
||||
classes += " completed" if completed
|
||||
|
||||
# show as completed if completed (naturally) or not required for today
|
||||
dayMapping = {0:'su',1:'m',2:'t',3:'w',4:'th',5:'f',6:'s',7:'su'}
|
||||
if completed or (repeat and repeat[dayMapping[moment().day()]]==false)
|
||||
classes += " completed"
|
||||
|
||||
switch
|
||||
when value<-8 then classes += ' color-worst'
|
||||
|
|
|
|||
|
|
@ -147,7 +147,10 @@ ready (model) ->
|
|||
when 'reward'
|
||||
list.push {type: type, text: text, notes: '', value: 20 }
|
||||
|
||||
when 'daily', 'todo'
|
||||
when 'daily'
|
||||
list.push {type: type, text: text, notes: '', value: 0, repeat:{su:true,m:true,t:true,w:true,th:true,f:true,s:true}, completed: false }
|
||||
|
||||
when 'todo'
|
||||
list.push {type: type, text: text, notes: '', value: 0, completed: false }
|
||||
|
||||
# list.on 'set', '*.completed', (i, completed, previous, isLocal) ->
|
||||
|
|
@ -184,6 +187,13 @@ ready (model) ->
|
|||
_.each model.get('_completedList'), (task) ->
|
||||
model.del('_user.tasks.'+task.id)
|
||||
model.set('_user.completedIds', [])
|
||||
|
||||
exports.toggleDay = (e, el) ->
|
||||
task = model.at(e.target)
|
||||
if /active/.test($(el).attr('class')) # previous state, not current
|
||||
task.set('repeat.' + $(el).attr('data-day'), false)
|
||||
else
|
||||
task.set('repeat.' + $(el).attr('data-day'), true)
|
||||
|
||||
exports.toggleTaskEdit = (e, el) ->
|
||||
hideId = $(el).attr('data-hide-id')
|
||||
|
|
@ -267,8 +277,9 @@ ready (model) ->
|
|||
daysPassed = helpers.daysBetween(lastCron, today)
|
||||
if daysPassed > 0
|
||||
model.set('_user.lastCron', today) # reset cron
|
||||
_(daysPassed).times () ->
|
||||
scoring.tally(model.at('_user'))
|
||||
_(daysPassed).times (n) ->
|
||||
tallyFor = moment(lastCron).add('d',n)
|
||||
scoring.tally(model.at('_user'), tallyFor)
|
||||
# FIXME seems can't call poormanscron() instantly, have to call after some time (2s here)
|
||||
# Doesn't do anything otherwise. Don't know why... model not initialized enough yet?
|
||||
setTimeout () -> # Run once on refresh
|
||||
|
|
@ -284,6 +295,6 @@ ready (model) ->
|
|||
scoring.tally(model)
|
||||
|
||||
# Temporary solution to running updates against the schema when the code changes
|
||||
exports.updateSchema = (e, el) ->
|
||||
schema.updateSchema(model)
|
||||
|
||||
# exports.updateSchema = (e, el) ->
|
||||
# schema.updateSchema(model)
|
||||
|
||||
|
|
@ -11,6 +11,7 @@ module.exports.userSchema = userSchema = {
|
|||
}
|
||||
|
||||
module.exports.updateSchema = (model) ->
|
||||
return
|
||||
# Reset history, remove inactive users
|
||||
model.fetch 'users', (err, users) ->
|
||||
_.each users.get(), (userObj) ->
|
||||
|
|
@ -40,4 +41,4 @@ module.exports.updateSchema = (model) ->
|
|||
_.each userObj.tasks, (taskObj) ->
|
||||
task = user.at "tasks.#{taskObj.id}"
|
||||
if task.get("history")
|
||||
task.set "history", []
|
||||
task.set "history", []
|
||||
|
|
|
|||
|
|
@ -139,24 +139,27 @@ module.exports.score = score = (spec = {user:null, task:null, direction:null, cr
|
|||
|
||||
# At end of day, add value to all incomplete Daily & Todo tasks (further incentive)
|
||||
# For incomplete Dailys, deduct experience
|
||||
module.exports.tally = (user) ->
|
||||
module.exports.tally = (user, momentDate) ->
|
||||
todoTally = 0
|
||||
_.each user.get('tasks'), (taskObj, taskId, list) ->
|
||||
#FIXME is it hiccuping here? taskId == "$_65255f4e-3728-4d50-bade-3b05633639af_2", & taskObj.id = undefined
|
||||
return unless taskObj.id? #this shouldn't be happening, some tasks seem to be corrupted
|
||||
[type, value, completed] = [taskObj.type, taskObj.value, taskObj.completed]
|
||||
[type, value, completed, repeat] = [taskObj.type, taskObj.value, taskObj.completed, taskObj.repeat]
|
||||
task = user.at("tasks.#{taskId}")
|
||||
if type in ['todo', 'daily']
|
||||
# Deduct experience for missed Daily tasks,
|
||||
# but not for Todos (just increase todo's value)
|
||||
score({user:user, task:task, direction:'down', cron:true}) unless completed
|
||||
unless completed
|
||||
dayMapping = {0:'su',1:'m',2:'t',3:'w',4:'th',5:'f',6:'s',7:'su'}
|
||||
if repeat && repeat[dayMapping[momentDate.day()]]==true
|
||||
score({user:user, task:task, direction:'down', cron:true})
|
||||
if type == 'daily'
|
||||
task.push "history", { date: new Date(), value: value }
|
||||
task.push "history", { date: new Date(momentDate), value: value }
|
||||
else
|
||||
absVal = if (completed) then Math.abs(value) else value
|
||||
todoTally += absVal
|
||||
task.pass({cron:true}).set('completed', false) if type == 'daily'
|
||||
user.push 'history.todos', { date: new Date(), value: todoTally }
|
||||
user.push 'history.todos', { date: new Date(momentDate), value: todoTally }
|
||||
|
||||
# tally experience
|
||||
expTally = user.get 'stats.exp'
|
||||
|
|
|
|||
|
|
@ -78,7 +78,6 @@
|
|||
<i class="icon-warning-sign"></i> <b>Debugging Options</b><br/><br/>
|
||||
<a x-bind=click:poormanscron class="btn">Cron</a>
|
||||
<a x-bind=click:endOfDayTally class="btn">Tally</a>
|
||||
<a x-bind=click:updateSchema class="btn">Update Schema</a>
|
||||
</div>
|
||||
{/}
|
||||
|
||||
|
|
@ -203,7 +202,7 @@
|
|||
</form>
|
||||
|
||||
<task:>
|
||||
<li data-id={{:task.id}} class="task {taskClasses(:task.type, :task.completed, :task.value)}">
|
||||
<li data-id={{:task.id}} class="task {taskClasses(:task.type, :task.completed, :task.value, :task.repeat)}">
|
||||
<pre>
|
||||
<div class="task-meta-controls">
|
||||
|
||||
|
|
@ -259,6 +258,19 @@
|
|||
<label class="checkbox inline"><input type=checkbox checked={:task.down}>Down</label>
|
||||
</div>
|
||||
{/}
|
||||
{#if equal(:task.type, 'daily')}
|
||||
<label>Repeat</label>
|
||||
<div class="control-group btn-group repeat-days">
|
||||
<!-- note, does not use data-toggle="buttons-checkbox" - it would interfere with our own click binding -->
|
||||
<button type="button" class="btn btn-info {#if :task.repeat.su}active{/}" data-day='su' x-bind=click:toggleDay>Su</button>
|
||||
<button type="button" class="btn btn-info {#if :task.repeat.m}active{/}" data-day='m' x-bind=click:toggleDay>M</button>
|
||||
<button type="button" class="btn btn-info {#if :task.repeat.t}active{/}" data-day='t' x-bind=click:toggleDay>T</button>
|
||||
<button type="button" class="btn btn-info {#if :task.repeat.w}active{/}" data-day='w' x-bind=click:toggleDay>W</button>
|
||||
<button type="button" class="btn btn-info {#if :task.repeat.th}active{/}" data-day='th' x-bind=click:toggleDay>Th</button>
|
||||
<button type="button" class="btn btn-info {#if :task.repeat.f}active{/}" data-day='f' x-bind=click:toggleDay>F</button>
|
||||
<button type="button" class="btn btn-info {#if :task.repeat.s}active{/}" data-day='s' x-bind=click:toggleDay>S</button>
|
||||
</div>
|
||||
{/}
|
||||
{#if equal(:task.type, 'reward')}
|
||||
<div class=control-group>
|
||||
<label>Price
|
||||
|
|
@ -293,6 +305,7 @@
|
|||
<Scripts:>
|
||||
<script src=/js/jquery.min.js></script>
|
||||
{#unless _mobileDevice}<script src=/js/jquery-ui.min.js></script>{/}
|
||||
<script src=/js/moment.min.js></script><!-- https://raw.github.com/timrwood/moment/1.7.0/min/moment.min.js -->
|
||||
<script src=/js/underscore-min.js></script><!-- http://underscorejs.org/underscore-min.js -->
|
||||
<script src=/js/bootstrap.min.js></script><!-- http://twitter.github.com/bootstrap/assets/js/bootstrap.min.js -->
|
||||
<script src=/js/jquery.cookie.js></script><!-- https://raw.github.com/carhartl/jquery-cookie/master/jquery.cookie.js -->
|
||||
|
|
|
|||
Loading…
Reference in a new issue