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
This commit is contained in:
Keith Holliday
2016-04-25 16:11:23 -05:00
committed by Matteo Pagliazzi
parent f2d5c921ed
commit ea490c9a1f
15 changed files with 565 additions and 210 deletions

View File

@@ -1,3 +1,5 @@
require('babel-polyfill');
var shared = require('./script/index');
var _ = require('lodash');
var moment = require('moment');

View File

@@ -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,11 +30,11 @@ 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') + '<br><br>"' +

View File

@@ -102,11 +102,11 @@ 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;

View File

@@ -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];
});

View File

@@ -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 = {

View File

@@ -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();
});
});

View File

@@ -28,6 +28,9 @@ var specHelper = {};
var user = {
_id: 'unique-user-id',
profile: {
name: 'dummy-name',
},
auth: { timestamps: {} },
stats: stats,
items: items,

View File

@@ -152,9 +152,10 @@ 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);
});
}]
})

View File

@@ -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'});
});
}
}]);

View File

@@ -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,7 +76,8 @@ 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');
});
}
@@ -55,7 +86,9 @@ habitrpg.controller("GuildsCtrl", ['$scope', 'Groups', 'User', 'Challenges', '$r
$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;
@@ -82,13 +115,5 @@ habitrpg.controller("GuildsCtrl", ['$scope', 'Groups', 'User', 'Challenges', '$r
}).popover('show');
});
}
$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});
}
}
}
]);

View File

@@ -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,7 +41,8 @@ habitrpg.controller('InviteToGroupCtrl', ['$scope', 'User', 'Groups', 'injectedG
return console.log('Invalid invite method.')
}
Groups.Group.invite({gid: $scope.group._id}, invitationDetails, function(){
Groups.Group.invite($scope.group._id, invitationDetails)
.then(function() {
Notification.text(window.env.t('invitationsSent'));
_resetInvitees();
}, function(){

View File

@@ -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,33 +46,32 @@ 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(){
$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});
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(){
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(
'<a ng-controller="GroupsCtrl" ng-click="leave(\'remove-all\')">' + window.env.t('removeTasks') + '</a><br/>\n<a ng-click="leave(\'keep-all\')">' + window.env.t('keepTasks') + '</a><br/>\n<a ng-click="leave(\'cancel\')">' + window.env.t('cancel') + '</a><br/>'
)($scope);
html = $compile('<a ng-controller="GroupsCtrl" ng-click="leave(\'remove-all\')">' + window.env.t('removeTasks') + '</a><br/>\n<a ng-click="leave(\'keep-all\')">' + window.env.t('keepTasks') + '</a><br/>\n<a ng-click="leave(\'cancel\')">' + window.env.t('cancel') + '</a><br/>')($scope);
title = window.env.t('leavePartyCha');
} else {
html = $compile(
'<a ng-controller="GroupsCtrl" ng-click="leave(\'keep-all\')">' + window.env.t('confirm') + '</a><br/>\n<a ng-click="leave(\'cancel\')">' + window.env.t('cancel') + '</a><br/>'
)($scope);
title = window.env.t('leaveParty');
}
//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(
// '<a ng-controller="GroupsCtrl" ng-click="leave(\'remove-all\')">' + window.env.t('removeTasks') + '</a><br/>\n<a ng-click="leave(\'keep-all\')">' + window.env.t('keepTasks') + '</a><br/>\n<a ng-click="leave(\'cancel\')">' + window.env.t('cancel') + '</a><br/>'
// )($scope);
// title = window.env.t('leavePartyCha');
// } else {
// html = $compile(
// '<a ng-controller="GroupsCtrl" ng-click="leave(\'keep-all\')">' + window.env.t('confirm') + '</a><br/>\n<a ng-click="leave(\'cancel\')">' + window.env.t('cancel') + '</a><br/>'
// )($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':{}});
}

View File

@@ -1,69 +1,180 @@
'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) {
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;
//@TODO: Add paging
Group.getGroups = function(type) {
var url = groupApiURLPrefix;
if (type) {
url += '?type=' + type;
}
return $http({
method: 'GET',
url: url,
});
};
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,
},
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'}
});
};
function party(cb) {
if (!data.party) return (data.party = Group.get({gid: 'party'}, cb));
return (cb) ? cb(party) : data.party;
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);
}
function publicGuilds() {
return deferred.promise;
}
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);
}
function tavern() {
if (!data.tavern) data.tavern = Group.get({gid:'habitrpg'});
return data.tavern;
return deferred.promise;
}
function inviteOrStartParty(group) {
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) {
Analytics.track({'hitType':'event','eventCategory':'button','eventAction':'click','eventLabel':'Invite Friends'});
if (group.type === "party" || $location.$$path === "/options/groups/party") {
group.type = 'party';
@@ -86,7 +197,6 @@
inviteOrStartParty: inviteOrStartParty,
data: data,
Group: Group
}
}
})();
Group: Group,
};
}]);

View File

@@ -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);

View File

@@ -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')