From ea490c9a1fedf53a96564e7d6970f8c04f9c810c Mon Sep 17 00:00:00 2001 From: Keith Holliday Date: Mon, 25 Apr 2016 16:11:23 -0500 Subject: [PATCH] Ported groups service to user new api v3 and ported dependent controllers (#7108) * Ported groups service to user new api v3 and ported dependent controllers * Remove and extra remove inviation code. Fixed group service caching and update group service tests * Fixed test logic and added party cache support * Added promise rejections and updated http interceptor --- common/browserify.js | 2 + common/script/public/config.js | 16 +- common/script/public/userServices.js | 8 +- test/spec/controllers/groupCtrlSpec.js | 51 +++- test/spec/controllers/partyCtrlSpec.js | 28 ++- test/spec/services/groupServicesSpec.js | 125 +++++++++- test/spec/specHelper.js | 3 + website/public/js/app.js | 9 +- website/public/js/controllers/groupsCtrl.js | 31 ++- website/public/js/controllers/guildsCtrl.js | 133 ++++++---- .../js/controllers/inviteToGroupCtrl.js | 21 +- website/public/js/controllers/partyCtrl.js | 106 ++++---- website/public/js/services/groupServices.js | 234 +++++++++++++----- website/src/controllers/api-v3/groups.js | 5 +- .../social/party/party-invitation.jade | 3 +- 15 files changed, 565 insertions(+), 210 deletions(-) diff --git a/common/browserify.js b/common/browserify.js index 0530144839..3653cb81b0 100644 --- a/common/browserify.js +++ b/common/browserify.js @@ -1,3 +1,5 @@ +require('babel-polyfill'); + var shared = require('./script/index'); var _ = require('lodash'); var moment = require('moment'); diff --git a/common/script/public/config.js b/common/script/public/config.js index bb78e244bd..53e5f19091 100644 --- a/common/script/public/config.js +++ b/common/script/public/config.js @@ -1,5 +1,7 @@ 'use strict'; -angular.module('habitrpg').config(['$httpProvider', function($httpProvider){ + +angular.module('habitrpg') +.config(['$httpProvider', function($httpProvider){ $httpProvider.interceptors.push(['$q', '$rootScope', function($q, $rootScope){ return { response: function(response) { @@ -28,15 +30,15 @@ angular.module('habitrpg').config(['$httpProvider', function($httpProvider){ // 400 range? } else if (response.status < 500) { - $rootScope.$broadcast('responseText', response.data.err || response.data); + $rootScope.$broadcast('responseText', response.data.err || response.data.message); // Need to reject the prompse so the error is handled correctly - if (response.status === 401) + if (response.status === 401) { return $q.reject(response); - + } // Error } else { - var error = window.env.t('requestError') + '

"' + - window.env.t('error') + ' ' + (response.data.err || response.data || 'something went wrong') + + var error = window.env.t('requestError') + '

"' + + window.env.t('error') + ' ' + (response.data.err || response.data || 'something went wrong') + '"

' + window.env.t('seeConsole'); if (mobileApp) error = 'Error contacting the server. Please try again in a few minutes.'; $rootScope.$broadcast('responseError', error); @@ -47,4 +49,4 @@ angular.module('habitrpg').config(['$httpProvider', function($httpProvider){ } }; }]); -}]); \ No newline at end of file +}]); diff --git a/common/script/public/userServices.js b/common/script/public/userServices.js index e5b1790086..4fb92ce42a 100644 --- a/common/script/public/userServices.js +++ b/common/script/public/userServices.js @@ -102,14 +102,14 @@ angular.module('habitrpg') op(req,function(err,response) { for(var updatedItem in req.body) { var itemUpdateResponse = userNotifications[updatedItem]; - if(itemUpdateResponse) Notification.text(itemUpdateResponse); + if(itemUpdateResponse) Notification.text(itemUpdateResponse.data.message); } if (err) { - var message = err.code ? err.message : err; - if (MOBILE_APP) Notification.push({type:'text',text:message}); + var message = err.code ? err.data.message : err; + if (MOBILE_APP) Notification.push({type:'text', text: message}); else Notification.text(message); // In the case of 200s, they're friendly alert messages like "Your pet has hatched!" - still send the op - if ((err.code && err.code >= 400) || !err.code) return; + if ((err.code && err.code >= 400) || !err.code) return; } userServices.log({op:k, params: req.params, query:req.query, body:req.body}); }); diff --git a/test/spec/controllers/groupCtrlSpec.js b/test/spec/controllers/groupCtrlSpec.js index dce054bef6..da00e2ba73 100644 --- a/test/spec/controllers/groupCtrlSpec.js +++ b/test/spec/controllers/groupCtrlSpec.js @@ -23,6 +23,53 @@ describe('Groups Controller', function() { }); }); + describe("isMemberOfPendingQuest", function() { + var party; + var partyStub; + + beforeEach(function () { + party = specHelper.newGroup({ + _id: "unique-party-id", + type: 'party', + members: ['leader-id'] // Ensure we wouldn't pass automatically. + }); + + partyStub = sandbox.stub(groups, "party", function() { + return party; + }); + }); + + it("returns false if group is does not have a quest", function() { + expect(scope.isMemberOfPendingQuest(user._id, party)).to.not.be.ok; + }); + + it("returns false if group quest has not members", function() { + party.quest = { + 'key': 'random-key', + }; + expect(scope.isMemberOfPendingQuest(user._id, party)).to.not.be.ok; + }); + + it("returns false if group quest is active", function() { + party.quest = { + 'key': 'random-key', + 'members': {}, + 'active': true, + }; + party.quest.members[user._id] = true; + expect(scope.isMemberOfPendingQuest(user._id, party)).to.not.be.ok; + }); + + it("returns true if user is a member of a pending quest", function() { + party.quest = { + 'key': 'random-key', + 'members': {}, + }; + party.quest.members[user._id] = true; + expect(scope.isMemberOfPendingQuest(user._id, party)).to.be.ok; + }); + }); + describe("isMemberOfGroup", function() { it("returns true if group is the user's party retrieved from groups service", function() { var party = specHelper.newGroup({ @@ -31,7 +78,7 @@ describe('Groups Controller', function() { members: ['leader-id'] // Ensure we wouldn't pass automatically. }); - var partyStub = sandbox.stub(groups,"party", function() { + var partyStub = sandbox.stub(groups, "party", function() { return party; }); @@ -46,7 +93,7 @@ describe('Groups Controller', function() { members: [user._id] }); - var myGuilds = sandbox.stub(groups,"myGuilds", function() { + var myGuilds = sandbox.stub(groups, "myGuilds", function() { return [guild]; }); diff --git a/test/spec/controllers/partyCtrlSpec.js b/test/spec/controllers/partyCtrlSpec.js index 92401f1987..623d63a74e 100644 --- a/test/spec/controllers/partyCtrlSpec.js +++ b/test/spec/controllers/partyCtrlSpec.js @@ -2,6 +2,7 @@ describe("Party Controller", function() { var scope, ctrl, user, User, questsService, groups, rootScope, $controller; + var party; beforeEach(function() { user = specHelper.newUser(), @@ -10,7 +11,13 @@ describe("Party Controller", function() { user: user, sync: sandbox.spy(), set: sandbox.spy() - } + }; + + party = specHelper.newGroup({ + _id: "unique-party-id", + type: 'party', + members: ['leader-id'] // Ensure we wouldn't pass automatically. + }); module(function($provide) { $provide.value('User', User); @@ -136,6 +143,25 @@ describe("Party Controller", function() { }); }); + describe("create", function() { + var partyStub; + + beforeEach(function () { + partyStub = sandbox.stub(groups.Group, "create", function() { + return party; + }); + }); + + it("creates a new party", function() { + var group = { + type: 'party', + }; + scope.create(group); + expect(partyStub).to.be.calledOnce; + //@TODO: Check user party console.log(User.user.party.id) + }); + }); + describe('questAccept', function() { beforeEach(function() { scope.group = { diff --git a/test/spec/services/groupServicesSpec.js b/test/spec/services/groupServicesSpec.js index e9a9285265..065af0b233 100644 --- a/test/spec/services/groupServicesSpec.js +++ b/test/spec/services/groupServicesSpec.js @@ -2,6 +2,7 @@ describe('groupServices', function() { var $httpBackend, $http, groups, user; + var groupApiUrlPrefix = '/api/v3/groups'; beforeEach(function() { module(function($provide) { @@ -16,27 +17,141 @@ describe('groupServices', function() { }); }); + it('calls get groups', function() { + $httpBackend.expectGET(groupApiUrlPrefix).respond({}); + groups.Group.getGroups(); + $httpBackend.flush(); + }); + + it('calls get group', function() { + var gid = 1; + $httpBackend.expectGET(groupApiUrlPrefix + '/' + gid).respond({}); + groups.Group.get(gid); + $httpBackend.flush(); + }); + it('calls party endpoint', function() { - $httpBackend.expectGET('/api/v2/groups/party').respond({}); + $httpBackend.expectGET(groupApiUrlPrefix + '/party').respond({}); + groups.Group.syncParty(); + $httpBackend.flush(); + }); + + it('calls create endpoint', function() { + $httpBackend.expectPOST(groupApiUrlPrefix).respond({}); + groups.Group.create({}); + $httpBackend.flush(); + }); + + it('calls update group', function() { + var gid = 1; + var groupDetails = { _id: gid }; + $httpBackend.expectPUT(groupApiUrlPrefix + '/' + gid).respond({}); + groups.Group.update(groupDetails); + $httpBackend.flush(); + }); + + it('calls join group', function() { + var gid = 1; + $httpBackend.expectPOST(groupApiUrlPrefix + '/' + gid + '/join').respond({}); + groups.Group.join(gid); + $httpBackend.flush(); + }); + + it('calls reject invite group', function() { + var gid = 1; + $httpBackend.expectPOST(groupApiUrlPrefix + '/' + gid + '/reject-invite').respond({}); + groups.Group.rejectInvite(gid); + $httpBackend.flush(); + }); + + it('calls invite group', function() { + var gid = 1; + $httpBackend.expectPOST(groupApiUrlPrefix + '/' + gid + '/invite').respond({}); + groups.Group.invite(gid, [], []); + $httpBackend.flush(); + }); + + it('calls party endpoint when party is not cached', function() { + $httpBackend.expectGET(groupApiUrlPrefix + '/party').respond({}); groups.party(); $httpBackend.flush(); }); - it('calls tavern endpoint', function() { - $httpBackend.expectGET('/api/v2/groups/habitrpg').respond({}); + it('returns party if cached', function (done) { + var uid = 'abc'; + var party = { + _id: uid, + }; + groups.data.party = party; + groups.party() + .then(function (result) { + expect(result).to.eql(party); + done(); + }); + $httpBackend.flush(); + }); + + it('calls tavern endpoint when tavern is not cached', function() { + $httpBackend.expectGET(groupApiUrlPrefix + '/habitrpg').respond({}); groups.tavern(); $httpBackend.flush(); }); + it('returns tavern if cached', function (done) { + var uid = 'abc'; + var tavern = { + _id: uid, + }; + groups.data.tavern = tavern; + groups.tavern() + .then(function (result) { + expect(result).to.eql(tavern); + done(); + }); + $httpBackend.flush(); + }); + it('calls public guilds endpoint', function() { - $httpBackend.expectGET('/api/v2/groups?type=public').respond([]); + $httpBackend.expectGET(groupApiUrlPrefix + '?type=publicGuilds').respond([]); groups.publicGuilds(); $httpBackend.flush(); }); + it('returns public guilds if cached', function (done) { + var uid = 'abc'; + var publicGuilds = [ + {_id: uid}, + ]; + groups.data.publicGuilds = publicGuilds; + + groups.publicGuilds() + .then(function (result) { + expect(result).to.eql(publicGuilds); + done(); + }); + + $httpBackend.flush(); + }); + it('calls my guilds endpoint', function() { - $httpBackend.expectGET('/api/v2/groups?type=guilds').respond([]); + $httpBackend.expectGET(groupApiUrlPrefix + '?type=privateGuilds').respond([]); groups.myGuilds(); $httpBackend.flush(); }); + + it('returns my guilds if cached', function (done) { + var uid = 'abc'; + var myGuilds = [ + {_id: uid}, + ]; + groups.data.myGuilds = myGuilds; + + groups.myGuilds() + .then(function (myGuilds) { + expect(myGuilds).to.eql(myGuilds); + done(); + }); + + $httpBackend.flush(); + }); }); diff --git a/test/spec/specHelper.js b/test/spec/specHelper.js index 9324fa5d8b..cbacac1988 100644 --- a/test/spec/specHelper.js +++ b/test/spec/specHelper.js @@ -28,6 +28,9 @@ var specHelper = {}; var user = { _id: 'unique-user-id', + profile: { + name: 'dummy-name', + }, auth: { timestamps: {} }, stats: stats, items: items, diff --git a/website/public/js/app.js b/website/public/js/app.js index 6b39f45d63..67215bc810 100644 --- a/website/public/js/app.js +++ b/website/public/js/app.js @@ -152,10 +152,11 @@ window.habitrpg = angular.module('habitrpg', title: env.t('titleGuilds'), controller: ['$scope', 'Groups', 'Chat', '$stateParams', function($scope, Groups, Chat, $stateParams){ - Groups.Group.get({gid:$stateParams.gid}, function(group){ - $scope.group = group; - Chat.seenMessage(group._id); - }); + Groups.Group.get($stateParams.gid) + .then(function (response) { + $scope.group = response.data.data; + Chat.seenMessage($scope.group._id); + }); }] }) diff --git a/website/public/js/controllers/groupsCtrl.js b/website/public/js/controllers/groupsCtrl.js index e7865afb55..224efc6fdb 100644 --- a/website/public/js/controllers/groupsCtrl.js +++ b/website/public/js/controllers/groupsCtrl.js @@ -3,24 +3,24 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', '$http', '$q', 'User', 'Members', '$state', 'Notification', function($scope, $rootScope, Shared, Groups, $http, $q, User, Members, $state, Notification) { - $scope.isMemberOfPendingQuest = function(userid, group) { + $scope.isMemberOfPendingQuest = function (userid, group) { if (!group.quest || !group.quest.members) return false; if (group.quest.active) return false; // quest is started, not pending return userid in group.quest.members && group.quest.members[userid] != false; }; - $scope.isMemberOfRunningQuest = function(userid, group) { + $scope.isMemberOfRunningQuest = function (userid, group) { if (!group.quest || !group.quest.members) return false; if (!group.quest.active) return false; // quest is pending, not started return group.quest.members[userid]; }; - $scope.isMemberOfGroup = function(userid, group){ - + $scope.isMemberOfGroup = function (userid, group) { // If the group is a guild, just check for an intersection with the // current user's guilds, rather than checking the members of the group. if(group.type === 'guild') { - return _.detect(Groups.myGuilds(), function(g) { return g._id === group._id }); + var guilds = Groups.myGuilds(); + return _.detect(guilds, function(g) { return g._id === group._id }); } // Similarly, if we're dealing with the user's current party, return true. @@ -34,7 +34,7 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' return ~(memberIds.indexOf(userid)); }; - $scope.isMember = function(user, group){ + $scope.isMember = function (user, group) { return ~(group.members.indexOf(user._id)); }; @@ -47,7 +47,6 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' group._editing = true; }; - $scope.saveEdit = function (group) { var newLeader = $scope.groupCopy._newLeader && $scope.groupCopy._newLeader._id; @@ -57,7 +56,7 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' angular.copy($scope.groupCopy, group); - group.$save(); + Groups.Group.update(group); $scope.cancelEdit(group); }; @@ -91,7 +90,6 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' } }; - $scope.removeMember = function(group, member, isMember){ // TODO find a better way to do this (share data with remove member modal) $scope.removeMemberData = { @@ -103,12 +101,12 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' }; $scope.confirmRemoveMember = function(confirm){ - if(confirm){ - Groups.Group.removeMember({ - gid: $scope.removeMemberData.group._id, - uuid: $scope.removeMemberData.member._id, - message: $scope.removeMemberData.message, - }, undefined, function(){ + if (confirm) { + Groups.Group.removeMember( + $scope.removeMemberData.group._id, + $scope.removeMemberData.member._id, + $scope.removeMemberData.message + ).then(function (response) { if($scope.removeMemberData.isMember){ _.pull($scope.removeMemberData.group.members, $scope.removeMemberData.member); }else{ @@ -117,7 +115,7 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $scope.removeMemberData = undefined; }); - }else{ + } else { $scope.removeMemberData = undefined; } }; @@ -141,5 +139,4 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $rootScope.openModal('private-message',{controller:'MemberModalCtrl'}); }); } - }]); diff --git a/website/public/js/controllers/guildsCtrl.js b/website/public/js/controllers/guildsCtrl.js index df6b001448..70493071c0 100644 --- a/website/public/js/controllers/guildsCtrl.js +++ b/website/public/js/controllers/guildsCtrl.js @@ -4,41 +4,71 @@ habitrpg.controller("GuildsCtrl", ['$scope', 'Groups', 'User', 'Challenges', '$r function($scope, Groups, User, Challenges, $rootScope, $state, $location, $compile, Analytics) { $scope.groups = { guilds: Groups.myGuilds(), - "public": Groups.publicGuilds() - } + public: Groups.publicGuilds(), + }; + + Groups.myGuilds() + .then(function (guilds) { + $scope.groups.guilds = guilds; + }); + + Groups.publicGuilds() + .then(function (guilds) { + $scope.groups.public = guilds; + }); + $scope.type = 'guild'; $scope.text = window.env.t('guild'); + var newGroup = function(){ - return new Groups.Group({type:'guild', privacy:'private'}); + return {type:'guild', privacy:'private'}; } $scope.newGroup = newGroup() + $scope.create = function(group){ - if (User.user.balance < 1) + if (User.user.balance < 1) { return $rootScope.openModal('buyGems', {track:"Gems > Create Group"}); + } if (confirm(window.env.t('confirmGuild'))) { - group.$save(function(saved){ - if (saved.privacy == 'public') {Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'join group','owner':true,'groupType':'guild','privacy':saved.privacy,'groupName':saved.name})} - else {Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'join group','owner':true,'groupType':'guild','privacy':saved.privacy})} - $rootScope.hardRedirect('/#/options/groups/guilds/' + saved._id); - }); + Groups.Group.create(group) + .then(function (response) { + var createdGroup = response.data.data; + if (createdGroup.privacy == 'public') { + Analytics.track({'hitType':'event', 'eventCategory':'behavior', 'eventAction':'join group', 'owner':true, 'groupType':'guild', 'privacy': createdGroup.privacy, 'groupName':createdGroup.name}) + } else { + Analytics.track({'hitType':'event', 'eventCategory':'behavior', 'eventAction':'join group', 'owner':true, 'groupType':'guild', 'privacy': createdGroup.privacy}) + } + $rootScope.hardRedirect('/#/options/groups/guilds/' + createdGroup._id); + }); } } - $scope.join = function(group){ - // If we're accepting an invitation, we don't have the actual group object, but a faux group object (for performance - // purposes) {id, name}. Let's trick ngResource into thinking we have a group, so we can call the same $join - // function (server calls .attachGroup(), which finds group by _id and handles this properly) + $scope.join = function (group) { + var groupId = group._id; + + // If we don't have the _id property, we are joining from an invitation + // which contains a id property of the group if (group.id && !group._id) { - group = new Groups.Group({_id:group.id}); + groupId = group.id; } - group.$join(function(joined){ - if (joined.privacy == 'public') {Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'join group','owner':false,'groupType':'guild','privacy':joined.privacy,'groupName':joined.name})} - else {Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'join group','owner':false,'groupType':'guild','privacy':joined.privacy})} - $rootScope.hardRedirect('/#/options/groups/guilds/' + joined._id); - }) + Groups.Group.join(groupId) + .then(function (response) { + var joinedGroup = response.data.data; + + if (joinedGroup.privacy == 'public') { + Analytics.track({'hitType':'event', 'eventCategory':'behavior', 'eventAction':'join group', 'owner':false, 'groupType':'guild','privacy': joinedGroup.privacy, 'groupName': joinedGroup.name}) + } else { + Analytics.track({'hitType':'event', 'eventCategory':'behavior', 'eventAction':'join group', 'owner':false, 'groupType':'guild','privacy': joinedGroup.privacy}) + } + $rootScope.hardRedirect('/#/options/groups/guilds/' + joinedGroup._id); + }); + } + + $scope.reject = function(guild) { + Groups.Group.rejectInvite(guild.id); } $scope.leave = function(keep) { @@ -46,49 +76,44 @@ habitrpg.controller("GuildsCtrl", ['$scope', 'Groups', 'User', 'Challenges', '$r $scope.selectedGroup = undefined; $scope.popoverEl.popover('destroy'); } else { - Groups.Group.leave({gid: $scope.selectedGroup._id, keep:keep}, undefined, function(){ + Groups.Group.leave($scope.selectedGroup._id, keep) + .success(function (data) { $rootScope.hardRedirect('/#/options/groups/guilds'); - }); + }); } } $scope.clickLeave = function(group, $event){ - $scope.selectedGroup = group; - $scope.popoverEl = $($event.target).closest('.btn'); - var html, title; - Challenges.Challenge.query(function(challenges) { - challenges = _.pluck(_.filter(challenges, function(c) { - return c.group._id == group._id; - }), '_id'); + $scope.selectedGroup = group; + $scope.popoverEl = $($event.target).closest('.btn'); - if (_.intersection(challenges, User.user.challenges).length > 0) { - html = $compile( - '' + window.env.t('removeTasks') + '
\n' + window.env.t('keepTasks') + '
\n' + window.env.t('cancel') + '
' - )($scope); - title = window.env.t('leaveGroupCha'); - } else { - html = $compile( - '' + window.env.t('confirm') + '
\n' + window.env.t('cancel') + '
' - )($scope); - title = window.env.t('leaveGroup') - } + var html, title; - $scope.popoverEl.popover('destroy').popover({ - html: true, - placement: 'top', - trigger: 'manual', - title: title, - content: html - }).popover('show'); - }); - } + Challenges.Challenge.query(function(challenges) { + challenges = _.pluck(_.filter(challenges, function(c) { + return c.group._id == group._id; + }), '_id'); - $scope.reject = function(guild){ - var i = _.findIndex(User.user.invitations.guilds, {id:guild.id}); - if (~i){ - User.user.invitations.guilds.splice(i, 1); - User.set({'invitations.guilds':User.user.invitations.guilds}); - } + if (_.intersection(challenges, User.user.challenges).length > 0) { + html = $compile( + '' + window.env.t('removeTasks') + '
\n' + window.env.t('keepTasks') + '
\n' + window.env.t('cancel') + '
' + )($scope); + title = window.env.t('leaveGroupCha'); + } else { + html = $compile( + '' + window.env.t('confirm') + '
\n' + window.env.t('cancel') + '
' + )($scope); + title = window.env.t('leaveGroup') + } + + $scope.popoverEl.popover('destroy').popover({ + html: true, + placement: 'top', + trigger: 'manual', + title: title, + content: html + }).popover('show'); + }); } } ]); diff --git a/website/public/js/controllers/inviteToGroupCtrl.js b/website/public/js/controllers/inviteToGroupCtrl.js index f4a74dbac9..ca155d8b98 100644 --- a/website/public/js/controllers/inviteToGroupCtrl.js +++ b/website/public/js/controllers/inviteToGroupCtrl.js @@ -1,6 +1,7 @@ 'use strict'; -habitrpg.controller('InviteToGroupCtrl', ['$scope', 'User', 'Groups', 'injectedGroup', '$http', 'Notification', function($scope, User, Groups, injectedGroup, $http, Notification) { +habitrpg.controller('InviteToGroupCtrl', ['$scope', 'User', 'Groups', 'injectedGroup', '$http', 'Notification', + function($scope, User, Groups, injectedGroup, $http, Notification) { $scope.group = injectedGroup; $scope.inviter = User.user.profile.name; @@ -17,8 +18,9 @@ habitrpg.controller('InviteToGroupCtrl', ['$scope', 'User', 'Groups', 'injectedG $scope.inviteNewUsers = function(inviteMethod) { if (!$scope.group._id) { $scope.group.name = $scope.group.name || env.t('possessiveParty', {name: User.user.profile.name}); - return $scope.group.$save() - .then(function(res) { + return Groups.Group.create($scope.group) + .then(function(response) { + $scope.group = response.data.data; _inviteByMethod(inviteMethod); }); } @@ -39,12 +41,13 @@ habitrpg.controller('InviteToGroupCtrl', ['$scope', 'User', 'Groups', 'injectedG return console.log('Invalid invite method.') } - Groups.Group.invite({gid: $scope.group._id}, invitationDetails, function(){ - Notification.text(window.env.t('invitationsSent')); - _resetInvitees(); - }, function(){ - _resetInvitees(); - }); + Groups.Group.invite($scope.group._id, invitationDetails) + .then(function() { + Notification.text(window.env.t('invitationsSent')); + _resetInvitees(); + }, function(){ + _resetInvitees(); + }); } function _getOnlyUuids() { diff --git a/website/public/js/controllers/partyCtrl.js b/website/public/js/controllers/partyCtrl.js index 6494679911..022d4044a7 100644 --- a/website/public/js/controllers/partyCtrl.js +++ b/website/public/js/controllers/partyCtrl.js @@ -1,20 +1,36 @@ 'use strict'; habitrpg.controller("PartyCtrl", ['$rootScope','$scope','Groups','Chat','User','Challenges','$state','$compile','Analytics','Quests','Social', - function($rootScope,$scope,Groups,Chat,User,Challenges,$state,$compile,Analytics,Quests,Social) { + function($rootScope, $scope, Groups, Chat, User, Challenges, $state, $compile, Analytics, Quests, Social) { var user = User.user; $scope.type = 'party'; $scope.text = window.env.t('party'); - $scope.group = $rootScope.party = Groups.party(); - $scope.newGroup = new Groups.Group({type:'party'}); + + //@TODO: cache + Groups.Group.syncParty() + .then(function successCallback(response) { + $scope.group = response.data.data; + checkForNotifications(); + }, function errorCallback(response) { + $scope.newGroup = $scope.group = { type: 'party' }; + }); + $scope.inviteOrStartParty = Groups.inviteOrStartParty; $scope.loadWidgets = Social.loadWidgets; if ($state.is('options.social.party')) { - $scope.group.$syncParty(); // Sync party automatically when navigating to party page - + Groups.Group.syncParty() + .then(function successCallback(response) { + $scope.group = response.data.data; + checkForNotifications(); + }, function errorCallback(response) { + $scope.newGroup = { type: 'party' }; + }); + } + // Chat.seenMessage($scope.group._id); + function checkForNotifications () { // Checks if user's party has reached 2 players for the first time. if(!user.achievements.partyUp && $scope.group.memberCount >= 2) { @@ -30,36 +46,35 @@ habitrpg.controller("PartyCtrl", ['$rootScope','$scope','Groups','Chat','User',' } } - Chat.seenMessage($scope.group._id); - - $scope.create = function(group){ + $scope.create = function (group) { if (!group.name) group.name = env.t('possessiveParty', {name: User.user.profile.name}); - group.$save(function(){ - Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'join group','owner':true,'groupType':'party','privacy':'private'}); - Analytics.updateUser({'partyID':group.id,'partySize':1}); + Groups.Group.create(group, function() { + Analytics.track({'hitType':'event', 'eventCategory':'behavior', 'eventAction':'join group', 'owner':true, 'groupType':'party', 'privacy':'private'}); + Analytics.updateUser({'party.id': group.id, 'partySize': 1}); $rootScope.hardRedirect('/#/options/groups/party'); }); }; - $scope.join = function(party){ - var group = new Groups.Group({_id: party.id, name: party.name}); - group.$join(function(){ - Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'join group','owner':false,'groupType':'party','privacy':'private'}); - Analytics.updateUser({'partyID':party.id}); - $rootScope.hardRedirect('/#/options/groups/party'); - }); + $scope.join = function (party) { + Groups.Group.join(party.id) + .then(function (response) { + Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'join group','owner':false,'groupType':'party','privacy':'private'}); + Analytics.updateUser({'partyID': party.id}); + $rootScope.hardRedirect('/#/options/groups/party'); + }); }; // TODO: refactor guild and party leave into one function - $scope.leave = function(keep) { + $scope.leave = function (keep) { if (keep == 'cancel') { $scope.selectedGroup = undefined; $scope.popoverEl.popover('destroy'); } else { - Groups.Group.leave({gid: $scope.selectedGroup._id, keep:keep}, undefined, function(){ - Analytics.updateUser({'partySize':null,'partyID':null}); - $rootScope.hardRedirect('/#/options/groups/party'); - }); + Groups.Group.leave($scope.selectedGroup._id, keep) + .then(function (response) { + Analytics.updateUser({'partySize':null,'partyID':null}); + $rootScope.hardRedirect('/#/options/groups/party'); + }); } }; @@ -69,21 +84,27 @@ habitrpg.controller("PartyCtrl", ['$rootScope','$scope','Groups','Chat','User',' $scope.selectedGroup = group; $scope.popoverEl = $($event.target).closest('.btn'); var html, title; - Challenges.Challenge.query(function(challenges) { - challenges = _.pluck(_.filter(challenges, function(c) { - return c.group._id == group._id; - }), '_id'); - if (_.intersection(challenges, User.user.challenges).length > 0) { - html = $compile( - '' + window.env.t('removeTasks') + '
\n' + window.env.t('keepTasks') + '
\n' + window.env.t('cancel') + '
' - )($scope); - title = window.env.t('leavePartyCha'); - } else { - html = $compile( - '' + window.env.t('confirm') + '
\n' + window.env.t('cancel') + '
' - )($scope); - title = window.env.t('leaveParty'); - } + html = $compile('' + window.env.t('removeTasks') + '
\n' + window.env.t('keepTasks') + '
\n' + window.env.t('cancel') + '
')($scope); + title = window.env.t('leavePartyCha'); + + //TODO: Move this to challenge service + //@TODO: Implement this when we convert front-end challenge service + // Challenges.Challenge.query(function(challenges) { + // challenges = _.pluck(_.filter(challenges, function(c) { + // return c.group._id == group._id; + // }), '_id'); + // if (_.intersection(challenges, User.user.challenges).length > 0) { + // html = $compile( + // '' + window.env.t('removeTasks') + '
\n' + window.env.t('keepTasks') + '
\n' + window.env.t('cancel') + '
' + // )($scope); + // title = window.env.t('leavePartyCha'); + // } else { + // html = $compile( + // '' + window.env.t('confirm') + '
\n' + window.env.t('cancel') + '
' + // )($scope); + // title = window.env.t('leaveParty'); + // } + $scope.popoverEl.popover('destroy').popover({ html: true, placement: 'top', @@ -91,10 +112,10 @@ habitrpg.controller("PartyCtrl", ['$rootScope','$scope','Groups','Chat','User',' title: title, content: html }).popover('show'); - }); + // }); }; - $scope.clickStartQuest = function(){ + $scope.clickStartQuest = function () { Analytics.track({'hitType':'event','eventCategory':'button','eventAction':'click','eventLabel':'Start a Quest'}); var hasQuests = _.find(User.user.items.quests, function(quest) { return quest > 0; @@ -109,7 +130,7 @@ habitrpg.controller("PartyCtrl", ['$rootScope','$scope','Groups','Chat','User',' $scope.leaveOldPartyAndJoinNewParty = function(newPartyId, newPartyName) { if (confirm('Are you sure you want to delete your party and join ' + newPartyName + '?')) { - Groups.Group.leave({gid: Groups.party()._id, keep:false}, undefined, function() { + Groups.Group.leave({gid: Groups.party()._id, keep: false}, undefined, function() { $scope.group = { loadingNewParty: true }; @@ -118,7 +139,8 @@ habitrpg.controller("PartyCtrl", ['$rootScope','$scope','Groups','Chat','User',' } } - $scope.reject = function(){ + $scope.reject = function(party) { + Groups.Group.rejectInvite(party.id); User.set({'invitations.party':{}}); } diff --git a/website/public/js/services/groupServices.js b/website/public/js/services/groupServices.js index 001a137d63..e208b5bbdf 100644 --- a/website/public/js/services/groupServices.js +++ b/website/public/js/services/groupServices.js @@ -1,80 +1,191 @@ 'use strict'; -(function() { - angular - .module('habitrpg') - .factory('Groups', groupsFactory); +angular.module('habitrpg') +.factory('Groups', [ '$location', '$rootScope', '$http', 'Analytics', 'ApiUrl', 'Challenges', 'User', '$q', + function($location, $rootScope, $http, Analytics, ApiUrl, Challenges, User, $q) { + var data = {party: undefined, myGuilds: undefined, publicGuilds: undefined, tavern: undefined }; + var groupApiURLPrefix = "/api/v3/groups"; - groupsFactory.$inject = [ - '$location', - '$resource', - '$rootScope', - 'Analytics', - 'ApiUrl', - 'Challenges', - 'User' - ]; + var Group = {}; - function groupsFactory($location, $resource, $rootScope, Analytics, ApiUrl, Challenges, User) { + //@TODO: Add paging + Group.getGroups = function(type) { + var url = groupApiURLPrefix; + if (type) { + url += '?type=' + type; + } - var data = {party: undefined, myGuilds: undefined, publicGuilds: undefined, tavern: undefined}; - var Group = $resource(ApiUrl.get() + '/api/v2/groups/:gid', - {gid:'@_id', messageId: '@_messageId'}, - { - get: { - method: "GET", - isArray:false, - // Wrap challenges as ngResource so they have functions like $leave or $join - transformResponse: function(data) { - data = angular.fromJson(data); - _.each(data && data.challenges, function(c) { - angular.extend(c, Challenges.Challenge.prototype); - }); - return data; - } - }, - - syncParty: {method: "GET", url: '/api/v2/groups/party'}, - join: {method: "POST", url: ApiUrl.get() + '/api/v2/groups/:gid/join'}, - leave: {method: "POST", url: ApiUrl.get() + '/api/v2/groups/:gid/leave'}, - invite: {method: "POST", url: ApiUrl.get() + '/api/v2/groups/:gid/invite'}, - removeMember: {method: "POST", url: ApiUrl.get() + '/api/v2/groups/:gid/removeMember'}, - startQuest: {method: "POST", url: ApiUrl.get() + '/api/v2/groups/:gid/questAccept'} + return $http({ + method: 'GET', + url: url, }); + }; - function party(cb) { - if (!data.party) return (data.party = Group.get({gid: 'party'}, cb)); - return (cb) ? cb(party) : data.party; + Group.get = function(gid) { + return $http({ + method: 'GET', + url: groupApiURLPrefix + '/' + gid, + }); + }; + + Group.syncParty = function() { + return this.get('party'); + }; + + Group.create = function(groupDetails) { + return $http({ + method: "POST", + url: groupApiURLPrefix, + data: groupDetails, + }); + }; + + Group.update = function(groupDetails) { + //@TODO: Check for what has changed? + return $http({ + method: "PUT", + url: groupApiURLPrefix + '/' + groupDetails._id, + data: groupDetails, + }); + }; + + Group.join = function(gid) { + return $http({ + method: "POST", + url: groupApiURLPrefix + '/' + gid + '/join', + }); + }; + + Group.rejectInvite = function(gid) { + return $http({ + method: "POST", + url: groupApiURLPrefix + '/' + gid + '/reject-invite', + }); + }; + + Group.leave = function(gid, keep) { + return $http({ + method: "POST", + url: groupApiURLPrefix + '/' + gid + '/leave', + data: { + keep: keep, + } + }); + }; + + Group.removeMember = function(gid, memberId, message) { + return $http({ + method: "POST", + url: groupApiURLPrefix + gid + '/removeMember/' + memberId, + data: { + message: message, + }, + }); + }; + + Group.invite = function(gid, invitationDetails) { + return $http({ + method: "POST", + url: groupApiURLPrefix + '/' + gid + '/invite', + data: { + uuids: invitationDetails.uuids, + emails: invitationDetails.emails, + }, + }); + }; + + Group.startQuest = function(gid) { + return $http({ + method: "POST", + url: groupApiURLPrefix + '/' + gid + '/questAccept', + }); + }; + + function party () { + var deferred = $q.defer(); + + if (!data.party) { + Group.get('party') + .then(function (response) { + data.party = response.data.data; + deferred.resolve(data.party); + }, function (response) { + deferred.reject(response); + }); + } else { + deferred.resolve(data.party); + } + + return deferred.promise; } - function publicGuilds() { + function publicGuilds () { + var deferred = $q.defer(); + + if (!data.publicGuilds) { + Group.getGroups('publicGuilds') + .then(function (response) { + data.publicGuilds = response.data.data; + deferred.resolve(data.publicGuilds); + }, function (response) { + deferred.reject(response); + }); + } else { + deferred.resolve(data.publicGuilds); + } + + return deferred.promise; //TODO combine these as {type:'guilds,public'} and create a $filter() to separate them - if (!data.publicGuilds) data.publicGuilds = Group.query({type:'public'}); - return data.publicGuilds; } - function myGuilds() { - if (!data.myGuilds) data.myGuilds = Group.query({type:'guilds'}); - return data.myGuilds; + function myGuilds () { + var deferred = $q.defer(); + + if (!data.myGuilds) { + Group.getGroups('privateGuilds') + .then(function (response) { + data.myGuilds = response.data.data; + deferred.resolve(data.myGuilds); + }, function (response) { + deferred.reject(response); + }); + } else { + deferred.resolve(data.myGuilds); + } + + return deferred.promise; } - function tavern() { - if (!data.tavern) data.tavern = Group.get({gid:'habitrpg'}); - return data.tavern; + function tavern () { + var deferred = $q.defer(); + + if (!data.tavern) { + Group.get('habitrpg') + .then(function (response) { + data.tavern = response.data.data; + deferred.resolve(data.tavern); + }, function (response) { + deferred.reject(response); + }); + } else { + deferred.resolve(data.tavern); + } + + return deferred.promise; } - function inviteOrStartParty(group) { + function inviteOrStartParty (group) { Analytics.track({'hitType':'event','eventCategory':'button','eventAction':'click','eventLabel':'Invite Friends'}); if (group.type === "party" || $location.$$path === "/options/groups/party") { - group.type = 'party'; - $rootScope.openModal('invite-party', { - controller:'InviteToGroupCtrl', - resolve: { - injectedGroup: function(){ return group; } - } - }); + group.type = 'party'; + $rootScope.openModal('invite-party', { + controller:'InviteToGroupCtrl', + resolve: { + injectedGroup: function(){ return group; } + } + }); } else { - $location.path("/options/groups/party"); + $location.path("/options/groups/party"); } } @@ -86,7 +197,6 @@ inviteOrStartParty: inviteOrStartParty, data: data, - Group: Group - } - } -})(); + Group: Group, + }; + }]); diff --git a/website/src/controllers/api-v3/groups.js b/website/src/controllers/api-v3/groups.js index 3f4a7aabb4..91800ea660 100644 --- a/website/src/controllers/api-v3/groups.js +++ b/website/src/controllers/api-v3/groups.js @@ -278,7 +278,10 @@ api.joinGroup = { await Q.all(promises); let response = Group.toJSONCleanChat(promises[0], user); - response.leader = (await User.findById(response.leader).select(nameFields).exec()).toJSON({minimize: true}); + let leader = await User.findById(response.leader).select(nameFields).exec(); + if (leader) { + response.leader = leader.toJSON({minimize: true}); + } res.respond(200, response); firebase.addUserToGroup(group._id, user._id); diff --git a/website/views/options/social/party/party-invitation.jade b/website/views/options/social/party/party-invitation.jade index f4972e6e41..0bfd72b6d8 100644 --- a/website/views/options/social/party/party-invitation.jade +++ b/website/views/options/social/party/party-invitation.jade @@ -5,5 +5,4 @@ data-type='party', ng-click='join(user.invitations.party)' )=env.t('accept') - a.btn.btn-danger(ng-click='reject()')=env.t('reject') - + a.btn.btn-danger(ng-click='reject(user.invitations.party)')=env.t('reject')