diff --git a/archive/derby_controllers/challenges.coffee b/archive/derby_controllers/challenges.coffee index 5d61133a7c..d89ebef890 100644 --- a/archive/derby_controllers/challenges.coffee +++ b/archive/derby_controllers/challenges.coffee @@ -1,83 +1,239 @@ _ = require 'lodash' -helpers = require 'habitrpg-shared/script/helpers' +{helpers} = require 'habitrpg-shared' +async = require 'async' -module.exports.app = (appExports, model) -> - browser = require './browser' - user = model.at '_user' +module.exports.app = (app) -> - $('#profile-challenges-tab-link').on 'show', (e) -> - _.each model.get('groups'), (g) -> - _.each g.challenges, (chal) -> - _.each ['habit','daily','todo'], (type) -> - _.each chal["#{type}s"], (task) -> - _.each chal.users, (member) -> - if (history = member?["#{type}s"]?[task.id]?.history) and !!history - data = google.visualization.arrayToDataTable _.map(history, (h)-> [h.date,h.value]) - options = - backgroundColor: { fill:'transparent' } - width: 150 - height: 50 - chartArea: width: '80%', height: '80%' - axisTitlePosition: 'none' - legend: position: 'bottom' - hAxis: gridlines: color: 'transparent' # since you can't seem to *remove* gridlines... - vAxis: gridlines: color: 'transparent' - chart = new google.visualization.LineChart $(".challenge-#{chal.id}-member-#{member.id}-history-#{task.id}")[0] - chart.draw(data, options) - - - appExports.challengeCreate = (e,el) -> - [type, gid] = [$(el).attr('data-type'), $(el).attr('data-gid')] - model.set '_challenge.new', - name: '' - habits: [] - dailys: [] - todos: [] - rewards: [] - id: model.id() - uid: user.get('id') - user: helpers.username(model.get('_user.auth'), model.get('_user.profile.name')) - group: {type, id:gid} - timestamp: +new Date - - appExports.challengeSave = -> - gid = model.get('_challenge.new.group.id') - model.unshift "groups.#{gid}.challenges", model.get('_challenge.new'), -> - browser.growlNotification('Challenge Created','success') - challengeDiscard() - - appExports.toggleChallengeEdit = (e, el) -> - path = "_editing.challenges.#{$(el).attr('data-id')}" - model.set path, !model.get(path) - - appExports.challengeDiscard = challengeDiscard = -> model.del '_challenge.new' - - appExports.challengeSubscribe = (e) -> - chal = e.get() - - # Add challenge name as a tag for user - tags = user.get('tags') - unless tags and _.find(tags,{id: chal.id}) - model.push '_user.tags', {id: chal.id, name: chal.name, challenge: true} - - tags = {}; tags[chal.id] = true - # Add all challenge's tasks to user's tasks - userChallenges = user.get('challenges') - user.unshift('challenges', chal.id) unless userChallenges and (userChallenges.indexOf(chal.id) != -1) - _.each ['habit', 'daily', 'todo', 'reward'], (type) -> - _.each chal["#{type}s"], (task) -> - task.tags = tags - task.challenge = chal.id - task.group = {id: chal.group.id, type: chal.group.type} - model.push("_#{type}List", task) + ### + Sync any updates to challenges since last refresh. Do it after cron, so we don't punish them for new tasks + This is challenge->user sync. user->challenge happens when user interacts with their tasks + ### + app.on 'ready', (model) -> + window.setTimeout -> + _.each model.get('groups'), (g) -> + if (@uid in g.members) and g.challenges + _.each(g.challenges, ->app.challenges.syncChalToUser g) true + , 500 - appExports.challengeUnsubscribe = (e) -> - chal = e.get() - i = user.get('challenges')?.indexOf chal.id - user.remove("challenges.#{i}") if i? and i != -1 - _.each ['habit', 'daily', 'todo', 'reward'], (type) -> - _.each chal["#{type}s"], (task) -> - model.remove "_#{type}List", _.findIndex(model.get("_#{type}List",{id:task.id})) - model.del "_user.tasks.#{task.id}" - true + ### + Sync user to challenge (when they score, add to statistics) + ### + app.model.on "change", "_page.user.priv.tasks.*.value", (id, value, previous, passed) -> + ### Sync to challenge, but do it later ### + async.nextTick => + model = app.model + ctx = {model: model} + task = model.at "_page.user.priv.tasks.#{id}" + tobj = task.get() + pub = model.get "_page.user.pub" + + if (chalTask = helpers.taskInChallenge.call ctx, tobj)? and chalTask.get() + chalTask.increment "value", value - previous + chal = model.at "groups.#{tobj.group.id}.challenges.#{tobj.challenge}" + chalUser = -> helpers.indexedAt.call(ctx, chal.path(), 'members', {id:pub.id}) + cu = chalUser() + unless cu?.get() + chal.push "members", {id: pub.id, name: model.get(pub.profile.name)} + cu = model.at chalUser() + else + cu.set 'name', pub.profile.name # update their name incase it changed + cu.set "#{tobj.type}s.#{tobj.id}", + value: tobj.value + history: tobj.history + + ### + Render graphs for user scores when the "Challenges" tab is clicked + ### + + ### + TODO + 1) on main tab click or party + * sort & render graphs for party + 2) guild -> all guilds + 3) public -> all public + ### + + + ### + $('#profile-challenges-tab-link').on 'shown', -> + async.each _.toArray(model.get('groups')), (g) -> + async.each _.toArray(g.challenges), (chal) -> + async.each _.toArray(chal.tasks), (task) -> + async.each _.toArray(chal.members), (member) -> + if (history = member?["#{task.type}s"]?[task.id]?.history) and !!history + data = google.visualization.arrayToDataTable _.map(history, (h)-> [h.date,h.value]) + options = + backgroundColor: { fill:'transparent' } + width: 150 + height: 50 + chartArea: width: '80%', height: '80%' + axisTitlePosition: 'none' + legend: position: 'bottom' + hAxis: gridlines: color: 'transparent' # since you can't seem to *remove* gridlines... + vAxis: gridlines: color: 'transparent' + chart = new google.visualization.LineChart $(".challenge-#{chal.id}-member-#{member.id}-history-#{task.id}")[0] + chart.draw(data, options) + ### + + app.fn + challenges: + + ### + Create + ### + create: (e,el) -> + [type, gid] = [$(el).attr('data-type'), $(el).attr('data-gid')] + cid = @model.id() + @model.set '_page.new.challenge', + id: cid + name: '' + habits: [] + dailys: [] + todos: [] + rewards: [] + user: + uid: @uid + name: @pub.get('profile.name') + group: {type, id:gid} + timestamp: +new Date + + ### + Save + ### + save: -> + newChal = @model.get('_page.new.challenge') + [gid, cid] = [newChal.group.id, newChal.id] + @model.push "_page.lists.challenges.#{gid}", newChal, -> + app.browser.growlNotification('Challenge Created','success') + app.challenges.discard() + app.browser.resetDom() # something is going absolutely haywire here, all model data at end of reflist after chal created + + ### + Toggle Edit + ### + toggleEdit: (e, el) -> + path = "_page.editing.challenges.#{$(el).attr('data-id')}" + @model.set path, !@model.get(path) + + ### + Discard + ### + discard: -> + @model.del '_page.new.challenge' + + ### + Delete + ### + delete: (e) -> + return unless confirm("Delete challenge, are you sure?") is true + e.at().remove() + + ### + Add challenge name as a tag for user + ### + syncChalToUser: (chal) -> + return unless chal + ### Sync tags ### + tags = @priv.get('tags') or [] + idx = _.findIndex tags, {id: chal.id} + if ~idx and (tags[idx].name isnt chal.name) + ### update the name - it's been changed since ### + @priv.set "tags.#{idx}.name", chal.name + else + @priv.push 'tags', {id: chal.id, name: chal.name, challenge: true} + + tags = {}; tags[chal.id] = true + _.each chal.habits.concat(chal.dailys.concat(chal.todos.concat(chal.rewards))), (task) => + _.defaults task, { tags, challenge: chal.id, group: {id: chal.group.id, type: chal.group.type} } + path = "tasks.#{task.id}" + if @priv.get path + @priv.set path, _.defaults(@priv.get(path), task) + else + @model.push "_page.lists.tasks.#{@uid}.#{task.type}s", task + true + + ### + Subscribe + ### + subscribe: (e) -> + chal = e.get() + ### Add all challenge's tasks to user's tasks ### + currChallenges = @pub.get('challenges') + @pub.unshift('challenges', chal.id) unless currChallenges and ~currChallenges.indexOf(chal.id) + e.at().push "members", + id: @uid + name: @pub.get('profile.name') + app.challenges.syncChalToUser(chal) + + ### + -------------------------- + Unsubscribe functions + -------------------------- + ### + + unsubscribe: (chal, keep=true) -> + + ### Remove challenge from user ### + i = @pub.get('challenges')?.indexOf(chal.id) + if i? and ~i + @pub.remove("challenges", i, 1) + + ### Remove user from challenge ### + if ~(i = _.findIndex chal.members, {id: @uid}) + @model.remove "groups.#{chal.group.id}.challenges.#{chal.id}.members", i, 1 + + ### Remove tasks from user ### + async.each chal.habits.concat(chal.dailys.concat(chal.todos.concat(chal.rewards))), (task) => + if keep is true + @priv.del "tasks.#{task.id}.challenge" + else + path = "_page.lists.tasks.#{@uid}.#{task.type}s" + if ~(i = _.findIndex(@model.get(path), {id:task.id})) + @model.remove(path, i, 1) + true + + taskUnsubscribe: (e, el) -> + + ### + since the challenge was deleted, we don't have its data to unsubscribe from - but we have the vestiges on the task + FIXME this is a really dumb way of doing this + ### + tasks = @priv.get('tasks') + tobj = tasks[$(el).attr("data-tid")] + deletedChal = + id: tobj.challenge + members: [@uid] + habits: _.where tasks, {type: 'habit', challenge: tobj.challenge} + dailys: _.where tasks, {type: 'daily', challenge: tobj.challenge} + todos: _.where tasks, {type: 'todo', challenge: tobj.challenge} + rewards: _.where tasks, {type: 'reward', challenge: tobj.challenge} + + switch $(el).attr('data-action') + when 'keep' + @priv.del "tasks.#{tobj.id}.challenge" + @priv.del "tasks.#{tobj.id}.group" + when 'keep-all' + app.challenges.unsubscribe.call @, deletedChal, true + when 'remove' + path = "_page.lists.tasks.#{@uid}.#{tobj.type}s" + if ~(i = _.findIndex @model.get(path), {id: tobj.id}) + @model.remove path, i + when 'remove-all' + app.challenges.unsubscribe.call @, deletedChal, false + + challengeUnsubscribe: (e, el) -> + $(el).popover('destroy').popover({ + html: true + placement: 'top' + trigger: 'manual' + title: 'Unsubscribe From Challenge And:' + content: """ + Remove Tasks
+ Keep Tasks
+ Cancel
+ """ + }).popover('show') + $('.challenge-unsubscribe-and-remove').click => app.challenges.unsubscribe.call @, e.get(), false + $('.challenge-unsubscribe-and-keep').click => app.challenges.unsubscribe.call @, e.get(), true + $('[class^=challenge-unsubscribe]').click => $(el).popover('destroy') \ No newline at end of file diff --git a/views/options/challenges.html b/views/options/challenges.html index c5ef9cba1e..edff5ba0e0 100644 --- a/views/options/challenges.html +++ b/views/options/challenges.html @@ -1,72 +1,58 @@ -
- + + <@headers> + + + + -
+ + {{#unless _page.party.id}} + Join a party first. + {{else}} + + {{/}} + -
- {{#unless _party.id}} - Join a party first. - {{else}} - {#if _challenge.new} - - {else} - - - {#each _party.challenges as :challenge} - - {/} - {/} + + +
+ {{#each _page.guilds as :guild}} +
+ +
{{/}}
+
-
- -
- {{#each _guilds as :guild}} -
- {#if _challenge.new} - - {else} - - {#each :guild.challenges as :challenge} - - {/} -
- {/} -
- {{/}} -
-
+ + + -
- {#if _challenge.new} - - {else} - - {#each _habitRPG.challenges as :challenge} - - {/} - {/} -
+ -
+ +
+ +
+
+ + {#each @list as :challenge} + + {/} +
- +
- {@challenge.name} (by {@challenge.user}) + {@challenge.name} (by {@challenge.user.name})
- - - - {#if and(not(_editing.challenges[@challenge.id]),equal(@challenge.uid,_user.id))} + {#if and(not(_page.editing.challenges[@challenge.id]),equal(@challenge.user.uid,_session.userId))} {else} {/} - {#if _editing.challenges[@challenge.id]} + {#if _page.editing.challenges[@challenge.id]}
- +
{{#with @challenge}} - Delete + Delete {{/}} {/} {#if @challenge.description}
{@challenge.description}
{/}
+ rewards={@challenge.rewards} + />

Statistics

- {#each @challenge.users as :member} + {#each @challenge.members as :member}

{:member.name}

- +
- +
- +
{/} @@ -146,11 +132,11 @@
{@header}
- {#each @challenge[@taskType]s as :task} + {#each @list as :task}
- {:task.text}: {challengeMemberScore(@member,@taskType,:task.id)} + {:task.text}: {challengeMemberScore(@member,:task)}
@@ -160,67 +146,25 @@ - Create {{@text}} Challenge + Create {{@text}} Challenge -
+
- +
- +
- - -
-
- - + \ No newline at end of file diff --git a/views/options/groups/group.jade b/views/options/groups/group.jade index 505e50bdb7..60e495dcf3 100644 --- a/views/options/groups/group.jade +++ b/views/options/groups/group.jade @@ -78,24 +78,30 @@ a.pull-right.gem-wallet(popover-trigger='mouseenter', popover-title='Guild Bank' input.btn(type='submit', value='Invite') - //-accordion-group(heading='Challenges') - span.label - i.icon-bullhorn - | Challenges - | coming soon! - a(target='_blank', href='https://trello.com/card/challenges-individual-party-guild-public/50e5d3684fe3a7266b0036d6/58') Details - //-{#if group.challenges} - //- - //- {#each group.challenges as :challenge} - //- - //- {/} - //-
- //- {:challenge.name} - //-
- //- Visit the Challenges for more information. - //-{else} - //- No challenges yet, visit the Challenges tab to create one. - //-{/} + //.accordion-group + .accordion-heading + a.accordion-toggle(data-toggle='collapse', data-parent='#accordion-{{@group.id}}-parent', href='#accordion-{{@group.id}}-challenges') Challenges + #accordion-{{@group.id}}-challenges.accordion-body.collapse + .accordion-inner + span.label + i.icon-bullhorn + | Challenges + | coming soon! + a(target='_blank', href='https://trello.com/card/challenges-individual-party-guild-public/50e5d3684fe3a7266b0036d6/58') Details + // + // {#if @group.challenges} + // + // {#each @group.challenges as :challenge} + // + // {/} + //
+ // {:challenge.name} + //
+ // Visit the Challenges for more information. + // {else} + // No challenges yet, visit the Challenges tab to create one. + // {/} + a.btn.btn-danger(data-id='{{group.id}}', ng-click='leave(group)') Leave .span8 diff --git a/views/tasks/task.jade b/views/tasks/task.jade index a2a2abdcde..c0a47fdb9b 100644 --- a/views/tasks/task.jade +++ b/views/tasks/task.jade @@ -18,13 +18,16 @@ li(ng-repeat='task in user[list.type + "s"]', class='task {{taskClasses(task,use a(ng-hide='!task._editing', ng-click='toggleEdit(task)', tooltip='Cancel') i.icon-remove(ng-hide='!task._editing') //- challenges - // {{#if task.challenge}} - // {{#if brokenChallengeLink(task)}} - // - // {{else}} - // - // {{/}} + // {{#if :task.challenge}} + // {{#if brokenChallengeLink(:task)}} + // + // {{else}} + // + // {{/}} // {{else}} + // + // + // {{/}} // delete a(ng-click='remove(task)', tooltip='Delete') @@ -78,12 +81,24 @@ li(ng-repeat='task in user[list.type + "s"]', class='task {{taskClasses(task,use // edit/options dialog .task-options(ng-show='task._editing') - // {{#if brokenChallengeLink(task)}} - //
- //

Broken Challenge Link: this task was part of a challenge, but (a) challenge (or containing group) has been deleted, or (b) the task was deleted from the challenge.

- //

Keep | Keep all from challenge | Delete | Delete all from challenge

- //
- // {{/}} + // + // {#if brokenChallengeLink(:task)} + //
+ // {{#if groups[:task.group.id].challenges[:task.challenge]}} + //

Broken Challenge Link: this task was part of a challenge, but has been removed from it. What would you like to do?

+ //

+ // Keep It |  + // Remove It + //

+ // {{else}} + //

Broken Challenge Link: this task was part of a challenge, but the challenge (or group) has been deleted. What would you like to do with these poor lil' orphans?

+ //

+ // Keep Them |  + // Remove Them + //

+ // {{/}} + //
+ // {/} form(ng-controller="TaskDetailsCtrl", ng-submit='save(task)') // text & notes fieldset.option-group