diff --git a/test/api/v3/integration/user/auth/POST-register_local.test.js b/test/api/v3/integration/user/auth/POST-register_local.test.js index 39683ce5b4..18eedd7c0f 100644 --- a/test/api/v3/integration/user/auth/POST-register_local.test.js +++ b/test/api/v3/integration/user/auth/POST-register_local.test.js @@ -516,6 +516,28 @@ describe('POST /user/auth/local/register', () => { }); }); + it('awards achievement to inviter', async () => { + let { group, groupLeader } = await createAndPopulateGroup({ + groupDetails: { type: 'party', privacy: 'private' }, + }); + + let invite = encrypt(JSON.stringify({ + id: group._id, + inviter: groupLeader._id, + sentAt: Date.now(), + })); + + await api.post(`/user/auth/local/register?groupInvite=${invite}`, { + username, + email, + password, + confirmPassword: password, + }); + + await groupLeader.sync(); + expect(groupLeader.achievements.invitedFriend).to.be.true; + }); + it('user not added to a party on expired invite', async () => { let { group, groupLeader } = await createAndPopulateGroup({ groupDetails: { type: 'party', privacy: 'private' }, diff --git a/website/assets/sprites/spritesmith/achievements/achievement-friends2x.png b/website/assets/sprites/spritesmith/achievements/achievement-friends2x.png new file mode 100644 index 0000000000..cf70f7c5ca Binary files /dev/null and b/website/assets/sprites/spritesmith/achievements/achievement-friends2x.png differ diff --git a/website/client-old/js/controllers/notificationCtrl.js b/website/client-old/js/controllers/notificationCtrl.js index 0653b88e10..2caf517039 100644 --- a/website/client-old/js/controllers/notificationCtrl.js +++ b/website/client-old/js/controllers/notificationCtrl.js @@ -228,6 +228,10 @@ habitrpg.controller('NotificationCtrl', $rootScope.playSound('Achievement_Unlocked'); Achievement.displayAchievement('joinedChallenge', {size: 'md'}); break; + case 'INVITED_FRIEND_ACHIEVEMENT': + $rootScope.playSound('Achievement_Unlocked'); + Achievement.displayAchievement('invitedFriend', {size: 'md'}); + break; case 'NEW_CONTRIBUTOR_LEVEL': $rootScope.playSound('Achievement_Unlocked'); Achievement.displayAchievement('contributor', {size: 'md'}); diff --git a/website/common/locales/en/groups.json b/website/common/locales/en/groups.json index 2d6ee1c816..99338176d7 100644 --- a/website/common/locales/en/groups.json +++ b/website/common/locales/en/groups.json @@ -149,6 +149,8 @@ "sendInvitations": "Send Invitations", "invitationsSent": "Invitations sent!", "invitationSent": "Invitation sent!", + "invitedFriend": "Invited a Friend", + "invitedFriendText": "This user invited a friend (or friends) who joined them on their adventure!", "inviteAlertInfo2": "Or share this link (copy/paste):", "inviteLimitReached": "You have already sent the maximum number of email invitations. We have a limit to prevent spamming, however if you would like more, please contact us at <%= techAssistanceEmail %> and we'll be happy to discuss it!", "sendGiftHeading": "Send Gift to <%= name %>", diff --git a/website/common/script/content/achievements.js b/website/common/script/content/achievements.js index ca2a7f9089..ad024101ed 100644 --- a/website/common/script/content/achievements.js +++ b/website/common/script/content/achievements.js @@ -112,6 +112,11 @@ let basicAchievs = { titleKey: 'joinedChallenge', textKey: 'joinedChallengeText', }, + invitedFriend: { + icon: 'achievement-friends', + titleKey: 'invitedFriend', + textKey: 'invitedFriendText', + }, }; Object.assign(achievementsData, basicAchievs); diff --git a/website/common/script/libs/achievements.js b/website/common/script/libs/achievements.js index 1d801d7d76..aa742b7960 100644 --- a/website/common/script/libs/achievements.js +++ b/website/common/script/libs/achievements.js @@ -182,6 +182,7 @@ function _getBasicAchievements (user, language) { _addSimple(result, user, {path: 'joinedGuild', language}); _addSimple(result, user, {path: 'royallyLoyal', language}); _addSimple(result, user, {path: 'joinedChallenge', language}); + _addSimple(result, user, {path: 'invitedFriend', language}); _addSimpleWithMasterCount(result, user, {path: 'beastMaster', language}); _addSimpleWithMasterCount(result, user, {path: 'mountMaster', language}); diff --git a/website/server/controllers/api-v3/auth.js b/website/server/controllers/api-v3/auth.js index 53a3548884..8990a40c98 100644 --- a/website/server/controllers/api-v3/auth.js +++ b/website/server/controllers/api-v3/auth.js @@ -49,6 +49,14 @@ async function _handleGroupInvitation (user, invite) { } else { user.invitations.guilds.push({id: group._id, name: group.name, inviter}); } + + // award the inviter with 'Invited a Friend' achievement + inviter = await User.findById(inviter); + if (!inviter.achievements.invitedFriend) { + inviter.achievements.invitedFriend = true; + inviter.addNotification('INVITED_FRIEND_ACHIEVEMENT'); + await inviter.save(); + } } catch (err) { logger.error(err); } diff --git a/website/server/models/user/schema.js b/website/server/models/user/schema.js index 915ed65c05..454d697b14 100644 --- a/website/server/models/user/schema.js +++ b/website/server/models/user/schema.js @@ -117,6 +117,7 @@ let schema = new Schema({ royallyLoyal: Boolean, joinedGuild: Boolean, joinedChallenge: Boolean, + invitedFriend: Boolean, }, backer: { diff --git a/website/server/models/userNotification.js b/website/server/models/userNotification.js index 72f790a56a..f0e445e524 100644 --- a/website/server/models/userNotification.js +++ b/website/server/models/userNotification.js @@ -21,6 +21,7 @@ const NOTIFICATION_TYPES = [ 'GUILD_PROMPT', 'GUILD_JOINED_ACHIEVEMENT', 'CHALLENGE_JOINED_ACHIEVEMENT', + 'INVITED_FRIEND_ACHIEVEMENT', ]; const Schema = mongoose.Schema; diff --git a/website/views/shared/modals/achievements.jade b/website/views/shared/modals/achievements.jade index 179c59a9b3..16f93961be 100644 --- a/website/views/shared/modals/achievements.jade +++ b/website/views/shared/modals/achievements.jade @@ -174,3 +174,14 @@ script(id='modals/achievements/joinedChallenge.html', type='text/ng-template') br button.btn.btn-primary(ng-click='$close()')=env.t('huzzah') +achievementFooter + +// Invited Friend +script(id='modals/achievements/invitedFriend.html', type='text/ng-template') + .modal-content(style='min-width:28em') + .modal-body.text-center + h3(style='margin-bottom:0')=env.t('modalAchievement') + +achievementAvatar('friends',0) + p=env.t('invitedFriendText') + br + button.btn.btn-primary(ng-click='$close()')=env.t('huzzah') + +achievementFooter