diff --git a/common/locales/en/pets.json b/common/locales/en/pets.json
index 5efc19d8bc..9f8bdaa65c 100644
--- a/common/locales/en/pets.json
+++ b/common/locales/en/pets.json
@@ -80,6 +80,7 @@
"petKeyPets": "Release My Pets",
"petKeyMounts": "Release My Mounts",
"petKeyBoth": "Release Both",
+ "confirmPetKey": "Are you sure?",
"petKeyNeverMind": "Not Yet",
"gemsEach": "gems each"
}
diff --git a/test/spec/controllers/settingsCtrlSpec.js b/test/spec/controllers/settingsCtrlSpec.js
index 858ca14180..527600b29e 100644
--- a/test/spec/controllers/settingsCtrlSpec.js
+++ b/test/spec/controllers/settingsCtrlSpec.js
@@ -1,9 +1,13 @@
'use strict';
-describe('Settings Controller', function() {
+describe('Settings Controller', function () {
var rootScope, scope, user, User, ctrl;
- beforeEach(function() {
+ const actionClickEvent = {
+ target: document.createElement('button'),
+ };
+
+ beforeEach(function () {
module(function($provide) {
user = specHelper.newUser();
User = {
@@ -14,6 +18,9 @@ describe('Settings Controller', function() {
User.user.ops = {
reroll: sandbox.stub(),
rebirth: sandbox.stub(),
+ releasePets: sandbox.stub(),
+ releaseMounts: sandbox.stub(),
+ releaseBoth: sandbox.stub(),
};
$provide.value('User', User);
@@ -31,20 +38,20 @@ describe('Settings Controller', function() {
});
});
- describe('#openDayStartModal', function() {
- beforeEach(function() {
+ describe('#openDayStartModal', function () {
+ beforeEach(function () {
sandbox.stub(rootScope, 'openModal');
sandbox.stub(window, 'alert');
});
- it('opens the day start modal', function() {
+ it('opens the day start modal', function () {
scope.openDayStartModal(5);
expect(rootScope.openModal).to.be.calledOnce;
expect(rootScope.openModal).to.be.calledWith('change-day-start', {scope: scope});
});
- it('sets nextCron variable', function() {
+ it('sets nextCron variable', function () {
expect(scope.nextCron).to.not.exist;
scope.openDayStartModal(5);
@@ -52,7 +59,7 @@ describe('Settings Controller', function() {
expect(scope.nextCron).to.exist;
});
- it('calculates the next time cron will run', function() {
+ it('calculates the next time cron will run', function () {
var fakeCurrentTime = new Date(2013, 3, 1, 3, 12).getTime();
var expectedTime = new Date(2013, 3, 1, 5, 0, 0).getTime();
sandbox.useFakeTimers(fakeCurrentTime);
@@ -62,7 +69,7 @@ describe('Settings Controller', function() {
expect(scope.nextCron).to.eq(expectedTime);
});
- it('calculates the next time cron will run and adds a day if cron would have already passed', function() {
+ it('calculates the next time cron will run and adds a day if cron would have already passed', function () {
var fakeCurrentTime = new Date(2013, 3, 1, 8, 12).getTime();
var expectedTime = new Date(2013, 3, 2, 5, 0, 0).getTime();
sandbox.useFakeTimers(fakeCurrentTime);
@@ -73,9 +80,9 @@ describe('Settings Controller', function() {
});
});
- describe('#saveDayStart', function() {
+ describe('#saveDayStart', function () {
- it('updates user\'s custom day start and last cron', function() {
+ it('updates user\'s custom day start and last cron', function () {
var fakeCurrentTime = new Date(2013, 3, 1, 8, 12).getTime();
var expectedTime = fakeCurrentTime;
sandbox.useFakeTimers(fakeCurrentTime);
@@ -90,80 +97,190 @@ describe('Settings Controller', function() {
});
});
- describe('#reroll', function() {
- beforeEach(function() {
- scope.clickReroll(document.createElement('div'));
+ context('Player Reroll', function () {
+ describe('#reroll', function () {
+ beforeEach(function () {
+ scope.clickReroll(actionClickEvent);
+ });
+
+ it('destroys the previous popover if it exists', function () {
+ sandbox.spy($.fn, 'popover');
+
+ scope.reroll(false);
+
+ expect(scope.popoverEl).to.exist;
+ expect($.fn.popover).to.be.calledWith('destroy');
+ });
+
+ it('doesn\'t call reroll when not confirmed', function () {
+ scope.reroll(false);
+
+ expect(user.ops.reroll).to.not.be.calledOnce;
+ });
+
+ it('calls reroll on the user when confirmed', function () {
+ sandbox.stub(rootScope.$state, 'go');
+
+ scope.reroll(true);
+
+ expect(user.ops.reroll).to.be.calledWith({});
+ });
+
+ it('navigates to the tasks page when confirmed', function () {
+ sandbox.stub(rootScope.$state, 'go');
+
+ scope.reroll(true);
+
+ expect(rootScope.$state.go).to.be.calledWith('tasks');
+ });
});
- it('destroys the previous popover if it exists', function() {
- expect(scope.popoverEl).to.exist;
- sandbox.spy($.fn, 'popover');
- scope.reroll(false);
+ describe('#clickReroll', function () {
+ it('displays a confirmation popover for the user', function () {
+ sandbox.spy($.fn, 'popover');
- $.fn.popover.should.have.been.calledWith('destroy');
- });
+ scope.clickReroll(actionClickEvent);
- it('doesn\'t call reroll when not confirmed', function() {
- scope.reroll(false);
- user.ops.reroll.should.not.have.been.called;
- });
-
- it('calls reroll on the user when confirmed and navigates to tasks', function() {
- sandbox.stub(rootScope.$state, 'go');
- scope.reroll(true);
-
- user.ops.reroll.should.have.been.calledWith({});
- rootScope.$state.go.should.have.been.calledWith('tasks');
+ expect($.fn.popover).to.be.calledWith('destroy');
+ expect($.fn.popover).to.be.calledWith('show');
+ });
});
});
- describe('#clickReroll', function() {
- it('displays a confirmation popover for the user', function() {
- sandbox.spy($.fn, 'popover');
- scope.clickReroll(document.createElement('div'));
+ context('Player Rebirth', function () {
+ describe('#rebirth', function () {
+ beforeEach(function () {
+ scope.clickRebirth(actionClickEvent);
+ });
- $.fn.popover.should.have.been.calledWith('destroy');
- $.fn.popover.should.have.been.called;
- $.fn.popover.should.have.been.calledWith('show');
+ it('destroys the previous popover if it exists', function () {
+ sandbox.spy($.fn, 'popover');
+
+ scope.rebirth(false);
+
+ expect(scope.popoverEl).to.exist;
+ expect($.fn.popover).to.be.calledWith('destroy');
+ });
+
+ it('doesn\'t call rebirth when not confirmed', function () {
+ scope.rebirth(false);
+
+ expect(user.ops.rebirth).to.not.be.calledOnce;
+ });
+
+ it('calls rebirth on the user when confirmed', function () {
+ sandbox.stub(rootScope.$state, 'go');
+
+ scope.rebirth(true);
+
+ expect(user.ops.rebirth).to.be.calledWith({});
+ });
+
+ it('navigates to tasks page when confirmed', function () {
+ sandbox.stub(rootScope.$state, 'go');
+
+ scope.rebirth(true);
+
+ expect(rootScope.$state.go).to.be.calledWith('tasks');
+ });
+ });
+
+ describe('#clickRebirth', function () {
+ it('displays a confirmation popover for the user', function () {
+ sandbox.spy($.fn, 'popover');
+
+ scope.clickRebirth(actionClickEvent);
+
+ expect($.fn.popover).to.be.calledWith('destroy');
+ expect($.fn.popover).to.be.calledWith('show');
+ });
+ });
+ })
+
+ context('Releasing pets and mounts', function () {
+ describe('#release', function () {
+ beforeEach(function () {
+ scope.clickRelease('dummy', actionClickEvent);
+
+ sandbox.stub(rootScope.$state, 'go');
+ });
+
+ it('destroys the previous popover if it exists', function () {
+ sandbox.spy($.fn, 'popover');
+
+ scope.releaseAnimals('', false);
+
+ expect($.fn.popover).to.be.calledWith('destroy');
+ });
+
+ it('doesn\'t call any release method if type is not provided', function () {
+ scope.releaseAnimals();
+
+ expect(User.user.ops.releasePets).to.not.be.called;
+ expect(User.user.ops.releaseMounts).to.not.be.called;
+ expect(User.user.ops.releaseBoth).to.not.be.called;
+ });
+
+ it('doesn\'t redirect to tasks page if type is not provided', function () {
+ scope.releaseAnimals();
+
+ expect(rootScope.$state.go).to.not.be.called;
+ })
+
+ it('calls releasePets when "pets" is provided', function () {
+ scope.releaseAnimals('pets');
+
+ expect(User.user.ops.releasePets).to.be.calledOnce;
+ });
+
+ it('navigates to the tasks page when "pets" is provided', function () {
+ scope.releaseAnimals('pets');
+
+ expect(rootScope.$state.go).to.be.calledOnce;
+ });
+
+ it('calls releaseMounts when "mounts" is provided', function () {
+ scope.releaseAnimals('mounts');
+
+ expect(User.user.ops.releaseMounts).to.be.calledOnce;
+ });
+
+ it('navigates to the tasks page when "mounts" is provided', function () {
+ scope.releaseAnimals('mounts');
+
+ expect(rootScope.$state.go).to.be.calledOnce;
+ });
+
+ it('calls releaseBoth when "both" is provided', function () {
+ scope.releaseAnimals('both');
+
+ expect(User.user.ops.releaseBoth).to.be.calledOnce;
+ });
+
+ it('navigates to the tasks page when "both" is provided', function () {
+ scope.releaseAnimals('both');
+
+ expect(rootScope.$state.go).to.be.calledOnce;
+ });
+
+ it('does not call release functions when non-applicable argument is passed in', function () {
+ scope.releaseAnimals('dummy');
+
+ expect(User.user.ops.releasePets).to.not.be.called;
+ expect(User.user.ops.releaseMounts).to.not.be.called;
+ expect(User.user.ops.releaseBoth).to.not.be.called;
+ });
+ });
+
+ describe('#clickRelease', function () {
+ it('displays a confirmation popover for the user', function () {
+ sandbox.spy($.fn, 'popover');
+ scope.clickRelease('dummy', actionClickEvent);
+
+ expect($.fn.popover).to.be.calledWith('destroy');
+ expect($.fn.popover).to.be.called;
+ expect($.fn.popover).to.be.calledWith('show');
+ });
});
});
-
- describe('#rebirth', function() {
- beforeEach(function() {
- scope.clickRebirth(document.createElement('div'));
- });
-
- it('destroys the previous popover if it exists', function() {
- expect(scope.popoverEl).to.exist;
- sandbox.spy($.fn, 'popover');
- scope.rebirth(false);
-
- $.fn.popover.should.have.been.calledWith('destroy');
- });
-
- it('doesn\'t call rebirth when not confirmed', function() {
- scope.rebirth(false);
- user.ops.rebirth.should.not.have.been.called;
- });
-
- it('calls rebirth on the user when confirmed and navigates to tasks', function() {
- sandbox.stub(rootScope.$state, 'go');
- scope.rebirth(true);
-
- user.ops.rebirth.should.have.been.calledWith({});
- rootScope.$state.go.should.have.been.calledWith('tasks');
- });
- });
-
- describe('#clickRebirth', function() {
- it('displays a confirmation popover for the user', function() {
- sandbox.spy($.fn, 'popover');
- scope.clickRebirth(document.createElement('div'));
-
- $.fn.popover.should.have.been.calledWith('destroy');
- $.fn.popover.should.have.been.called;
- $.fn.popover.should.have.been.calledWith('show');
- });
- });
-
});
diff --git a/website/public/js/controllers/settingsCtrl.js b/website/public/js/controllers/settingsCtrl.js
index 32f30290bf..3672a4d703 100644
--- a/website/public/js/controllers/settingsCtrl.js
+++ b/website/public/js/controllers/settingsCtrl.js
@@ -4,6 +4,11 @@
habitrpg.controller('SettingsCtrl',
['$scope', 'User', '$rootScope', '$http', 'ApiUrl', 'Guide', '$location', '$timeout', 'Content', 'Notification', 'Shared', '$compile',
function($scope, User, $rootScope, $http, ApiUrl, Guide, $location, $timeout, Content, Notification, Shared, $compile) {
+ var RELEASE_ANIMAL_TYPES = {
+ pets: 'releasePets',
+ mounts: 'releaseMounts',
+ both: 'releaseBoth',
+ };
// FIXME we have this re-declared everywhere, figure which is the canonical version and delete the rest
// $scope.auth = function (id, token) {
@@ -91,7 +96,7 @@ habitrpg.controller('SettingsCtrl',
$scope.availableFormats = ['MM/dd/yyyy','dd/MM/yyyy', 'yyyy/MM/dd'];
$scope.reroll = function(confirm){
- $scope.popoverEl.popover('destroy')
+ $scope.popoverEl.popover('destroy');
if (confirm) {
User.user.ops.reroll({});
@@ -116,7 +121,7 @@ habitrpg.controller('SettingsCtrl',
}
$scope.rebirth = function(confirm){
- $scope.popoverEl.popover('destroy')
+ $scope.popoverEl.popover('destroy');
if (confirm) {
User.user.ops.rebirth({});
@@ -200,19 +205,39 @@ habitrpg.controller('SettingsCtrl',
})
}
- $scope.releasePets = function() {
- User.user.ops.releasePets({});
- $rootScope.$state.go('tasks');
+ $scope.clickRelease = function(type, $event){
+ // Close other popovers if they're open
+ $(".release_popover").not($event.target).popover('destroy');
+
+ // Handle clicking on the gem icon
+ if ($event.target.nodeName == "SPAN") {
+ $scope.releasePopoverEl = $($event.target.parentNode);
+ } else {
+ $scope.releasePopoverEl = $($event.target);
+ }
+
+ var html = $compile(
+ '' + window.env.t('confirm') + '
\n' + window.env.t('cancel') + '
'
+ )($scope);
+
+ $scope.releasePopoverEl.popover('destroy').popover({
+ html: true,
+ placement: 'top',
+ trigger: 'manual',
+ title: window.env.t('confirmPetKey'),
+ content: html
+ }).popover('show');
}
- $scope.releaseMounts = function() {
- User.user.ops.releaseMounts({});
- $rootScope.$state.go('tasks');
- }
+ $scope.releaseAnimals = function (type) {
+ $scope.releasePopoverEl.popover('destroy');
- $scope.releaseBoth = function() {
- User.user.ops.releaseBoth({});
- $rootScope.$state.go('tasks');
+ var releaseFunction = RELEASE_ANIMAL_TYPES[type];
+
+ if (releaseFunction) {
+ User.user.ops[releaseFunction]({});
+ $rootScope.$state.go('tasks');
+ }
}
// ---- Webhooks ------
diff --git a/website/views/shared/modals/drops.jade b/website/views/shared/modals/drops.jade
index b91c9ae4b0..b5e5c0cbe7 100644
--- a/website/views/shared/modals/drops.jade
+++ b/website/views/shared/modals/drops.jade
@@ -36,17 +36,17 @@ script(type='text/ng-template', id='modals/pet-key.html')
a.btn.btn-success(ng-click='openModal("buyGems")')=env.t('buyMoreGems')
span.gem-cost=env.t('notEnoughGems')
span(ng-if='user.balance >= 1 && petCount >= 90', ng-controller='SettingsCtrl')
- a.btn.btn-danger(ng-click='$close(); releasePets()')
+ a.btn.btn-danger.release_popover(ng-click='clickRelease("pets", $event)')
=env.t('petKeyPets')
| : 4
span.Pet_Currency_Gem1x.inline-gems
span(ng-if='user.balance >= 1 && mountCount >= 90', ng-controller='SettingsCtrl')
- a.btn.btn-danger(ng-click='$close(); releaseMounts()')
+ a.btn.btn-danger.release_popover(ng-click='clickRelease("mounts", $event)')
=env.t('petKeyMounts')
| : 4
span.Pet_Currency_Gem1x.inline-gems
span(ng-if='(user.balance >= 1.5 || user.achievements.triadBingo) && petCount >= 90 && mountCount >= 90', ng-controller='SettingsCtrl')
- a.btn.btn-danger(ng-click='$close(); releaseBoth()')
+ a.btn.btn-danger.release_popover(ng-click='clickRelease("both", $event)')
=env.t('petKeyBoth')
span(ng-if='!user.achievements.triadBingo')
| : 6