Implemented new Achievement and Badge: Invited a Friend (Fixes #8615) (#8819)

* Added text to locale

* Added achievement to content and libs

* Added achievement modal

* Added achievement to notification model and controller

* Added achievement to user schema

* Grant achievement to inviter when user registers using emailed link

* Fix icon name

* Added integration test

* Fix linting

* Added sprite
This commit is contained in:
Kevin Smith
2017-07-20 02:39:39 +01:00
committed by Sabe Jones
parent 625b159880
commit 11a4c1c95d
10 changed files with 55 additions and 0 deletions

View File

@@ -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 () => { it('user not added to a party on expired invite', async () => {
let { group, groupLeader } = await createAndPopulateGroup({ let { group, groupLeader } = await createAndPopulateGroup({
groupDetails: { type: 'party', privacy: 'private' }, groupDetails: { type: 'party', privacy: 'private' },

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -228,6 +228,10 @@ habitrpg.controller('NotificationCtrl',
$rootScope.playSound('Achievement_Unlocked'); $rootScope.playSound('Achievement_Unlocked');
Achievement.displayAchievement('joinedChallenge', {size: 'md'}); Achievement.displayAchievement('joinedChallenge', {size: 'md'});
break; break;
case 'INVITED_FRIEND_ACHIEVEMENT':
$rootScope.playSound('Achievement_Unlocked');
Achievement.displayAchievement('invitedFriend', {size: 'md'});
break;
case 'NEW_CONTRIBUTOR_LEVEL': case 'NEW_CONTRIBUTOR_LEVEL':
$rootScope.playSound('Achievement_Unlocked'); $rootScope.playSound('Achievement_Unlocked');
Achievement.displayAchievement('contributor', {size: 'md'}); Achievement.displayAchievement('contributor', {size: 'md'});

View File

@@ -149,6 +149,8 @@
"sendInvitations": "Send Invitations", "sendInvitations": "Send Invitations",
"invitationsSent": "Invitations sent!", "invitationsSent": "Invitations sent!",
"invitationSent": "Invitation 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):", "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!", "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 %>", "sendGiftHeading": "Send Gift to <%= name %>",

View File

@@ -112,6 +112,11 @@ let basicAchievs = {
titleKey: 'joinedChallenge', titleKey: 'joinedChallenge',
textKey: 'joinedChallengeText', textKey: 'joinedChallengeText',
}, },
invitedFriend: {
icon: 'achievement-friends',
titleKey: 'invitedFriend',
textKey: 'invitedFriendText',
},
}; };
Object.assign(achievementsData, basicAchievs); Object.assign(achievementsData, basicAchievs);

View File

@@ -182,6 +182,7 @@ function _getBasicAchievements (user, language) {
_addSimple(result, user, {path: 'joinedGuild', language}); _addSimple(result, user, {path: 'joinedGuild', language});
_addSimple(result, user, {path: 'royallyLoyal', language}); _addSimple(result, user, {path: 'royallyLoyal', language});
_addSimple(result, user, {path: 'joinedChallenge', language}); _addSimple(result, user, {path: 'joinedChallenge', language});
_addSimple(result, user, {path: 'invitedFriend', language});
_addSimpleWithMasterCount(result, user, {path: 'beastMaster', language}); _addSimpleWithMasterCount(result, user, {path: 'beastMaster', language});
_addSimpleWithMasterCount(result, user, {path: 'mountMaster', language}); _addSimpleWithMasterCount(result, user, {path: 'mountMaster', language});

View File

@@ -49,6 +49,14 @@ async function _handleGroupInvitation (user, invite) {
} else { } else {
user.invitations.guilds.push({id: group._id, name: group.name, inviter}); 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) { } catch (err) {
logger.error(err); logger.error(err);
} }

View File

@@ -117,6 +117,7 @@ let schema = new Schema({
royallyLoyal: Boolean, royallyLoyal: Boolean,
joinedGuild: Boolean, joinedGuild: Boolean,
joinedChallenge: Boolean, joinedChallenge: Boolean,
invitedFriend: Boolean,
}, },
backer: { backer: {

View File

@@ -21,6 +21,7 @@ const NOTIFICATION_TYPES = [
'GUILD_PROMPT', 'GUILD_PROMPT',
'GUILD_JOINED_ACHIEVEMENT', 'GUILD_JOINED_ACHIEVEMENT',
'CHALLENGE_JOINED_ACHIEVEMENT', 'CHALLENGE_JOINED_ACHIEVEMENT',
'INVITED_FRIEND_ACHIEVEMENT',
]; ];
const Schema = mongoose.Schema; const Schema = mongoose.Schema;

View File

@@ -174,3 +174,14 @@ script(id='modals/achievements/joinedChallenge.html', type='text/ng-template')
br br
button.btn.btn-primary(ng-click='$close()')=env.t('huzzah') button.btn.btn-primary(ng-click='$close()')=env.t('huzzah')
+achievementFooter +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