From 836787906d4519d1709478b34ebaaab0bd4f78c7 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 12 May 2013 00:05:13 +0100 Subject: [PATCH 001/111] bstart building challenges --- src/app/challenges.coffee | 19 +++++++++++ src/app/index.coffee | 3 ++ styles/app/index.styl | 3 +- views/app/challenges.html | 67 +++++++++++++++++++++++++++++++++++++++ views/app/game-pane.html | 5 +++ views/app/index.html | 1 + 6 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 src/app/challenges.coffee create mode 100644 views/app/challenges.html diff --git a/src/app/challenges.coffee b/src/app/challenges.coffee new file mode 100644 index 0000000000..2464631e70 --- /dev/null +++ b/src/app/challenges.coffee @@ -0,0 +1,19 @@ +module.exports.app = (appExports, model) -> + user = model.at '_user' + + appExports.challengeCreate = -> + model.set '_challenge.new', + name: '' + habits: [] + dailies: [] + todos: [] + rewards: [] + assignees: 'party' + model.set '_challenge.creating', true + + appExports.challengeSave = -> + #TODO + + appExports.challengeDiscard = -> + model.set '_challenge.new', {} + model.set '_challenge.creating', false \ No newline at end of file diff --git a/src/app/index.coffee b/src/app/index.coffee index 5ef298b46f..04ce6e1c2a 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -102,6 +102,8 @@ setupSubscriptions = (page, model, params, next, cb) -> get '/', (page, model, params, next) -> return page.redirect '/' if page.params?.query?.play? + model.set '_gamePane', true + # removed force-ssl (handled in nginx), see git for code setupSubscriptions page, model, params, next, -> cleanupCorruptTasks(model) # https://github.com/lefnire/habitrpg/issues/634 @@ -131,3 +133,4 @@ ready (model) -> require('./browser').app(exports, model, app) require('./unlock').app(exports, model) require('./filters').app(exports, model) + require('./challenges').app(exports, model) diff --git a/styles/app/index.styl b/styles/app/index.styl index c9128a3dc7..9995a66c94 100644 --- a/styles/app/index.styl +++ b/styles/app/index.styl @@ -165,4 +165,5 @@ hr background-color #dfe9ea padding 1px 3px 1px 3px - +.nav li > a + cursor: pointer diff --git a/views/app/challenges.html b/views/app/challenges.html new file mode 100644 index 0000000000..49a89d8dce --- /dev/null +++ b/views/app/challenges.html @@ -0,0 +1,67 @@ + + + + +
+ +
+ +
+ +
+ +
+
+ + +
+ + +
+ +
+ Mine +
+ +
+ Party +
+ +
+ Guild +
+ +
+ Public +
+ +
+
+ + +
+ {#unless _challenge.creating} + Create New Challenge + {else} +
+ + +
+ ... +
+ + + +
+ {/} + +
+ diff --git a/views/app/game-pane.html b/views/app/game-pane.html index 0e9728d943..6ab4f76658 100644 --- a/views/app/game-pane.html +++ b/views/app/game-pane.html @@ -14,6 +14,7 @@ {/if}
  • Tavern
  • Achievements
  • +
  • Challenges
  • {{#if _loggedIn}}
  • Settings
  • {{/}} @@ -64,6 +65,10 @@ +
    + +
    +
    {{#if _loggedIn}} diff --git a/views/app/index.html b/views/app/index.html index f8f64a866f..b1364ae829 100644 --- a/views/app/index.html +++ b/views/app/index.html @@ -10,6 +10,7 @@ + HabitRPG | Gamify Your Life From 0a37637dafb4b95fd0e56ef92ac9d7a71729594d Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 12 May 2013 13:12:02 +0100 Subject: [PATCH 002/111] challenges: fix up lists to use new list templates --- src/app/challenges.coffee | 2 +- views/app/challenges.html | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/app/challenges.coffee b/src/app/challenges.coffee index 2464631e70..f6f5ea67cb 100644 --- a/src/app/challenges.coffee +++ b/src/app/challenges.coffee @@ -5,7 +5,7 @@ module.exports.app = (appExports, model) -> model.set '_challenge.new', name: '' habits: [] - dailies: [] + daily: [] todos: [] rewards: [] assignees: 'party' diff --git a/views/app/challenges.html b/views/app/challenges.html index 49a89d8dce..88051459a5 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -51,16 +51,19 @@ {#unless _challenge.creating} Create New Challenge {else} -
    - + -
    - ... -
    +
    + +
    - - -
    + + {/}
    From d28edaf30dce57e4ff443c86aea4e0dfed025c16 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 12 May 2013 13:36:30 +0100 Subject: [PATCH 003/111] challenges: list taken dynamically from scope so we can push/unshift to various task lists depending on location of add-task form. be careful with this commit, not the {{}} instead of {} - seems to be required otherwise things get weird --- src/app/tasks.coffee | 2 +- views/app/tasks.html | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/app/tasks.coffee b/src/app/tasks.coffee index d9b2811bf9..095314b0f7 100644 --- a/src/app/tasks.coffee +++ b/src/app/tasks.coffee @@ -25,7 +25,7 @@ module.exports.app = (appExports, model) -> newTask = _.defaults {repeat:{su:true,m:true,t:true,w:true,th:true,f:true,s:true}, completed: false }, newTask when 'todo' newTask = _.defaults {completed: false }, newTask - model.unshift "_#{type}List", newTask + e.at().unshift newTask # e.at() in this case is the list, which was scoped here using {#with @list}...{/} newModel.set '' appExports.del = (e, el) -> diff --git a/views/app/tasks.html b/views/app/tasks.html index cf404e8445..6996b68382 100644 --- a/views/app/tasks.html +++ b/views/app/tasks.html @@ -135,10 +135,15 @@ {{#if equal(@type,'todo')}}{{/}} {{#if @editable}} -
    - - -
    + + + + {{#with @list}} +
    + + +
    + {{/}}
    {{/}}
      From 5ea6e55e1193aa529c1e1959ffd65d693ad3697a Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 12 May 2013 13:57:17 +0100 Subject: [PATCH 004/111] challenges: add assignTo option on challenge-creation --- src/app/challenges.coffee | 2 +- views/app/challenges.html | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/app/challenges.coffee b/src/app/challenges.coffee index f6f5ea67cb..3f7562baf8 100644 --- a/src/app/challenges.coffee +++ b/src/app/challenges.coffee @@ -8,7 +8,7 @@ module.exports.app = (appExports, model) -> daily: [] todos: [] rewards: [] - assignees: 'party' + assignTo: 'Party' model.set '_challenge.creating', true appExports.challengeSave = -> diff --git a/views/app/challenges.html b/views/app/challenges.html index 88051459a5..a71cd30f12 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -62,6 +62,37 @@ editable=true /> +
      +
      + +
      +
      + {#if equal(_challenge.new.assignTo,'Party')} +
      +
      +
      All Party
      + No individual privacy on the challenge, all party members can see progress even if they decline the challenge. Any new party members can subscribe to this challenge. +
      +
      + Individual Members + {{#each _partyMembers as :member}} +
      {{username(:member.auth,:member.profile.name)}}
      + {{/}} + Only the invited party members can subscribe to this challenge. New party joins won't see this challenge. +
      +
      + + {/} + {#if equal(_challenge.new.assignTo,'Guild')} + Which Guild? + {/} +
      +
      + {/} From b8ef0789dfc1241e059f11ace856a135dc6a61d3 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 12 May 2013 16:47:18 +0100 Subject: [PATCH 005/111] challneges: semi-functional challenge creation for parties --- src/app/challenges.coffee | 16 ++++++++++++++-- views/app/challenges.html | 14 ++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/app/challenges.coffee b/src/app/challenges.coffee index 3f7562baf8..e6461d3d84 100644 --- a/src/app/challenges.coffee +++ b/src/app/challenges.coffee @@ -1,4 +1,9 @@ +_ = require 'underscore' + module.exports.app = (appExports, model) -> + browser = require './browser' + helpers = require './helpers' + user = model.at '_user' appExports.challengeCreate = -> @@ -12,8 +17,15 @@ module.exports.app = (appExports, model) -> model.set '_challenge.creating', true appExports.challengeSave = -> - #TODO + challenge = _.defaults model.get('_challenge.new'), + id: model.id() + uuid: user.get('id') + user: helpers.username(model.get('_user.auth'), model.get('_user.profile.name')) + timestamp: +new Date + model.unshift '_party.challenges', challenge + challengeDiscard() + browser.growlNotification('Challenge Created','success') - appExports.challengeDiscard = -> + appExports.challengeDiscard = challengeDiscard = -> model.set '_challenge.new', {} model.set '_challenge.creating', false \ No newline at end of file diff --git a/views/app/challenges.html b/views/app/challenges.html index a71cd30f12..ff71e04a5f 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -32,7 +32,17 @@
      - Party + {#each _party.challenges as :challenge} +

      {:challenge.name} (by {:challenge.user})

      +
      + +
      +
      + {/}
      @@ -93,7 +103,7 @@
      - + {/} From 57f51048a16f014a7e699b07e20eabf722c78dd3 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 12 May 2013 17:22:07 +0100 Subject: [PATCH 006/111] challenges: basics for challenge subscription --- src/app/challenges.coffee | 29 ++++++++++++++++++++--------- src/app/helpers.coffee | 1 - views/app/challenges.html | 23 ++++++++++++++++++----- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/app/challenges.coffee b/src/app/challenges.coffee index e6461d3d84..6eddf286b6 100644 --- a/src/app/challenges.coffee +++ b/src/app/challenges.coffee @@ -7,25 +7,36 @@ module.exports.app = (appExports, model) -> user = model.at '_user' appExports.challengeCreate = -> + id = model.id() model.set '_challenge.new', name: '' habits: [] - daily: [] + dailys: [] todos: [] rewards: [] assignTo: 'Party' - model.set '_challenge.creating', true - - appExports.challengeSave = -> - challenge = _.defaults model.get('_challenge.new'), - id: model.id() + id: id uuid: user.get('id') user: helpers.username(model.get('_user.auth'), model.get('_user.profile.name')) timestamp: +new Date - model.unshift '_party.challenges', challenge - challengeDiscard() + + model.set '_challenge.creating', true + + appExports.challengeSave = -> + model.unshift '_party.challenges', model.get('_challenge.new'), -> challengeDiscard() browser.growlNotification('Challenge Created','success') appExports.challengeDiscard = challengeDiscard = -> model.set '_challenge.new', {} - model.set '_challenge.creating', false \ No newline at end of file + model.set '_challenge.creating', false + + appExports.challengeSubscribe = (e) -> + userChallenges = user.get('challenges') + chal = e.get() + user.unshift('challenges', chal.id) unless userChallenges and (userChallenges.indexOf(chal.id) != -1) + _.each ['habit', 'daily', 'todo', 'reward'], (type) -> + _.each chal["#{type}s"], (task) -> model.push("_#{type}List", task) + + appExports.challengeUnsubscribe = (e) -> + i = user.get('challenges')?.indexOf e.get('id') + user.remove("challenges.#{i}") if i? and i != -1 diff --git a/src/app/helpers.coffee b/src/app/helpers.coffee index 16fa20b286..ef990d7d26 100644 --- a/src/app/helpers.coffee +++ b/src/app/helpers.coffee @@ -214,7 +214,6 @@ viewHelpers = (view) -> view.fn 'itemText', (type, item=0) -> items[type][parseInt(item)].text view.fn 'itemStat', (type, item=0) -> if type is 'weapon' then items[type][parseInt(item)].strength else items[type][parseInt(item)].defense - # view.fn 'activeFilters', (filters) -> # debugger # (_.find filters, (f) -> f)? diff --git a/views/app/challenges.html b/views/app/challenges.html index ff71e04a5f..d2a65965f0 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -33,7 +33,16 @@
      {#each _party.challenges as :challenge} +
      + {#if indexOf(_user.challenges,:challenge.id)} + Unsubscribe + {else} + Subscribe + {/} +

      {:challenge.name} (by {:challenge.user})

      + +
      No individual privacy on the challenge, all party members can see progress even if they decline the challenge. Any new party members can subscribe to this challenge.
      - Individual Members - {{#each _partyMembers as :member}} -
      {{username(:member.auth,:member.profile.name)}}
      - {{/}} - Only the invited party members can subscribe to this challenge. New party joins won't see this challenge. +
      Individual Members
      +
      + +
      +
      Only the invited party members can subscribe to this challenge. New party joins won't see this challenge.
      From ae8a283d0e6b022d15094b67f57f13b9e5b1f803 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 12 May 2013 18:54:45 +0100 Subject: [PATCH 007/111] challenges: chevron in collapsing title --- src/app/challenges.coffee | 7 +++++-- views/app/challenges.html | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/app/challenges.coffee b/src/app/challenges.coffee index 6eddf286b6..0614dcb557 100644 --- a/src/app/challenges.coffee +++ b/src/app/challenges.coffee @@ -7,7 +7,6 @@ module.exports.app = (appExports, model) -> user = model.at '_user' appExports.challengeCreate = -> - id = model.id() model.set '_challenge.new', name: '' habits: [] @@ -15,7 +14,7 @@ module.exports.app = (appExports, model) -> todos: [] rewards: [] assignTo: 'Party' - id: id + id: model.id() uuid: user.get('id') user: helpers.username(model.get('_user.auth'), model.get('_user.profile.name')) timestamp: +new Date @@ -40,3 +39,7 @@ module.exports.app = (appExports, model) -> appExports.challengeUnsubscribe = (e) -> i = user.get('challenges')?.indexOf e.get('id') user.remove("challenges.#{i}") if i? and i != -1 + + appExports.challengeCollapse = (e, el) -> + $(el).next().toggle() + i = $(el).find('i').toggleClass 'icon-chevron-down' \ No newline at end of file diff --git a/views/app/challenges.html b/views/app/challenges.html index d2a65965f0..080c6a6b82 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -40,7 +40,7 @@ Subscribe {/} -

      {:challenge.name} (by {:challenge.user})

      +

      {:challenge.name} (by {:challenge.user})

      From 0a291bf8922d31e5e3300df3c8fa87d2506fa364 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 12 May 2013 19:23:16 +0100 Subject: [PATCH 008/111] challenges: add challenge.id to task so we can get the icon-bullhorn --- src/app/tasks.coffee | 6 ++++++ views/app/tasks.html | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/app/tasks.coffee b/src/app/tasks.coffee index 095314b0f7..f5c307bb4d 100644 --- a/src/app/tasks.coffee +++ b/src/app/tasks.coffee @@ -16,6 +16,12 @@ module.exports.app = (appExports, model) -> activeFilters = _.reduce user.get('filters'), ((memo,v,k) -> memo[k]=v if v;memo), {} newTask = {id: model.id(), type: type, text: text, notes: '', value: 0, tags: activeFilters} + + isChallenge = e.at().path().indexOf('_challenge.new') != -1 + if isChallenge + activeFilters = {} + newTask.challenge = model.get '_challenge.new.id' + switch type when 'habit' newTask = _.defaults {up: true, down: true}, newTask diff --git a/views/app/tasks.html b/views/app/tasks.html index 6996b68382..e6b2083618 100644 --- a/views/app/tasks.html +++ b/views/app/tasks.html @@ -174,6 +174,8 @@ + + {{#if :task.challenge}}{{/}} From 6507795d99388bee2bfba70e21df6b6f65a2aad1 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Fri, 17 May 2013 20:59:29 +0100 Subject: [PATCH 009/111] challenges: prevent some task options (such as scoring, check-marking, tags, etc) for challenge edits --- views/app/tasks.html | 51 +++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/views/app/tasks.html b/views/app/tasks.html index e6b2083618..be26185091 100644 --- a/views/app/tasks.html +++ b/views/app/tasks.html @@ -147,7 +147,7 @@
      {{/}}
        - {#each @list as :task}{/} + {#each @list as :task}{/}
      {{@extra}}
      @@ -192,24 +192,41 @@
      - {#if equal(:task.type, 'habit')} - {#if :task.up}{/} - {#if :task.down}{/} + {{#if equal(:task.type,'habit')}} + {{#if @main}} + {#if :task.up}{/} + {#if :task.down}{/} + {{else}} + {#if :task.up}{/} + {#if :task.down}{/} + {{/}} - {else if equal(:task.type, 'reward')} - - {:task.value} - - + {{else if equal(:task.type,'reward')}} + {{#if @main}} + + {:task.value} + + + {{else}} + + {:task.value} + + + {{/}} - {else} - - - - - {/} + {{else}} + + {{#if @main}} + + + {{else}} + + + {{/}} + + {{/}}
      @@ -218,7 +235,7 @@

      - + @@ -279,7 +296,7 @@ {/} - + {{#if @main}}{{/}} From 9b41712ca6bfe9721af09f1293803e4b58542878 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Fri, 17 May 2013 23:32:24 +0100 Subject: [PATCH 010/111] challenges: fix to not apply user's active tags to new challenge tasks, but also not apply filters except to main user's list --- src/app/helpers.coffee | 12 +++++++----- src/app/tasks.coffee | 8 ++------ views/app/tasks.html | 4 ++-- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/app/helpers.coffee b/src/app/helpers.coffee index ef990d7d26..23da04f8f5 100644 --- a/src/app/helpers.coffee +++ b/src/app/helpers.coffee @@ -138,17 +138,19 @@ viewHelpers = (view) -> ### Tasks ### - view.fn 'taskClasses', (task, filters, dayStart, lastCron, showCompleted=false) -> + view.fn 'taskClasses', (task, filters, dayStart, lastCron, showCompleted=false, main) -> return unless task {type, completed, value, repeat} = task # completed / remaining toggle return 'hidden' if (type is 'todo') and (completed != showCompleted) - for filter, enabled of filters - if enabled and not task.tags?[filter] - # All the other classes don't matter - return 'hidden' + # Filters + if main # only show when on your own list + for filter, enabled of filters + if enabled and not task.tags?[filter] + # All the other classes don't matter + return 'hidden' classes = type diff --git a/src/app/tasks.coffee b/src/app/tasks.coffee index f5c307bb4d..763793f7ab 100644 --- a/src/app/tasks.coffee +++ b/src/app/tasks.coffee @@ -14,13 +14,9 @@ module.exports.app = (appExports, model) -> # Don't add a blank task; 20/02/13 Added a check for undefined value, more at issue #463 -lancemanfv return if /^(\s)*$/.test(text) || text == undefined - activeFilters = _.reduce user.get('filters'), ((memo,v,k) -> memo[k]=v if v;memo), {} - newTask = {id: model.id(), type: type, text: text, notes: '', value: 0, tags: activeFilters} - + newTask = {id: model.id(), type: type, text: text, notes: '', value: 0, tags:{}} isChallenge = e.at().path().indexOf('_challenge.new') != -1 - if isChallenge - activeFilters = {} - newTask.challenge = model.get '_challenge.new.id' + newTask.tags = if isChallenge then {} else _.reduce user.get('filters'), ((memo,v,k) -> memo[k]=v if v;memo), {} switch type when 'habit' diff --git a/views/app/tasks.html b/views/app/tasks.html index be26185091..be372908a9 100644 --- a/views/app/tasks.html +++ b/views/app/tasks.html @@ -147,7 +147,7 @@
      {{/}}
        - {#each @list as :task}{/} + {#each @list as :task}{/}
      {{@extra}}
      @@ -163,7 +163,7 @@ -
    • +
    • From e7558139e67001d3d06f107c0300b4566ac0909e Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Fri, 17 May 2013 23:33:24 +0100 Subject: [PATCH 011/111] challenges: require challenge title, when user subscribe add new tag of challenge name. unsubscribe deletes tasks --- package.json | 4 +- src/app/challenges.coffee | 23 +++++++-- views/app/challenges.html | 103 ++++++++++++++++++++------------------ 3 files changed, 76 insertions(+), 54 deletions(-) diff --git a/package.json b/package.json index fd5f3a31de..39da71142d 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ "guid": "*", "moment": "*", "stripe": "*", - "lodash": "1.0.x", "coffee-script": "1.4.x", "underscore": "*", "mongoskin": "*", @@ -26,7 +25,8 @@ "resolve": "~0.2.3", "expect.js": "~0.2.0", "derby-i18n": "git://github.com/switz/derby-i18n#master", - "relative-date": "~1.1.1" + "relative-date": "~1.1.1", + "lodash": "~1.2.1" }, "private": true, "subdomain": "habitrpg", diff --git a/src/app/challenges.coffee b/src/app/challenges.coffee index 0614dcb557..aeb2982d4f 100644 --- a/src/app/challenges.coffee +++ b/src/app/challenges.coffee @@ -1,4 +1,5 @@ _ = require 'underscore' +lodash = require 'lodash' module.exports.app = (appExports, model) -> browser = require './browser' @@ -30,15 +31,31 @@ module.exports.app = (appExports, model) -> model.set '_challenge.creating', false appExports.challengeSubscribe = (e) -> - userChallenges = user.get('challenges') chal = e.get() + + # Add challenge name as a tag for user + tags = user.get('tags') + unless tags and _.findWhere(tags,{id: chal.id}) + model.push('_user.tags', {id: chal.id, name: chal.name}) + + 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) -> model.push("_#{type}List", task) + _.each chal["#{type}s"], (task) -> + task.tags = tags + task.challenge = chal.id + model.push("_#{type}List", task) appExports.challengeUnsubscribe = (e) -> - i = user.get('challenges')?.indexOf e.get('id') + 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", lodash.findIndex(model.get("_#{type}List",{id:task.id})) + model.del "_user.tasks.#{task.id}" appExports.challengeCollapse = (e, el) -> $(el).next().toggle() diff --git a/views/app/challenges.html b/views/app/challenges.html index 080c6a6b82..9f66d21ada 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -33,24 +33,23 @@
      {#each _party.challenges as :challenge} -
      - {#if indexOf(_user.challenges,:challenge.id)} - Unsubscribe - {else} - Subscribe - {/} -
      -

      {:challenge.name} (by {:challenge.user})

      +
      + +

      {:challenge.name} (by {:challenge.user})

      -
      - +
      + +
      +
      -
      {/}
      @@ -70,7 +69,6 @@ {#unless _challenge.creating} Create New Challenge {else} -
      -
      -
      - -
      -
      - {#if equal(_challenge.new.assignTo,'Party')} -
      -
      -
      All Party
      - No individual privacy on the challenge, all party members can see progress even if they decline the challenge. Any new party members can subscribe to this challenge. -
      -
      -
      Individual Members
      -
      - +
      + + + +
      +
      + +
      +
      + {#if equal(_challenge.new.assignTo,'Party')} +
      +
      +
      All Party
      + No individual privacy on the challenge, all party members can see progress even if they decline the challenge. Any new party members can subscribe to this challenge. +
      +
      +
      Individual Members
      +
      + +
      +
      Only the invited party members can subscribe to this challenge. New party joins won't see this challenge.
      -
      Only the invited party members can subscribe to this challenge. New party joins won't see this challenge.
      -
      - {/} - {#if equal(_challenge.new.assignTo,'Guild')} - Which Guild? - {/} -
      -
      + {/} + {#if equal(_challenge.new.assignTo,'Guild')} + Which Guild? + {/} +
      + + + + + + - - {/}
      From 03c40c1571e9c6c18816328bd2eed3e1d3ea3918 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sat, 18 May 2013 12:22:54 +0100 Subject: [PATCH 012/111] challenges: migration & store: rename parties to groups, create habitrpg guild, move tavern into habitrpg, overhaul of motifs and subscriptions --- migrations/20130518_setup_groups.js | 38 +++++++++++++ src/app/index.coffee | 35 ++++++++---- src/app/party.coffee | 14 ++--- src/server/store.coffee | 85 ++++++++++++++--------------- 4 files changed, 110 insertions(+), 62 deletions(-) create mode 100644 migrations/20130518_setup_groups.js diff --git a/migrations/20130518_setup_groups.js b/migrations/20130518_setup_groups.js new file mode 100644 index 0000000000..638d882295 --- /dev/null +++ b/migrations/20130518_setup_groups.js @@ -0,0 +1,38 @@ +/** + * In adding the Guilds feature (which supports the Challenges feature), we are consolidating parties and guilds + * into one collection: groups, with group.type either 'party' or 'guild'. We are also creating the 'habitrpg' guild, + * which everyone is auto-subscribed to, and moving tavern chat into that guild + * + * mongo habitrpg ./node_modules/underscore/underscore.js ./migrations/20130518_setup_groups.js + */ + +/** + * TODO + * 1) rename collection parties => groups + * 2) add group.type = 'party' for each current group + * 3) create habitrpg group, .type='guild' + * 4) move tavern.chat.chat into habitrpg guild + * 5) subscribe everyone to habitrpg (be sure to set that for default user too!) + */ + +db.parties.renameCollection('groups'); +//db.parties.dropCollection(); // doesn't seem to do this step during rename... +//db.parties.ensureIndex( { 'members': 1, 'background': 1} ); + +db.groups.update({}, {$set:{type:'party'}}, {multi:true}); + +tavern = db.tavern.findOne(); +db.tavern.drop(); + +//TODO make as a callback of previous, or make sure group.type is still 'guild' for habitrpg in the end +db.groups.insert({ + _id: "habitrpg", + leader: '9', + type: 'guild', + name: "HabitRPG", + chat: tavern.messages, + info: { + blurb: '', + websites: [] + } +}); \ No newline at end of file diff --git a/src/app/index.coffee b/src/app/index.coffee index 04ce6e1c2a..9fe2649698 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -67,11 +67,10 @@ cleanupCorruptTasks = (model) -> setupSubscriptions = (page, model, params, next, cb) -> uuid = model.get('_userId') or model.session.userId # see http://goo.gl/TPYIt selfQ = model.query('users').withId(uuid) #keep this for later - partyQ = model.query('parties').withMember(uuid) + groupsQ = model.query('groups').withMember(uuid) - partyQ.fetch (err, party) -> + groupsQ.fetch (err, groups, extra) -> return next(err) if err - finished = (descriptors, paths) -> model.subscribe.apply model, descriptors.concat -> [err, refs] = [arguments[0], arguments] @@ -80,22 +79,36 @@ setupSubscriptions = (page, model, params, next, cb) -> unless model.get('_user') console.error "User not found - this shouldn't be happening!" return page.redirect('/logout') #delete model.session.userId + extra(arguments) if extra return cb() + groupsObj = groups.get() + # (1) Solo player - return finished([selfQ, 'tavern'], ['_user', '_tavern']) unless party.get() + return finished([selfQ, "groups.habitrpg"], ['_user', '_habitRPG']) if _.isEmpty(groupsObj) - ## (2) Party has members, subscribe to those users too - membersQ = model.query('users').party(party.get('members')) + ## (2) Party or Guild has members, fetch those users too + groupsInfo = _.reduce groupsObj, ((m,g)-> + if g.type is 'guild' then m.guildIds.push(g.id) else m.partyId = g.id + m.members = m.members.concat(g.members) + m + ), {guildIds:[], partyId:null, members:[]} - # Fetch instead of subscribe. There's nothing dynamic we need from members just yet, they'll update _party instead. - # This may change in the future. - membersQ.fetch (err, members) -> + # Fetch, not subscribe. There's nothing dynamic we need from members, just the the Group (below) which includes chat, challenges, etc + model.query('users').publicInfo(groupsInfo.members).fetch (err, members) -> return next(err) if err - model.ref '_partyMembers', members + # we need _members as an object in the view, so we can iterate over _party.members as :id, and access _members[:id] for the info + mObj = members.get() + model.set "_members", _.object(_.pluck(mObj,'id'), mObj) + ## Then subscribe to the groups themselves. We separate them by _party, _guilds, and _habitRPG (the "global" guild). # Note - selfQ *must* come after membersQ in subscribe, otherwise _user will only get the fields restricted by party-members in store.coffee. Strang bug, but easy to get around - return finished([partyQ, selfQ, 'tavern'], ['_party', '_user', '_tavern']) + partyQ = model.query('groups').withIds(groupsInfo.partyId) + if _.isEmpty(groupsInfo.guildIds) + finished [partyQ, 'groups.habitrpg', selfQ], ['_party', '_habitRPG', '_user'] + else + guildsQ = model.query('groups').withIds(groupsInfo.guildIds) + finished [partyQ, guildsQ, 'groups.habitrpg', selfQ], ['_party', '_guilds', '_habitRPG', '_user'] # ========== ROUTES ========== diff --git a/src/app/party.coffee b/src/app/party.coffee index f84762ae5d..b2edbb2a89 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -20,7 +20,7 @@ module.exports.app = (appExports, model, app) -> model.on 'set', '_user.party.invitation', (after, before) -> if !before? and after? # they just got invited - partyQ = model.query('parties').withId(after) + partyQ = model.query('groups').withId(after) partyQ.fetch (err, party) -> return next(err) if err model.ref '_party', party @@ -28,15 +28,15 @@ module.exports.app = (appExports, model, app) -> appExports.partyCreate = -> newParty = model.get("_newParty") - id = model.add 'parties', { name: newParty, leader: user.get('id'), members: [user.get('id')], invites:[] } - user.set 'party', {current: id, invitation: null, leader: true}, -> + id = model.add 'groups', { name: newParty, leader: user.get('id'), members: [user.get('id')], invites:[] } + user.set 'party', {current: id, invitation: null}, -> window.location.reload true appExports.partyInvite = -> id = model.get('_newPartyMember').replace(/[\s"]/g, '') return if _.isEmpty(id) - model.query('users').party([id]).fetch (err, users) -> + model.query('users').publicInfo([id]).fetch (err, users) -> throw err if err u = users.at(0).get() if !u? @@ -55,8 +55,8 @@ module.exports.app = (appExports, model, app) -> partyId = user.get('party.invitation') user.set 'party.invitation', null user.set 'party.current', partyId - model.at("parties.#{partyId}.members").push user.get('id'), -> window.location.reload() -# model.query('parties').withId(partyId).fetch (err, p) -> + model.at("groups.#{partyId}.members").push user.get('id'), -> window.location.reload() +# model.query('groups').withId(partyId).fetch (err, p) -> # members = p.get('members') # members.push user.get('id') # p.set 'members', members, -> @@ -77,7 +77,7 @@ module.exports.app = (appExports, model, app) -> index = members.indexOf(user.get('id')) party.remove 'members', index, 1, -> if members.length is 1 # # last member out, kill the party - model.del "parties.#{id}", (-> window.location.reload true) + model.del "groups.#{id}", (-> window.location.reload true) else window.location.reload true diff --git a/src/server/store.coffee b/src/server/store.coffee index 7c2d5cb3fe..c7cc878384 100644 --- a/src/server/store.coffee +++ b/src/server/store.coffee @@ -5,10 +5,15 @@ Setup read / write access @param store ### +publicAccess = -> + accept = arguments[arguments.length-2] + #return err(derbyAuth.SESSION_INVALIDATED_ERROR) if derbyAuth.bustedSession(@) + return accept(false) if derbyAuth.bustedSession(@) + accept(true) + module.exports.customAccessControl = (store) -> userAccess(store) - partySystem(store) - tavernSystem(store) + groupSystem(store) REST(store) ### @@ -84,10 +89,14 @@ REST = (store) -> ### - Party permissions + Party & Guild Permissions ### -partySystem = (store) -> - store.query.expose "users", "party", (ids) -> +groupSystem = (store) -> + + ### + Public User Info + ### + store.query.expose "users", "publicInfo", (ids) -> @where("id").within(ids) .only('stats', 'items', @@ -98,49 +107,37 @@ partySystem = (store) -> 'preferences', 'auth.local.username', 'auth.facebook.displayName') + store.queryAccess "users", "publicInfo", publicAccess - store.queryAccess "users", "party", (ids, accept, err) -> -# return err(derbyAuth.SESSION_INVALIDATED_ERROR) if derbyAuth.bustedSession(@) - return accept(false) if derbyAuth.bustedSession(@) - accept(true) # no harm in public user stats + ### + Fetch group info (ie, they just got invited) + ### + store.query.expose "groups", "withIds", (ids) -> + if typeof ids is 'string' + @where("id").equals(ids).findOne() # find a single group + else + @where("id").within(ids) # find multiple groups + store.queryAccess "groups", "withIds", publicAccess - store.query.expose "parties", "withId", (id) -> - @where("id").equals(id).findOne() + ### + Read / Write groups, so they can create new groups + ### + store.readPathAccess "groups.*", publicAccess + store.writeAccess "*", "groups.*", publicAccess - store.queryAccess "parties", "withId", (id, accept, err) -> -# return err(derbyAuth.SESSION_INVALIDATED_ERROR) if derbyAuth.bustedSession(@) - return accept(false) if derbyAuth.bustedSession(@) - accept(true) + ### + Find group which has member by id + ### + store.query.expose "groups", "withMember", (id) -> + @where('members').contains([id]).only(['id','members']) + store.queryAccess 'groups', 'withMember', publicAccess - store.readPathAccess "parties.*", -> - accept = arguments[arguments.length-2] - accept(true) + ### + Public HabitRPG Guild + ### - store.writeAccess "*", "parties.*", -> - accept = arguments[arguments.length-2] - err = arguments[arguments.length - 1] -# return err(derbyAuth.SESSION_INVALIDATED_ERROR) if derbyAuth.bustedSession(@) - return accept(false) if derbyAuth.bustedSession(@) - accept(true) + store.readPathAccess 'groups.habitrpg', publicAccess + store.writeAccess "*", "groups.habitrpg.chat.*", publicAccess + store.writeAccess "*", "groups.habitrpg.challenges.*", publicAccess - store.query.expose "parties", "withMember", (id) -> - @where('members').contains([id]).findOne() - - store.queryAccess 'parties', 'withMember', (id, accept, err) -> - return accept(false) if derbyAuth.bustedSession(@) - accept(true) - -### - LFG / tavern system -### -tavernSystem = (store) -> - store.readPathAccess 'tavern', -> - accept = arguments[arguments.length-2] - return accept(false) if derbyAuth.bustedSession(@) - accept(true) - - store.writeAccess "*", "tavern.*", -> - accept = arguments[arguments.length-2] - return accept(false) if derbyAuth.bustedSession(@) - accept(true) From 079e7eb30fb7eedef72a52f96b0f5b2142d7e218 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 26 May 2013 15:09:41 +0100 Subject: [PATCH 013/111] challenges: fix up htmls to use new party-member subscription methods --- views/app/avatar.html | 6 +++--- views/app/challenges.html | 4 ++-- views/app/header.html | 12 ++++++------ views/app/party.html | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/views/app/avatar.html b/views/app/avatar.html index 543b5053dc..b3a0c79619 100644 --- a/views/app/avatar.html +++ b/views/app/avatar.html @@ -1,7 +1,7 @@ - {{#each _partyMembers as :profile}} - - + {{#each _party.members as :memberId}} + + <@footer> diff --git a/views/app/challenges.html b/views/app/challenges.html index 9f66d21ada..eb3171e8ac 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -102,8 +102,8 @@
      Individual Members
      diff --git a/views/app/header.html b/views/app/header.html index 2238925593..02121a3efc 100644 --- a/views/app/header.html +++ b/views/app/header.html @@ -1,5 +1,5 @@ - diff --git a/views/app/party.html b/views/app/party.html index a2afcc2fa3..d0bd00f54f 100644 --- a/views/app/party.html +++ b/views/app/party.html @@ -1,11 +1,11 @@ - {#if _partyMembers} + {#if _party.members}

      {{_party.name}}

      - {{#each _partyMembers as :member}} - + {{#each _party.members as :memberId}} + {{/}}
      {{username(:member.auth, :member.profile.name)}}({{:member.id}})
      {{username(_party[:memberId].auth, _party[:memberId].profile.name)}}({{:memberId}})
      From 9dfdb52c988b90ab61d9371a99008a85c46650e2 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 26 May 2013 15:33:01 +0100 Subject: [PATCH 014/111] challenges: rename party.html to groups.html --- views/app/game-pane.html | 4 ++-- views/app/{party.html => groups.html} | 2 +- views/app/header.html | 2 +- views/app/index.html | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename views/app/{party.html => groups.html} (97%) diff --git a/views/app/game-pane.html b/views/app/game-pane.html index b6615e0f9e..c3d02cffda 100644 --- a/views/app/game-pane.html +++ b/views/app/game-pane.html @@ -37,7 +37,7 @@
      - +
      @@ -132,7 +132,7 @@
        {#each _tavern.chat.messages as :message} - + {/}
      diff --git a/views/app/party.html b/views/app/groups.html similarity index 97% rename from views/app/party.html rename to views/app/groups.html index 01a8fb51f6..7c7880fa6f 100644 --- a/views/app/party.html +++ b/views/app/groups.html @@ -27,7 +27,7 @@
        {#each _party.chat as :message} - + {/}
      diff --git a/views/app/header.html b/views/app/header.html index 02121a3efc..9a326ca5c7 100644 --- a/views/app/header.html +++ b/views/app/header.html @@ -31,7 +31,7 @@
      {{#unless equal(:memberId, _userId)}} - + {{/}} diff --git a/views/app/index.html b/views/app/index.html index b1364ae829..4cfd3048e3 100644 --- a/views/app/index.html +++ b/views/app/index.html @@ -6,7 +6,7 @@ - + From e32ee04baa27b93905eb562304216e50c8ef005e Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 26 May 2013 15:35:58 +0100 Subject: [PATCH 015/111] guilds: bug fix on _tavern => _habitRPG --- src/app/party.coffee | 4 ++-- views/app/game-pane.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/party.coffee b/src/app/party.coffee index 3a7a16d5fa..7205c55569 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -116,14 +116,14 @@ module.exports.app = (appExports, model, app) -> chat.remove(200) model.on 'unshift', '_party.chat', -> $('.chat-message').tooltip() - model.on 'unshift', '_tavern.chat.messages', -> $('.chat-message').tooltip() + model.on 'unshift', '_habitrpg.chat', -> $('.chat-message').tooltip() appExports.partySendChat = -> sendChat('_party.chat', '_chatMessage') model.set '_user.party.lastMessageSeen', model.get('_party.chat')[0].id appExports.tavernSendChat = -> - sendChat('_tavern.chat.messages', '_tavernMessage') + sendChat('_habitRPG.chat', '_tavernMessage') appExports.partyMessageKeyup = (e, el, next) -> return next() unless e.keyCode is 13 diff --git a/views/app/game-pane.html b/views/app/game-pane.html index c3d02cffda..b7c1ff4036 100644 --- a/views/app/game-pane.html +++ b/views/app/game-pane.html @@ -131,7 +131,7 @@
        - {#each _tavern.chat.messages as :message} + {#each _habitRPG.chat as :message} {/}
      From 0f8dbd044e0549dba70262f7da202a4a949a1ee9 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 26 May 2013 20:21:07 +0100 Subject: [PATCH 016/111] guilds: got all groups html generalized (using same party html as tavern, will add guilds soon) --- src/app/party.coffee | 33 ++++---- views/app/game-pane.html | 102 +++++-------------------- views/app/groups.html | 159 +++++++++++++++++++++++++++++---------- 3 files changed, 150 insertions(+), 144 deletions(-) diff --git a/src/app/party.coffee b/src/app/party.coffee index 7205c55569..05fd657967 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -84,12 +84,17 @@ module.exports.app = (appExports, model, app) -> Chat Functionality ### - sendChat = (path, input) -> - chat = model.at path - text = model.get input + model.on 'unshift', '_party.chat', -> $('.chat-message').tooltip() + model.on 'unshift', '_habitrpg.chat', -> $('.chat-message').tooltip() + + appExports.sendChat = (e,el) -> + text = model.get '_chatMessage' # Check for non-whitespace characters return unless /\S/.test text - model.set(input, '') + + group = e.at() + chat = group.at('chat') + model.set('_chatMessage', '') message = id: model.id() @@ -114,24 +119,12 @@ module.exports.app = (appExports, model, app) -> chat.set messages else chat.remove(200) + type = $(el).attr('data-type') + model.set '_user.party.lastMessageSeen', chat.get()[0].id if group.get('type') is 'party' - model.on 'unshift', '_party.chat', -> $('.chat-message').tooltip() - model.on 'unshift', '_habitrpg.chat', -> $('.chat-message').tooltip() - - appExports.partySendChat = -> - sendChat('_party.chat', '_chatMessage') - model.set '_user.party.lastMessageSeen', model.get('_party.chat')[0].id - - appExports.tavernSendChat = -> - sendChat('_habitRPG.chat', '_tavernMessage') - - appExports.partyMessageKeyup = (e, el, next) -> + appExports.chatKeyup = (e, el, next) -> return next() unless e.keyCode is 13 - appExports.partySendChat() - - appExports.tavernMessageKeyup = (e, el, next) -> - return next() unless e.keyCode is 13 - appExports.tavernSendChat() + appExports.sendChat(e, el) appExports.deleteChatMessage = (e) -> if confirm("Delete chat message?") is true diff --git a/views/app/game-pane.html b/views/app/game-pane.html index b7c1ff4036..d96f1ca136 100644 --- a/views/app/game-pane.html +++ b/views/app/game-pane.html @@ -6,23 +6,21 @@
      -
      +
      @@ -36,15 +34,15 @@
      -
      - +
      +
      -
      +
      -
      +

      Inventory

      @@ -57,87 +55,25 @@
      -
      +
      -
      - +
      +
      -
      +
      -
      - {{#if _loggedIn}} - - {{/}} +
      +
      - -
      -
      -
      - - - -
      -
      -
      -

      Daniel Johansson

      -
      - Welcome to the Tavern! I'm Daniel, the bar keep. If you want to rest a while (going on vacation? sudden illness?), I'll set you up at the inn - dailies won't hurt you while you're resting. Stay a while & meet the locals. -
      -
      -
      -
      -
      - -
      Whilst resting your dailies are saved and aren't effected by day turn-over. Whether you check out tomorrow or in a weeks time you'll continue in the same state as when you checked in.
      - - - -
      -
      -

      Tavern Talk & LFG

      -
      - -
      -
      -
      - -
      -
      -
      - -
        - {#each _habitRPG.chat as :message} - - {/} -
      -
      -
      - diff --git a/views/app/groups.html b/views/app/groups.html index 7c7880fa6f..300d3c7c2f 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -1,11 +1,87 @@ - - {#if _party.members} -
      -
      -

      {{_party.name}}

      + + + +
      +
      + {#if _party.id} + + {else if _user.party.invitation} + +

      You're Invited To {_party.name}

      + Accept + Reject + {else} +

      Create A Party

      + +

      You are not in a party. You can either create one and invite friends, or if you want to join an existing party, have them enter:

      +
      {_user.id}
      +
      + {#if _partyError} +
      {_partyError}
      + {/} +
      + + +
      +
      + {/} +
      + +
      + + + {#each _guilds as :guild} +
      + +
      + {/} +
      +
      + + +
      +
      + {{#if equal(@group.id,'habitrpg')}} +
      + + + +
      +
      +
      +

      Daniel Johansson

      +
      + Welcome to the Tavern! I'm Daniel, the bar keep. If you want to rest a while (going on vacation? sudden illness?), I'll set you up at the inn - dailies won't hurt you while you're resting. Stay a while & meet the locals. +
      +
      +
      +
      +
      +
      Whilst resting your dailies are saved and aren't effected by day turn-over. Whether you check out tomorrow or in a weeks time you'll continue in the same state as when you checked in.
      + + + {{else}} +

      {{@group.name}}

      - {{#each _party.members as :memberId}} - + {{#each @group.members as :memberId}} + {{/}}
      {{username(_party[:memberId].auth, _party[:memberId].profile.name)}}({{:memberId}})
      {{username(_members[:memberId].auth, _members[:memberId].profile.name)}}({{:memberId}})
      @@ -18,44 +94,45 @@
      Leave -
      -
      -

      Chat

      -
      -
      - -
      -
        - {#each _party.chat as :message} - - {/} -
      -
      + {{/}} +
      +
      - {else if _user.party.invitation} - -

      You're Invited To {_party.name}

      - Accept - Reject + {{#if equal(@group.id,'habitrpg')}} +

      Tavern Talk & LFG

      +
      + +
      + +
      +
      + {{else}} +

      Chat

      + + {{/}} - {else} -

      Create A Party

      - -

      You are not in a party. You can either create one and invite friends, or if you want to join an existing party, have them enter:

      -
      {_user.id}
      -
      - {#if _partyError} -
      {_partyError}
      - {/} -
      - - -
      -
      - - {/} +
        + {#each @group.chat as :message} + + {/} +
      +
      +
      + + {{#with @group as :group}} +
      +
      + +
      + {{/}}
    • From 619b92563ea8a5db5311e7b4db1e9063b934949d Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 26 May 2013 20:25:15 +0100 Subject: [PATCH 017/111] guilds: rename party.coffee => groups.coffee --- src/app/{party.coffee => groups.coffee} | 0 src/app/index.coffee | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/app/{party.coffee => groups.coffee} (100%) diff --git a/src/app/party.coffee b/src/app/groups.coffee similarity index 100% rename from src/app/party.coffee rename to src/app/groups.coffee diff --git a/src/app/index.coffee b/src/app/index.coffee index 2e8bc783e1..f26604ee76 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -140,7 +140,7 @@ ready (model) -> require('./character').app(exports, model) require('./tasks').app(exports, model) require('./items').app(exports, model) - require('./party').app(exports, model, app) + require('./groups').app(exports, model, app) require('./profile').app(exports, model) require('./pets').app(exports, model) require('../server/private').app(exports, model) From ff35cbf1e782e132bd2edf6d4091ace724abf43e Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 26 May 2013 21:32:21 +0100 Subject: [PATCH 018/111] guilds: generalize more groups stuff, creation invitation leaving, etc. also fixes party invite bugs --- src/app/groups.coffee | 78 +++++++++++++++++------------------------ src/server/store.coffee | 5 +-- views/app/groups.html | 18 +++++----- 3 files changed, 45 insertions(+), 56 deletions(-) diff --git a/src/app/groups.coffee b/src/app/groups.coffee index 05fd657967..d2d3989324 100644 --- a/src/app/groups.coffee +++ b/src/app/groups.coffee @@ -6,14 +6,9 @@ module.exports.app = (appExports, model, app) -> browser = require './browser' _currentTime = model.at '_currentTime' - - _currentTime.setNull +new Date() - - # Every 60 seconds, reset the current time so that the chat - # can update relative times - setInterval -> - _currentTime.set +new Date() - , 60000 + _currentTime.setNull +new Date + # Every 60 seconds, reset the current time so that the chat can update relative times + setInterval (->_currentTime.set +new Date), 60000 user = model.at('_user') @@ -25,60 +20,51 @@ module.exports.app = (appExports, model, app) -> model.ref '_party', party browser.resetDom(model) - appExports.partyCreate = -> - newParty = model.get("_newParty") - id = model.add 'groups', { name: newParty, leader: user.get('id'), members: [user.get('id')], invites:[] } - user.set 'party', {current: id, invitation: null}, -> - window.location.reload true + appExports.groupCreate = (e,el) -> + model.add('groups', + name: model.get("_newGroup") + leader: user.get('id') + members: [user.get('id')] + type: $(el).attr('data-type') + , ->location.reload()) - appExports.partyInvite = -> - id = model.get('_newPartyMember').replace(/[\s"]/g, '') + appExports.groupInvite = (e,el) -> + id = model.get('_groupInvitee').replace(/[\s"]/g, '') + model.set '_groupInvitee', '' return if _.isEmpty(id) - model.query('users').publicInfo([id]).fetch (err, users) -> + model.query('users').publicInfo([id]).fetch (err, profiles) -> throw err if err - u = users.at(0).get() - if !u? - model.set "_partyError", "User with id #{id} not found." - return - else if u.party.current? or u.party.invitation? - model.set "_partyError", "User already in a party or pending invitation." - return - else + profile = profiles.at(0) + return model.set("_groupError", "User with id #{id} not found.") unless profile.get() + + invite = -> $.bootstrapGrowl "Invitation Sent." - model.set "users.#{id}.party.invitation", model.get('_party.id'), -> window.location.reload() - #model.set '_newPartyMember', '' - #partySubscribe model + model.set("users.#{id}.party.invitation", e.get('id'), ->location.reload()) + if e.get('type') is 'party' + model.query('groups').withMember(id).fetch (err,groups) -> + if profile.get('party.invitation') or !_.isEmpty(groups.get()) + return model.set("_groupError", "User already in a party or pending invitation.") + else invite() + else invite() appExports.partyAccept = -> partyId = user.get('party.invitation') - user.set 'party.invitation', null - user.set 'party.current', partyId - model.at("groups.#{partyId}.members").push user.get('id'), -> window.location.reload() -# model.query('groups').withId(partyId).fetch (err, p) -> -# members = p.get('members') -# members.push user.get('id') -# p.set 'members', members, -> -# window.location.reload true - -# partySubscribe model, -> -# p = model.at('_party') -# p.push 'members', user.get('id') + user.set 'party.invitation', null, -> + model.push("groups.#{partyId}.members", user.get('id'), ->location.reload()) appExports.partyReject = -> user.set 'party.invitation', null browser.resetDom(model) - appExports.partyLeave = -> - id = user.set 'party.current', null - party = model.at '_party' - members = party.get('members') + appExports.groupLeave = (e,el) -> + members = e.get('members') index = members.indexOf(user.get('id')) - party.remove 'members', index, 1, -> + e.at().remove 'members', index, 1, -> if members.length is 1 # # last member out, kill the party - model.del "groups.#{id}", (-> window.location.reload true) + model.del("groups.#{id}", ->location.reload()) else - window.location.reload true + location.reload() ### Chat Functionality diff --git a/src/server/store.coffee b/src/server/store.coffee index c7cc878384..62ac1024d9 100644 --- a/src/server/store.coffee +++ b/src/server/store.coffee @@ -128,8 +128,9 @@ groupSystem = (store) -> ### Find group which has member by id ### - store.query.expose "groups", "withMember", (id) -> - @where('members').contains([id]).only(['id','members']) + store.query.expose "groups", "withMember", (id, type) -> + q = @where('members').contains([id]).only(['id','members']) + q = q.where('type').equals(type) if type? store.queryAccess 'groups', 'withMember', publicAccess ### diff --git a/views/app/groups.html b/views/app/groups.html index 300d3c7c2f..db4df02678 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -18,9 +18,9 @@

      You are not in a party. You can either create one and invite friends, or if you want to join an existing party, have them enter:

      {_user.id}
      -
      - {#if _partyError} -
      {_partyError}
      + + {#if _groupError} +
      {_groupError}
      {/}
      @@ -84,16 +84,18 @@ {{username(_members[:memberId].auth, _members[:memberId].profile.name)}}({{:memberId}}) {{/}} - - {#if _partyError} -
      {_partyError}
      + {#with @group as :group} + + {#if _groupError} +
      {_groupError}
      {/}
      - +
      - Leave + Leave + {/} {{/}}
      From d4275867ccb2730ce8f24767722114514664c722 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 26 May 2013 22:26:25 +0100 Subject: [PATCH 019/111] guilds: html & store.coffee bug fixes --- src/server/store.coffee | 3 ++- views/app/groups.html | 42 ++++++++++++++++++++++++----------------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/server/store.coffee b/src/server/store.coffee index 62ac1024d9..5219ec7133 100644 --- a/src/server/store.coffee +++ b/src/server/store.coffee @@ -113,6 +113,7 @@ groupSystem = (store) -> Fetch group info (ie, they just got invited) ### store.query.expose "groups", "withIds", (ids) -> + return unless ids #FIXME this is sometimes null when ids is array (guilds) if typeof ids is 'string' @where("id").equals(ids).findOne() # find a single group else @@ -129,7 +130,7 @@ groupSystem = (store) -> Find group which has member by id ### store.query.expose "groups", "withMember", (id, type) -> - q = @where('members').contains([id]).only(['id','members']) + q = @where('members').contains([id]).only(['id','members','type']) q = q.where('type').equals(type) if type? store.queryAccess 'groups', 'withMember', publicAccess diff --git a/views/app/groups.html b/views/app/groups.html index db4df02678..ff73e768ab 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -18,33 +18,41 @@

      You are not in a party. You can either create one and invite friends, or if you want to join an existing party, have them enter:

      {_user.id}
      -
      - {#if _groupError} -
      {_groupError}
      - {/} -
      - - -
      -
      + {/}
    • - - {#each _guilds as :guild} -
      - +
      +
      +
      - {/} + {#each _guilds as :guild} +
      + +
      + {/} +
      + +
      + {#if _groupError} +
      {_groupError}
      + {/} +
      + + +
      +
      +
      From d3dd20cdf8784dd2a81d76b4cbbe68d582edbd43 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 26 May 2013 22:36:30 +0100 Subject: [PATCH 020/111] guilds: allow clicking guild-members for stats. needed to include _membersArray in model ref --- src/app/index.coffee | 1 + views/app/avatar.html | 6 +++--- views/app/groups.html | 6 +++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/app/index.coffee b/src/app/index.coffee index f26604ee76..23552b805a 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -104,6 +104,7 @@ setupSubscriptions = (page, model, params, next, cb) -> # we need _members as an object in the view, so we can iterate over _party.members as :id, and access _members[:id] for the info mObj = members.get() model.set "_members", _.object(_.pluck(mObj,'id'), mObj) + model.set "_membersArray", mObj # Note - selfQ *must* come after membersQ in subscribe, otherwise _user will only get the fields restricted by party-members in store.coffee. Strang bug, but easy to get around partyQ = model.query('groups').withIds(groupsInfo.partyId) diff --git a/views/app/avatar.html b/views/app/avatar.html index b3a0c79619..e567203114 100644 --- a/views/app/avatar.html +++ b/views/app/avatar.html @@ -1,7 +1,7 @@ - {{#each _party.members as :memberId}} - - + {{#each _membersArray as :member}} + + <@footer> diff --git a/views/app/groups.html b/views/app/groups.html index ff73e768ab..57a7dbeb70 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -89,7 +89,11 @@

      {{@group.name}}

      {{#each @group.members as :memberId}} - + {{/}}
      {{username(_members[:memberId].auth, _members[:memberId].profile.name)}}({{:memberId}})
      + + {{username(_members[:memberId].auth, _members[:memberId].profile.name)}}({{:memberId}}) + +
      {#with @group as :group} From 67643f796ac15fca396c2a40c28dfa7ad2166c42 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 27 May 2013 15:14:21 +0100 Subject: [PATCH 021/111] challenges: underscore => lodash --- src/app/challenges.coffee | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/app/challenges.coffee b/src/app/challenges.coffee index 161167b216..4e742e85e4 100644 --- a/src/app/challenges.coffee +++ b/src/app/challenges.coffee @@ -1,5 +1,4 @@ -_ = require 'underscore' -lodash = require 'lodash' +_ = require 'lodash' helpers = require 'habitrpg-shared/script/helpers' module.exports.app = (appExports, model) -> @@ -34,7 +33,7 @@ module.exports.app = (appExports, model) -> # Add challenge name as a tag for user tags = user.get('tags') - unless tags and _.findWhere(tags,{id: chal.id}) + unless tags and _.find(tags,{id: chal.id}) model.push('_user.tags', {id: chal.id, name: chal.name}) tags = {}; tags[chal.id] = true @@ -46,6 +45,7 @@ module.exports.app = (appExports, model) -> task.tags = tags task.challenge = chal.id model.push("_#{type}List", task) + true appExports.challengeUnsubscribe = (e) -> chal = e.get() @@ -53,8 +53,9 @@ module.exports.app = (appExports, model) -> user.remove("challenges.#{i}") if i? and i != -1 _.each ['habit', 'daily', 'todo', 'reward'], (type) -> _.each chal["#{type}s"], (task) -> - model.remove "_#{type}List", lodash.findIndex(model.get("_#{type}List",{id:task.id})) + model.remove "_#{type}List", _.findIndex(model.get("_#{type}List",{id:task.id})) model.del "_user.tasks.#{task.id}" + true appExports.challengeCollapse = (e, el) -> $(el).next().toggle() From 695bb301f4489961f1f0aff0edfc150295bcde87 Mon Sep 17 00:00:00 2001 From: Mickael Date: Mon, 27 May 2013 18:40:26 +0100 Subject: [PATCH 022/111] Adding a QRCode with the user's information QRCode retrieved from Google APIs, and parsed with json like that: {address:"",user:"",key:""} --- views/app/settings.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/views/app/settings.html b/views/app/settings.html index 2d46a63301..7da782b3fd 100644 --- a/views/app/settings.html +++ b/views/app/settings.html @@ -35,6 +35,11 @@
      API Token
      {_user.apiToken}
      + +
      QR Code
      + qrcode +
      From 4d6ca749af995934ad3fec9548bacce6d34155cc Mon Sep 17 00:00:00 2001 From: Mickael Date: Mon, 27 May 2013 20:52:09 +0100 Subject: [PATCH 023/111] double bracket on user id and token for the qrcode --- views/app/settings.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/app/settings.html b/views/app/settings.html index 7da782b3fd..104614fd3a 100644 --- a/views/app/settings.html +++ b/views/app/settings.html @@ -38,7 +38,7 @@
      QR Code
      qrcode + %7Baddress%3A%22https%3A%2F%2Fhabitrpg.com%22%2Cuser%3A%22{{_user.id}}%22%2Ckey%3A%22{{_user.apiToken}}%22%7D,&choe=UTF-8&chld=L' alt="qrcode"/> From e9eb509682d9698022451e95d3ea1c100261f785 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 27 May 2013 21:14:04 +0100 Subject: [PATCH 024/111] challenges: generalized listings for groups/parties/public --- src/app/challenges.coffee | 8 +++- views/app/challenges.html | 99 ++++++++++++++++++++++----------------- 2 files changed, 63 insertions(+), 44 deletions(-) diff --git a/src/app/challenges.coffee b/src/app/challenges.coffee index 4e742e85e4..73927ccef7 100644 --- a/src/app/challenges.coffee +++ b/src/app/challenges.coffee @@ -16,12 +16,18 @@ module.exports.app = (appExports, model) -> id: model.id() uuid: user.get('id') user: helpers.username(model.get('_user.auth'), model.get('_user.profile.name')) + # FIXME group is a stop-gap since derby's not picking up the initial select option `selected={}` until it's changed + group: type:'party', id:model.get('_guilds.0.id') timestamp: +new Date model.set '_challenge.creating', true appExports.challengeSave = -> - model.unshift '_party.challenges', model.get('_challenge.new'), -> challengeDiscard() + gid = + if model.get('_challenge.new.group.type') is 'party' then model.get('_party.id') + else model.get('_challenge.new.group.id') + debugger + model.unshift "groups.#{gid}.challenges", model.get('_challenge.new'), challengeDiscard browser.growlNotification('Challenge Created','success') appExports.challengeDiscard = challengeDiscard = -> diff --git a/views/app/challenges.html b/views/app/challenges.html index eb3171e8ac..1f631fbb31 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -33,37 +33,46 @@
      {#each _party.challenges as :challenge} -
      - -

      {:challenge.name} (by {:challenge.user})

      - - -
      - -
      -
      -
      + {/}
      - Guild + {#each _guilds as :guild} +

      {:guild.name}

      + {#each :guild.challenges as :challenge} + + {/} +
      + {/}
      - Public + {#each _habitRPG.challenges as :challenge} + + {/}
      + +
      + +

      {@challenge.name} (by {@challenge.user})

      + +
      + +
      +
      +
      {#unless _challenge.creating} @@ -86,34 +95,38 @@
      - {#if equal(_challenge.new.assignTo,'Party')} -
      -
      -
      All Party
      - No individual privacy on the challenge, all party members can see progress even if they decline the challenge. Any new party members can subscribe to this challenge. -
      -
      -
      Individual Members
      -
      - -
      -
      Only the invited party members can subscribe to this challenge. New party joins won't see this challenge.
      -
      -
      + + + + + + + + + + + + + + + + + + - {/} - {#if equal(_challenge.new.assignTo,'Guild')} - Which Guild? + + {#if equal(_challenge.new.group.type,'guild')} + {/}
      From fa9e6f5453a9b9d185f5e3dd8a9ab1e207b35be7 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 27 May 2013 21:14:22 +0100 Subject: [PATCH 025/111] challenges: migrated drop groups collection if exists (mostly for debugging) --- migrations/20130518_setup_groups.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migrations/20130518_setup_groups.js b/migrations/20130518_setup_groups.js index 638d882295..85f3bbb431 100644 --- a/migrations/20130518_setup_groups.js +++ b/migrations/20130518_setup_groups.js @@ -3,7 +3,7 @@ * into one collection: groups, with group.type either 'party' or 'guild'. We are also creating the 'habitrpg' guild, * which everyone is auto-subscribed to, and moving tavern chat into that guild * - * mongo habitrpg ./node_modules/underscore/underscore.js ./migrations/20130518_setup_groups.js + * mongo habitrpg ./node_modules/lodash/lodash.js ./migrations/20130518_setup_groups.js */ /** @@ -15,7 +15,7 @@ * 5) subscribe everyone to habitrpg (be sure to set that for default user too!) */ -db.parties.renameCollection('groups'); +db.parties.renameCollection('groups',true); //db.parties.dropCollection(); // doesn't seem to do this step during rename... //db.parties.ensureIndex( { 'members': 1, 'background': 1} ); From 2666ebb1fc06ecb4630f693d3554931d90d1c401 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 28 May 2013 00:46:51 +0100 Subject: [PATCH 026/111] guilds: generic handling of invitations (incl. accept, reject, invite, etc) --- migrations/20130518_setup_groups.js | 10 ++++ src/app/groups.coffee | 71 +++++++++++++++++------------ src/app/index.coffee | 2 +- src/server/store.coffee | 4 +- views/app/challenges.html | 2 +- views/app/groups.html | 17 +++++-- 6 files changed, 70 insertions(+), 36 deletions(-) diff --git a/migrations/20130518_setup_groups.js b/migrations/20130518_setup_groups.js index 85f3bbb431..0e04bfa1b9 100644 --- a/migrations/20130518_setup_groups.js +++ b/migrations/20130518_setup_groups.js @@ -21,6 +21,16 @@ db.parties.renameCollection('groups',true); db.groups.update({}, {$set:{type:'party'}}, {multi:true}); +//migrate invitation mechanisms +db.users.update( + {}, + { + $remove:{party:1}, + $set:{invitations:{party:null,guilds:[]}} + }, + {multi:1} +); + tavern = db.tavern.findOne(); db.tavern.drop(); diff --git a/src/app/groups.coffee b/src/app/groups.coffee index d2d3989324..87242fc59b 100644 --- a/src/app/groups.coffee +++ b/src/app/groups.coffee @@ -12,14 +12,6 @@ module.exports.app = (appExports, model, app) -> user = model.at('_user') - model.on 'set', '_user.party.invitation', (after, before) -> - if !before? and after? # they just got invited - partyQ = model.query('groups').withId(after) - partyQ.fetch (err, party) -> - return next(err) if err - model.ref '_party', party - browser.resetDom(model) - appExports.groupCreate = (e,el) -> model.add('groups', name: model.get("_newGroup") @@ -29,33 +21,56 @@ module.exports.app = (appExports, model, app) -> , ->location.reload()) appExports.groupInvite = (e,el) -> - id = model.get('_groupInvitee').replace(/[\s"]/g, '') + uid = model.get('_groupInvitee').replace(/[\s"]/g, '') model.set '_groupInvitee', '' - return if _.isEmpty(id) + return if _.isEmpty(uid) - model.query('users').publicInfo([id]).fetch (err, profiles) -> + model.query('users').publicInfo([uid]).fetch (err, profiles) -> throw err if err - profile = profiles.at(0) - return model.set("_groupError", "User with id #{id} not found.") unless profile.get() + profile = profiles.at(0).get() + return model.set("_groupError", "User with id #{uid} not found.") unless profile + model.query('groups').withMember(uid).fetch (err, g) -> + throw err if err - invite = -> - $.bootstrapGrowl "Invitation Sent." - model.set("users.#{id}.party.invitation", e.get('id'), ->location.reload()) - if e.get('type') is 'party' - model.query('groups').withMember(id).fetch (err,groups) -> - if profile.get('party.invitation') or !_.isEmpty(groups.get()) - return model.set("_groupError", "User already in a party or pending invitation.") + {type, name} = e.get() + gid = e.get('id') + groups = g.get() + groupError =(msg) -> model.set("_groupError", msg) + invite = -> + debugger + $.bootstrapGrowl "Invitation Sent." + if type is 'guild' + model.push("users.#{uid}.invitations.guilds", {id:gid, name}, ->location.reload()) + else model.set "users.#{uid}.invitations.party", {id:gid, name}, -> + debugger + location.reload() + + if type is 'guild' + if _.find(profile.invitations.guilds, {id:gid}) + return groupError("User already invited to that group") + else if _.find groups, ((group)-> uid in group.members) + return groupError("User already in that group") else invite() - else invite() + if type is 'party' + if profile.invitations.party + return groupError("User already pending invitation.") + else if _.find(groups, {type:'party'}) + return groupError("User already in a party.") + else invite() - appExports.partyAccept = -> - partyId = user.get('party.invitation') - user.set 'party.invitation', null, -> - model.push("groups.#{partyId}.members", user.get('id'), ->location.reload()) + appExports.acceptInvitation = (e,el) -> + group = e.at().get() + pushMember = -> model.push("groups.#{group.id}.members", user.get('id'), ->location.reload()) + if $(el).attr('data-type') is 'party' + user.set 'invitations.party', null, pushMember + else + e.at().remove pushMember - appExports.partyReject = -> - user.set 'party.invitation', null - browser.resetDom(model) + appExports.rejectInvitation = (e, el) -> + clear = -> browser.resetDom(model) + if e.at().path().indexOf('party') != -1 + model.del e.at().path(), clear + else e.at().remove clear appExports.groupLeave = (e,el) -> members = e.get('members') diff --git a/src/app/index.coffee b/src/app/index.coffee index dcfdb9856b..f83c9dd18a 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -88,7 +88,7 @@ setupSubscriptions = (page, model, params, next, cb) -> groupsObj = groups.get() # (1) Solo player - return finished([selfQ, "groups.habitrpg"], ['_user', '_habitRPG']) if _.isEmpty(groupsObj) + return finished(["groups.habitrpg", selfQ], ['_habitRPG', '_user']) if _.isEmpty(groupsObj) ## (2) Party or Guild has members, fetch those users too # Subscribe to the groups themselves. We separate them by _party, _guilds, and _habitRPG (the "global" guild). diff --git a/src/server/store.coffee b/src/server/store.coffee index 5219ec7133..5d93b2dcf4 100644 --- a/src/server/store.coffee +++ b/src/server/store.coffee @@ -48,7 +48,7 @@ userAccess = (store) -> return accept(false) # we can only manually set this stuff in the database # public access to users.*.party.invitation (TODO, lock down a bit more) - if attrPath is 'party.invitation' + if attrPath.indexOf('invitations.') is 0 return accept(true) # Same session (user.id = this.session.userId) @@ -100,7 +100,7 @@ groupSystem = (store) -> @where("id").within(ids) .only('stats', 'items', - 'party', + 'invitations', 'profile', 'achievements', 'backer', diff --git a/views/app/challenges.html b/views/app/challenges.html index 1f631fbb31..a6bb089394 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -49,7 +49,7 @@
      {#each _habitRPG.challenges as :challenge} - + {/}
      diff --git a/views/app/groups.html b/views/app/groups.html index 57a7dbeb70..5870afe288 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -8,11 +8,13 @@
      {#if _party.id} - {else if _user.party.invitation} + {else if _user.invitations.party} -

      You're Invited To {_party.name}

      - Accept - Reject +

      You're Invited To {_user.invitations.party.name}

      + {#with _user.invitations.party} + Accept + Reject + {/} {else}

      Create A Party

      @@ -30,9 +32,16 @@ {/}
    +
    + {#each _user.invitations.guilds as :invitation} +

    You're Invited To {:invitation.name}

    + Accept + Reject + {/}
    + {#each _guilds as :guild}
    From 2fbff2a0bc839e18e204fcffe438ee687f3b1eae Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 28 May 2013 11:52:32 +0100 Subject: [PATCH 027/111] challenges: lock down editing capabilities on certain task properties, instead showing the challenge's properties (in case they change challenge-side).This includes editing the tag name --- src/app/challenges.coffee | 3 ++- src/app/misc.coffee | 19 +++++++++++++++- src/app/tasks.coffee | 5 ++--- views/app/filters.html | 2 +- views/app/tasks.html | 46 ++++++++++++++++++++++++++++----------- 5 files changed, 56 insertions(+), 19 deletions(-) diff --git a/src/app/challenges.coffee b/src/app/challenges.coffee index 73927ccef7..3ef9b3e910 100644 --- a/src/app/challenges.coffee +++ b/src/app/challenges.coffee @@ -40,7 +40,7 @@ module.exports.app = (appExports, model) -> # 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}) + 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 @@ -50,6 +50,7 @@ module.exports.app = (appExports, model) -> _.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) true diff --git a/src/app/misc.coffee b/src/app/misc.coffee index 735addb804..30a4246368 100644 --- a/src/app/misc.coffee +++ b/src/app/misc.coffee @@ -101,4 +101,21 @@ module.exports.viewHelpers = (view) -> #Tags view.fn 'noTags', helpers.noTags - view.fn 'appliedTags', helpers.appliedTags \ No newline at end of file + view.fn 'appliedTags', helpers.appliedTags + + #Challenges + view.fn 'taskAttrFromChallenge', (task, attr) -> + [tid, gid, cid, tType, gType] = [task.id, task.group.id, task.challenge, task.type, task.group.type] + findAttr = (challenges) -> + challenge = _.find(challenges,{id:cid}) + val = _.find(challenge["#{tType}s"],{id:tid})[attr] + if attr is 'priority' + val = switch val + when '!!!' then 'Hard' + when '!!' then 'Medium' + else 'Easy' + return val + if gType is 'party' + findAttr @model.get("_party.challenges") + else if gType is 'guild' + findAttr _.find(@model.get("_guilds"),{id:gid}).challenges \ No newline at end of file diff --git a/src/app/tasks.coffee b/src/app/tasks.coffee index 8fa4ebe27d..8023bdc636 100644 --- a/src/app/tasks.coffee +++ b/src/app/tasks.coffee @@ -19,9 +19,8 @@ module.exports.app = (appExports, model) -> # Don't add a blank task; 20/02/13 Added a check for undefined value, more at issue #463 -lancemanfv return if /^(\s)*$/.test(text) || text == undefined - newTask = {id: model.id(), type: type, text: text, notes: '', value: 0, tags:{}} - isChallenge = e.at().path().indexOf('_challenge.new') != -1 - newTask.tags = if isChallenge then {} else _.reduce user.get('filters'), ((memo,v,k) -> memo[k]=v if v;memo), {} + newTask = {id: model.id(), type, text, notes: '', value: 0} + newTask.tags = _.reduce user.get('filters'), ((memo,v,k) -> memo[k]=v if v; memo), {} switch type when 'habit' diff --git a/views/app/filters.html b/views/app/filters.html index 1a04d02a3a..b2e58dc26e 100644 --- a/views/app/filters.html +++ b/views/app/filters.html @@ -9,7 +9,7 @@ {#each _user.tags as :tag}
  • - {#if _editingTags} + {#if and(_editingTags,not(:tag.challenge))}
    diff --git a/views/app/tasks.html b/views/app/tasks.html index 656a3fb6d2..bb361ce32f 100644 --- a/views/app/tasks.html +++ b/views/app/tasks.html @@ -175,11 +175,13 @@ - {{#if :task.challenge}}{{/}} - - + {{#if :task.challenge}} + + {{else}} + + + {{/}} - {#if :task.history} {/} @@ -231,7 +233,11 @@

    - {:task.text} + {{#if :task.challenge}} + {{taskAttrFromChallenge(:task,'text')}} + {{else}} + {:task.text} + {{/}}

    @@ -244,12 +250,19 @@
    - - + {{#unless :task.challenge}} + + {{/}} + + {{#if :task.challenge}} + + {{else}} + + {{/}}
    - {#if equal(:task.type, 'habit')} + {#if and(equal(:task.type, 'habit'),not(:task.challenge))}
    Direction/Actions @@ -304,11 +317,18 @@

    Advanced Options

    Difficulty -
    - - - -
    + + {{#if :task.challenge}} + + {{else}} +
    + + + +
    + {{/}} {{#if equal(:task.type,'daily')}} Restore Streak From 21a6ea19f536dbd8258436a4db681f656fae8060 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 28 May 2013 11:59:09 +0100 Subject: [PATCH 028/111] challenges: remove "mine" challenges tab --- views/app/challenges.html | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/views/app/challenges.html b/views/app/challenges.html index a6bb089394..0fe7584b39 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -19,19 +19,14 @@
    -
    - Mine -
    - -
    +
    {#each _party.challenges as :challenge} {/} From 60bdc3666e9901e83656757445ad6422e2673bf6 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 28 May 2013 11:59:19 +0100 Subject: [PATCH 029/111] challenges: fix public-challnege creation --- src/app/challenges.coffee | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/app/challenges.coffee b/src/app/challenges.coffee index 3ef9b3e910..ffffd39729 100644 --- a/src/app/challenges.coffee +++ b/src/app/challenges.coffee @@ -12,9 +12,8 @@ module.exports.app = (appExports, model) -> dailys: [] todos: [] rewards: [] - assignTo: 'Party' id: model.id() - uuid: user.get('id') + uid: user.get('id') user: helpers.username(model.get('_user.auth'), model.get('_user.profile.name')) # FIXME group is a stop-gap since derby's not picking up the initial select option `selected={}` until it's changed group: type:'party', id:model.get('_guilds.0.id') @@ -23,10 +22,10 @@ module.exports.app = (appExports, model) -> model.set '_challenge.creating', true appExports.challengeSave = -> - gid = - if model.get('_challenge.new.group.type') is 'party' then model.get('_party.id') - else model.get('_challenge.new.group.id') - debugger + gid = switch model.get('_challenge.new.group.type') + when 'party' then model.get('_party.id') + when 'guild' then model.get('_challenge.new.group.id') + when 'public' then 'habitrpg' model.unshift "groups.#{gid}.challenges", model.get('_challenge.new'), challengeDiscard browser.growlNotification('Challenge Created','success') From 12fccbc99d03af2f9b6242019097c4f3c8eb6d6a Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 28 May 2013 12:32:57 +0100 Subject: [PATCH 030/111] challenges: move challenge-creation to their respective tabs, instead of separate "create" tab --- src/app/challenges.coffee | 25 +++--- views/app/challenges.html | 175 +++++++++++++++++++------------------- 2 files changed, 96 insertions(+), 104 deletions(-) diff --git a/src/app/challenges.coffee b/src/app/challenges.coffee index ffffd39729..011567e340 100644 --- a/src/app/challenges.coffee +++ b/src/app/challenges.coffee @@ -5,7 +5,8 @@ module.exports.app = (appExports, model) -> browser = require './browser' user = model.at '_user' - appExports.challengeCreate = -> + appExports.challengeCreate = (e,el) -> + [type, gid] = [$(el).attr('data-type'), $(el).attr('data-gid')] model.set '_challenge.new', name: '' habits: [] @@ -15,23 +16,17 @@ module.exports.app = (appExports, model) -> id: model.id() uid: user.get('id') user: helpers.username(model.get('_user.auth'), model.get('_user.profile.name')) - # FIXME group is a stop-gap since derby's not picking up the initial select option `selected={}` until it's changed - group: type:'party', id:model.get('_guilds.0.id') + group: {type, id:gid} timestamp: +new Date - model.set '_challenge.creating', true - appExports.challengeSave = -> - gid = switch model.get('_challenge.new.group.type') - when 'party' then model.get('_party.id') - when 'guild' then model.get('_challenge.new.group.id') - when 'public' then 'habitrpg' - model.unshift "groups.#{gid}.challenges", model.get('_challenge.new'), challengeDiscard - browser.growlNotification('Challenge Created','success') + gid = model.get('_challenge.new.group.id') + debugger + model.unshift "groups.#{gid}.challenges", model.get('_challenge.new'), -> + browser.growlNotification('Challenge Created','success') + challengeDiscard() - appExports.challengeDiscard = challengeDiscard = -> - model.set '_challenge.new', {} - model.set '_challenge.creating', false + appExports.challengeDiscard = challengeDiscard = -> model.del '_challenge.new' appExports.challengeSubscribe = (e) -> chal = e.get() @@ -65,4 +60,4 @@ module.exports.app = (appExports, model) -> appExports.challengeCollapse = (e, el) -> $(el).next().toggle() - i = $(el).find('i').toggleClass 'icon-chevron-down' \ No newline at end of file + i = $(el).find('i').toggleClass('icon-chevron-right icon-chevron-down') \ No newline at end of file diff --git a/views/app/challenges.html b/views/app/challenges.html index 0fe7584b39..3c9c1f9a76 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -1,24 +1,6 @@ - - - -
    - -
    - -
    - -
    - -
    -
    - -
    -
    + {/} + {#if equal(_challenge.new.group.type,'guild')} + + {/} +
    + --> - - - - - - {/} + +
    +
    + From 21b6cd952d0ff175d8d5be502931e47fdd6bd01a Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 28 May 2013 12:47:41 +0100 Subject: [PATCH 031/111] challenges: guild listings in tabs, just like guilds-groups --- views/app/challenges.html | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/views/app/challenges.html b/views/app/challenges.html index 3c9c1f9a76..5229a8b412 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -20,18 +20,26 @@
  • - {{#each _guilds as :guild}} -

    {:guild.name}

    - {#if _challenge.new} - - {else} - - {#each :guild.challenges as :challenge} - - {/} -
    + +
    + {{#each _guilds as :guild}} +
    + {#if _challenge.new} + + {else} + + {#each :guild.challenges as :challenge} + + {/} +
    + {/} +
    {/} - {{/}} +
    From bd1866742036f2018ffb2705f2fb33dd69b41bf1 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 28 May 2013 16:43:16 +0100 Subject: [PATCH 032/111] convert task-editing state to private path so we don't have conflicting ids --- src/app/tasks.coffee | 35 ++++++++++++++++++++--------------- views/app/tasks.html | 14 +++++++------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/src/app/tasks.coffee b/src/app/tasks.coffee index 8023bdc636..a0efc2d088 100644 --- a/src/app/tasks.coffee +++ b/src/app/tasks.coffee @@ -76,31 +76,36 @@ module.exports.app = (appExports, model) -> task.set('repeat.' + $(el).attr('data-day'), true) appExports.toggleTaskEdit = (e, el) -> - hideId = $(el).attr('data-hide-id') - toggleId = $(el).attr('data-toggle-id') - $(document.getElementById(hideId)).addClass('visuallyhidden') - $(document.getElementById(toggleId)).toggleClass('visuallyhidden') + id = e.get('id') + path = "_tasks.editing.#{id}" + model.set path, !model.get(path) + $(".#{id}-chart").hide() appExports.toggleChart = (e, el) -> - hideSelector = $(el).attr('data-hide-id') - chartSelector = $(el).attr('data-toggle-id') - historyPath = $(el).attr('data-history-path') - $(document.getElementById(hideSelector)).hide() - $(document.getElementById(chartSelector)).toggle() + id = $(el).attr('data-id') + history = [] + + if id is 'todos' + model.set "_tasks.charts.todos", !model.get("_tasks.charts.todos") + history = model.get("_user.history.todos") + $(".#{id}-chart").toggle() + else + [id, path] = [$(el).attr('data-id'), "_tasks.charts.#{id}"] + model.set path, !model.get(path) + model.set "_tasks.editing.#{id}", false + $(".#{id}-chart").toggle() + history = model.get("_user.tasks.#{id}.history") matrix = [['Date', 'Score']] - for obj in model.get(historyPath) + for obj in history date = +new Date(obj.date) readableDate = moment(date).format('MM/DD') matrix.push [ readableDate, obj.value ] data = google.visualization.arrayToDataTable matrix - - options = { + options = title: 'History' backgroundColor: { fill:'transparent' } - } - - chart = new google.visualization.LineChart(document.getElementById( chartSelector )) + chart = new google.visualization.LineChart $(".#{id}-chart")[0] chart.draw(data, options) appExports.todosShowRemaining = -> model.set '_showCompleted', false diff --git a/views/app/tasks.html b/views/app/tasks.html index bb361ce32f..e30465aa90 100644 --- a/views/app/tasks.html +++ b/views/app/tasks.html @@ -53,7 +53,7 @@ {#if _user.history.todos} - + {/} @@ -132,7 +132,7 @@

    {{t(@header)}}

    - {{#if equal(@type,'todo')}}{{/}} + {{#if equal(@type,'todo')}}{{/}} {{#if @editable}} @@ -173,7 +173,7 @@
    - + {{#if :task.challenge}} @@ -183,7 +183,7 @@ {{/}} {#if :task.history} - + {/} {#if :task.notes} @@ -246,8 +246,8 @@ -
    -
    +
    +
    {{#unless :task.challenge}} @@ -342,4 +342,4 @@
    - + From cce03c8312630cb57d271482aa932695747b6e2c Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 29 May 2013 18:19:38 +0100 Subject: [PATCH 033/111] guilds: prettier group-creation --- views/app/groups.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/views/app/groups.html b/views/app/groups.html index 5870afe288..187787c696 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -56,8 +56,8 @@ {#if _groupError}
    {_groupError}
    {/} -
    - +
    +
    From 88ff245aecd9d61a99622d38af013a17dd027070 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 29 May 2013 18:57:50 +0100 Subject: [PATCH 034/111] challenges: add taskInChallenge() view helper and broken task.challenge link --- src/app/misc.coffee | 35 ++++++++++++++++++++--------------- views/app/tasks.html | 20 ++++++++++++++------ 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/app/misc.coffee b/src/app/misc.coffee index 268bd5796a..5ae183c5dc 100644 --- a/src/app/misc.coffee +++ b/src/app/misc.coffee @@ -116,19 +116,24 @@ module.exports.viewHelpers = (view) -> view.fn 'noTags', helpers.noTags view.fn 'appliedTags', helpers.appliedTags - #Challenges - view.fn 'taskAttrFromChallenge', (task, attr) -> - [tid, gid, cid, tType, gType] = [task.id, task.group.id, task.challenge, task.type, task.group.type] - findAttr = (challenges) -> + #TODO put this in habitrpg-shared + taskInChallenge = (task) -> + return false unless task?.challenge + [gid, cid, gType] = [task.group.id, task.challenge, task.group.type] + getTask = (challenges) -> challenge = _.find(challenges,{id:cid}) - val = _.find(challenge["#{tType}s"],{id:tid})[attr] - if attr is 'priority' - val = switch val - when '!!!' then 'Hard' - when '!!' then 'Medium' - else 'Easy' - return val - if gType is 'party' - findAttr @model.get("_party.challenges") - else if gType is 'guild' - findAttr _.find(@model.get("_guilds"),{id:gid}).challenges + challenge and _.find(challenge["#{task.type}s"],{id:task.id}) + switch gType + when 'party' + party = @model.get('_party') + return party?.challenges and getTask(party.challenges) + when 'guild' + guilds = @model.get("_guilds") + return guilds and getTask(_.find(guilds,{id:gid}).challenges) + + #Challenges + view.fn 'taskInChallenge', taskInChallenge + view.fn 'taskAttrFromChallenge', (task, attr) -> + t = taskInChallenge(task) + t and t[attr] + view.fn 'brokenChallengeLink', (task) -> task?.challenge and !taskInChallenge.call(@,task) diff --git a/views/app/tasks.html b/views/app/tasks.html index e30465aa90..a4d5e58613 100644 --- a/views/app/tasks.html +++ b/views/app/tasks.html @@ -176,7 +176,9 @@ {{#if :task.challenge}} - + {{#if brokenChallengeLink(:task)}} + + {{/}} {{else}} @@ -233,7 +235,7 @@

    - {{#if :task.challenge}} + {{#if taskInChallenge(:task)}} {{taskAttrFromChallenge(:task,'text')}} {{else}} {:task.text} @@ -247,14 +249,20 @@

    + {{#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

    +
    + {{/}}
    - {{#unless :task.challenge}} + {{#unless taskInChallenge(:task)}} {{/}} - {{#if :task.challenge}} + {{#if taskInChallenge(:task)}} {{else}} @@ -262,7 +270,7 @@
    - {#if and(equal(:task.type, 'habit'),not(:task.challenge))} + {#if and(equal(:task.type, 'habit'),not(taskInChallenge(:task)))}
    Direction/Actions @@ -318,7 +326,7 @@
    Difficulty - {{#if :task.challenge}} + {{#if taskInChallenge(:task)}} From a213d6e9971586b5921b8d2c5763b665fb2a8d11 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 29 May 2013 22:47:07 +0100 Subject: [PATCH 035/111] groups: accordion information on left side, more edit options for group information --- src/app/groups.coffee | 9 +++ src/app/index.coffee | 2 + src/app/misc.coffee | 10 ++-- views/app/groups.html | 126 +++++++++++++++++++++++++++++++++++------- 4 files changed, 120 insertions(+), 27 deletions(-) diff --git a/src/app/groups.coffee b/src/app/groups.coffee index 7578e42ba1..1d74906ab2 100644 --- a/src/app/groups.coffee +++ b/src/app/groups.coffee @@ -19,6 +19,15 @@ module.exports.app = (appExports, model, app) -> type: $(el).attr('data-type') , ->location.reload()) + appExports.toggleGroupEdit = (e, el) -> + path = "_editing.groups.#{$(el).attr('data-gid')}" + model.set path, !model.get(path) + + appExports.groupAddWebsite = (e, el) -> + test = e.get() + e.at().unshift 'websites', model.get('_newGroupWebsite') + model.del '_newGroupWebsite' + appExports.groupInvite = (e,el) -> uid = model.get('_groupInvitee').replace(/[\s"]/g, '') model.set '_groupInvitee', '' diff --git a/src/app/index.coffee b/src/app/index.coffee index 5eabf24199..da5e3279d9 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -133,6 +133,8 @@ ready (model) -> user = model.at('_user') browser = require './browser' + exports.removeAt = (e) -> e.at().remove() # used for things like remove website, chat, etc + require('./tasks').app(exports, model) require('./items').app(exports, model) require('./groups').app(exports, model, app) diff --git a/src/app/misc.coffee b/src/app/misc.coffee index 5ae183c5dc..8c960323ce 100644 --- a/src/app/misc.coffee +++ b/src/app/misc.coffee @@ -119,17 +119,15 @@ module.exports.viewHelpers = (view) -> #TODO put this in habitrpg-shared taskInChallenge = (task) -> return false unless task?.challenge - [gid, cid, gType] = [task.group.id, task.challenge, task.group.type] + [tid, gid, cid, gType] = [task.id, task.group.id, task.challenge, task.group.type] getTask = (challenges) -> challenge = _.find(challenges,{id:cid}) - challenge and _.find(challenge["#{task.type}s"],{id:task.id}) + challenge and _.find(challenge["#{task.type}s"],{id:tid}) switch gType when 'party' - party = @model.get('_party') - return party?.challenges and getTask(party.challenges) + (party = @model.get '_party') and party and (getTask party.challenges) when 'guild' - guilds = @model.get("_guilds") - return guilds and getTask(_.find(guilds,{id:gid}).challenges) + (guilds = @model.get "_guilds") and (guild = _.find guilds,{id:gid}) and guild and (getTask guild.challenges) #Challenges view.fn 'taskInChallenge', taskInChallenge diff --git a/views/app/groups.html b/views/app/groups.html index 187787c696..6b1ed5c40c 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -62,9 +62,10 @@
    +
    -
    +
    {{#if equal(@group.id,'habitrpg')}}
    @@ -95,28 +96,111 @@ {{else}} -

    {{@group.name}}

    -
    - {{#each @group.members as :memberId}} - - {{/}} -
    - - {{username(_members[:memberId].auth, _members[:memberId].profile.name)}}({{:memberId}}) - -
    - {#with @group as :group} -
    - {#if _groupError} -
    {_groupError}
    - {/} -
    - - +
    +
    + +
    +
    + {#if _editing.groups[@group.id]} +
    + +
    +
    + +
    + + + {#with @group} + + + + + {#if @group.websites} +

    Resources

    +
      + {#each @group.websites as :website} +
    • {:website}
    • + {/} +
    + {/} + {/} + {else} + {#if @group.logo}{/} + {{#if equal(@group.leader,_user.id)}} + + {{/}} +

    {@group.name}

    +
    {@group.description}
    + {/} + {#if @group.websites} +

    Resources

    +
      + {#each @group.websites as :website} +
    • {:website}
    • + {/} +
    + {/} +
    +
    - + +
    +
    + Members +
    +
    +
    + + {{#each @group.members as :memberId}} + + + + {{/}} +
    + + {{username(_members[:memberId].auth, _members[:memberId].profile.name)}} + + + ({{:memberId}}) +
    + {#with @group as :group} +
    + {#if _groupError} +
    {_groupError}
    + {/} +
    + + +
    +
    + {/} +
    +
    +
    + +
    + +
    +
    + + {{#each @group.challenges as :challenge}} + + {{/}} +
    + {{:challenge.name}} +
    +
    + +
    +
    + + +
    Leave - {/} {{/}}
    From 8ad2bf56870a0b6b86d56baaf838cf2a3d3f3704 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 29 May 2013 22:55:20 +0100 Subject: [PATCH 036/111] groups: don't show websites twice when editing --- views/app/groups.html | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/views/app/groups.html b/views/app/groups.html index 6b1ed5c40c..281cf1a846 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -117,14 +117,14 @@ - {#if @group.websites} -

    Resources

    -
      - {#each @group.websites as :website} -
    • {:website}
    • - {/} -
    {/} + {#if @group.websites} +

    Resources

    +
      + {#each @group.websites as :website} +
    • {:website}
    • + {/} +
    {/} {else} {#if @group.logo}{/} @@ -133,14 +133,14 @@ {{/}}

    {@group.name}

    {@group.description}
    - {/} - {#if @group.websites} -

    Resources

    -
      - {#each @group.websites as :website} -
    • {:website}
    • + {#if @group.websites} +

      Resources

      +
        + {#each @group.websites as :website} +
      • {:website}
      • + {/} +
      {/} -
    {/}
    From 48e302763823216d94fb566dc4cde5658642f479 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 29 May 2013 23:06:07 +0100 Subject: [PATCH 037/111] hotfix: getting a lot of "DERBY is not defined" --- src/app/browser.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/browser.coffee b/src/app/browser.coffee index 685621c993..44dcedb810 100644 --- a/src/app/browser.coffee +++ b/src/app/browser.coffee @@ -195,8 +195,8 @@ setupGrowlNotifications = (model) -> statsNotification ' Level Up!', 'lvl' module.exports.resetDom = (model) -> - DERBY.app.dom.clear() - DERBY.app.view.render(model, DERBY.app.view._lastRender.ns, DERBY.app.view._lastRender.context); + window.DERBY.app.dom.clear() + window.DERBY.app.view.render(model, window.DERBY.app.view._lastRender.ns, window.DERBY.app.view._lastRender.context); # Note, Google Analyatics giving beef if in this file. Moved back to index.html. It's ok, it's async - really the # syncronous requires up top are what benefit the most from this file. From 64a99e734f9eba7bcf3cffb039f1f72baf524887 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 29 May 2013 23:18:44 +0100 Subject: [PATCH 038/111] challenges: use bootstrap accordiong, much cleaner --- views/app/challenges.html | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/views/app/challenges.html b/views/app/challenges.html index 5229a8b412..4a2adff061 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -57,19 +57,26 @@
    -
    -
    - Unsubscribe - Subscribe +
    + -

    {@challenge.name} (by {@challenge.user})

    +
    +
    -
    From b34caaabd6a9d2502dfd0ee48036d0678f3f4ddf Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 29 May 2013 23:23:17 +0100 Subject: [PATCH 039/111] challenges: view.fn bug fix --- src/app/misc.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/misc.coffee b/src/app/misc.coffee index 8c960323ce..6187bfc9f1 100644 --- a/src/app/misc.coffee +++ b/src/app/misc.coffee @@ -132,6 +132,6 @@ module.exports.viewHelpers = (view) -> #Challenges view.fn 'taskInChallenge', taskInChallenge view.fn 'taskAttrFromChallenge', (task, attr) -> - t = taskInChallenge(task) + t = taskInChallenge.call(@,task) t and t[attr] view.fn 'brokenChallengeLink', (task) -> task?.challenge and !taskInChallenge.call(@,task) From ea8f245b845d8f6c7d9f316333e3b229b0e16c71 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 29 May 2013 23:33:47 +0100 Subject: [PATCH 040/111] challenges: edit challenge title & description --- src/app/challenges.coffee | 8 ++++---- views/app/challenges.html | 10 ++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/app/challenges.coffee b/src/app/challenges.coffee index 011567e340..25d1f696f4 100644 --- a/src/app/challenges.coffee +++ b/src/app/challenges.coffee @@ -26,6 +26,10 @@ module.exports.app = (appExports, model) -> 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) -> @@ -57,7 +61,3 @@ module.exports.app = (appExports, model) -> model.remove "_#{type}List", _.findIndex(model.get("_#{type}List",{id:task.id})) model.del "_user.tasks.#{task.id}" true - - appExports.challengeCollapse = (e, el) -> - $(el).next().toggle() - i = $(el).find('i').toggleClass('icon-chevron-right icon-chevron-down') \ No newline at end of file diff --git a/views/app/challenges.html b/views/app/challenges.html index 4a2adff061..2be554c6b3 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -68,6 +68,16 @@
    + {#if _editing.challenges[@challenge.id]} + Done +
    + + +
    + {else if equal(@challenge.uid,_user.id)} + Edit + {/} + {#if @challenge.description}
    {@challenge.description}
    {/}
    Date: Wed, 29 May 2013 23:40:13 +0100 Subject: [PATCH 041/111] challenges: add in challenge prize, just for show-purposes for now --- views/app/challenges.html | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/views/app/challenges.html b/views/app/challenges.html index 2be554c6b3..6b44a33af7 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -73,11 +73,18 @@
    +
    {else if equal(@challenge.uid,_user.id)} Edit {/} - {#if @challenge.description}
    {@challenge.description}
    {/} + {#if @challenge.prize} + +
    {@challenge.prize} Gem Prize
    +
    + {/} + {#if @challenge.description}
    {@challenge.description}
    {/} +
    Date: Thu, 30 May 2013 08:41:07 +0100 Subject: [PATCH 042/111] challenges: couple bug fixes --- views/app/challenges.html | 14 ++++++++------ views/app/tasks.html | 2 ++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/views/app/challenges.html b/views/app/challenges.html index 6b44a33af7..4f36bc8415 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -67,6 +67,11 @@
    + {#if @challenge.prize} + +
    {@challenge.prize} Gem Prize
    +
    + {/} {#if _editing.challenges[@challenge.id]} Done @@ -75,13 +80,10 @@
    - {else if equal(@challenge.uid,_user.id)} - Edit {/} - {#if @challenge.prize} - -
    {@challenge.prize} Gem Prize
    -
    + + {#if and(not(_editing.challenges[@challenge.id]),equal(@challenge.uid,_user.id))} + Edit {/} {#if @challenge.description}
    {@challenge.description}
    {/} diff --git a/views/app/tasks.html b/views/app/tasks.html index a4d5e58613..152a53dc07 100644 --- a/views/app/tasks.html +++ b/views/app/tasks.html @@ -178,6 +178,8 @@ {{#if :task.challenge}} {{#if brokenChallengeLink(:task)}} + {{else}} + {{/}} {{else}} From 15d76d93f48119a7951015a00fe31fe08e16e4e8 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Thu, 30 May 2013 18:07:00 +0100 Subject: [PATCH 043/111] return delta bug fix --- src/app/misc.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/misc.coffee b/src/app/misc.coffee index 6187bfc9f1..261a0e38df 100644 --- a/src/app/misc.coffee +++ b/src/app/misc.coffee @@ -11,7 +11,7 @@ module.exports.batchTxn = batchTxn = (model, cb, options) -> get: (k) -> helpers.dotGet(k,uObj) paths = {} model._dontPersist = true - cb uObj, paths, batch + ret = cb uObj, paths, batch _.each paths, (v,k) -> user.pass({cron:options?.cron}).set(k,helpers.dotGet(k, uObj));true model._dontPersist = false # some hackery in our own branched racer-db-mongo, see findAndModify of lefnire/racer-db-mongo#habitrpg index.js @@ -19,6 +19,8 @@ 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 + ret + ### algos.score wrapper for habitrpg-helpers to work in Derby. We need to do model.set() instead of simply setting the From 9399ca1d9848707ba9946166520aac1aa14776cc Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Thu, 30 May 2013 14:56:28 -0400 Subject: [PATCH 044/111] remove console.log --- src/app/misc.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/misc.coffee b/src/app/misc.coffee index e2b0743aa0..b0428bd9b0 100644 --- a/src/app/misc.coffee +++ b/src/app/misc.coffee @@ -82,7 +82,6 @@ module.exports.fixCorruptUser = (model) -> ## fix https://github.com/lefnire/habitrpg/issues/1086 uniqPets = _.uniq(uObj.items.pets) batch.set('items.pets', uniqPets) if !_.isEqual(uniqPets, uObj.items.pets) - console.log {uniqPets, count:_.size(uniqPets)} ## Task List Cleanup ['habit','daily','todo','reward'].forEach (type) -> From 9180c9dfd41b1bd74370dbb3e20cba8026b45103 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Thu, 30 May 2013 17:10:35 -0400 Subject: [PATCH 045/111] challenges: add indexedPath() function & view helper, which lets us look up array indices by object id, similar to reflist benefits. very handy, using for challenges & challnege tasks --- src/app/misc.coffee | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/app/misc.coffee b/src/app/misc.coffee index 66c50c67ca..8378d38b43 100644 --- a/src/app/misc.coffee +++ b/src/app/misc.coffee @@ -21,6 +21,21 @@ module.exports.batchTxn = batchTxn = (model, cb, options) -> user.set "update__", setOps ret +#TODO put this in habitrpg-shared +### + We can't always use refLists, but we often still need to get a positional path by id: eg, users.1234.tasks.5678.value + For arrays (which use indexes, not id-paths), here's a helper function so we can run indexedPath('users',:user.id,'tasks',:task.id,'value) +### +indexedPath = -> + _.reduce arguments, (m,v) => + return v if !m #first iteration + return "#{m}.#{v}" if _.isString v #string paths + return "#{m}." + _.findIndex(@model.get(m),v) + , '' + +taskInChallenge = (task) -> + return undefined unless task?.challenge + @model.at indexedPath.call(@, "groups.#{task.group.id}.challenges", {id:task.challenge}, "#{task.type}s", {id:task.id}) ### algos.score wrapper for habitrpg-helpers to work in Derby. We need to do model.set() instead of simply setting the @@ -124,6 +139,8 @@ module.exports.viewHelpers = (view) -> view.fn 'int', get: (num) -> num set: (num) -> [parseInt(num)] + view.fn 'indexedPath', indexedPath + #iCal view.fn "encodeiCalLink", helpers.encodeiCalLink @@ -159,22 +176,10 @@ module.exports.viewHelpers = (view) -> view.fn 'noTags', helpers.noTags view.fn 'appliedTags', helpers.appliedTags - #TODO put this in habitrpg-shared - taskInChallenge = (task) -> - return false unless task?.challenge - [tid, gid, cid, gType] = [task.id, task.group.id, task.challenge, task.group.type] - getTask = (challenges) -> - challenge = _.find(challenges,{id:cid}) - challenge and _.find(challenge["#{task.type}s"],{id:tid}) - switch gType - when 'party' - (party = @model.get '_party') and party and (getTask party.challenges) - when 'guild' - (guilds = @model.get "_guilds") and (guild = _.find guilds,{id:gid}) and guild and (getTask guild.challenges) - #Challenges - view.fn 'taskInChallenge', taskInChallenge + view.fn 'taskInChallenge', (task) -> + taskInChallenge.call(@,task)?.get() view.fn 'taskAttrFromChallenge', (task, attr) -> - t = taskInChallenge.call(@,task) - t and t[attr] - view.fn 'brokenChallengeLink', (task) -> task?.challenge and !taskInChallenge.call(@,task) + taskInChallenge.call(@,task)?.get(attr) + view.fn 'brokenChallengeLink', (task) -> + task?.challenge and !(taskInChallenge.call(@,task)?.get()) From 6e672078358dc37bcec758102891f99c22430bf1 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Thu, 30 May 2013 18:42:27 -0400 Subject: [PATCH 046/111] groups: fix leave button --- src/app/groups.coffee | 7 ++++--- views/app/groups.html | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/app/groups.coffee b/src/app/groups.coffee index 1d74906ab2..dd01c9d23d 100644 --- a/src/app/groups.coffee +++ b/src/app/groups.coffee @@ -81,11 +81,12 @@ module.exports.app = (appExports, model, app) -> else e.at().remove clear appExports.groupLeave = (e,el) -> - members = e.get('members') + group = model.at "groups.#{$(el).attr('data-id')}" + members = group.get('members') index = members.indexOf(user.get('id')) - e.at().remove 'members', index, 1, -> + group.remove 'members', index, 1, -> if members.length is 1 # # last member out, kill the party - model.del("groups.#{id}", ->location.reload()) + group.del ->location.reload() else location.reload() diff --git a/views/app/groups.html b/views/app/groups.html index 281cf1a846..123d35bdf2 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -200,7 +200,7 @@
    - Leave + Leave {{/}}
    From 842ac45ab58a738d88f6c806e21ace7a1d409411 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Thu, 30 May 2013 18:43:39 -0400 Subject: [PATCH 047/111] move fixCorruptUser to the client --- src/app/index.coffee | 10 ++++------ src/app/misc.coffee | 3 +++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/app/index.coffee b/src/app/index.coffee index aad5420dbf..4e09f78cb3 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -78,11 +78,8 @@ setupSubscriptions = (page, model, params, next, cb) -> get '/', (page, model, params, next) -> return page.redirect '/' if page.params?.query?.play? - model.set '_gamePane', true - # removed force-ssl (handled in nginx), see git for code setupSubscriptions page, model, params, next, -> - misc.fixCorruptUser(model) # https://github.com/lefnire/habitrpg/issues/634 require('./items').server(model) #refLists _.each ['habit', 'daily', 'todo', 'reward'], (type) -> @@ -94,11 +91,12 @@ get '/', (page, model, params, next) -> # ========== CONTROLLER FUNCTIONS ========== ready (model) -> - user = model.at('_user') - browser = require './browser' - exports.removeAt = (e) -> e.at().remove() # used for things like remove website, chat, etc + user = model.at('_user') + misc.fixCorruptUser(model) # https://github.com/lefnire/habitrpg/issues/634 + + browser = require './browser' require('./tasks').app(exports, model) require('./items').app(exports, model) require('./groups').app(exports, model, app) diff --git a/src/app/misc.coffee b/src/app/misc.coffee index 8378d38b43..0bc69625f5 100644 --- a/src/app/misc.coffee +++ b/src/app/misc.coffee @@ -92,6 +92,7 @@ module.exports.fixCorruptUser = (model) -> delete tasks[key] true + resetDom = false batchTxn model, (uObj, paths, batch) -> ## fix https://github.com/lefnire/habitrpg/issues/1086 @@ -115,6 +116,8 @@ module.exports.fixCorruptUser = (model) -> batch.set("#{type}Ids", preened) console.error uObj.id + "'s #{type}s were corrupt." true + resetDom = !_.isEmpty(paths) + require('./browser').resetDom(model) if resetDom module.exports.viewHelpers = (view) -> From 1411d045180857d67e00c7b4cd2a60ca52523dd0 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Fri, 31 May 2013 09:35:50 -0400 Subject: [PATCH 048/111] challenges: add basic task stats for challenge subscribers --- src/app/challenges.coffee | 15 +++++++++++++++ src/app/misc.coffee | 27 +++++++++++++++++++++++---- views/app/challenges.html | 25 +++++++++++++++++++++++++ views/app/game-pane.html | 2 +- 4 files changed, 64 insertions(+), 5 deletions(-) diff --git a/src/app/challenges.coffee b/src/app/challenges.coffee index 25d1f696f4..dff8653269 100644 --- a/src/app/challenges.coffee +++ b/src/app/challenges.coffee @@ -5,6 +5,21 @@ module.exports.app = (appExports, model) -> browser = require './browser' user = model.at '_user' + appExports.renderChallengeGraphs = -> + challenges = model.get('_party.challenges') + _.each model.get('_guilds'), (g) -> challenges.concat(g.challenges) + _.each 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' } + axisTitlesPosition: 'none' + 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', diff --git a/src/app/misc.coffee b/src/app/misc.coffee index 0bc69625f5..1964b42625 100644 --- a/src/app/misc.coffee +++ b/src/app/misc.coffee @@ -44,8 +44,7 @@ taskInChallenge = (task) -> perform the updates while tracking paths, then all the values at those paths ### module.exports.score = (model, taskId, direction, allowUndo=false) -> - #return setTimeout( (-> score(taskId, direction)), 500) if model._txnQueue.length > 0 - batchTxn model, (uObj, paths) -> + delta = batchTxn model, (uObj, paths) -> tObj = uObj.tasks[taskId] # Stuff for undo @@ -62,7 +61,27 @@ module.exports.score = (model, taskId, direction, allowUndo=false) -> if uObj._tmp?.drop and $? model.set '_drop', uObj._tmp.drop $('#item-dropped-modal').modal 'show' - delta + + # Update challenge statistics + # FIXME put this in it's own batchTxn, make batchTxn model.at() ref aware (not just _user) + # FIXME use reflists for users & challenges + if (chalTask = taskInChallenge.call({model}, tObj)) and chalTask?.get() + model._dontPersist = false + chalTask.incr "value", delta + chal = model.at indexedPath.call({model}, "groups.#{tObj.group.id}.challenges", {id:tObj.challenge}) + chalUser = -> indexedPath.call({model}, chal.path(), 'users', {id:uObj.id}) + cu = model.at chalUser() + unless cu?.get() + chal.push "users", {id: uObj.id, name: helpers.username(uObj.auth, uObj.profile?.name)} + cu = model.at chalUser() + else + cu.set 'name', helpers.username(uObj.auth, uObj.profile?.name) # update their name incase it changed + cu.set "#{tObj.type}s.#{tObj.id}", + value: tObj.value + history: tObj.history + model._dontPersist = true + + delta ### Make sure model.get() returns all properties, see https://github.com/codeparty/racer/issues/116 @@ -105,7 +124,7 @@ module.exports.fixCorruptUser = (model) -> # 1. remove duplicates # 2. restore missing zombie tasks back into list idList = uObj["#{type}Ids"] - taskIds = _.pluck( _.where(tasks, {type:type}), 'id') + taskIds = _.pluck( _.where(tasks, {type}), 'id') union = _.union idList, taskIds # 2. remove empty (grey) tasks diff --git a/views/app/challenges.html b/views/app/challenges.html index 4f36bc8415..b281667f06 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -95,10 +95,35 @@ todos={@challenge.todos} rewards={@challenge.rewards} />
    + +

    Statistics

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

    {:member.name}

    +
    +
    + +
    +
    + +
    +
    + +
    +
    + {/}
    + +
    {@header}
    + {#each @challenge[@taskType]s as :task} +
    + {:task.text}: {round(@member[@taskType]s[:task.id].value)} +
    +
    + {/} + Create {{@text}} Challenge diff --git a/views/app/game-pane.html b/views/app/game-pane.html index d96f1ca136..9211621806 100644 --- a/views/app/game-pane.html +++ b/views/app/game-pane.html @@ -14,7 +14,7 @@ {/if}
  • Tavern
  • Achievements
  • -
  • Challenges
  • +
  • Challenges
  • Settings
  • From 39d29f50f412d644c8543c4e4f886d6bd3ee8c38 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Fri, 31 May 2013 09:36:00 -0400 Subject: [PATCH 049/111] challenge: don't allow to create new challenge unless in group --- views/app/challenges.html | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/views/app/challenges.html b/views/app/challenges.html index b281667f06..df2be92dc7 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -9,12 +9,16 @@
    - {#if _challenge.new} - + {#unless _party.id} + Join a party first {else} - - {#each _party.challenges as :challenge} - + {#if _challenge.new} + + {else} + + {#each _party.challenges as :challenge} + + {/} {/} {/}
    From fa0813dc9e4093445fa77a70e5fa72f296ba84cd Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Fri, 31 May 2013 17:53:17 -0400 Subject: [PATCH 050/111] challenges: use tags fix (268157c) to allow guild info editing (not working for _party for some reason) --- views/app/challenges.html | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/views/app/challenges.html b/views/app/challenges.html index df2be92dc7..fcd09c8ed7 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -9,18 +9,19 @@
    - {#unless _party.id} - Join a party first - {else} + {{#unless _party.id}} + Join a party first. + {{else}} {#if _challenge.new} {else} + {#each _party.challenges as :challenge} {/} {/} - {/} + {{/}}
    @@ -37,7 +38,7 @@ {else} {#each :guild.challenges as :challenge} - + {/}
    {/} @@ -52,7 +53,7 @@ {else} {#each _habitRPG.challenges as :challenge} - + {/} {/}
    From a4e60251851df6a2dc97f3bfdee2a3124b003601 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Fri, 31 May 2013 18:31:37 -0400 Subject: [PATCH 051/111] challenges: slightly prettier graphs (i'm still not satisfied, but later) --- src/app/challenges.coffee | 32 ++++++++++++++++++-------------- views/app/challenges.html | 14 ++++++++++---- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/app/challenges.coffee b/src/app/challenges.coffee index dff8653269..7d5d1b1ffd 100644 --- a/src/app/challenges.coffee +++ b/src/app/challenges.coffee @@ -6,19 +6,24 @@ module.exports.app = (appExports, model) -> user = model.at '_user' appExports.renderChallengeGraphs = -> - challenges = model.get('_party.challenges') - _.each model.get('_guilds'), (g) -> challenges.concat(g.challenges) - _.each 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' } - axisTitlesPosition: 'none' - chart = new google.visualization.LineChart $(".challenge-#{chal.id}-member-#{member.id}-history-#{task.id}")[0] - chart.draw(data, options) + _.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')] @@ -36,7 +41,6 @@ module.exports.app = (appExports, model) -> appExports.challengeSave = -> gid = model.get('_challenge.new.group.id') - debugger model.unshift "groups.#{gid}.challenges", model.get('_challenge.new'), -> browser.growlNotification('Challenge Created','success') challengeDiscard() diff --git a/views/app/challenges.html b/views/app/challenges.html index fcd09c8ed7..4c65e1828b 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -122,12 +122,18 @@
    {@header}
    +
    {#each @challenge[@taskType]s as :task} -
    - {:task.text}: {round(@member[@taskType]s[:task.id].value)} -
    -
    + + + +
    + {:task.text}: {round(@member[@taskType]s[:task.id].value)} + +
    +
    {/} +
    Create {{@text}} Challenge From 28f45baf7bd012823949a9c17c1e12b359ae0890 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Fri, 31 May 2013 18:31:41 -0400 Subject: [PATCH 052/111] challenges: can delete challenge --- views/app/challenges.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/views/app/challenges.html b/views/app/challenges.html index 4c65e1828b..f63ea6b89e 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -85,6 +85,9 @@
    + {{#with @challenge}} + Delete + {{/}} {/} {#if and(not(_editing.challenges[@challenge.id]),equal(@challenge.uid,_user.id))} From 0fd74680c20df34eee1a70bd37c7b3a4fd6a82f5 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Fri, 31 May 2013 18:42:02 -0400 Subject: [PATCH 053/111] challenges: float-right edit button, us icons for consistency --- views/app/challenges.html | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/views/app/challenges.html b/views/app/challenges.html index f63ea6b89e..e3ecd093f8 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -72,30 +72,39 @@
    + + {#if @challenge.prize} - +
    {@challenge.prize} Gem Prize
    {/} + + + {#if and(not(_editing.challenges[@challenge.id]),equal(@challenge.uid,_user.id))} + + {else} + + {/} + + {#if _editing.challenges[@challenge.id]} - Done
    {{#with @challenge}} - Delete + Delete {{/}} {/} - - {#if and(not(_editing.challenges[@challenge.id]),equal(@challenge.uid,_user.id))} - Edit - {/} {#if @challenge.description}
    {@challenge.description}
    {/} -
    Date: Fri, 31 May 2013 18:47:49 -0400 Subject: [PATCH 054/111] challenges: more html futzing --- views/app/challenges.html | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/views/app/challenges.html b/views/app/challenges.html index e3ecd093f8..00bdaa1d14 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -64,15 +64,16 @@
    -
    +
    + + {#if @challenge.prize} From 5f78d801a8485318bdf1b93263bcfc301326e9f1 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Fri, 31 May 2013 18:59:20 -0400 Subject: [PATCH 055/111] challenges: make @editable dynamic so we can add tasks when in edit mode --- views/app/challenges.html | 1 + views/app/tasks.html | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/views/app/challenges.html b/views/app/challenges.html index 00bdaa1d14..216ccfd3ea 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -108,6 +108,7 @@
    <@ads>The Power of Habit: Why We Do What We Do in Life and Business @@ -38,7 +38,7 @@ placeHolder="New Daily" list={@dailys} main={{@main}} - editable={{@editable}} + editable={@editable} > <@ads>Getting Things Done: The Art of Stress-Free Productivity @@ -68,7 +68,7 @@ placeHolder="New Todo" list={@todos} main={{@main}} - editable={{@editable}} + editable={@editable} > <@ads>The Checklist Manifesto: How to Get Things Right @@ -108,7 +108,7 @@ placeHolder="New Reward" list={@rewards} main={{@main}} - editable={{@editable}} + editable={@editable} > <@extra> {{#if @main}} @@ -134,7 +134,7 @@ {{#if equal(@type,'todo')}}{{/}} - {{#if @editable}} + {#if @editable} @@ -145,9 +145,9 @@ {{/}}
    - {{/}} + {/}
      - {#each @list as :task}{/} + {#each @list as :task}{/}
    {{@extra}}
    From 780e3ccabcdb07574a9d34605ca068d717ac0298 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Fri, 31 May 2013 19:26:11 -0400 Subject: [PATCH 056/111] challenges: move challenge specs to accordion header, add subscribers count --- styles/app/challenges.styl | 7 +++++++ styles/app/index.styl | 1 + styles/app/inventory.styl | 8 ++++++++ views/app/challenges.html | 29 +++++++++++++++++++---------- 4 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 styles/app/challenges.styl diff --git a/styles/app/challenges.styl b/styles/app/challenges.styl new file mode 100644 index 0000000000..c28ee8be0e --- /dev/null +++ b/styles/app/challenges.styl @@ -0,0 +1,7 @@ +ul.challenge-accordion-header-specs + list-style:none + + li + background-color: darken($neutral, 10%) + margin: 2px 5px + float:left diff --git a/styles/app/index.styl b/styles/app/index.styl index 9995a66c94..cf5a6f2fb4 100644 --- a/styles/app/index.styl +++ b/styles/app/index.styl @@ -26,6 +26,7 @@ @import "./game-pane.styl"; @import "./backer.styl"; @import "./npcs.styl"; +@import "./challenges.styl"; // fix exploding to very wide for some reason .datepicker diff --git a/styles/app/inventory.styl b/styles/app/inventory.styl index 911dcead61..789d2d42d9 100644 --- a/styles/app/inventory.styl +++ b/styles/app/inventory.styl @@ -42,6 +42,14 @@ width:34px height:34px +.Pet_Currency_Gem, .Pet_Currency_Gem2x, .Pet_Currency_Gem1x + background: url("/img/sprites/Egg_Sprite_Sheet.png") no-repeat + display:block + +.Pet_Currency_Gem {background-position: 0px -510px; width: 51px; height: 45px} /* Not an egg or potion so has a different size */ +.Pet_Currency_Gem2x {background-position: -55px -513px; width: 34px; height: 30px} +.Pet_Currency_Gem1x {background-position: -63px -542px; width: 19px; height: 17px} + .inventory-list li clear:both .pets-menu > div diff --git a/views/app/challenges.html b/views/app/challenges.html index 216ccfd3ea..222c8452fe 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -64,22 +64,31 @@
    +
      +
    • + {count(@challenge.users)} Subscribers +
    • +
    • + + {#if @challenge.prize} +
      {@challenge.prize} Prize
      + {/} +
    • +
    • + + Unsubscribe + Subscribe +
    • +
    {@challenge.name} (by {@challenge.user}) + +
    - - - {#if @challenge.prize} - -
    {@challenge.prize} Gem Prize
    -
    - {/} + From 2ab5772c0a26367d395d40f7524ac3058f2d6354 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Fri, 31 May 2013 19:44:29 -0400 Subject: [PATCH 057/111] groups: make edit button consistent too --- views/app/groups.html | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/views/app/groups.html b/views/app/groups.html index 123d35bdf2..f4fd55b0ce 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -104,12 +104,10 @@
    {#if _editing.groups[@group.id]} -
    - -
    -
    - +
    +
    + {#with @group} @@ -129,7 +127,7 @@ {else} {#if @group.logo}{/} {{#if equal(@group.leader,_user.id)}} - + {{/}}

    {@group.name}

    {@group.description}
    From 178d3846a41b61a99b9d599b99534efc4b7046eb Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sat, 1 Jun 2013 10:57:38 -0400 Subject: [PATCH 058/111] challenges: html bug --- views/app/challenges.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/app/challenges.html b/views/app/challenges.html index 222c8452fe..126dd44f66 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -43,7 +43,7 @@
    {/}
    - {/} + {{/}}
    From 363a77cbe78c03c79e7003b0dbb769851fab9148 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sat, 1 Jun 2013 12:35:20 -0400 Subject: [PATCH 059/111] guilds: add public guilds --- src/app/groups.coffee | 16 +++++++++---- src/app/index.coffee | 16 ++++++++++--- src/server/store.coffee | 14 +++++++++++ views/app/challenges.html | 4 +--- views/app/groups.html | 49 +++++++++++++++++++++++++++++++++++---- 5 files changed, 83 insertions(+), 16 deletions(-) diff --git a/src/app/groups.coffee b/src/app/groups.coffee index dd01c9d23d..93a249cc02 100644 --- a/src/app/groups.coffee +++ b/src/app/groups.coffee @@ -13,7 +13,9 @@ module.exports.app = (appExports, model, app) -> appExports.groupCreate = (e,el) -> model.add('groups', - name: model.get("_newGroup") + name: model.get("_new.group.name") + description: model.get("_new.group.description") + privacy: model.get("_new.group.privacy") || 'public' leader: user.get('id') members: [user.get('id')] type: $(el).attr('data-type') @@ -66,13 +68,17 @@ module.exports.app = (appExports, model, app) -> return groupError("User already in a party.") else invite() + joinGroup = (gid) -> + model.push("groups.#{gid}.members", user.get('id'), ->location.reload()) + + appExports.joingGroup = (e, el) -> joinGroup e.get('id') + appExports.acceptInvitation = (e,el) -> - group = e.at().get() - pushMember = -> model.push("groups.#{group.id}.members", user.get('id'), ->location.reload()) + gid = e.get('id') if $(el).attr('data-type') is 'party' - user.set 'invitations.party', null, pushMember + user.set 'invitations.party', null, ->joinGroup(gid) else - e.at().remove pushMember + e.at().remove ->joinGroup(gid) appExports.rejectInvitation = (e, el) -> clear = -> browser.resetDom(model) diff --git a/src/app/index.coffee b/src/app/index.coffee index 4e09f78cb3..308484d4a7 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -35,6 +35,10 @@ setupSubscriptions = (page, model, params, next, cb) -> groupsQ.fetch (err, groups) -> return next(err) if err finished = (descriptors, paths) -> + # Add public "Tavern" guild in + descriptors.push('groups.habitrpg'); paths.push('_habitRPG') + + # Subscribe to each descriptor model.subscribe.apply model, descriptors.concat -> [err, refs] = [arguments[0], arguments] return next(err) if err @@ -42,12 +46,18 @@ setupSubscriptions = (page, model, params, next, cb) -> unless model.get('_user') console.error "User not found - this shouldn't be happening!" return page.redirect('/logout') #delete model.session.userId + + # Fetch public groups as _publicGroups - not it has to come at very end due to racer bug + model.query('groups').publicGroups().fetch (err, pg) -> + return next(err) if err + model.set '_publicGroups', _.sortBy pg.get(), (g) -> -_.size(g.members) + return cb() groupsObj = groups.get() # (1) Solo player - return finished(["groups.habitrpg", selfQ], ['_habitRPG', '_user']) if _.isEmpty(groupsObj) + return finished([selfQ], ['_user']) if _.isEmpty(groupsObj) ## (2) Party or Guild has members, fetch those users too # Subscribe to the groups themselves. We separate them by _party, _guilds, and _habitRPG (the "global" guild). @@ -68,10 +78,10 @@ setupSubscriptions = (page, model, params, next, cb) -> # Note - selfQ *must* come after membersQ in subscribe, otherwise _user will only get the fields restricted by party-members in store.coffee. Strang bug, but easy to get around partyQ = model.query('groups').withIds(groupsInfo.partyId) if _.isEmpty(groupsInfo.guildIds) - finished [partyQ, 'groups.habitrpg', selfQ], ['_party', '_habitRPG', '_user'] + finished [partyQ, selfQ], ['_party', '_user'] else guildsQ = model.query('groups').withIds(groupsInfo.guildIds) - finished [partyQ, guildsQ, 'groups.habitrpg', selfQ], ['_party', '_guilds', '_habitRPG', '_user'] + finished [partyQ, guildsQ, selfQ], ['_party', '_guilds', '_user'] # ========== ROUTES ========== diff --git a/src/server/store.coffee b/src/server/store.coffee index 5d93b2dcf4..32a152d773 100644 --- a/src/server/store.coffee +++ b/src/server/store.coffee @@ -134,6 +134,20 @@ groupSystem = (store) -> q = q.where('type').equals(type) if type? store.queryAccess 'groups', 'withMember', publicAccess + ### + Public Groups Info + ### + store.query.expose "groups", "publicGroups", -> + @where("privacy").equals('public') + .only [ + 'name' + 'description' + 'users' + 'members' + 'privacy' + ] + store.queryAccess "groups", "publicGroups", publicAccess + ### Public HabitRPG Guild ### diff --git a/views/app/challenges.html b/views/app/challenges.html index 126dd44f66..272a66f355 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -159,7 +159,7 @@
    - Create {{@text}} Challenge + Create {{@text}} Challenge
    @@ -172,8 +172,6 @@
    - - + Leave + Join + + + + {:group.name} + +
    +
    +
    +
    {{:group.description}}
    +
    +
    +
    + {/} +
    + {#if _groupError}
    {_groupError}
    {/}
    - + + + +
    + Public + Invite Only +
    From 8fdeccf43cb87a1ccf2ed441c622972009cd628e Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 2 Jun 2013 10:07:15 -0400 Subject: [PATCH 060/111] challenges: fix disappearing repeat bug --- views/app/tasks.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/views/app/tasks.html b/views/app/tasks.html index de9fe50106..08e2229c41 100644 --- a/views/app/tasks.html +++ b/views/app/tasks.html @@ -272,7 +272,8 @@ - {#if and(equal(:task.type, 'habit'),not(taskInChallenge(:task)))} + {#if equal(:task.type, 'habit')} + {#unless taskInChallenge(:task)}
    Direction/Actions @@ -284,6 +285,7 @@
    + {/} {else if equal(:task.type, 'daily')} From dc36651029da45c58fe7ed77540706cce800e247 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 2 Jun 2013 11:20:57 -0400 Subject: [PATCH 061/111] groups: only add privacy field on guilds --- src/app/groups.coffee | 9 +++++---- views/app/groups.html | 4 +++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/app/groups.coffee b/src/app/groups.coffee index 93a249cc02..e24f59abe7 100644 --- a/src/app/groups.coffee +++ b/src/app/groups.coffee @@ -12,14 +12,15 @@ module.exports.app = (appExports, model, app) -> user = model.at('_user') appExports.groupCreate = (e,el) -> - model.add('groups', + type = $(el).attr('data-type') + newGroup = name: model.get("_new.group.name") description: model.get("_new.group.description") - privacy: model.get("_new.group.privacy") || 'public' leader: user.get('id') members: [user.get('id')] - type: $(el).attr('data-type') - , ->location.reload()) + type: type + newGroup.privacy = (model.get("_new.group.privacy") || 'public') if type is 'guild' + model.add 'groups', newGroup, ->location.reload() appExports.toggleGroupEdit = (e, el) -> path = "_editing.groups.#{$(el).attr('data-gid')}" diff --git a/views/app/groups.html b/views/app/groups.html index 38cc01eb9f..bc61de23db 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -91,12 +91,14 @@ {/}
    - + + {{#if equal(@type,'guild')}}
    Public Invite Only
    + {{/}}
    From 38781cc012a02e9b34bcf2aac579411166a92600 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 2 Jun 2013 11:22:33 -0400 Subject: [PATCH 062/111] groups: some static bindings where possible --- views/app/groups.html | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/views/app/groups.html b/views/app/groups.html index bc61de23db..5674dcf7ed 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -58,30 +58,29 @@
    -
    - {#each _publicGroups as :group} + {{#each _publicGroups as :public}}
    - - {:group.name} + + {{:public.name}}
    -
    +
    -
    {{:group.description}}
    +
    {{:public.description}}
    - {/} + {{/}}
    From 01b9a420b8609680bfc976811e7ef9763efdd8f4 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 2 Jun 2013 11:23:02 -0400 Subject: [PATCH 063/111] groups: only show invitations if there are any (was previously iterating empty array) --- views/app/groups.html | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/views/app/groups.html b/views/app/groups.html index 5674dcf7ed..57383870f2 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -35,10 +35,14 @@
    - {#each _user.invitations.guilds as :invitation} -

    You're Invited To {:invitation.name}

    - Accept - Reject + {#if _user.invitations.guilds} + {#each _user.invitations.guilds as :invitation} +
    +

    You're Invited To {:invitation.name}

    + Accept + Reject +
    + {/} {/}
    From 8a1aae37a8977fccabef8a99ba03d3f075b90004 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 2 Jun 2013 11:24:03 -0400 Subject: [PATCH 064/111] groups: very precarious placement of query motifs and subscription / fetch order due to https://github.com/codeparty/racer/issues/57 for guilds, party, habitRPG group, and publicGroups. --- src/app/index.coffee | 12 +++++++----- src/server/store.coffee | 21 +++++++++++---------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/app/index.coffee b/src/app/index.coffee index 308484d4a7..116b8357de 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -32,11 +32,18 @@ setupSubscriptions = (page, model, params, next, cb) -> selfQ = model.query('users').withId(uuid) #keep this for later groupsQ = model.query('groups').withMember(uuid) + # Fetch public groups as _publicGroups - not it has to come at very end due to racer bug + model.query('groups').publicGroups().fetch (err, pg) -> + return next(err) if err + model.set '_publicGroups', _.sortBy(pg.get(), (g) -> -_.size(g.members)) + groupsQ.fetch (err, groups) -> return next(err) if err finished = (descriptors, paths) -> # Add public "Tavern" guild in descriptors.push('groups.habitrpg'); paths.push('_habitRPG') +# descriptors.unshift model.query('groups').publicGroups() +# paths.unshift('_publicGroups') # Subscribe to each descriptor model.subscribe.apply model, descriptors.concat -> @@ -47,11 +54,6 @@ setupSubscriptions = (page, model, params, next, cb) -> console.error "User not found - this shouldn't be happening!" return page.redirect('/logout') #delete model.session.userId - # Fetch public groups as _publicGroups - not it has to come at very end due to racer bug - model.query('groups').publicGroups().fetch (err, pg) -> - return next(err) if err - model.set '_publicGroups', _.sortBy pg.get(), (g) -> -_.size(g.members) - return cb() groupsObj = groups.get() diff --git a/src/server/store.coffee b/src/server/store.coffee index 32a152d773..bab1a5c64c 100644 --- a/src/server/store.coffee +++ b/src/server/store.coffee @@ -130,22 +130,23 @@ groupSystem = (store) -> Find group which has member by id ### store.query.expose "groups", "withMember", (id, type) -> - q = @where('members').contains([id]).only(['id','members','type']) + q = @where('members').contains([id])#.only(['id','members','type']) q = q.where('type').equals(type) if type? store.queryAccess 'groups', 'withMember', publicAccess ### - Public Groups Info + Public Groups Info ### store.query.expose "groups", "publicGroups", -> - @where("privacy").equals('public') - .only [ - 'name' - 'description' - 'users' - 'members' - 'privacy' - ] + @where('privacy').equals('public') + .where('type').equals('guild') +# .only [ +# 'name' +# 'description' +# 'users' +# 'members' +# 'privacy' +# ] store.queryAccess "groups", "publicGroups", publicAccess ### From ad2b37fe50b60c3f8b81f7a5061ce509b9385b97 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 2 Jun 2013 11:49:56 -0400 Subject: [PATCH 065/111] groups: some join / leave bug fixes --- src/app/groups.coffee | 15 +++++++-------- views/app/groups.html | 7 +++++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/app/groups.coffee b/src/app/groups.coffee index e24f59abe7..d8222c7b36 100644 --- a/src/app/groups.coffee +++ b/src/app/groups.coffee @@ -72,7 +72,7 @@ module.exports.app = (appExports, model, app) -> joinGroup = (gid) -> model.push("groups.#{gid}.members", user.get('id'), ->location.reload()) - appExports.joingGroup = (e, el) -> joinGroup e.get('id') + appExports.joinGroup = (e, el) -> joinGroup e.get('id') appExports.acceptInvitation = (e,el) -> gid = e.get('id') @@ -89,13 +89,12 @@ module.exports.app = (appExports, model, app) -> appExports.groupLeave = (e,el) -> group = model.at "groups.#{$(el).attr('data-id')}" - members = group.get('members') - index = members.indexOf(user.get('id')) - group.remove 'members', index, 1, -> - if members.length is 1 # # last member out, kill the party - group.del ->location.reload() - else - location.reload() + index = group.get('members').indexOf(user.get('id')) + if index != -1 + group.remove 'members', index, 1, -> + if _.isEmpty group.get('members') # last member out, delete the party + group.del ->location.reload() + else location.reload() ### Chat Functionality diff --git a/views/app/groups.html b/views/app/groups.html index 57383870f2..e9a68495ca 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -70,8 +70,11 @@
  • {{count(:public.members)}} member(s)
  • - Leave - Join + {{#if indexOf(:public.members,_user.id)}} + Leave + {{else}} + Join + {{/}}
  • From 89c6192e284432d99c220d344399408bdcacbe5e Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 2 Jun 2013 12:50:43 -0400 Subject: [PATCH 066/111] groups: possible solution to 8a1aae3 (fixes groups permissioning) --- src/app/index.coffee | 93 ++++++++++++++++++++--------------------- src/server/store.coffee | 10 +---- 2 files changed, 48 insertions(+), 55 deletions(-) diff --git a/src/app/index.coffee b/src/app/index.coffee index 116b8357de..125fb5566a 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -30,60 +30,59 @@ algos = require 'habitrpg-shared/script/algos' setupSubscriptions = (page, model, params, next, cb) -> uuid = model.get('_userId') or model.session.userId # see http://goo.gl/TPYIt selfQ = model.query('users').withId(uuid) #keep this for later - groupsQ = model.query('groups').withMember(uuid) - # Fetch public groups as _publicGroups - not it has to come at very end due to racer bug + # Fetch public groups as _publicGroups + # Note: due to https://github.com/codeparty/racer/issues/57, this has to come at the very beginning. The more limited + # the returned fields in motifs, the sooner they must come in fetch / subscribes. model.query('groups').publicGroups().fetch (err, pg) -> return next(err) if err model.set '_publicGroups', _.sortBy(pg.get(), (g) -> -_.size(g.members)) - groupsQ.fetch (err, groups) -> - return next(err) if err - finished = (descriptors, paths) -> - # Add public "Tavern" guild in - descriptors.push('groups.habitrpg'); paths.push('_habitRPG') -# descriptors.unshift model.query('groups').publicGroups() -# paths.unshift('_publicGroups') - - # Subscribe to each descriptor - model.subscribe.apply model, descriptors.concat -> - [err, refs] = [arguments[0], arguments] - return next(err) if err - _.each paths, (path, idx) -> model.ref path, refs[idx+1]; true - unless model.get('_user') - console.error "User not found - this shouldn't be happening!" - return page.redirect('/logout') #delete model.session.userId - - return cb() - - groupsObj = groups.get() - - # (1) Solo player - return finished([selfQ], ['_user']) if _.isEmpty(groupsObj) - - ## (2) Party or Guild has members, fetch those users too - # Subscribe to the groups themselves. We separate them by _party, _guilds, and _habitRPG (the "global" guild). - groupsInfo = _.reduce groupsObj, ((m,g)-> - if g.type is 'guild' then m.guildIds.push(g.id) else m.partyId = g.id - m.members = m.members.concat(g.members) - m - ), {guildIds:[], partyId:null, members:[]} - - # Fetch, not subscribe. There's nothing dynamic we need from members, just the the Group (below) which includes chat, challenges, etc - model.query('users').publicInfo(groupsInfo.members).fetch (err, members) -> + model.query('groups').withMember(uuid).fetch (err, groups) -> return next(err) if err - # we need _members as an object in the view, so we can iterate over _party.members as :id, and access _members[:id] for the info - mObj = members.get() - model.set "_members", _.object(_.pluck(mObj,'id'), mObj) - model.set "_membersArray", mObj + finished = (descriptors, paths) -> + # Add public "Tavern" guild in + descriptors.push('groups.habitrpg'); paths.push('_habitRPG') - # Note - selfQ *must* come after membersQ in subscribe, otherwise _user will only get the fields restricted by party-members in store.coffee. Strang bug, but easy to get around - partyQ = model.query('groups').withIds(groupsInfo.partyId) - if _.isEmpty(groupsInfo.guildIds) - finished [partyQ, selfQ], ['_party', '_user'] - else - guildsQ = model.query('groups').withIds(groupsInfo.guildIds) - finished [partyQ, guildsQ, selfQ], ['_party', '_guilds', '_user'] + # Subscribe to each descriptor + model.subscribe.apply model, descriptors.concat -> + [err, refs] = [arguments[0], arguments] + return next(err) if err + _.each paths, (path, idx) -> model.ref path, refs[idx+1]; true + unless model.get('_user') + console.error "User not found - this shouldn't be happening!" + return page.redirect('/logout') #delete model.session.userId + + return cb() + + groupsObj = groups.get() + + # (1) Solo player + return finished([selfQ], ['_user']) if _.isEmpty(groupsObj) + + ## (2) Party or Guild has members, fetch those users too + # Subscribe to the groups themselves. We separate them by _party, _guilds, and _habitRPG (the "global" guild). + groupsInfo = _.reduce groupsObj, ((m,g)-> + if g.type is 'guild' then m.guildIds.push(g.id) else m.partyId = g.id + m.members = m.members.concat(g.members) + m + ), {guildIds:[], partyId:null, members:[]} + + # Fetch, not subscribe. There's nothing dynamic we need from members, just the the Group (below) which includes chat, challenges, etc + model.query('users').publicInfo(groupsInfo.members).fetch (err, members) -> + return next(err) if err + # we need _members as an object in the view, so we can iterate over _party.members as :id, and access _members[:id] for the info + mObj = members.get() + model.set "_members", _.object(_.pluck(mObj,'id'), mObj) + model.set "_membersArray", mObj + + # Note - selfQ *must* come after membersQ in subscribe, otherwise _user will only get the fields restricted by party-members in store.coffee. Strang bug, but easy to get around + partyQ = model.query('groups').withIds(groupsInfo.partyId) + if _.isEmpty(groupsInfo.guildIds) + finished [partyQ, selfQ], ['_party', '_user'] + else + guildsQ = model.query('groups').withIds(groupsInfo.guildIds) + finished [partyQ, guildsQ, selfQ], ['_party', '_guilds', '_user'] # ========== ROUTES ========== diff --git a/src/server/store.coffee b/src/server/store.coffee index bab1a5c64c..df8d3e1b61 100644 --- a/src/server/store.coffee +++ b/src/server/store.coffee @@ -130,7 +130,7 @@ groupSystem = (store) -> Find group which has member by id ### store.query.expose "groups", "withMember", (id, type) -> - q = @where('members').contains([id])#.only(['id','members','type']) + q = @where('members').contains([id]).only(['id', 'type', 'name', 'description', 'members' , 'privacy']) q = q.where('type').equals(type) if type? store.queryAccess 'groups', 'withMember', publicAccess @@ -140,13 +140,7 @@ groupSystem = (store) -> store.query.expose "groups", "publicGroups", -> @where('privacy').equals('public') .where('type').equals('guild') -# .only [ -# 'name' -# 'description' -# 'users' -# 'members' -# 'privacy' -# ] + .only(['id', 'type', 'name', 'description', 'members' , 'privacy']) store.queryAccess "groups", "publicGroups", publicAccess ### From f9a978d0434aec148cc17253e7e0f3cb0168caad Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 2 Jun 2013 13:06:03 -0400 Subject: [PATCH 067/111] groups: bug-fix to private party invitation --- src/app/groups.coffee | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/app/groups.coffee b/src/app/groups.coffee index d8222c7b36..18f4217830 100644 --- a/src/app/groups.coffee +++ b/src/app/groups.coffee @@ -42,27 +42,23 @@ module.exports.app = (appExports, model, app) -> return model.set("_groupError", "User with id #{uid} not found.") unless profile model.query('groups').withMember(uid).fetch (err, g) -> throw err if err - - {type, name} = e.get() - gid = e.get('id') - groups = g.get() - groupError =(msg) -> model.set("_groupError", msg) + group = e.get(); groups = g.get() + {type, name} = group; gid = group.id + groupError = (msg) -> model.set("_groupError", msg) invite = -> - debugger $.bootstrapGrowl "Invitation Sent." - if type is 'guild' - model.push("users.#{uid}.invitations.guilds", {id:gid, name}, ->location.reload()) - else model.set "users.#{uid}.invitations.party", {id:gid, name}, -> - debugger - location.reload() + switch type + when 'guild' then model.push "users.#{uid}.invitations.guilds", {id:gid, name}, ->location.reload() + when 'party' then model.set "users.#{uid}.invitations.party", {id:gid, name}, ->location.reload() - if type is 'guild' - if _.find(profile.invitations.guilds, {id:gid}) - return groupError("User already invited to that group") - else if _.find groups, ((group)-> uid in group.members) - return groupError("User already in that group") - else invite() - if type is 'party' + switch type + when 'guild' + if _.find(profile.invitations.guilds, {id:gid}) + return groupError("User already invited to that group") + else if uid in group.members + return groupError("User already in that group") + else invite() + when 'party' if profile.invitations.party return groupError("User already pending invitation.") else if _.find(groups, {type:'party'}) From 770af3c4d45c808a5223c8903537097486236e4b Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 2 Jun 2013 13:27:00 -0400 Subject: [PATCH 068/111] groups: group-leave bug fixes (most things have to be static-bound, when somethign is deleted it causes errors on dom listeners). also make second-most-recent-member the new leader if the leaver is leader --- src/app/groups.coffee | 10 ++++++++-- views/app/groups.html | 14 +++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/app/groups.coffee b/src/app/groups.coffee index 18f4217830..d1a6201bb6 100644 --- a/src/app/groups.coffee +++ b/src/app/groups.coffee @@ -84,12 +84,18 @@ module.exports.app = (appExports, model, app) -> else e.at().remove clear appExports.groupLeave = (e,el) -> + uid = user.get('id') group = model.at "groups.#{$(el).attr('data-id')}" - index = group.get('members').indexOf(user.get('id')) + index = group.get('members').indexOf(uid) if index != -1 group.remove 'members', index, 1, -> - if _.isEmpty group.get('members') # last member out, delete the party + updated = group.get() + # last member out, delete the party + if _.isEmpty(updated.members) group.del ->location.reload() + # assign new leader, so the party is editable #TODO allow old leader to assign new leader, this is just random + else if (updated.leader is uid) + group.set "leader", updated.members[0], ->location.reload() else location.reload() ### diff --git a/views/app/groups.html b/views/app/groups.html index e9a68495ca..89954a907a 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -27,9 +27,9 @@
    @@ -47,11 +47,11 @@
    - {#each _guilds as :guild} -
    - + {{#each _guilds as :guild}} +
    +
    - {/} + {{/}}
    From e4c727520149ee380d348a9d942c06c497cc0323 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 2 Jun 2013 14:58:19 -0400 Subject: [PATCH 069/111] groups: move title above info box --- views/app/groups.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/views/app/groups.html b/views/app/groups.html index 89954a907a..7fdbeb0939 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -7,7 +7,7 @@
    {#if _party.id} - + {else if _user.invitations.party}

    You're Invited To {_user.invitations.party.name}

    @@ -28,7 +28,7 @@ @@ -143,6 +143,7 @@
    {{else}} +

    {@group.name}

    @@ -167,7 +168,7 @@

    Resources

    {/} @@ -176,7 +177,6 @@ {{#if equal(@group.leader,_user.id)}} {{/}} -

    {@group.name}

    {@group.description}
    {#if @group.websites}

    Resources

    From 47453d6133e5cbeb7b083bab5fcd488116f2e296 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 2 Jun 2013 18:28:33 -0400 Subject: [PATCH 070/111] challenges: simply display names of challenges in groups, with "visit the challenges tab for more info". --- views/app/groups.html | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/views/app/groups.html b/views/app/groups.html index 7fdbeb0939..22cb51a343 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -101,8 +101,8 @@ {{#if equal(@type,'guild')}}
    - Public - Invite Only + Public + Invite Only
    {{/}} @@ -231,15 +231,19 @@
    - - {{#each @group.challenges as :challenge}} - - {{/}} -
    - {{:challenge.name}} -
    + {#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. + {/}
    -
    From f92ef9935552dcd92b6933c461faa47c15b6b404 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 2 Jun 2013 19:03:03 -0400 Subject: [PATCH 071/111] groups: show groups as striped table instead of accordion. cleaner this way --- views/app/groups.html | 46 ++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/views/app/groups.html b/views/app/groups.html index 22cb51a343..68fb5e75d2 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -62,33 +62,25 @@
    -
    - {{#each _publicGroups as :public}} -
    -
    -
      -
    • {{count(:public.members)}} member(s)
    • -
    • - - {{#if indexOf(:public.members,_user.id)}} - Leave - {{else}} - Join - {{/}} -
    • -
    - - {{:public.name}} - -
    -
    -
    -
    {{:public.description}}
    -
    -
    -
    - {{/}} -
    + + {#each _publicGroups as :public} + + {/} +
    +
      +
    • {count(:public.members)} member(s)
    • +
    • + + {#if indexOf(:public.members,_user.id)} + Leave + {else} + Join + {/} +
    • +
    +

    {:public.name}

    +

    {:public.description}

    +
    From c47291fe7c7042f0f00673f76fc440ae74f119c1 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 2 Jun 2013 22:37:59 -0400 Subject: [PATCH 072/111] challenges: fix dynamic user-challenge score --- src/app/misc.coffee | 4 ++++ views/app/challenges.html | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/app/misc.coffee b/src/app/misc.coffee index 1964b42625..1236949db5 100644 --- a/src/app/misc.coffee +++ b/src/app/misc.coffee @@ -205,3 +205,7 @@ module.exports.viewHelpers = (view) -> taskInChallenge.call(@,task)?.get(attr) view.fn 'brokenChallengeLink', (task) -> task?.challenge and !(taskInChallenge.call(@,task)?.get()) + + view.fn 'challengeMemberScore', (member, tType, tid) -> + Math.round(member["#{tType}s"]?[tid]?.value) + diff --git a/views/app/challenges.html b/views/app/challenges.html index 272a66f355..c5ef9cba1e 100644 --- a/views/app/challenges.html +++ b/views/app/challenges.html @@ -149,7 +149,8 @@ {#each @challenge[@taskType]s as :task}
    - {:task.text}: {round(@member[@taskType]s[:task.id].value)} + + {:task.text}: {challengeMemberScore(@member,@taskType,:task.id)}
    From c6df323b07d7e429c8858cba34d90fa16f6f6c2b Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 2 Jun 2013 22:57:07 -0400 Subject: [PATCH 073/111] challenges: graph rendering now dynamic --- src/app/challenges.coffee | 5 +++-- views/app/game-pane.html | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/challenges.coffee b/src/app/challenges.coffee index 7d5d1b1ffd..5d61133a7c 100644 --- a/src/app/challenges.coffee +++ b/src/app/challenges.coffee @@ -5,13 +5,13 @@ module.exports.app = (appExports, model) -> browser = require './browser' user = model.at '_user' - appExports.renderChallengeGraphs = -> + $('#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 + 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' } @@ -25,6 +25,7 @@ module.exports.app = (appExports, model) -> 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', diff --git a/views/app/game-pane.html b/views/app/game-pane.html index 9211621806..42ab861773 100644 --- a/views/app/game-pane.html +++ b/views/app/game-pane.html @@ -14,7 +14,7 @@ {/if}
  • Tavern
  • Achievements
  • -
  • Challenges
  • +
  • Challenges
  • Settings
  • From 33ac1788fd3118472df1a57972e596675244b81d Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 2 Jun 2013 23:12:31 -0400 Subject: [PATCH 074/111] groups: add group leader message --- src/app/groups.coffee | 4 ++++ views/app/groups.html | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/app/groups.coffee b/src/app/groups.coffee index d1a6201bb6..2ff6617802 100644 --- a/src/app/groups.coffee +++ b/src/app/groups.coffee @@ -26,6 +26,10 @@ module.exports.app = (appExports, model, app) -> path = "_editing.groups.#{$(el).attr('data-gid')}" model.set path, !model.get(path) + appExports.toggleLeaderMessageEdit = (e, el) -> + path = "_editing.leaderMessage.#{$(el).attr('data-gid')}" + model.set path, !model.get(path) + appExports.groupAddWebsite = (e, el) -> test = e.get() e.at().unshift 'websites', model.get('_newGroupWebsite') diff --git a/views/app/groups.html b/views/app/groups.html index 68fb5e75d2..85670693a7 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -262,6 +262,26 @@ {{else}} + {{#if equal(@group.leader,_user.id)}} + {#if _editing.leaderMessage[@group.id]} + + + {else} + Edit leader message + {/} + {{/}} + {#if @group.leaderMessage} + + + +
    +
    +
    +

    {{username(_members[@group.leader].auth,_members[@group.leader].profile.name)}}

    +
    {@group.leaderMessage}
    +
    +
    + {/}

    Chat

    {{/}} From 9be6f1a0ddbaccc2fc901adbc141c6abf4baab28 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 2 Jun 2013 23:35:36 -0400 Subject: [PATCH 075/111] groups: static-binding of _party, was causing "Uncaught TypeError:Cannot read property '0' of undefined" since groups[_party.id] doesn't exist pre-subcription --- views/app/groups.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/views/app/groups.html b/views/app/groups.html index 85670693a7..0d8ab12f1b 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -6,22 +6,22 @@
    - {#if _party.id} + {{#if _party.id}} - {else if _user.invitations.party} + {{else if _user.invitations.party}}

    You're Invited To {_user.invitations.party.name}

    {#with _user.invitations.party} Accept Reject {/} - {else} + {{else}}

    Create A Party

    You are not in a party. You can either create one and invite friends, or if you want to join an existing party, have them enter:

    {_user.id}
    - {/} + {{/}}
    From 00d62edbc9a72dff78e246c5ba453430d7e358bf Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 2 Jun 2013 23:41:47 -0400 Subject: [PATCH 076/111] !IMPORTANT! xhr-polling for heroku, while testing challenges. remember to revert when pushing to prod --- src/server/index.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/index.coffee b/src/server/index.coffee index 3f707666e2..57e777128d 100644 --- a/src/server/index.coffee +++ b/src/server/index.coffee @@ -16,7 +16,7 @@ helpers = require("habitrpg-shared/script/helpers") ## RACER CONFIGURATION ## -#racer.io.set('transports', ['xhr-polling']) +racer.io.set('transports', ['xhr-polling']) racer.ioClient.set('reconnection limit', 300000) # max reconect timeout to 5 minutes racer.set('bundleTimeout', 40000) #unless process.env.NODE_ENV == 'production' From e565659ef33fd56ec20a65d606fdb92893fd831a Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 2 Jun 2013 23:56:33 -0400 Subject: [PATCH 077/111] change "report a problem" link to go to FAQ, to cut back on all these duplicates --- views/app/groups.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/app/groups.html b/views/app/groups.html index 0d8ab12f1b..7ab3a1c29c 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -129,7 +129,7 @@
  • LFG Posts

  • Tutorial

  • FAQ

  • -
  • Report a Problem

  • +
  • Report a Problem

  • Request a Feature

  • Community Forum

  • From 4bd6246e75a9ef1dbe4ea32fbd53c51bb1b78a5b Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 2 Jun 2013 23:57:59 -0400 Subject: [PATCH 078/111] change "report a problem" link to go to FAQ, to cut back on all these duplicates --- views/app/groups.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/app/groups.html b/views/app/groups.html index 7ab3a1c29c..197c8d08b8 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -253,7 +253,7 @@ From fbcf21cd9d15a53568c6ee56c7f11fe65dd41dfc Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 3 Jun 2013 00:16:02 -0400 Subject: [PATCH 079/111] groups: optimize public groups fetch by putting in same fetch as myGroups (but before, due to projections bug) --- src/app/index.coffee | 89 ++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/src/app/index.coffee b/src/app/index.coffee index 125fb5566a..2da29431fd 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -34,55 +34,54 @@ setupSubscriptions = (page, model, params, next, cb) -> # Fetch public groups as _publicGroups # Note: due to https://github.com/codeparty/racer/issues/57, this has to come at the very beginning. The more limited # the returned fields in motifs, the sooner they must come in fetch / subscribes. - model.query('groups').publicGroups().fetch (err, pg) -> + publicGroupsQuery = model.query('groups').publicGroups() + myGroupsQuery = model.query('groups').withMember(uuid) + model.fetch publicGroupsQuery, myGroupsQuery, (err, publicGroups, groups) -> return next(err) if err - model.set '_publicGroups', _.sortBy(pg.get(), (g) -> -_.size(g.members)) + model.set '_publicGroups', _.sortBy(publicGroups.get(), (g) -> -_.size(g.members)) + finished = (descriptors, paths) -> + # Add public "Tavern" guild in + descriptors.push('groups.habitrpg'); paths.push('_habitRPG') - model.query('groups').withMember(uuid).fetch (err, groups) -> - return next(err) if err - finished = (descriptors, paths) -> - # Add public "Tavern" guild in - descriptors.push('groups.habitrpg'); paths.push('_habitRPG') - - # Subscribe to each descriptor - model.subscribe.apply model, descriptors.concat -> - [err, refs] = [arguments[0], arguments] - return next(err) if err - _.each paths, (path, idx) -> model.ref path, refs[idx+1]; true - unless model.get('_user') - console.error "User not found - this shouldn't be happening!" - return page.redirect('/logout') #delete model.session.userId - - return cb() - - groupsObj = groups.get() - - # (1) Solo player - return finished([selfQ], ['_user']) if _.isEmpty(groupsObj) - - ## (2) Party or Guild has members, fetch those users too - # Subscribe to the groups themselves. We separate them by _party, _guilds, and _habitRPG (the "global" guild). - groupsInfo = _.reduce groupsObj, ((m,g)-> - if g.type is 'guild' then m.guildIds.push(g.id) else m.partyId = g.id - m.members = m.members.concat(g.members) - m - ), {guildIds:[], partyId:null, members:[]} - - # Fetch, not subscribe. There's nothing dynamic we need from members, just the the Group (below) which includes chat, challenges, etc - model.query('users').publicInfo(groupsInfo.members).fetch (err, members) -> + # Subscribe to each descriptor + model.subscribe.apply model, descriptors.concat -> + [err, refs] = [arguments[0], arguments] return next(err) if err - # we need _members as an object in the view, so we can iterate over _party.members as :id, and access _members[:id] for the info - mObj = members.get() - model.set "_members", _.object(_.pluck(mObj,'id'), mObj) - model.set "_membersArray", mObj + _.each paths, (path, idx) -> model.ref path, refs[idx+1]; true + unless model.get('_user') + console.error "User not found - this shouldn't be happening!" + return page.redirect('/logout') #delete model.session.userId - # Note - selfQ *must* come after membersQ in subscribe, otherwise _user will only get the fields restricted by party-members in store.coffee. Strang bug, but easy to get around - partyQ = model.query('groups').withIds(groupsInfo.partyId) - if _.isEmpty(groupsInfo.guildIds) - finished [partyQ, selfQ], ['_party', '_user'] - else - guildsQ = model.query('groups').withIds(groupsInfo.guildIds) - finished [partyQ, guildsQ, selfQ], ['_party', '_guilds', '_user'] + return cb() + + groupsObj = groups.get() + + # (1) Solo player + return finished([selfQ], ['_user']) if _.isEmpty(groupsObj) + + ## (2) Party or Guild has members, fetch those users too + # Subscribe to the groups themselves. We separate them by _party, _guilds, and _habitRPG (the "global" guild). + groupsInfo = _.reduce groupsObj, ((m,g)-> + if g.type is 'guild' then m.guildIds.push(g.id) else m.partyId = g.id + m.members = m.members.concat(g.members) + m + ), {guildIds:[], partyId:null, members:[]} + + # Fetch, not subscribe. There's nothing dynamic we need from members, just the the Group (below) which includes chat, challenges, etc + model.query('users').publicInfo(groupsInfo.members).fetch (err, members) -> + return next(err) if err + # we need _members as an object in the view, so we can iterate over _party.members as :id, and access _members[:id] for the info + mObj = members.get() + model.set "_members", _.object(_.pluck(mObj,'id'), mObj) + model.set "_membersArray", mObj + + # Note - selfQ *must* come after membersQ in subscribe, otherwise _user will only get the fields restricted by party-members in store.coffee. Strang bug, but easy to get around + partyQ = model.query('groups').withIds(groupsInfo.partyId) + if _.isEmpty(groupsInfo.guildIds) + finished [partyQ, selfQ], ['_party', '_user'] + else + guildsQ = model.query('groups').withIds(groupsInfo.guildIds) + finished [partyQ, guildsQ, selfQ], ['_party', '_guilds', '_user'] # ========== ROUTES ========== From f8e546ae2af42ddf1d65c23b7fafbbc470100ccb Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 3 Jun 2013 14:08:54 -0400 Subject: [PATCH 080/111] groups: bug fixes: {#if _party} dynamic binding, subscription allowed for either party or guild with out requiring the other --- src/app/index.coffee | 23 +++++++++++++---------- src/server/store.coffee | 33 +++++++++++++++------------------ views/app/groups.html | 8 ++++---- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/app/index.coffee b/src/app/index.coffee index 2da29431fd..bde4b038d7 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -31,17 +31,15 @@ setupSubscriptions = (page, model, params, next, cb) -> uuid = model.get('_userId') or model.session.userId # see http://goo.gl/TPYIt selfQ = model.query('users').withId(uuid) #keep this for later - # Fetch public groups as _publicGroups # Note: due to https://github.com/codeparty/racer/issues/57, this has to come at the very beginning. The more limited # the returned fields in motifs, the sooner they must come in fetch / subscribes. publicGroupsQuery = model.query('groups').publicGroups() myGroupsQuery = model.query('groups').withMember(uuid) model.fetch publicGroupsQuery, myGroupsQuery, (err, publicGroups, groups) -> return next(err) if err - model.set '_publicGroups', _.sortBy(publicGroups.get(), (g) -> -_.size(g.members)) finished = (descriptors, paths) -> # Add public "Tavern" guild in - descriptors.push('groups.habitrpg'); paths.push('_habitRPG') + descriptors.unshift('groups.habitrpg'); paths.unshift('_habitRPG') # Subscribe to each descriptor model.subscribe.apply model, descriptors.concat -> @@ -51,9 +49,11 @@ setupSubscriptions = (page, model, params, next, cb) -> unless model.get('_user') console.error "User not found - this shouldn't be happening!" return page.redirect('/logout') #delete model.session.userId - return cb() + # Get public groups first, order most-to-least # subscribers + model.set '_publicGroups', _.sortBy(publicGroups.get(), (g) -> -_.size(g.members)) + groupsObj = groups.get() # (1) Solo player @@ -76,12 +76,15 @@ setupSubscriptions = (page, model, params, next, cb) -> model.set "_membersArray", mObj # Note - selfQ *must* come after membersQ in subscribe, otherwise _user will only get the fields restricted by party-members in store.coffee. Strang bug, but easy to get around - partyQ = model.query('groups').withIds(groupsInfo.partyId) - if _.isEmpty(groupsInfo.guildIds) - finished [partyQ, selfQ], ['_party', '_user'] - else - guildsQ = model.query('groups').withIds(groupsInfo.guildIds) - finished [partyQ, guildsQ, selfQ], ['_party', '_guilds', '_user'] + descriptors = [selfQ]; paths = ['_user'] + if groupsInfo.partyId + descriptors.unshift model.query('groups').withIds(groupsInfo.partyId) + paths.unshift '_party' + unless _.isEmpty(groupsInfo.guildIds) + descriptors.unshift model.query('groups').withIds(groupsInfo.guildIds) + paths.unshift '_guilds' + finished descriptors, paths + # ========== ROUTES ========== diff --git a/src/server/store.coffee b/src/server/store.coffee index df8d3e1b61..4c81c2775c 100644 --- a/src/server/store.coffee +++ b/src/server/store.coffee @@ -109,23 +109,19 @@ groupSystem = (store) -> 'auth.facebook.displayName') store.queryAccess "users", "publicInfo", publicAccess - ### - Fetch group info (ie, they just got invited) - ### - store.query.expose "groups", "withIds", (ids) -> - return unless ids #FIXME this is sometimes null when ids is array (guilds) - if typeof ids is 'string' - @where("id").equals(ids).findOne() # find a single group - else - @where("id").within(ids) # find multiple groups - store.queryAccess "groups", "withIds", publicAccess - ### Read / Write groups, so they can create new groups ### store.readPathAccess "groups.*", publicAccess store.writeAccess "*", "groups.*", publicAccess + ### + Public HabitRPG Guild + ### + store.readPathAccess 'groups.habitrpg', publicAccess + store.writeAccess "*", "groups.habitrpg.chat.*", publicAccess + store.writeAccess "*", "groups.habitrpg.challenges.*", publicAccess + ### Find group which has member by id ### @@ -144,11 +140,12 @@ groupSystem = (store) -> store.queryAccess "groups", "publicGroups", publicAccess ### - Public HabitRPG Guild + Fetch group info (ie, they just got invited) ### - - store.readPathAccess 'groups.habitrpg', publicAccess - store.writeAccess "*", "groups.habitrpg.chat.*", publicAccess - store.writeAccess "*", "groups.habitrpg.challenges.*", publicAccess - - + store.query.expose "groups", "withIds", (ids) -> + return unless ids #FIXME this is sometimes null when ids is array (guilds) + if typeof ids is 'string' + @where("id").equals(ids).findOne() # find a single group + else + @where("id").within(ids) # find multiple groups + store.queryAccess "groups", "withIds", publicAccess \ No newline at end of file diff --git a/views/app/groups.html b/views/app/groups.html index 197c8d08b8..7e5ae23f2e 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -6,22 +6,22 @@
    - {{#if _party.id}} + {#if _party.id} - {{else if _user.invitations.party}} + {else if _user.invitations.party}

    You're Invited To {_user.invitations.party.name}

    {#with _user.invitations.party} Accept Reject {/} - {{else}} + {else}

    Create A Party

    You are not in a party. You can either create one and invite friends, or if you want to join an existing party, have them enter:

    {_user.id}
    - {{/}} + {/}
    From aa684fe52e08613d435e33b0b0f4303525ed2888 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 3 Jun 2013 14:20:22 -0400 Subject: [PATCH 081/111] comments on cron stuff --- src/app/index.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app/index.coffee b/src/app/index.coffee index bde4b038d7..ea0d31781a 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -129,7 +129,10 @@ ready (model) -> # 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} + # for new user, just set lastCron - no need to reset dom. + # remember that the properties are set from uObj & paths AFTER the return of this callback return if _.isEmpty(paths) or (paths['lastCron'] and _.size(paths) is 1) + # for everyone else, we need to reset dom - too many changes have been made and won't it breaks dom listeners. if lostHp = delete paths['stats.hp'] # we'll set this manually so we can get a cool animation setTimeout -> browser.resetDom(model) From f91427b112020147eaf56fb2482b042cce9fa5c4 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 3 Jun 2013 14:36:11 -0400 Subject: [PATCH 082/111] groups: small html modifications --- views/app/groups.html | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/views/app/groups.html b/views/app/groups.html index 7e5ae23f2e..c88a71ebb0 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -6,18 +6,17 @@
    - {#if _party.id} + {#if _party.id} {else if _user.invitations.party} - -

    You're Invited To {_user.invitations.party.name}

    - {#with _user.invitations.party} - Accept - Reject + + {#with _user.invitations.party as :party} +

    You're Invited To {:party.name}

    + Accept + Reject {/} {else}

    Create A Party

    -

    You are not in a party. You can either create one and invite friends, or if you want to join an existing party, have them enter:

    {_user.id}
    From 2bb7ed37b99253ce3a9bbaebd6eb884465bec1a5 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 3 Jun 2013 14:41:48 -0400 Subject: [PATCH 083/111] challenges: hide challenges tab for now --- views/app/game-pane.html | 2 +- views/app/groups.html | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/views/app/game-pane.html b/views/app/game-pane.html index 42ab861773..fb568f859c 100644 --- a/views/app/game-pane.html +++ b/views/app/game-pane.html @@ -14,7 +14,7 @@ {/if}
  • Tavern
  • Achievements
  • -
  • Challenges
  • +
  • Settings
  • diff --git a/views/app/groups.html b/views/app/groups.html index c88a71ebb0..bb3855441b 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -222,7 +222,8 @@
    - {#if @group.challenges} + Challenges coming soon! Details +
    From 0d875969080b64de570293c210d24ccc9ceff645 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 3 Jun 2013 14:42:52 -0400 Subject: [PATCH 084/111] Revert "!IMPORTANT! xhr-polling for heroku, while testing challenges. remember" This reverts commit 00d62edbc9a72dff78e246c5ba453430d7e358bf. --- src/server/index.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/index.coffee b/src/server/index.coffee index 57e777128d..3f707666e2 100644 --- a/src/server/index.coffee +++ b/src/server/index.coffee @@ -16,7 +16,7 @@ helpers = require("habitrpg-shared/script/helpers") ## RACER CONFIGURATION ## -racer.io.set('transports', ['xhr-polling']) +#racer.io.set('transports', ['xhr-polling']) racer.ioClient.set('reconnection limit', 300000) # max reconect timeout to 5 minutes racer.set('bundleTimeout', 40000) #unless process.env.NODE_ENV == 'production' From d6069e71749981a6589d35967535d105102b0832 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 3 Jun 2013 14:51:58 -0400 Subject: [PATCH 085/111] guilds: bailey. --- views/app/alerts.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/views/app/alerts.html b/views/app/alerts.html index bd2de15273..89dde6ce33 100644 --- a/views/app/alerts.html +++ b/views/app/alerts.html @@ -14,6 +14,11 @@

    +

    5/03/2013

    +
      +
    • Guilds! You can now belong to multiple groups, not just your party. There are public and private guilds, think "Subreddits" v "multiple friend groups".
    • +
    +

    5/27/2013

    • Get the "Helped Habit Grow" badge by filling out this survey.
    • From 7c829afb0957db870d9dc5d0c9987247a25210d2 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 3 Jun 2013 15:45:49 -0400 Subject: [PATCH 086/111] guilds: bug fix for inconsistently invisible public guilds --- views/app/groups.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/views/app/groups.html b/views/app/groups.html index bb3855441b..ec3ee12a5b 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -34,7 +34,8 @@
      - {#if _user.invitations.guilds} + + {#if and(_user.invitations,_user.invitations.guilds)} {#each _user.invitations.guilds as :invitation}

      You're Invited To {:invitation.name}

      From 658b4aa2a4788381a0e2bb17bdb866e2876604b9 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 3 Jun 2013 16:09:37 -0400 Subject: [PATCH 087/111] groups: bug fix to can't invite new user for users who haven't already been migrated --- src/app/groups.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/groups.coffee b/src/app/groups.coffee index 2ff6617802..222579f268 100644 --- a/src/app/groups.coffee +++ b/src/app/groups.coffee @@ -57,13 +57,13 @@ module.exports.app = (appExports, model, app) -> switch type when 'guild' - if _.find(profile.invitations.guilds, {id:gid}) + if profile.invitations?.guilds and _.find(profile.invitations.guilds, {id:gid}) return groupError("User already invited to that group") else if uid in group.members return groupError("User already in that group") else invite() when 'party' - if profile.invitations.party + if profile.invitations?.party return groupError("User already pending invitation.") else if _.find(groups, {type:'party'}) return groupError("User already in a party.") From 42b7848a6090f07d112fea36bb0466de6186abed Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 3 Jun 2013 16:55:57 -0400 Subject: [PATCH 088/111] guilds: now costs 4G to create a guild, as per http://community.habitrpg.com/content/charge-gems-guild-creation --- src/app/groups.coffee | 17 +++++++++++++++-- views/app/groups.html | 39 +++++++++++++++++++++++++++++++-------- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/app/groups.coffee b/src/app/groups.coffee index 222579f268..150a107e9e 100644 --- a/src/app/groups.coffee +++ b/src/app/groups.coffee @@ -19,8 +19,21 @@ module.exports.app = (appExports, model, app) -> leader: user.get('id') members: [user.get('id')] type: type - newGroup.privacy = (model.get("_new.group.privacy") || 'public') if type is 'guild' - model.add 'groups', newGroup, ->location.reload() + + # parties - free + if type is 'party' + return model.add 'groups', newGroup, ->location.reload() + + # guilds - 4G + balance = user.get('balance') + unless balance >= 1 + return $('#more-gems-modal').modal 'show' + if confirm "Create Guild for 4 Gems?" + newGroup.privacy = (model.get("_new.group.privacy") || 'public') if type is 'guild' + newGroup.balance = 1 # they spent $ to open the guild, it goes into their guild bank + model.add 'groups', newGroup, -> + user.set 'balance', (balance - 1) + location.reload() appExports.toggleGroupEdit = (e, el) -> path = "_editing.groups.#{$(el).attr('data-gid')}" diff --git a/views/app/groups.html b/views/app/groups.html index ec3ee12a5b..bd56cd9cd7 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -83,21 +83,44 @@
    - + {#if _groupError}
    {_groupError}
    {/}
    - - +
    + +
    + +
    +
    +
    + +
    + +
    +
    {{#if equal(@type,'guild')}} -
    - Public - Invite Only -
    +
    +
    + + + 4 Gems +
    +
    + {{else}} +
    +
    + +
    +
    {{/}} - +
    From b52888f4911c3d6f8446f4626d421925ab52535d Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 3 Jun 2013 16:58:19 -0400 Subject: [PATCH 089/111] fix bailey date --- views/app/alerts.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/app/alerts.html b/views/app/alerts.html index 89dde6ce33..e5be824953 100644 --- a/views/app/alerts.html +++ b/views/app/alerts.html @@ -14,7 +14,7 @@

    -

    5/03/2013

    +

    6/03/2013

    • Guilds! You can now belong to multiple groups, not just your party. There are public and private guilds, think "Subreddits" v "multiple friend groups".
    From 501e016e099150583a92cf1d8bd662bb815d90d5 Mon Sep 17 00:00:00 2001 From: Slappybag Date: Mon, 3 Jun 2013 23:08:42 +0100 Subject: [PATCH 090/111] CSV Export supports null values --- migrations/csvexport.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/migrations/csvexport.py b/migrations/csvexport.py index f50799e73b..4aa607cb33 100644 --- a/migrations/csvexport.py +++ b/migrations/csvexport.py @@ -1,10 +1,10 @@ import csv -data = csv.reader(open('/home/slappybag/backrs/800dollar.csv', 'rb'), delimiter=",", quotechar='|') -column = [] +with open(r"/home/slappybag/Documents/SurveyScrape.csv") as f: + reader = csv.reader(f, delimiter=',', quotechar='"') + column = [] + for row in reader: + if row: + column.append(row[4]) -for row in data: - column.append(row[9]) - -print "one:" print column \ No newline at end of file From 81297823da5f1db23edd9abd267cbf1820282f84 Mon Sep 17 00:00:00 2001 From: Slappybag Date: Mon, 3 Jun 2013 23:08:58 +0100 Subject: [PATCH 091/111] Added gem explanation text --- views/app/groups.html | 1 + 1 file changed, 1 insertion(+) diff --git a/views/app/groups.html b/views/app/groups.html index bd56cd9cd7..a91396ef6d 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -111,6 +111,7 @@ Invite Only 4 Gems +

    The Gem cost promotes high quality guilds and is transferred into your guild's bank so you can use as rewards in the upcoming challenges feature!

    {{else}} From bba008bd6139f1832fa61a75c9071e9ad7f76a03 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 3 Jun 2013 19:15:05 -0400 Subject: [PATCH 092/111] guild: add guild bank popover --- views/app/groups.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/views/app/groups.html b/views/app/groups.html index bd56cd9cd7..373fff2a69 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -126,6 +126,12 @@ + {{#if equal(@group.type,'guild')}} + + +
    {{@group.balance}} Guild Gems
    +
    + {{/}}
    {{#if equal(@group.id,'habitrpg')}} From c1cda305824770a73bc19cec8a0534fcb2423af3 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 3 Jun 2013 19:15:48 -0400 Subject: [PATCH 093/111] achievements: add migration for survey achievements --- migrations/20130602_survey_rewards.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 migrations/20130602_survey_rewards.js diff --git a/migrations/20130602_survey_rewards.js b/migrations/20130602_survey_rewards.js new file mode 100644 index 0000000000..0329643f4f --- /dev/null +++ b/migrations/20130602_survey_rewards.js @@ -0,0 +1,20 @@ +//mongo habitrpg ./node_modules/lodash/lodash.js migrations/20130602_survey_rewards.js + +var members = []; +members = _.uniq(members); +print(members.length) + +db.users.update({ + _id: {$exists:1}, + $or:[ + {_id: {$in: members}}, +// {'profile.name': {$in: members}}, + {'auth.facebook.name': {$in: members}}, + {'auth.local.username': {$in: members}}, + {'auth.local.email': {$in: members}} + ] +}, +{ + $set: { 'achievements.helpedHabit': true }, + $inc: { balance: (2.5) } +}) \ No newline at end of file From 35e0e404c7e7d49174e377969ca87ed673175460 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 3 Jun 2013 20:18:11 -0400 Subject: [PATCH 094/111] groups: don't show gems for tavern --- views/app/groups.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/app/groups.html b/views/app/groups.html index d8ffae656c..c9306bd446 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -127,7 +127,7 @@ - {{#if equal(@group.type,'guild')}} + {{#if and(equal(@group.type,'guild'),not(equal(@group.id,'habitrpg')))}}
    {{@group.balance}} Guild Gems
    From d13db1623f3d75e365ade38a33875ef42ae316d3 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 3 Jun 2013 20:22:51 -0400 Subject: [PATCH 095/111] fix survey gem inc --- migrations/20130602_survey_rewards.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations/20130602_survey_rewards.js b/migrations/20130602_survey_rewards.js index 0329643f4f..02ea768b0e 100644 --- a/migrations/20130602_survey_rewards.js +++ b/migrations/20130602_survey_rewards.js @@ -16,5 +16,5 @@ db.users.update({ }, { $set: { 'achievements.helpedHabit': true }, - $inc: { balance: (2.5) } + $inc: { balance: 2.5 } }) \ No newline at end of file From 9ec6455f2a77a5b2cbbd26dbe481367f7f078dcc Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 3 Jun 2013 20:46:56 -0400 Subject: [PATCH 096/111] groups: some ridiculous workarounds on the model.push/unshift bug --- src/app/groups.coffee | 5 +++++ src/app/misc.coffee | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/app/groups.coffee b/src/app/groups.coffee index 150a107e9e..a30dad6f79 100644 --- a/src/app/groups.coffee +++ b/src/app/groups.coffee @@ -128,6 +128,11 @@ module.exports.app = (appExports, model, app) -> return unless /\S/.test text group = e.at() + + # get rid of duplicate member ids - this is a weird place to put it, but works for now + members = group.get('members'); uniqMembers = _.uniq(members) + group.set('members', uniqMembers) if !_.isEqual(uniqMembers, members) + chat = group.at('chat') model.set('_chatMessage', '') diff --git a/src/app/misc.coffee b/src/app/misc.coffee index 1236949db5..91c3b4f7ae 100644 --- a/src/app/misc.coffee +++ b/src/app/misc.coffee @@ -110,14 +110,15 @@ module.exports.fixCorruptUser = (model) -> user.del("tasks.#{key}") delete tasks[key] true - resetDom = false batchTxn model, (uObj, paths, batch) -> - ## fix https://github.com/lefnire/habitrpg/issues/1086 uniqPets = _.uniq(uObj.items.pets) batch.set('items.pets', uniqPets) if !_.isEqual(uniqPets, uObj.items.pets) + uniqInvites = _.uniq(uObj.invitations?.guilds) + batch.set('invitations.guilds', uniqInvites) if !_.isEqual(uniqInvites, uObj.invitations?.guilds) + ## Task List Cleanup ['habit','daily','todo','reward'].forEach (type) -> From 985b7676d8c0f8c887795bf6e38351d64f1e9d32 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 3 Jun 2013 20:52:56 -0400 Subject: [PATCH 097/111] groups: subscriptions setup as callback after public members information fetched. otherwise we hit client/server snapshots different, and we get the field permissions error --- src/app/index.coffee | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/app/index.coffee b/src/app/index.coffee index ea0d31781a..069eef103b 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -75,15 +75,15 @@ setupSubscriptions = (page, model, params, next, cb) -> model.set "_members", _.object(_.pluck(mObj,'id'), mObj) model.set "_membersArray", mObj - # Note - selfQ *must* come after membersQ in subscribe, otherwise _user will only get the fields restricted by party-members in store.coffee. Strang bug, but easy to get around - descriptors = [selfQ]; paths = ['_user'] - if groupsInfo.partyId - descriptors.unshift model.query('groups').withIds(groupsInfo.partyId) - paths.unshift '_party' - unless _.isEmpty(groupsInfo.guildIds) - descriptors.unshift model.query('groups').withIds(groupsInfo.guildIds) - paths.unshift '_guilds' - finished descriptors, paths + # Note - selfQ *must* come after membersQ in subscribe, otherwise _user will only get the fields restricted by party-members in store.coffee. Strang bug, but easy to get around + descriptors = [selfQ]; paths = ['_user'] + if groupsInfo.partyId + descriptors.unshift model.query('groups').withIds(groupsInfo.partyId) + paths.unshift '_party' + unless _.isEmpty(groupsInfo.guildIds) + descriptors.unshift model.query('groups').withIds(groupsInfo.guildIds) + paths.unshift '_guilds' + finished descriptors, paths # ========== ROUTES ========== From 59f4c88940a3b6ef0fda724004ecaea99d335bb7 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 3 Jun 2013 21:28:03 -0400 Subject: [PATCH 098/111] groups: guild bank is in gems, not balance --- src/app/misc.coffee | 2 +- views/app/groups.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/misc.coffee b/src/app/misc.coffee index 91c3b4f7ae..f83ea7f8dd 100644 --- a/src/app/misc.coffee +++ b/src/app/misc.coffee @@ -169,7 +169,7 @@ module.exports.viewHelpers = (view) -> view.fn "encodeiCalLink", helpers.encodeiCalLink #User - view.fn "gems", (balance) -> return balance/0.25 + view.fn "gems", (balance) -> balance * 4 view.fn "username", helpers.username view.fn "tnl", algos.tnl view.fn 'equipped', helpers.equipped diff --git a/views/app/groups.html b/views/app/groups.html index c9306bd446..e033266d22 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -130,7 +130,7 @@ {{#if and(equal(@group.type,'guild'),not(equal(@group.id,'habitrpg')))}}
    -
    {{@group.balance}} Guild Gems
    +
    {{gems(@group.balance)}} Guild Gems
    {{/}}
    From 7fe86ac924ac18d69320f46f23944b7bcf116916 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 3 Jun 2013 22:38:03 -0400 Subject: [PATCH 099/111] fixes #1130 --- src/app/misc.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/app/misc.coffee b/src/app/misc.coffee index f83ea7f8dd..3a41a22aae 100644 --- a/src/app/misc.coffee +++ b/src/app/misc.coffee @@ -116,8 +116,9 @@ module.exports.fixCorruptUser = (model) -> uniqPets = _.uniq(uObj.items.pets) batch.set('items.pets', uniqPets) if !_.isEqual(uniqPets, uObj.items.pets) - uniqInvites = _.uniq(uObj.invitations?.guilds) - batch.set('invitations.guilds', uniqInvites) if !_.isEqual(uniqInvites, uObj.invitations?.guilds) + if uObj.invitations?.guilds + uniqInvites = _.uniq(uObj.invitations.guilds) + batch.set('invitations.guilds', uniqInvites) if !_.isEqual(uniqInvites, uObj.invitations.guilds) ## Task List Cleanup ['habit','daily','todo','reward'].forEach (type) -> From a5a57a244bcbddb5bcded9fb9c6c73f69cc3de13 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 4 Jun 2013 10:32:33 -0400 Subject: [PATCH 100/111] fix survey migration --- migrations/20130602_survey_rewards.js | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/migrations/20130602_survey_rewards.js b/migrations/20130602_survey_rewards.js index 02ea768b0e..5f883f7ad1 100644 --- a/migrations/20130602_survey_rewards.js +++ b/migrations/20130602_survey_rewards.js @@ -1,20 +1,25 @@ //mongo habitrpg ./node_modules/lodash/lodash.js migrations/20130602_survey_rewards.js -var members = []; +var members = [] members = _.uniq(members); -print(members.length) -db.users.update({ +var query = { _id: {$exists:1}, $or:[ {_id: {$in: members}}, -// {'profile.name': {$in: members}}, + //{'profile.name': {$in: members}}, {'auth.facebook.name': {$in: members}}, {'auth.local.username': {$in: members}}, {'auth.local.email': {$in: members}} ] -}, -{ - $set: { 'achievements.helpedHabit': true }, - $inc: { balance: 2.5 } -}) \ No newline at end of file +}; + +print(db.users.count(query)); + +db.users.update(query, + { + $set: { 'achievements.helpedHabit': true }, + $inc: { balance: 2.5 } + }, + {multi:true} +) \ No newline at end of file From ac8312677214c127a22ac036e8e3d8d961a869c1 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 4 Jun 2013 13:10:55 -0400 Subject: [PATCH 101/111] grammar --- views/app/groups.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/app/groups.html b/views/app/groups.html index e033266d22..86e137afb1 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -151,7 +151,7 @@
    -
    Whilst resting your dailies are saved and aren't effected by day turn-over. Whether you check out tomorrow or in a weeks time you'll continue in the same state as when you checked in.
    +
    Whilst resting your dailies are saved and aren't affected by day turn-over. Whether you check out tomorrow or in a weeks time you'll continue in the same state as when you checked in.

    Resources

    From 6f1ac0c79e75ddbe00df7f06199144801fd873ae Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 5 Jun 2013 08:25:11 -0400 Subject: [PATCH 102/111] groups: confirm dialog for leaving group, only delete group on 0-members if party (since they paid for guild) --- src/app/groups.coffee | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/app/groups.coffee b/src/app/groups.coffee index a30dad6f79..13d30ce87f 100644 --- a/src/app/groups.coffee +++ b/src/app/groups.coffee @@ -101,19 +101,20 @@ module.exports.app = (appExports, model, app) -> else e.at().remove clear appExports.groupLeave = (e,el) -> - uid = user.get('id') - group = model.at "groups.#{$(el).attr('data-id')}" - index = group.get('members').indexOf(uid) - if index != -1 - group.remove 'members', index, 1, -> - updated = group.get() - # last member out, delete the party - if _.isEmpty(updated.members) - group.del ->location.reload() - # assign new leader, so the party is editable #TODO allow old leader to assign new leader, this is just random - else if (updated.leader is uid) - group.set "leader", updated.members[0], ->location.reload() - else location.reload() + if confirm("Leave this group, are you sure?") is true + uid = user.get('id') + group = model.at "groups.#{$(el).attr('data-id')}" + index = group.get('members').indexOf(uid) + if index != -1 + group.remove 'members', index, 1, -> + updated = group.get() + # last member out, delete the party + if _.isEmpty(updated.members) and (updated.type is 'party') + group.del ->location.reload() + # assign new leader, so the party is editable #TODO allow old leader to assign new leader, this is just random + else if (updated.leader is uid) + group.set "leader", updated.members[0], ->location.reload() + else location.reload() ### Chat Functionality From 385704aad709eab70ff038cc406decdf4ada79f1 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 5 Jun 2013 11:50:30 -0400 Subject: [PATCH 103/111] add optional confirm dialog to removeAt() --- src/app/index.coffee | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/app/index.coffee b/src/app/index.coffee index 069eef103b..0b464a7b13 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -104,7 +104,12 @@ get '/', (page, model, params, next) -> # ========== CONTROLLER FUNCTIONS ========== ready (model) -> - exports.removeAt = (e) -> e.at().remove() # used for things like remove website, chat, etc + # used for things like remove website, chat, etc + exports.removeAt = (e, el) -> + if (confirmMessage = $(el).attr 'data-confirm')? + return unless confirm(confirmMessage) is true + debugger + e.at().remove() user = model.at('_user') misc.fixCorruptUser(model) # https://github.com/lefnire/habitrpg/issues/634 From fcf02d33d9d90990fc1bc49b087e05407d84a7ba Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 5 Jun 2013 11:55:09 -0400 Subject: [PATCH 104/111] groups: add "ban" feature for group leaders. unfortunately not updating dom, so require a refresh for now --- src/app/index.coffee | 14 +++++++------- views/app/groups.html | 15 ++++++++++++--- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/app/index.coffee b/src/app/index.coffee index 0b464a7b13..89cd993836 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -104,13 +104,6 @@ get '/', (page, model, params, next) -> # ========== CONTROLLER FUNCTIONS ========== ready (model) -> - # used for things like remove website, chat, etc - exports.removeAt = (e, el) -> - if (confirmMessage = $(el).attr 'data-confirm')? - return unless confirm(confirmMessage) is true - debugger - e.at().remove() - user = model.at('_user') misc.fixCorruptUser(model) # https://github.com/lefnire/habitrpg/issues/634 @@ -127,6 +120,13 @@ ready (model) -> require('./filters').app(exports, model) require('./challenges').app(exports, model) + # used for things like remove website, chat, etc + exports.removeAt = (e, el) -> + if (confirmMessage = $(el).attr 'data-confirm')? + return unless confirm(confirmMessage) is true + e.at().remove() + browser.resetDom(model) if $(el).attr('data-refresh') + ### Cron ### diff --git a/views/app/groups.html b/views/app/groups.html index 86e137afb1..164f59a71c 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -49,7 +49,7 @@ {{#each _guilds as :guild}}
    - +
    {{/}} @@ -220,9 +220,18 @@
    - {{#each @group.members as :memberId}} + {#each @group.members as :memberId} @@ -230,7 +239,7 @@ ({{:memberId}}) - {{/}} + {/}
    + + {{#if equal(@group.leader,_user.id)}} + {{#with @group.members[$index]}} + + + + {{/}} +   + {{/}} {{username(_members[:memberId].auth, _members[:memberId].profile.name)}}
    {#with @group as :group}
    From a7c8c843cde2de5e41659b6efdbb24392f75b82b Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 5 Jun 2013 14:14:00 -0400 Subject: [PATCH 105/111] groups: balance deduction cleanup --- src/app/groups.coffee | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/app/groups.coffee b/src/app/groups.coffee index 13d30ce87f..ef1f363e24 100644 --- a/src/app/groups.coffee +++ b/src/app/groups.coffee @@ -25,15 +25,13 @@ module.exports.app = (appExports, model, app) -> return model.add 'groups', newGroup, ->location.reload() # guilds - 4G - balance = user.get('balance') - unless balance >= 1 + unless user.get('balance') >= 1 return $('#more-gems-modal').modal 'show' if confirm "Create Guild for 4 Gems?" newGroup.privacy = (model.get("_new.group.privacy") || 'public') if type is 'guild' newGroup.balance = 1 # they spent $ to open the guild, it goes into their guild bank model.add 'groups', newGroup, -> - user.set 'balance', (balance - 1) - location.reload() + user.incr 'balance', -1, ->location.reload() appExports.toggleGroupEdit = (e, el) -> path = "_editing.groups.#{$(el).attr('data-gid')}" From d944dc1687e54d0ab200cf906dfb46854a69110f Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 5 Jun 2013 14:18:09 -0400 Subject: [PATCH 106/111] groups: can't ban self. rename to "boot" (since i ahven't implemented full-ban) --- views/app/groups.html | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/views/app/groups.html b/views/app/groups.html index 164f59a71c..f6f240190f 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -222,16 +222,16 @@ {#each @group.members as :memberId} From 6480be63eb0e65dd1029c0f29a9d1a18700ab2c6 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 5 Jun 2013 14:44:59 -0400 Subject: [PATCH 107/111] groups: highlight leader in member list --- views/app/groups.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/views/app/groups.html b/views/app/groups.html index f6f240190f..388d79c7c5 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -232,7 +232,9 @@   {{/}} - {{username(_members[:memberId].auth, _members[:memberId].profile.name)}} + + {{username(_members[:memberId].auth, _members[:memberId].profile.name)}} +
    - - - {{#if equal(@group.leader,_user.id)}} - {{#with @group.members[$index]}} - - - - {{/}} -   + + {{#if and(equal(@group.leader,_user.id),not(equal(_user.id,:memberId)))}} + {{#with @group.members[$index]}} + + + {{/}} +   + {{/}} + {{username(_members[:memberId].auth, _members[:memberId].profile.name)}} From ef39dd8a0f0b603e23165692d4c1b1fcb91c45f9 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 5 Jun 2013 14:45:05 -0400 Subject: [PATCH 108/111] groups: can assign new leader --- src/app/groups.coffee | 5 +++++ views/app/groups.html | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/src/app/groups.coffee b/src/app/groups.coffee index ef1f363e24..a580abbe5a 100644 --- a/src/app/groups.coffee +++ b/src/app/groups.coffee @@ -178,3 +178,8 @@ module.exports.app = (appExports, model, app) -> appExports.gotoPartyChat = -> model.set '_gamePane', true, -> $('#party-tab-link').tab('show') + + appExports.assignGroupLeader = (e, el) -> + newLeader = model.get('_new.groupLeader') + if newLeader and (confirm("Assign new leader, you sure?") is true) + e.at().set('leader', newLeader, ->browser.resetDom(model)) if newLeader diff --git a/views/app/groups.html b/views/app/groups.html index 388d79c7c5..6cd9c97600 100644 --- a/views/app/groups.html +++ b/views/app/groups.html @@ -185,6 +185,14 @@ +

    Assign Group Leader

    + + + {/} {#if @group.websites}

    Resources

    From a4de2e5ec5f916f054f7d84164fe21992c98d8e4 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 5 Jun 2013 16:20:03 -0400 Subject: [PATCH 109/111] don't show drop notification unless it went through to the database. oohhh this is glorious!! @Shaners @Slappybag @lemoness , you're gonna love this one. --- src/app/misc.coffee | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/app/misc.coffee b/src/app/misc.coffee index 3a41a22aae..ce14ca9f2a 100644 --- a/src/app/misc.coffee +++ b/src/app/misc.coffee @@ -18,7 +18,7 @@ module.exports.batchTxn = batchTxn = (model, cb, options) -> # pass true if we have levelled to supress xp notification unless _.isEmpty paths setOps = _.reduce paths, ((m,v,k)-> m[k] = helpers.dotGet(k,uObj);m), {} - user.set "update__", setOps + user.set "update__", setOps, options?.done ret #TODO put this in habitrpg-shared @@ -44,6 +44,7 @@ taskInChallenge = (task) -> perform the updates while tracking paths, then all the values at those paths ### module.exports.score = (model, taskId, direction, allowUndo=false) -> + drop = undefined delta = batchTxn model, (uObj, paths) -> tObj = uObj.tasks[taskId] @@ -58,9 +59,7 @@ module.exports.score = (model, taskId, direction, allowUndo=false) -> delta = algos.score(uObj, tObj, direction, {paths}) model.set('_streakBonus', uObj._tmp.streakBonus) if uObj._tmp?.streakBonus - if uObj._tmp?.drop and $? - model.set '_drop', uObj._tmp.drop - $('#item-dropped-modal').modal 'show' + drop = uObj._tmp?.drop # Update challenge statistics # FIXME put this in it's own batchTxn, make batchTxn model.at() ref aware (not just _user) @@ -80,6 +79,10 @@ module.exports.score = (model, taskId, direction, allowUndo=false) -> value: tObj.value history: tObj.history model._dontPersist = true + , done:-> + if drop and $? + model.set '_drop', drop + $('#item-dropped-modal').modal 'show' delta From 6cc412313cc9ad8966224c442e946cd316aa9b71 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 5 Jun 2013 16:28:43 -0400 Subject: [PATCH 110/111] pets: only remove hatching potion & egg on successful user.push('pets') --- src/app/pets.coffee | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/app/pets.coffee b/src/app/pets.coffee index 2528672995..46288b5164 100644 --- a/src/app/pets.coffee +++ b/src/app/pets.coffee @@ -25,12 +25,11 @@ module.exports.app = (appExports, model) -> return alert "You don't own that egg yet, complete more tasks!" if eggIdx is -1 return alert "You already have that pet, hatch a different combo." if myPets and myPets.indexOf("#{egg.name}-#{hatchingPotionName}") != -1 - user.push 'items.pets', egg.name + '-' + hatchingPotionName - - eggs.splice eggIdx, 1 - myHatchingPotion.splice hatchingPotionIdx, 1 - user.set 'items.eggs', eggs - user.set 'items.hatchingPotions', myHatchingPotion + user.push 'items.pets', egg.name + '-' + hatchingPotionName, -> + eggs.splice eggIdx, 1 + myHatchingPotion.splice hatchingPotionIdx, 1 + user.set 'items.eggs', eggs + user.set 'items.hatchingPotions', myHatchingPotion alert 'Your egg hatched! Visit your stable to equip your pet.' From 205bf24e02ab2486cf3356ae272b8a0a5cbd7d10 Mon Sep 17 00:00:00 2001 From: Stan Lindsey Date: Wed, 5 Jun 2013 23:31:56 +0200 Subject: [PATCH 111/111] fixes 1116 --- styles/app/game-pane.styl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/styles/app/game-pane.styl b/styles/app/game-pane.styl index 1c2f139106..c24db0d7d6 100644 --- a/styles/app/game-pane.styl +++ b/styles/app/game-pane.styl @@ -36,4 +36,7 @@ height: 40px .buttonList li - margin: 5px; \ No newline at end of file + margin: 5px; + +.option-group .option-time + padding: 0px 5px