mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-04-14 19:56:23 +00:00
Quarters system, generalized modal dialogs, bug fixes
This commit is contained in:
parent
694691f109
commit
be9d419313
11 changed files with 169 additions and 70 deletions
|
|
@ -208,11 +208,7 @@ module.exports = {
|
|||
type: 'reroll',
|
||||
text: "Re-Roll",
|
||||
icon: 'favicon',
|
||||
notes: "Reset your tasks. When you're struggling and everything's red, use for a clean slate. Use sparingly, it costs real money.",
|
||||
rerollWarning: {
|
||||
title: "This item costs real money",
|
||||
content: "Each use is $1, can use 5 times before checkout."
|
||||
},
|
||||
notes: "Resets your tasks. When you're struggling and everything's red, use for a clean slate.",
|
||||
value: 0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,11 +68,24 @@ module.exports.viewHelpers = function(view) {
|
|||
return "0";
|
||||
}
|
||||
});
|
||||
return view.fn("silver", function(num) {
|
||||
view.fn("silver", function(num) {
|
||||
if (num) {
|
||||
return num.toFixed(1).split('.')[1];
|
||||
} else {
|
||||
return "0";
|
||||
}
|
||||
});
|
||||
view.fn("money", function(num) {
|
||||
if (num) {
|
||||
return num.toFixed(2);
|
||||
} else {
|
||||
return "0.00";
|
||||
}
|
||||
});
|
||||
view.fn("lessThan", function(a, b) {
|
||||
return a < b;
|
||||
});
|
||||
return view.fn("quarters", function(money) {
|
||||
return money / 0.25;
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ get('/:uidParam?', function(page, model, _arg, next) {
|
|||
model.set('_userId', sess.userId);
|
||||
return model.subscribe("users." + sess.userId, function(err, user) {
|
||||
model.ref('_user', user);
|
||||
user.setNull('balance', 2);
|
||||
model.set('_items', {
|
||||
armor: content.items.armor[parseInt(user.get('items.armor')) + 1],
|
||||
weapon: content.items.weapon[parseInt(user.get('items.weapon')) + 1],
|
||||
|
|
@ -51,9 +52,7 @@ get('/:uidParam?', function(page, model, _arg, next) {
|
|||
});
|
||||
|
||||
ready(function(model) {
|
||||
var pathParts, poormanscron, setupSortable, step, tour, type, _i, _j, _len, _len1, _ref1, _ref2;
|
||||
pathParts = window.location.toString().split('/');
|
||||
$('#purl').val("" + pathParts[0] + "//" + pathParts[2] + "/" + (model.get('_userId')));
|
||||
var poormanscron, setupSortable, step, tour, type, _i, _j, _len, _len1, _ref1, _ref2;
|
||||
$('[rel=tooltip]').tooltip();
|
||||
$('[rel=popover]').popover();
|
||||
model.on('set', '*', function() {
|
||||
|
|
@ -309,7 +308,8 @@ ready(function(model) {
|
|||
model.set('_user.items.armor', 0);
|
||||
model.set('_user.items.weapon', 0);
|
||||
model.set('_items.armor', content.items.armor[1]);
|
||||
return model.set('_items.weapon', content.items.weapon[1]);
|
||||
model.set('_items.weapon', content.items.weapon[1]);
|
||||
return model.set('_user.balance', model.get('_user.balance') - 0.50);
|
||||
};
|
||||
exports.poormanscron = poormanscron = function() {
|
||||
var daysPassed, lastCron, today;
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ module.exports.score = score = function(spec) {
|
|||
_ref2 = [user.get('stats.money'), user.get('stats.hp'), user.get('stats.exp'), user.get('stats.lvl')], money = _ref2[0], hp = _ref2[1], exp = _ref2[2], lvl = _ref2[3];
|
||||
if (type === 'reward') {
|
||||
money -= task.get('value');
|
||||
num = task.get('value').toFixed(2);
|
||||
num = parseFloat(task.get('value')).toFixed(2);
|
||||
statsNotification("<i class='icon-star'></i>GP -" + num, 'success');
|
||||
if (money < 0) {
|
||||
hp += money;
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ module.exports.newUserAndPurl = function() {
|
|||
}
|
||||
model.set("users." + sess.userId, newUser);
|
||||
}
|
||||
acceptableUid = require('guid').isGuid(uidParam) || (uidParam === '3' || uidParam === '9');
|
||||
if (acceptableUid && sess.userId !== uidParam) {
|
||||
acceptableUid = require('guid').isGuid(uidParam) || (uidParam === '3');
|
||||
if (acceptableUid && sess.userId !== uidParam && !(sess.habitRpgAuth && sess.habitRpgAuth.facebook)) {
|
||||
return sess.userId = uidParam;
|
||||
}
|
||||
};
|
||||
|
|
@ -53,7 +53,7 @@ module.exports.setupEveryauth = function(everyauth) {
|
|||
everyauth.everymodule.findUserById(function(id, callback) {
|
||||
return callback(null, null);
|
||||
});
|
||||
return everyauth.facebook.appId(process.env.FACEBOOK_KEY).appSecret(process.env.FACEBOOK_SECRET).findOrCreateUser(function(session, accessToken, accessTokenExtra, fbUserMetadata) {
|
||||
everyauth.facebook.appId(process.env.FACEBOOK_KEY).appSecret(process.env.FACEBOOK_SECRET).findOrCreateUser(function(session, accessToken, accessTokenExtra, fbUserMetadata) {
|
||||
var model, q;
|
||||
session.habitRpgAuth || (session.habitRpgAuth = {});
|
||||
session.habitRpgAuth.facebook = fbUserMetadata.id;
|
||||
|
|
@ -78,6 +78,14 @@ module.exports.setupEveryauth = function(everyauth) {
|
|||
});
|
||||
return fbUserMetadata;
|
||||
}).redirectPath("/");
|
||||
return everyauth.everymodule.handleLogout(function(req, res) {
|
||||
if (req.session.habitRpgAuth && req.session.habitRpgAuth.facebook) {
|
||||
req.session.habitRpgAuth.facebook = void 0;
|
||||
}
|
||||
req.session.userId = void 0;
|
||||
req.logout();
|
||||
return this.redirect(res, this.logoutRedirectPath());
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.setupQueries = function(store) {
|
||||
|
|
|
|||
|
|
@ -81,10 +81,7 @@ module.exports = {
|
|||
type: 'reroll'
|
||||
text: "Re-Roll"
|
||||
icon: 'favicon'
|
||||
notes: "Reset your tasks. When you're struggling and everything's red, use for a clean slate. Use sparingly, it costs real money."
|
||||
rerollWarning:
|
||||
title: "This item costs real money"
|
||||
content: "Each use is $1, can use 5 times before checkout."
|
||||
notes: "Resets your tasks. When you're struggling and everything's red, use for a clean slate."
|
||||
value:0
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,3 +46,15 @@ module.exports.viewHelpers = (view) ->
|
|||
num.toFixed(1).split('.')[1]
|
||||
else
|
||||
return "0"
|
||||
|
||||
view.fn "money", (num) ->
|
||||
if num
|
||||
return num.toFixed(2)
|
||||
else
|
||||
return "0.00"
|
||||
|
||||
view.fn "lessThan", (a, b) ->
|
||||
a < b
|
||||
|
||||
view.fn "quarters", (money) ->
|
||||
return money/0.25
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ get '/:uidParam?', (page, model, {uidParam}, next) ->
|
|||
model.subscribe "users.#{sess.userId}", (err, user) ->
|
||||
model.ref '_user', user
|
||||
|
||||
user.setNull 'balance', 2
|
||||
|
||||
# Store
|
||||
model.set '_items'
|
||||
armor: content.items.armor[parseInt(user.get('items.armor')) + 1]
|
||||
|
|
@ -234,7 +236,6 @@ ready (model) ->
|
|||
direction = 'down' if direction == 'false/'
|
||||
user = model.at('_user')
|
||||
task = model.at $(el).parents('li')[0]
|
||||
|
||||
scoring.score({user:user, task:task, direction:direction})
|
||||
|
||||
exports.revive = (e, el) ->
|
||||
|
|
@ -244,6 +245,7 @@ ready (model) ->
|
|||
model.set '_user.items.weapon', 0
|
||||
model.set '_items.armor', content.items.armor[1]
|
||||
model.set '_items.weapon', content.items.weapon[1]
|
||||
model.set '_user.balance', (model.get('_user.balance') - 0.50)
|
||||
|
||||
# ========== CRON ==========
|
||||
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ module.exports.score = score = (spec = {user:null, task:null, direction:null, cr
|
|||
if type == 'reward'
|
||||
# purchase item
|
||||
money -= task.get('value')
|
||||
num = task.get('value').toFixed(2)
|
||||
num = parseFloat(task.get('value')).toFixed(2)
|
||||
statsNotification "<i class='icon-star'></i>GP -#{num}", 'success'
|
||||
# if too expensive, reduce health & zero money
|
||||
if money < 0
|
||||
|
|
|
|||
|
|
@ -1,22 +1,45 @@
|
|||
url = 'http://localhost:3000'
|
||||
utils = require('utils')
|
||||
casper = require("casper").create()
|
||||
_ = require('../public/js/underscore-min.js')
|
||||
|
||||
# ---------- Main Stuff ------------
|
||||
|
||||
casper.start url, ->
|
||||
@test.assertTitle "HabitRPG", "homepage title is the one expected"
|
||||
|
||||
# ---------- Todos gain delta on cron ------------
|
||||
# ---------- Setup Tasks ------------
|
||||
casper.then ->
|
||||
todoBefore = @evaluate -> window.DERBY.model.get('_todoList')[0]
|
||||
@then ->
|
||||
@evaluate ->
|
||||
window.DERBY.model.set('_user.lastCron', new Date('09/17/2012'))
|
||||
@then -> @reload()
|
||||
@then ->
|
||||
todoAfter = @evaluate -> window.DERBY.model.get('_todoList')[0]
|
||||
@then -> @test.assert(todoBefore.value > todoAfter.value, "Incomplete TODO gained value on cron")
|
||||
_.each ['habit', 'daily', 'todo', 'reward'], (type) ->
|
||||
# Add 15 of each task type
|
||||
casper.repeat 15, ->
|
||||
casper.fill "form#new-#{type}", {'new-task': type } # why can't I use true here?
|
||||
casper.click "form#new-#{type} input[type=submit]"
|
||||
# @then ->
|
||||
# utils.dump @evaluate -> _.pluck(window.DERBY.model.get('_user.tasks'), 'text')
|
||||
|
||||
|
||||
# ---------- Run Cron ------------
|
||||
casper.then ->
|
||||
@evaluate -> window.DERBY.model.set('_user.lastCron', new Date('09/01/2012'));
|
||||
# @then -> @reload ->
|
||||
# @echo 'Refreshing pag (running cron)'
|
||||
# @then -> @reload ->
|
||||
# @echo 'Refreshing page (trying to trigger the database-spaz bug)'
|
||||
|
||||
# ---------- Todos gain delta on cron ------------
|
||||
# casper.then ->
|
||||
# todoBefore = @evaluate -> window.DERBY.model.get('_todoList')[0]
|
||||
# @then ->
|
||||
# @evaluate ->
|
||||
# window.DERBY.model.set('_user.lastCron', new Date('09/17/2012'))
|
||||
# @then -> @reload()
|
||||
# @then ->
|
||||
# @wait 2000, ->
|
||||
# todoAfter = @evaluate -> window.DERBY.model.get('_todoList')[0]
|
||||
# @then -> @test.assert(todoBefore.value > todoAfter.value, "Incomplete TODO gained value on cron")
|
||||
# utils.dump todoBefore
|
||||
# utils.dump todoAfter
|
||||
|
||||
# ---------- User Death ------------
|
||||
# casper.then ->
|
||||
|
|
@ -35,11 +58,11 @@ casper.then ->
|
|||
|
||||
# ---------- Misc Pages ------------
|
||||
|
||||
casper.thenOpen "#{url}/terms", ->
|
||||
@test.assertTitle "Terms Of Use", "terms page works"
|
||||
|
||||
casper.thenOpen "#{url}/privacy", ->
|
||||
@test.assertTitle "Privacy Policy", "privacy page works"
|
||||
# casper.thenOpen "#{url}/terms", ->
|
||||
# @test.assertTitle "Terms Of Use", "terms page works"
|
||||
#
|
||||
# casper.thenOpen "#{url}/privacy", ->
|
||||
# @test.assertTitle "Privacy Policy", "privacy page works"
|
||||
|
||||
casper.run ->
|
||||
@test.renderResults true
|
||||
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
<Head:>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<script type="text/javascript" src="https://js.stripe.com/v1/"></script>
|
||||
|
||||
<Header:>
|
||||
<!-- SEO text, else Google gets strange copy -->
|
||||
<h2 style="display:none;">A habit tracker app which treats your goals like a Role Playing Game. As you accomplish goals, you level up. If you fail your goals, you lose hit points. Lose all your HP and you die.</h2>
|
||||
|
|
@ -25,24 +26,14 @@
|
|||
<li><a onclick="window.location='/logout'">Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- User Settings Modal -->
|
||||
<div class="modal hide" id="settings-modal" role="dialog">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h3>Settings</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<strong>User ID</strong><br/>
|
||||
<small>Copy this ID for use in third party applications.</small>
|
||||
<pre class=prettyprint>{_user.id}</pre><br/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/}
|
||||
<app:myModal modalId="settings-modal" header="Settings">
|
||||
<strong>User ID</strong><br/>
|
||||
<small>Copy this ID for use in third party applications.</small>
|
||||
<pre class=prettyprint>{_user.id}</pre><br/>
|
||||
</app:myModal>
|
||||
{/}
|
||||
</div>
|
||||
|
||||
|
||||
<div class='container-fluid'>
|
||||
<div class='row-fluid'>
|
||||
<div id=character class='span4'>
|
||||
|
|
@ -82,16 +73,26 @@
|
|||
<Body:>
|
||||
<br/>
|
||||
|
||||
<!-- Revive Modal -->
|
||||
<!-- Game Over Modal -->
|
||||
<div style="{#unless equal(_user.stats.lvl,0)}display:none;{/}">
|
||||
<div data-action="backdrop" class="modal-backdrop"></div>
|
||||
<div class="modal" id="dead-modal">
|
||||
<div class="modal-body">
|
||||
<img src="/img/BrowserQuest/habitrpg_mods/dead.png" />
|
||||
<h3>You're Dead</h3>
|
||||
<p><a x-bind=click:revive class="btn btn-danger btn-large">Revive</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<app:myModal noDismiss=true modalId='dead-modal'>
|
||||
<span class="well pull-right">Quarters: {quarters(_user.balance)}</span>
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="/img/BrowserQuest/habitrpg_mods/dead.png" /></td>
|
||||
<td>
|
||||
<h3>Game Over</h3>
|
||||
{#if lessThan(_user.balance,0.50)}
|
||||
<a data-toggle="modal" data-target="#stripe-modal" class="btn btn-danger btn-large">Buy More Quarters</a> <span class='label'>Not enough quarters</span>
|
||||
{else}
|
||||
<p>
|
||||
<a x-bind=click:revive class="btn btn-danger btn-large">Continue</a> <span class='label'>2 Quarters</span>
|
||||
</p>
|
||||
{/}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</app:myModal>
|
||||
</div>
|
||||
|
||||
<div id=wrap class=container-fluid>
|
||||
|
|
@ -111,12 +112,12 @@
|
|||
<!--helpTitle & helpContent moved to tour -->
|
||||
<app:taskColumn title="Habits">
|
||||
<ul class='habits'>{#each _habitList as :task}<app:task />{/}</ul>
|
||||
<app:newTask type=habit><input value={_newHabit} type="text" placeholder="New Habit"/></app:newTask>
|
||||
<app:newTask type=habit><input value={_newHabit} type="text" name=new-task placeholder="New Habit"/></app:newTask>
|
||||
</app:taskColumn>
|
||||
|
||||
<app:taskColumn title="Daily">
|
||||
<ul class='dailys'>{#each _dailyList as :task}<app:task />{/}</ul>
|
||||
<app:newTask type=daily><input value={_newDaily} type="text" placeholder="New Daily"/></app:newTask>
|
||||
<app:newTask type=daily><input value={_newDaily} type="text" name=new-task placeholder="New Daily"/></app:newTask>
|
||||
</app:taskColumn>
|
||||
|
||||
<app:taskColumn title="Todos" type="todo">
|
||||
|
|
@ -130,7 +131,7 @@
|
|||
<ul class='todos'>
|
||||
{#each _todoList as :task}<app:task />{/}
|
||||
</ul>
|
||||
<app:newTask type=todo><input value={_newTodo} type="text" placeholder="New Todo"/></app:newTask>
|
||||
<app:newTask type=todo><input value={_newTodo} type="text" name=new-task placeholder="New Todo"/></app:newTask>
|
||||
</div>
|
||||
<div class="tab-pane" id="tab2">
|
||||
<ul class='completeds'>
|
||||
|
|
@ -161,13 +162,46 @@
|
|||
</ul>
|
||||
{/}
|
||||
|
||||
<app:newTask type=reward><input value={_newReward} type="text" placeholder="New Reward"/></app:newTask>
|
||||
<app:newTask type=reward><input value={_newReward} type="text" name=new-task placeholder="New Reward"/></app:newTask>
|
||||
</app:taskColumn>
|
||||
<!-- Re-Roll modal -->
|
||||
<app:myModal modalId='reroll-modal' header="Reset Your Tasks">
|
||||
<span class="well pull-right">Quarters: {quarters(_user.balance)}</span>
|
||||
<p>Highly discouraged because red tasks provide good incentive to improve (<a target="_blank" href="https://github.com/lefnire/habitrpg#all-my-tasks-are-red-im-dying-too-fast">read more</a>). However, this becomes necessary after long bouts of bad habits.</p>
|
||||
{#if lessThan(_user.balance,1)}
|
||||
<a data-toggle="modal" data-target="#stripe-modal" class="btn btn-danger btn-large">Buy More Quarters</a> <span class='label'>Not enough quarters</span>
|
||||
{else}
|
||||
<button data-dismiss="modal" x-bind=click:buyReroll class="btn btn-danger btn-large">Re-Roll</button> <span class='label'>4 Quarters</span>
|
||||
{/}
|
||||
</app:myModal>
|
||||
</div>
|
||||
</div>
|
||||
<app:myFooter/>
|
||||
|
||||
<myFooter:>
|
||||
|
||||
<!-- Stripe Modal -->
|
||||
<app:myModal modalId="stripe-modal" header='Buy More Quarters'>
|
||||
<div class='payment-errors'></div>
|
||||
<!-- Stripe form from https://stripe.com/docs/tutorials/forms -->
|
||||
<form action="/" method="POST" id="payment-form" x-bind=submit:submitPayment>
|
||||
<div class="alert alert-info pull-right"><h2>$5</h2></div>
|
||||
<div class="form-row">
|
||||
<label>Card Number</label>
|
||||
<input type="text" size="20" autocomplete="off" class="card-number"/>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>CVC</label>
|
||||
<input type="text" size="4" autocomplete="off" class="card-cvc"/>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>Expiration (MM/YYYY)</label>
|
||||
<input type="text" size="2" class="card-expiry-month"/>
|
||||
<span> / </span>
|
||||
<input type="text" size="4" class="card-expiry-year"/>
|
||||
</div>
|
||||
<button type="submit" class="submit-button">Submit Payment</button>
|
||||
</form>
|
||||
</app:myModal>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class=footer>
|
||||
<div class=container>
|
||||
{#unless _mobileDevice}
|
||||
|
|
@ -209,6 +243,18 @@
|
|||
</div>
|
||||
</footer>
|
||||
|
||||
<myModal: nonvoid>
|
||||
{{{#if noDismiss}}}<div data-action="backdrop" class="modal-backdrop"></div>{{{/}}}
|
||||
<div class="modal {{{#unless noDismiss}}}hide{{{/}}}" id={{{modalId}}} role="dialog">
|
||||
{{{#if header}}}
|
||||
<div class="modal-header">
|
||||
{{{#unless noDismiss}}}<button type="button" class="close" data-dismiss="modal">×</button>{{{/}}}
|
||||
<h3>{{{unescaped header}}}</h3>
|
||||
</div>
|
||||
{{{/}}}
|
||||
<div class="modal-body">{{{content}}}</div>
|
||||
</div>
|
||||
|
||||
<taskColumn: nonvoid>
|
||||
<div class="span3 well {{{type}}}s">
|
||||
<!--removing for now, help-info in the tour instead-->
|
||||
|
|
@ -218,7 +264,7 @@
|
|||
</div>
|
||||
|
||||
<newTask: nonvoid>
|
||||
<form class="form-inline" data-task-type={{{type}}} x-bind=submit:addTask>
|
||||
<form class="form-inline" id=new-{{{type}}} data-task-type={{{type}}} x-bind=submit:addTask>
|
||||
{{{content}}}
|
||||
<input class="btn" type=submit value=Add>
|
||||
</form>
|
||||
|
|
@ -315,14 +361,16 @@
|
|||
<span rel="popover" data-placement="left" data-content="{:item.notes}" data-original-title="{:item.text}" class='task-notes'><i class="icon-comment"></i></span>
|
||||
</div>
|
||||
<div class="task-controls">
|
||||
{{{#unless disabled}}}
|
||||
{{{#if reroll}}}
|
||||
<a data-toggle="modal" data-target="#reroll-modal" class="item-buy-link" style='cursor:pointer'><i class=icon-retweet></i></a>
|
||||
{{{else}}}
|
||||
<a x-bind=click:buyItem class="item-buy-link" data-type={:item.type} data-value={:item.value} data-index={:item.index}>{:item.value}<img src="/img/coin_single_gold.png"/></a>
|
||||
{{{/}}}
|
||||
</div>
|
||||
<div class="task-text"><img src="/img/BrowserQuest/habitrpg_mods/{:item.icon}.png" /> {:item.text}</div>
|
||||
</pre>
|
||||
</li>
|
||||
|
||||
|
||||
<!-- Required scripts, needed by ready() -->
|
||||
<Scripts:>
|
||||
<script src=/js/jquery.min.js></script>
|
||||
|
|
|
|||
Loading…
Reference in a new issue