From 95031fb6ee3e7fa3f8caf1e09435f3868c9fa74a Mon Sep 17 00:00:00 2001 From: Nicole Foster Date: Sat, 9 Nov 2013 15:25:31 -0500 Subject: [PATCH 1/9] Even space between limited edition skins and rainbow skins --- public/css/customizer.styl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/css/customizer.styl b/public/css/customizer.styl index e61402f24b..fd9391fa9d 100644 --- a/public/css/customizer.styl +++ b/public/css/customizer.styl @@ -54,4 +54,4 @@ menu .well.limited-edition padding: 5px - margin: 0px \ No newline at end of file + margin: 25px 0 0 \ No newline at end of file From 9fbb45c253344594db8d26e7307a0a353a88177e Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 10 Nov 2013 18:13:12 -0800 Subject: [PATCH 2/9] halloween: bailey notif for last day of spooky skins --- views/shared/modals/new-stuff.jade | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/views/shared/modals/new-stuff.jade b/views/shared/modals/new-stuff.jade index e0066f301e..8058eef317 100644 --- a/views/shared/modals/new-stuff.jade +++ b/views/shared/modals/new-stuff.jade @@ -13,6 +13,14 @@ div(modal='modals.newStuff') a(target='_blank', href='https://twitter.com/Mihakuu') Bailey .popover-content table + tr + td + h4 Last Day of Spooky Skins + p Today is the very last day to buy the Spooky Skins! If you want to be a zombie or a skeleton warrior, this is your last chance. They will be retired at midnight PST, so get them while you can. + td + li.customize-menu + menu + button.customize-option(type='button', class='{{user.preferences.gender}}_skin_zombie') tr td //ul.modal-indented-list From 72f64fa85c1db6c699ed0cb9a6fb2c68cb907ac2 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 10 Nov 2013 23:41:42 -0800 Subject: [PATCH 3/9] [#1751] /challenges/:cid/:mid task filtering by "in this challenge" at application level, not Mongo. $elemMatch doesn't support returning multiple items, see http://goo.gl/MxmWdQ & http://goo.gl/Iku44w --- src/controllers/challenges.js | 10 ++++++++-- views/options/social/challenges.jade | 12 ++++++------ views/shared/header/header.jade | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/controllers/challenges.js b/src/controllers/challenges.js index 233a9a543b..1f566923b4 100644 --- a/src/controllers/challenges.js +++ b/src/controllers/challenges.js @@ -67,12 +67,18 @@ api.get = function(req, res) { api.getMember = function(req, res) { var cid = req.params.cid, uid = req.params.uid; - var elMatch = {$elemMatch:{'challenge.id':cid}}; + // TMK we can't use $elemMatch (which would make things much cleaner) @see http://goo.gl/MxmWdQ & http://goo.gl/Iku44w + // Revert back to 9fbb45c to see the $elemMatch solution User.findById(uid) - .select({'profile.name':1, habits:elMatch, dailys:elMatch, rewards:elMatch, todos:elMatch}) + .select('profile.name habits dailys rewards todos') .exec(function(err, member){ if(err) return res.json(500, {err:err}); if (!member) return res.json(404, {err: 'Member '+uid+' for challenge '+cid+' not found'}); + _.each(['habits','dailys','todos', 'rewards'], function(type){ + member[type] = _.filter(member[type], function(task){ + return task.challenge && task.challenge.id && task.challenge.id == cid; + }); + }); res.json(member); }) } diff --git a/views/options/social/challenges.jade b/views/options/social/challenges.jade index 5811ec2cc9..dabced873f 100644 --- a/views/options/social/challenges.jade +++ b/views/options/social/challenges.jade @@ -9,12 +9,12 @@ script(type='text/ng-template', id='partials/options.social.challenges.detail.cl script(type='text/ng-template', id='partials/options.social.challenges.detail.member.html') .modal.wide-modal .modal-header - button.close(type='button', ng-click='$state.go("^")', aria-hidden='true') × - h3 {{obj.profile.name}} - .modal-body - habitrpg-tasks(main=false) - .modal-footer - a.btn(ng-click='$state.go("^")') Close + button.close(type='button', ng-click='$state.go("^")', aria-hidden='true') × + h3 {{obj.profile.name}} + .modal-body + habitrpg-tasks(main=false) + .modal-footer + a.btn(ng-click='$state.go("^")') Close script(type='text/ng-template', id='partials/options.social.challenges.detail.html') // Edit button diff --git a/views/shared/header/header.jade b/views/shared/header/header.jade index 28a32bee52..626d295786 100644 --- a/views/shared/header/header.jade +++ b/views/shared/header/header.jade @@ -1,4 +1,4 @@ -// @TODO ui:connectionAlert +//.header-wrap(ng-controller='HeaderCtrl', data-spy="affix", data-offset-top="148") .header-wrap(ng-controller='HeaderCtrl') a.label.undo-button(x-bind='click:undo', ng-show='_undo') Undo div(ng-if='!user.preferences.hideHeader') From cde96bf28bf6557dcb931852fcf8140a4d9774d1 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 11 Nov 2013 00:27:20 -0800 Subject: [PATCH 4/9] halloween: take down halloween skins --- views/options/profile.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/profile.jade b/views/options/profile.jade index abcf987fc8..41d1998fff 100644 --- a/views/options/profile.jade +++ b/views/options/profile.jade @@ -57,7 +57,7 @@ script(id='partials/options.profile.avatar.html', type='text/ng-template') button.btn.btn-small.btn-primary(ng-hide='user.purchased.skin.eb052b && user.purchased.skin.f69922 && user.purchased.skin.f5d70f && user.purchased.skin.0ff591 && user.purchased.skin.2b43f6 && user.purchased.skin.d7a9f7 && user.purchased.skin.800ed0 && user.purchased.skin.rainbow', ng-click='unlock(["skin.eb052b", "skin.f69922", "skin.f5d70f", "skin.0ff591", "skin.2b43f6", "skin.d7a9f7", "skin.800ed0", "skin.rainbow"])') Unlock Set - 5 // Special Events - div.well.limited-edition + //-div.well.limited-edition .label.label-info.pull-right(popover='Available for purchase until November 10th (but permanently in your options if purchased).', popover-title='Limited Edition', popover-placement='right', popover-trigger='mouseenter') | Limited Edition  i.icon.icon-question-sign From 42534b5233831ec347207942b13ce5162b1f5fa3 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 11 Nov 2013 08:21:29 -0800 Subject: [PATCH 5/9] [#1792] put purchased limited skins back on avatar tab --- views/options/profile.jade | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/views/options/profile.jade b/views/options/profile.jade index 41d1998fff..7ae3f23b01 100644 --- a/views/options/profile.jade +++ b/views/options/profile.jade @@ -57,21 +57,16 @@ script(id='partials/options.profile.avatar.html', type='text/ng-template') button.btn.btn-small.btn-primary(ng-hide='user.purchased.skin.eb052b && user.purchased.skin.f69922 && user.purchased.skin.f5d70f && user.purchased.skin.0ff591 && user.purchased.skin.2b43f6 && user.purchased.skin.d7a9f7 && user.purchased.skin.800ed0 && user.purchased.skin.rainbow', ng-click='unlock(["skin.eb052b", "skin.f69922", "skin.f5d70f", "skin.0ff591", "skin.2b43f6", "skin.d7a9f7", "skin.800ed0", "skin.rainbow"])') Unlock Set - 5 // Special Events - //-div.well.limited-edition - .label.label-info.pull-right(popover='Available for purchase until November 10th (but permanently in your options if purchased).', popover-title='Limited Edition', popover-placement='right', popover-trigger='mouseenter') - | Limited Edition  - i.icon.icon-question-sign - h5. - Spooky Skins - 2/skin - //menu(label='Spooky Skins (2 Gems / skin)') + // restore to d4df481 to see purchasing + "limited edition" code + div(ng-if='user.purchased.skin.monster || user.purchased.skin.pumpkin || user.purchased.skin.skeleton || user.purchased.skin.zombie || user.purchased.skin.ghost || user.purchased.skin.shadow') + h5 Spooky Skins menu - button.customize-option(type='button', class='{{user.preferences.gender}}_skin_monster', ng-class='{locked: !user.purchased.skin.monster}', ng-click='unlock("skin.monster")') - button.customize-option(type='button', class='{{user.preferences.gender}}_skin_pumpkin', ng-class='{locked: !user.purchased.skin.pumpkin}', ng-click='unlock("skin.pumpkin")') - button.customize-option(type='button', class='{{user.preferences.gender}}_skin_skeleton', ng-class='{locked: !user.purchased.skin.skeleton}', ng-click='unlock("skin.skeleton")') - button.customize-option(type='button', class='{{user.preferences.gender}}_skin_zombie', ng-class='{locked: !user.purchased.skin.zombie}', ng-click='unlock("skin.zombie")') - button.customize-option(type='button', class='{{user.preferences.gender}}_skin_ghost', ng-class='{locked: !user.purchased.skin.ghost}', ng-click='unlock("skin.ghost")') - button.customize-option(type='button', class='{{user.preferences.gender}}_skin_shadow', ng-class='{locked: !user.purchased.skin.shadow}', ng-click='unlock("skin.shadow")') - button.btn.btn-small.btn-primary(ng-hide='user.purchased.skin.monster && user.purchased.skin.pumpkin && user.purchased.skin.skeleton && user.purchased.skin.zombie && user.purchased.skin.ghost && user.purchased.skin.shadow', ng-click='unlock(["skin.monster", "skin.pumpkin", "skin.skeleton", "skin.zombie", "skin.ghost", "skin.shadow"])') Unlock Set - 5 + button.customize-option(type='button', class='{{user.preferences.gender}}_skin_monster', ng-if='user.purchased.skin.monster', ng-click='unlock("skin.monster")') + button.customize-option(type='button', class='{{user.preferences.gender}}_skin_pumpkin', ng-if='user.purchased.skin.pumpkin', ng-click='unlock("skin.pumpkin")') + button.customize-option(type='button', class='{{user.preferences.gender}}_skin_skeleton', ng-if='user.purchased.skin.skeleton', ng-click='unlock("skin.skeleton")') + button.customize-option(type='button', class='{{user.preferences.gender}}_skin_zombie', ng-if='user.purchased.skin.zombie', ng-click='unlock("skin.zombie")') + button.customize-option(type='button', class='{{user.preferences.gender}}_skin_ghost', ng-if='user.purchased.skin.ghost', ng-click='unlock("skin.ghost")') + button.customize-option(type='button', class='{{user.preferences.gender}}_skin_shadow', ng-if='user.purchased.skin.shadow', ng-click='unlock("skin.shadow")') script(id='partials/options.profile.stats.html', type='text/ng-template') .row-fluid From 86f47cb6f12d8683696093d736784185fefe6d70 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 11 Nov 2013 08:30:51 -0800 Subject: [PATCH 6/9] halloween: remove "last day" bailey notif --- views/shared/modals/new-stuff.jade | 8 -------- 1 file changed, 8 deletions(-) diff --git a/views/shared/modals/new-stuff.jade b/views/shared/modals/new-stuff.jade index 8058eef317..e0066f301e 100644 --- a/views/shared/modals/new-stuff.jade +++ b/views/shared/modals/new-stuff.jade @@ -13,14 +13,6 @@ div(modal='modals.newStuff') a(target='_blank', href='https://twitter.com/Mihakuu') Bailey .popover-content table - tr - td - h4 Last Day of Spooky Skins - p Today is the very last day to buy the Spooky Skins! If you want to be a zombie or a skeleton warrior, this is your last chance. They will be retired at midnight PST, so get them while you can. - td - li.customize-menu - menu - button.customize-option(type='button', class='{{user.preferences.gender}}_skin_zombie') tr td //ul.modal-indented-list From 2978842d233d7a0e4bd54a75484969ff04a25fb8 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 11 Nov 2013 19:00:43 -0800 Subject: [PATCH 7/9] [#1781] challenges: select-all / select-none buttons --- public/js/controllers/challengesCtrl.js | 56 +++++++++++++++---------- views/options/social/challenges.jade | 4 ++ 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/public/js/controllers/challengesCtrl.js b/public/js/controllers/challengesCtrl.js index 96f61acf77..fa719270b7 100644 --- a/public/js/controllers/challengesCtrl.js +++ b/public/js/controllers/challengesCtrl.js @@ -7,34 +7,13 @@ habitrpg.controller("ChallengesCtrl", ['$scope', 'User', 'Challenges', 'Notifica Groups.Group.query({type:'party,guilds,tavern'}, function(groups){ $scope.groups = groups; $scope.search = { - group: _.reduce(groups, function(m,g){m[g._id]=true;return m;}, {}) + group: _.transform(groups, function(m,g){m[g._id]=true;}) }; }); // FIXME $scope.challenges needs to be resolved first (see app.js) $scope.challenges = Challenges.Challenge.query(); // we should fix this, that's pretty brittle -// $scope.$watch('search', function(search){ -// if (!search) $scope.filteredChallenges = $scope.challenges; -// $scope.filteredChallenges = $filter('filter')($scope.challenges, function(chal) { -// return (search.group[chal.group._id] && -// (typeof search._isMember == 'undefined' || search._isMember == chal._isMember)); -// }) -// }) - // TODO probably better to use $watch above, to avoid this being calculated on every digest cycle - $scope.filterChallenges = function(chal){ - return (!$scope.search) ? true : - ($scope.search.group[chal.group._id] && - (typeof $scope.search._isMember == 'undefined' || $scope.search._isMember == chal._isMember)); - } - - $scope.$watch('newChallenge.group', function(gid){ - if (!gid) return; - var group = _.find($scope.groups, {_id:gid}); - $scope.maxPrize = User.user.balance*4 + ((group && group.balance && group.leader==User.user._id) ? group.balance*4 : 0); - if (gid == 'habitrpg') $scope.newChallenge.prize = 1; - }) - // override score() for tasks listed in challenges-editing pages, so that nothing happens $scope.score = function(){} @@ -216,4 +195,37 @@ habitrpg.controller("ChallengesCtrl", ['$scope', 'User', 'Challenges', 'Notifica }).popover('show'); } + //------------------------------------------------------------ + // Filtering + //------------------------------------------------------------ + +// $scope.$watch('search', function(search){ +// if (!search) $scope.filteredChallenges = $scope.challenges; +// $scope.filteredChallenges = $filter('filter')($scope.challenges, function(chal) { +// return (search.group[chal.group._id] && +// (typeof search._isMember == 'undefined' || search._isMember == chal._isMember)); +// }) +// }) + // TODO probably better to use $watch above, to avoid this being calculated on every digest cycle + $scope.filterChallenges = function(chal){ + return (!$scope.search) ? true : + ($scope.search.group[chal.group._id] && + (typeof $scope.search._isMember == 'undefined' || $scope.search._isMember == chal._isMember)); + } + + $scope.$watch('newChallenge.group', function(gid){ + if (!gid) return; + var group = _.find($scope.groups, {_id:gid}); + $scope.maxPrize = User.user.balance*4 + ((group && group.balance && group.leader==User.user._id) ? group.balance*4 : 0); + if (gid == 'habitrpg') $scope.newChallenge.prize = 1; + }) + + $scope.selectAll = function(){ + $scope.search.group = _.transform($scope.groups, function(m,g){m[g._id] = true}); + } + + $scope.selectNone = function(){ + $scope.search.group = _.transform($scope.groups, function(m,g){m[g._id] = false}); + } + }]); \ No newline at end of file diff --git a/views/options/social/challenges.jade b/views/options/social/challenges.jade index dabced873f..075b82cac2 100644 --- a/views/options/social/challenges.jade +++ b/views/options/social/challenges.jade @@ -61,6 +61,10 @@ script(type='text/ng-template', id='partials/options.social.challenges.html') .span2.well#challenges-filters h3 Filter: h4 Groups + ul.unstyled + a.btn.btn-mini.pull-left(ng-click='selectAll()') All + a.btn.btn-mini(ng-click='selectNone()') None + br label.checkbox(ng-repeat='group in groups') input(type='checkbox', ng-model='search.group[group._id]') | {{group.name}} From 9a5af96ba158fb4d5941e4b797d441c411e0fb26 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 11 Nov 2013 20:16:54 -0800 Subject: [PATCH 8/9] [#1763] migration to restore NaN tasks.value to 0 --- migrations/20131111_task_NaN.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 migrations/20131111_task_NaN.js diff --git a/migrations/20131111_task_NaN.js b/migrations/20131111_task_NaN.js new file mode 100644 index 0000000000..16e6788003 --- /dev/null +++ b/migrations/20131111_task_NaN.js @@ -0,0 +1,15 @@ +// This migration has already been run in the past. It's vital to fix these users presently, but we need to find +// out why task values are ever getting in as NaN. My guess is API PUT /tasks/:tid routes +db.users.find({},{habits:1,dailys:1,todos:1,rewards:1}).forEach(function(user){ + _.each(['habits','dailys','todos','rewards'], function(type){ + _.each(user[type], function(task){ + task.value = +task.value; + if (_.isNaN(task.value)) { + task.value = 0; + print(user._id); + } + }) + }) + + db.users.update({_id:user._id}, {$set:{habits: user.habits, dailys: user.dailys, todos: user.todos, rewards: user.rewards}}); +}); \ No newline at end of file From cd693835cef552f3dc943f215b405554ac16c31e Mon Sep 17 00:00:00 2001 From: Nick Gordon Date: Mon, 11 Nov 2013 22:32:52 -0800 Subject: [PATCH 9/9] Autocompletion of nicknames in chat works. this commit will require a npm install to fetch new packages --- bower.json | 3 ++- public/css/game-pane.styl | 36 ++++++++++++++++++++++++++++- public/js/app.js | 2 +- public/js/controllers/groupsCtrl.js | 26 +++++++++++++++++++++ public/manifest.json | 2 ++ views/options/social/chat-box.jade | 8 +++++-- 6 files changed, 72 insertions(+), 5 deletions(-) diff --git a/bower.json b/bower.json index 5dc7878e1c..cc329d8e71 100644 --- a/bower.json +++ b/bower.json @@ -33,7 +33,8 @@ "BrowserQuest": "https://github.com/mozilla/BrowserQuest.git", "github-buttons": "git://github.com/mdo/github-buttons.git", "marked": "~0.2.9", - "JavaScriptButtons": "git://github.com/paypal/JavaScriptButtons.git#master" + "JavaScriptButtons": "git://github.com/paypal/JavaScriptButtons.git#master", + "Angular-At-Directive": "git://github.com/scarletsky/Angular-At-Directive.git#master" }, "resolutions": { "jquery": "~2.0.3", diff --git a/public/css/game-pane.styl b/public/css/game-pane.styl index 086e922734..67f33b6d8f 100644 --- a/public/css/game-pane.styl +++ b/public/css/game-pane.styl @@ -57,4 +57,38 @@ margin: 5px .option-group .option-time - padding: 0px 5px \ No newline at end of file + padding: 0px 5px + +// Autocomplete [TODO] make this nicer +.list-at-user { + width: 100%; + max-width: 120px; + position: absolute; + background: white; + border: 1px solid #a4a4a4; + z-index: 10; + margin-left: -435px; + margin-top: -355px; +} + +.list-at-user li { + line-height:14px; + border-bottom: 1px solid #a4a4a4; + list-style-type: none; +} + +.list-at-user li:hover { + cursor: pointer; + background: #b9dff4; +} + +.list-at-user li span { + margin: 2px; + font-size: 11.844px; + margin-left: 5px; + display: inline-block; +} + +.list-cur { + background: #b9dff4; +} \ No newline at end of file diff --git a/public/js/app.js b/public/js/app.js index cdb890c820..261931989f 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -3,7 +3,7 @@ window.habitrpg = angular.module('habitrpg', ['ngResource', 'ngSanitize', 'userServices', 'groupServices', 'memberServices', 'challengeServices', 'sharedServices', 'authServices', 'notificationServices', 'guideServices', - 'ui.bootstrap', 'ui.keypress', 'ui.router', 'chieffancypants.loadingBar']) + 'ui.bootstrap', 'ui.keypress', 'ui.router', 'chieffancypants.loadingBar', 'At']) // @see https://github.com/angular-ui/ui-router/issues/110 and https://github.com/HabitRPG/habitrpg/issues/1705 // temporary hack until they have a better solution diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index 5f6bd6e115..4c8e41685e 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -79,6 +79,32 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Groups', '$http', 'A } ]) + .controller('AutocompleteCtrl', ['$scope', 'Groups', 'User', function ($scope,Groups,User) { + $scope.clearUserlist = function() { + $scope.response = []; + $scope.usernames = []; + } + + $scope.addNewUser = function(user) { + if($.inArray(user.user,$scope.usernames) == -1) { + $scope.usernames.push(user.user); + $scope.response.push(user); + } + } + + $scope.clearUserlist(); + + $scope.chatChanged = function(newvalue,oldvalue){ + if($scope.group.chat && $scope.group.chat.length > 0){ + for(var i = 0; i < $scope.group.chat.length; i++) { + $scope.addNewUser($scope.group.chat[i]); + } + } + } + + $scope.$watch('group.chat',$scope.chatChanged); + }]) + .controller('ChatCtrl', ['$scope', 'Groups', 'User', function($scope, Groups, User){ $scope._chatMessage = ''; $scope._sending = false; diff --git a/public/manifest.json b/public/manifest.json index ee6d559b95..a522b3f140 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -13,6 +13,8 @@ "bower_components/angular-ui/build/angular-ui.js", "bower_components/angular-ui-utils/modules/keypress/keypress.js", "bower_components/angular-loading-bar/build/loading-bar.js", + "bower_components/Angular-At-Directive/src/at.js", + "bower_components/Angular-At-Directive/src/caret.js", "bower_components/bootstrap/docs/assets/js/bootstrap.js", "bower_components/angular-bootstrap/ui-bootstrap.js", diff --git a/views/options/social/chat-box.jade b/views/options/social/chat-box.jade index fde98515b5..09a6460346 100644 --- a/views/options/social/chat-box.jade +++ b/views/options/social/chat-box.jade @@ -1,8 +1,12 @@ form(ng-submit='postChat(group,_chatMessage)') .-options //FIXME ng-model makes this painfully slow! using jquery for now, which is really non-angular-like - .control-group.option-large - textarea.chat-textarea.option-content(style='height:6em;', ui-keypress='{13:"postChat(group,_chatMessage)"}', ng-model='_chatMessage') + .control-group.option-large(ng-controller='AutocompleteCtrl') + textarea.chat-textarea.option-content(style='height:6em;', ui-keypress='{13:"postChat(group,_chatMessage)"}', ng-model='_chatMessage', flag='@', at-user, auto-complete) + span.user-list(ng-show='!isAtListHidden', auto-follow='true') + ul.list-at-user + li(ng-repeat='user in users | filter:query.text | limitTo: 5', ng-click='autoComplete(user)') + span.username.label(class='label-contributor-{{user.contributor.level}}') {{user.user}} table.pull-right tr td