diff --git a/test/api/v3/integration/groups/POST-groups.test.js b/test/api/v3/integration/groups/POST-groups.test.js index ad8f7a6c92..a5471a0166 100644 --- a/test/api/v3/integration/groups/POST-groups.test.js +++ b/test/api/v3/integration/groups/POST-groups.test.js @@ -74,6 +74,18 @@ describe('POST /group', () => { expect(updatedUser.guilds).to.include(guild._id); }); + it('awards the Joined Guild achievement', async () => { + await user.post('/groups', { + name: 'some guild', + type: 'guild', + privacy: 'public', + }); + + let updatedUser = await user.get('/user'); + + expect(updatedUser.achievements.joinedGuild).to.eql(true); + }); + context('public guild', () => { it('creates a group', async () => { let groupName = 'Test Public Guild'; diff --git a/test/api/v3/integration/groups/POST-groups_groupId_join.test.js b/test/api/v3/integration/groups/POST-groups_groupId_join.test.js index 13572f79bb..d6fe6a7a6d 100644 --- a/test/api/v3/integration/groups/POST-groups_groupId_join.test.js +++ b/test/api/v3/integration/groups/POST-groups_groupId_join.test.js @@ -68,6 +68,12 @@ describe('POST /group/:groupId/join', () => { await expect(joiningUser.get(`/groups/${publicGuild._id}`)).to.eventually.have.property('memberCount', oldMemberCount + 1); }); + + it('awards Joined Guild achievement', async () => { + await joiningUser.post(`/groups/${publicGuild._id}/join`); + + await expect(joiningUser.get('/user')).to.eventually.have.deep.property('achievements.joinedGuild', true); + }); }); context('Joining a private guild', () => { @@ -147,8 +153,14 @@ describe('POST /group/:groupId/join', () => { }), }; - expect(inviter.notifications[0].type).to.eql('GROUP_INVITE_ACCEPTED'); - expect(inviter.notifications[0].data).to.eql(expectedData); + expect(inviter.notifications[1].type).to.eql('GROUP_INVITE_ACCEPTED'); + expect(inviter.notifications[1].data).to.eql(expectedData); + }); + + it('awards Joined Guild achievement', async () => { + await invitedUser.post(`/groups/${guild._id}/join`); + + await expect(invitedUser.get('/user')).to.eventually.have.deep.property('achievements.joinedGuild', true); }); }); }); diff --git a/test/api/v3/integration/tasks/groups/POST-group_tasks_id_approve_userId.test.js b/test/api/v3/integration/tasks/groups/POST-group_tasks_id_approve_userId.test.js index 0805255502..9972d6c33f 100644 --- a/test/api/v3/integration/tasks/groups/POST-group_tasks_id_approve_userId.test.js +++ b/test/api/v3/integration/tasks/groups/POST-group_tasks_id_approve_userId.test.js @@ -112,8 +112,8 @@ describe('POST /tasks/:id/approve/:userId', () => { await user.sync(); await member2.sync(); - expect(user.notifications.length).to.equal(1); - expect(user.notifications[0].type).to.equal('GROUP_TASK_APPROVAL'); + expect(user.notifications.length).to.equal(2); + expect(user.notifications[1].type).to.equal('GROUP_TASK_APPROVAL'); expect(member2.notifications.length).to.equal(1); expect(member2.notifications[0].type).to.equal('GROUP_TASK_APPROVAL'); @@ -122,7 +122,7 @@ describe('POST /tasks/:id/approve/:userId', () => { await user.sync(); await member2.sync(); - expect(user.notifications.length).to.equal(0); + expect(user.notifications.length).to.equal(1); expect(member2.notifications.length).to.equal(0); }); diff --git a/test/api/v3/integration/tasks/groups/POST-group_tasks_id_score_direction.test.js b/test/api/v3/integration/tasks/groups/POST-group_tasks_id_score_direction.test.js index 6d24f9c0a4..a707e445a2 100644 --- a/test/api/v3/integration/tasks/groups/POST-group_tasks_id_score_direction.test.js +++ b/test/api/v3/integration/tasks/groups/POST-group_tasks_id_score_direction.test.js @@ -52,14 +52,14 @@ describe('POST /tasks/:id/score/:direction', () => { await user.sync(); - expect(user.notifications.length).to.equal(1); - expect(user.notifications[0].type).to.equal('GROUP_TASK_APPROVAL'); - expect(user.notifications[0].data.message).to.equal(t('userHasRequestedTaskApproval', { + expect(user.notifications.length).to.equal(2); + expect(user.notifications[1].type).to.equal('GROUP_TASK_APPROVAL'); + expect(user.notifications[1].data.message).to.equal(t('userHasRequestedTaskApproval', { user: member.auth.local.username, taskName: updatedTask.text, taskId: updatedTask._id, }, 'cs')); // This test only works if we have the notification translated - expect(user.notifications[0].data.groupId).to.equal(guild._id); + expect(user.notifications[1].data.groupId).to.equal(guild._id); expect(updatedTask.group.approval.requested).to.equal(true); expect(updatedTask.group.approval.requestedDate).to.be.a('string'); // date gets converted to a string as json doesn't have a Date type @@ -82,14 +82,14 @@ describe('POST /tasks/:id/score/:direction', () => { await user.sync(); await member2.sync(); - expect(user.notifications.length).to.equal(1); - expect(user.notifications[0].type).to.equal('GROUP_TASK_APPROVAL'); - expect(user.notifications[0].data.message).to.equal(t('userHasRequestedTaskApproval', { + expect(user.notifications.length).to.equal(2); + expect(user.notifications[1].type).to.equal('GROUP_TASK_APPROVAL'); + expect(user.notifications[1].data.message).to.equal(t('userHasRequestedTaskApproval', { user: member.auth.local.username, taskName: updatedTask.text, taskId: updatedTask._id, })); - expect(user.notifications[0].data.groupId).to.equal(guild._id); + expect(user.notifications[1].data.groupId).to.equal(guild._id); expect(member2.notifications.length).to.equal(1); expect(member2.notifications[0].type).to.equal('GROUP_TASK_APPROVAL'); 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 bb2f588bab..baf7bf3fa4 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 @@ -251,7 +251,6 @@ describe('POST /user/auth/local/register', () => { confirmPassword: password, }); - await expect(getProperty('users', user._id, '_ABtest')).to.eventually.be.a('string'); await expect(getProperty('users', user._id, '_ABtests')).to.eventually.be.a('object'); }); diff --git a/test/api/v3/integration/user/auth/POST-user_auth_social.test.js b/test/api/v3/integration/user/auth/POST-user_auth_social.test.js index 6ca68e1339..dc399c619c 100644 --- a/test/api/v3/integration/user/auth/POST-user_auth_social.test.js +++ b/test/api/v3/integration/user/auth/POST-user_auth_social.test.js @@ -83,7 +83,7 @@ describe('POST /user/auth/social', () => { network, }); - await expect(getProperty('users', user._id, '_ABtest')).to.eventually.be.a('string'); + await expect(getProperty('users', user._id, '_ABtests')).to.eventually.be.a('object'); }); }); @@ -139,7 +139,7 @@ describe('POST /user/auth/social', () => { network, }); - await expect(getProperty('users', user._id, '_ABtest')).to.eventually.be.a('string'); + await expect(getProperty('users', user._id, '_ABtests')).to.eventually.be.a('object'); }); }); }); diff --git a/website/assets/sprites/spritesmith/achievements/achievement-armor.png b/website/assets/sprites/spritesmith/achievements/achievement-armor.png deleted file mode 100644 index 7e90f0135f..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-armor.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-bewilder.png b/website/assets/sprites/spritesmith/achievements/achievement-bewilder.png deleted file mode 100644 index 133193f297..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-bewilder.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-birthday.png b/website/assets/sprites/spritesmith/achievements/achievement-birthday.png deleted file mode 100644 index 89720342de..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-birthday.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-boot.png b/website/assets/sprites/spritesmith/achievements/achievement-boot.png deleted file mode 100644 index 0ab9b8869d..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-boot.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-bow.png b/website/assets/sprites/spritesmith/achievements/achievement-bow.png deleted file mode 100644 index 0a3be8d3ff..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-bow.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-burnout.png b/website/assets/sprites/spritesmith/achievements/achievement-burnout.png deleted file mode 100644 index d986994714..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-burnout.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-cactus.png b/website/assets/sprites/spritesmith/achievements/achievement-cactus.png deleted file mode 100644 index 0d9dce5f51..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-cactus.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-cake.png b/website/assets/sprites/spritesmith/achievements/achievement-cake.png deleted file mode 100644 index 443af47d6d..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-cake.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-cave.png b/website/assets/sprites/spritesmith/achievements/achievement-cave.png deleted file mode 100644 index 4b962d053a..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-cave.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-coffin.png b/website/assets/sprites/spritesmith/achievements/achievement-coffin.png deleted file mode 100644 index 417029737c..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-coffin.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-comment.png b/website/assets/sprites/spritesmith/achievements/achievement-comment.png deleted file mode 100644 index 5c8089b2e0..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-comment.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-costumeContest.png b/website/assets/sprites/spritesmith/achievements/achievement-costumeContest.png deleted file mode 100644 index a9f8e4b350..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-costumeContest.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-dilatory.png b/website/assets/sprites/spritesmith/achievements/achievement-dilatory.png deleted file mode 100644 index fc0fc5de7e..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-dilatory.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-firefox.png b/website/assets/sprites/spritesmith/achievements/achievement-firefox.png deleted file mode 100644 index e8478444cc..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-firefox.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-greeting.png b/website/assets/sprites/spritesmith/achievements/achievement-greeting.png deleted file mode 100644 index 0ff4add43d..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-greeting.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-alpha.png b/website/assets/sprites/spritesmith/achievements/achievement-guild2x.png similarity index 76% rename from website/assets/sprites/spritesmith/achievements/achievement-alpha.png rename to website/assets/sprites/spritesmith/achievements/achievement-guild2x.png index dcf4e8b9bf..90f749c1ef 100644 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-alpha.png and b/website/assets/sprites/spritesmith/achievements/achievement-guild2x.png differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-habitBirthday.png b/website/assets/sprites/spritesmith/achievements/achievement-habitBirthday.png deleted file mode 100644 index 32f4855fb2..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-habitBirthday.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-habiticaDay.png b/website/assets/sprites/spritesmith/achievements/achievement-habiticaDay.png deleted file mode 100644 index 6b8cc5eb0f..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-habiticaDay.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-heart.png b/website/assets/sprites/spritesmith/achievements/achievement-heart.png deleted file mode 100644 index e3cee3e8f4..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-heart.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-ninja.png b/website/assets/sprites/spritesmith/achievements/achievement-ninja.png deleted file mode 100644 index f9214837d7..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-ninja.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-nye.png b/website/assets/sprites/spritesmith/achievements/achievement-nye.png deleted file mode 100644 index f2ef7aa2b8..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-nye.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-partyOn.png b/website/assets/sprites/spritesmith/achievements/achievement-partyOn.png deleted file mode 100644 index d3362c6fcd..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-partyOn.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-partyUp.png b/website/assets/sprites/spritesmith/achievements/achievement-partyUp.png deleted file mode 100644 index 7918337362..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-partyUp.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-perfect.png b/website/assets/sprites/spritesmith/achievements/achievement-perfect.png deleted file mode 100644 index b9c9afafe1..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-perfect.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-rat.png b/website/assets/sprites/spritesmith/achievements/achievement-rat.png deleted file mode 100644 index f4666a6975..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-rat.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-seafoam.png b/website/assets/sprites/spritesmith/achievements/achievement-seafoam.png deleted file mode 100644 index ea9e917b84..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-seafoam.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-shield.png b/website/assets/sprites/spritesmith/achievements/achievement-shield.png deleted file mode 100644 index e0d8f16dbe..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-shield.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-shinySeed.png b/website/assets/sprites/spritesmith/achievements/achievement-shinySeed.png deleted file mode 100644 index 679883fd8f..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-shinySeed.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-snowball.png b/website/assets/sprites/spritesmith/achievements/achievement-snowball.png deleted file mode 100644 index a028c17fab..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-snowball.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-spookySparkles.png b/website/assets/sprites/spritesmith/achievements/achievement-spookySparkles.png deleted file mode 100644 index d1c0ab9bed..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-spookySparkles.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-stoikalm.png b/website/assets/sprites/spritesmith/achievements/achievement-stoikalm.png deleted file mode 100644 index 06323f3d17..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-stoikalm.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-sun.png b/website/assets/sprites/spritesmith/achievements/achievement-sun.png deleted file mode 100644 index a4aacec52f..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-sun.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-sword.png b/website/assets/sprites/spritesmith/achievements/achievement-sword.png deleted file mode 100644 index 7f93d749fe..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-sword.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-thankyou.png b/website/assets/sprites/spritesmith/achievements/achievement-thankyou.png deleted file mode 100644 index 53034e4597..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-thankyou.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-thermometer.png b/website/assets/sprites/spritesmith/achievements/achievement-thermometer.png deleted file mode 100644 index 7e4db3150f..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-thermometer.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-tree.png b/website/assets/sprites/spritesmith/achievements/achievement-tree.png deleted file mode 100644 index 376ad647d1..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-tree.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-triadbingo.png b/website/assets/sprites/spritesmith/achievements/achievement-triadbingo.png deleted file mode 100644 index 26db28450b..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-triadbingo.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-ultimate-healer.png b/website/assets/sprites/spritesmith/achievements/achievement-ultimate-healer.png deleted file mode 100644 index 1bcece61dc..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-ultimate-healer.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-ultimate-mage.png b/website/assets/sprites/spritesmith/achievements/achievement-ultimate-mage.png deleted file mode 100644 index fd822f83f5..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-ultimate-mage.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-ultimate-rogue.png b/website/assets/sprites/spritesmith/achievements/achievement-ultimate-rogue.png deleted file mode 100644 index dc46234170..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-ultimate-rogue.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-ultimate-warrior.png b/website/assets/sprites/spritesmith/achievements/achievement-ultimate-warrior.png deleted file mode 100644 index 93cb7bac7f..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-ultimate-warrior.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-valentine.png b/website/assets/sprites/spritesmith/achievements/achievement-valentine.png deleted file mode 100644 index 666d93d3cb..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-valentine.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith/achievements/achievement-wolf.png b/website/assets/sprites/spritesmith/achievements/achievement-wolf.png deleted file mode 100644 index 469441980e..0000000000 Binary files a/website/assets/sprites/spritesmith/achievements/achievement-wolf.png and /dev/null differ diff --git a/website/assets/sprites/spritesmith_large/scenes/scene_guilds.png b/website/assets/sprites/spritesmith_large/scenes/scene_guilds.png new file mode 100644 index 0000000000..25ac3e101f Binary files /dev/null and b/website/assets/sprites/spritesmith_large/scenes/scene_guilds.png differ diff --git a/website/client-old/js/controllers/notificationCtrl.js b/website/client-old/js/controllers/notificationCtrl.js index 06246bd088..092da25a71 100644 --- a/website/client-old/js/controllers/notificationCtrl.js +++ b/website/client-old/js/controllers/notificationCtrl.js @@ -78,7 +78,7 @@ habitrpg.controller('NotificationCtrl', // Avoid showing the same notiication more than once var lastShownNotifications = []; - function trasnferGroupNotification(notification) { + function transferGroupNotification(notification) { if (!User.user.groupNotifications) User.user.groupNotifications = []; User.user.groupNotifications.push(notification); } @@ -110,6 +110,13 @@ habitrpg.controller('NotificationCtrl', var markAsRead = true; switch (notification.type) { + case 'GUILD_PROMPT': + if (notification.data.textVariant === -1) { + $rootScope.openModal('testing'); + } else { + $rootScope.openModal('testingVariant'); + } + break; case 'DROPS_ENABLED': $rootScope.openModal('dropsEnabled'); break; @@ -129,12 +136,19 @@ habitrpg.controller('NotificationCtrl', } break; case 'ULTIMATE_GEAR_ACHIEVEMENT': + $rootScope.playSound('Achievement_Unlocked'); Achievement.displayAchievement('ultimateGear', {size: 'md'}); break; case 'REBIRTH_ACHIEVEMENT': + $rootScope.playSound('Achievement_Unlocked'); Achievement.displayAchievement('rebirth'); break; + case 'GUILD_JOINED_ACHIEVEMENT': + $rootScope.playSound('Achievement_Unlocked'); + Achievement.displayAchievement('joinedGuild', {size: 'md'}); + break; case 'NEW_CONTRIBUTOR_LEVEL': + $rootScope.playSound('Achievement_Unlocked'); Achievement.displayAchievement('contributor', {size: 'md'}); break; case 'CRON': @@ -144,11 +158,11 @@ habitrpg.controller('NotificationCtrl', } break; case 'GROUP_TASK_APPROVAL': - trasnferGroupNotification(notification); + transferGroupNotification(notification); markAsRead = false; break; case 'GROUP_TASK_APPROVED': - trasnferGroupNotification(notification); + transferGroupNotification(notification); markAsRead = false; break; case 'SCORED_TASK': diff --git a/website/common/locales/en/groups.json b/website/common/locales/en/groups.json index d418a1b53b..20b9ccddb0 100644 --- a/website/common/locales/en/groups.json +++ b/website/common/locales/en/groups.json @@ -282,5 +282,7 @@ "userIsNotManager": "User is not manager", "canOnlyApproveTaskOnce": "This task has already been approved.", "leaderMarker": " - Leader", - "managerMarker": " - Manager" + "managerMarker": " - Manager", + "joinedGuild": "Joined a Guild", + "joinedGuildText": "Ventured into the social side of Habitica by joining a Guild!" } diff --git a/website/common/locales/en/testing.json b/website/common/locales/en/testing.json new file mode 100644 index 0000000000..449740b4ea --- /dev/null +++ b/website/common/locales/en/testing.json @@ -0,0 +1,7 @@ +{ + "guildReminderTitle": "Check out Guilds!", + "guildReminderText1": "Now that you've mastered Habitica's basics, come check out Guilds, where you can chat with other people who are committed to improving their lives!", + "guildReminderText2": "Ready for more? Join a Guild to meet other people who are committed to improving their lives!", + "guildReminderCTA": "Take me there!", + "guildReminderDismiss": "Not right now." +} diff --git a/website/common/script/content/achievements.js b/website/common/script/content/achievements.js index f05d83154e..067a4a0ea9 100644 --- a/website/common/script/content/achievements.js +++ b/website/common/script/content/achievements.js @@ -102,6 +102,11 @@ let basicAchievs = { titleKey: 'royallyLoyal', textKey: 'royallyLoyalText', }, + joinedGuild: { + icon: 'achievement-guild', + titleKey: 'joinedGuild', + textKey: 'joinedGuildText', + }, }; Object.assign(achievementsData, basicAchievs); diff --git a/website/common/script/libs/achievements.js b/website/common/script/libs/achievements.js index 318beca18d..1d49449a3a 100644 --- a/website/common/script/libs/achievements.js +++ b/website/common/script/libs/achievements.js @@ -179,6 +179,7 @@ function _getBasicAchievements (user, language) { _addSimple(result, user, {path: 'partyUp', language}); _addSimple(result, user, {path: 'partyOn', language}); + _addSimple(result, user, {path: 'joinedGuild', language}); _addSimple(result, user, {path: 'royallyLoyal', language}); _addSimpleWithMasterCount(result, user, {path: 'beastMaster', language}); diff --git a/website/server/controllers/api-v3/groups.js b/website/server/controllers/api-v3/groups.js index 855175e74b..35858e2f73 100644 --- a/website/server/controllers/api-v3/groups.js +++ b/website/server/controllers/api-v3/groups.js @@ -122,6 +122,14 @@ api.createGroup = { user.balance--; user.guilds.push(group._id); + if (!user.achievements.joinedGuild) { + user.achievements.joinedGuild = true; + user.addNotification('GUILD_JOINED_ACHIEVEMENT'); + } + if (user._ABtests && user._ABtests.guildReminder && user._ABtests.counter !== -1) { + user._ABtests.counter = -1; + user.markModified('_ABtests'); + } } else { if (group.privacy !== 'private') throw new NotAuthorized(res.t('partyMustbePrivate')); if (user.party._id) throw new NotAuthorized(res.t('messageGroupAlreadyInParty')); @@ -510,6 +518,14 @@ api.joinGroup = { throw new NotAuthorized(res.t('userAlreadyInGroup')); } user.guilds.push(group._id); // Add group to user's guilds + if (!user.achievements.joinedGuild) { + user.achievements.joinedGuild = true; + user.addNotification('GUILD_JOINED_ACHIEVEMENT'); + } + if (user._ABtests && user._ABtests.guildReminder && user._ABtests.counter !== -1) { + user._ABtests.counter = -1; + user.markModified('_ABtests'); + } } if (!isUserInvited) throw new NotAuthorized(res.t('messageGroupRequiresInvite')); diff --git a/website/server/controllers/api-v3/tasks.js b/website/server/controllers/api-v3/tasks.js index 401a432e24..ef61c1b8f3 100644 --- a/website/server/controllers/api-v3/tasks.js +++ b/website/server/controllers/api-v3/tasks.js @@ -589,6 +589,18 @@ api.scoreTask = { } } + if (user._ABtests && user._ABtests.guildReminder && user._ABtests.counter !== -1) { + user._ABtests.counter++; + if (user._ABtests.counter > 1) { + if (user._ABtests.guildReminder.indexOf('timing1') !== -1 || user._ABtests.counter > 4) { + user._ABtests.counter = -1; + let textVariant = user._ABtests.guildReminder.indexOf('text2'); + user.addNotification('GUILD_PROMPT', {textVariant}); + } + } + user.markModified('_ABtests'); + } + if (task.type === 'daily') { task.isDue = common.shouldDo(Date.now(), task, user.preferences); } diff --git a/website/server/controllers/api-v3/user.js b/website/server/controllers/api-v3/user.js index 49d9e17747..342013e780 100644 --- a/website/server/controllers/api-v3/user.js +++ b/website/server/controllers/api-v3/user.js @@ -121,6 +121,8 @@ api.getBuyList = { }; let updatablePaths = [ + '_ABtests.counter', + 'flags.customizationsNotification', 'flags.showTour', 'flags.tour', diff --git a/website/server/models/user/hooks.js b/website/server/models/user/hooks.js index 16ca40dc29..fa22e5f998 100644 --- a/website/server/models/user/hooks.js +++ b/website/server/models/user/hooks.js @@ -10,7 +10,7 @@ import schema from './schema'; schema.plugin(baseModel, { // noSet is not used as updating uses a whitelist and creating only accepts specific params (password, email, username, ...) noSet: [], - private: ['auth.local.hashed_password', 'auth.local.passwordHashMethod', 'auth.local.salt', '_cronSignature', '_ABtest', '_ABtests'], + private: ['auth.local.hashed_password', 'auth.local.passwordHashMethod', 'auth.local.salt', '_cronSignature', '_ABtests'], toJSONTransform: function userToJSON (plainObj, originalDoc) { plainObj._tmp = originalDoc._tmp; // be sure to send down drop notifs delete plainObj.filters; @@ -94,13 +94,25 @@ function _setUpNewUser (user) { let taskTypes; let iterableFlags = user.flags.toObject(); - user._ABtest = ''; - // A/B test 2016-12-21: Should we deliver notifications for upcoming incentives on days when users don't receive rewards? - if (Math.random() < 0.5) { - user._ABtests.checkInModals = '20161221_noCheckInPreviews'; // no 'preview' check-in modals + // A/B test 2017-05-11: Can we encourage people to join Guilds with a pester modal? + let testGroup = Math.random(); + if (testGroup < 0.1) { + user._ABtests.guildReminder = '20170511_noGuildReminder'; // control group, don't pester about Guilds + user._ABtests.counter = -1; + } else if (testGroup < 0.235) { + user._ABtests.guildReminder = '20170511_text1timing1'; // first sample text, show after two clicks + user._ABtests.counter = 0; + } else if (testGroup < 0.46) { + user._ABtests.guildReminder = '20170511_text2timing1'; // second sample text, show after two clicks + user._ABtests.counter = 0; + } else if (testGroup < 0.685) { + user._ABtests.guildReminder = '20170511_text1timing2'; // first sample text, show after five clicks + user._ABtests.counter = 0; } else { - user._ABtests.checkInModals = '20161221_showCheckInPreviews'; // show 'preview' check-in modals + user._ABtests.guildReminder = '20170511_text2timing2'; // second sample text, show after five clicks + user._ABtests.counter = 0; } + user.items.quests.dustbunnies = 1; user.purchased.background.violet = true; user.preferences.background = 'violet'; diff --git a/website/server/models/user/schema.js b/website/server/models/user/schema.js index d3b09315b7..77800ac422 100644 --- a/website/server/models/user/schema.js +++ b/website/server/models/user/schema.js @@ -112,6 +112,7 @@ let schema = new Schema({ partyUp: Boolean, partyOn: Boolean, royallyLoyal: Boolean, + joinedGuild: Boolean, }, backer: { @@ -545,7 +546,6 @@ let schema = new Schema({ return {}; }}, pushDevices: [PushDeviceSchema], - _ABtest: {type: String}, // deprecated. Superseded by _ABtests _ABtests: {type: Schema.Types.Mixed, default: () => { return {}; }}, diff --git a/website/server/models/userNotification.js b/website/server/models/userNotification.js index 639017cc77..4bc7a281df 100644 --- a/website/server/models/userNotification.js +++ b/website/server/models/userNotification.js @@ -18,6 +18,8 @@ const NOTIFICATION_TYPES = [ 'GROUP_INVITE_ACCEPTED', 'SCORED_TASK', 'BOSS_DAMAGE', // Not used currently but kept to avoid validation errors + 'GUILD_PROMPT', + 'GUILD_JOINED_ACHIEVEMENT', ]; const Schema = mongoose.Schema; diff --git a/website/views/shared/modals/achievements.jade b/website/views/shared/modals/achievements.jade index a8954d2be8..e9fe6fe538 100644 --- a/website/views/shared/modals/achievements.jade +++ b/website/views/shared/modals/achievements.jade @@ -117,7 +117,7 @@ script(id='modals/achievements/contributor.html', type='text/ng-template') button.btn.btn-primary(style='margin-top:1em' ng-click='$close()')=env.t('huzzah') +achievementFooter -//Rebirth +// Rebirth script(id='modals/achievements/rebirth.html', type='text/ng-template') .modal-content(style='min-width:28em') .modal-body.text-center @@ -152,3 +152,14 @@ script(id='modals/achievements/partyOn.html', type='text/ng-template') br button.btn.btn-primary(ng-click='$close()')=env.t('huzzah') +achievementFooter + +// Joined Guild +script(id='modals/achievements/joinedGuild.html', type='text/ng-template') + .modal-content(style='min-width:28em') + .modal-body.text-center + h3(style='margin-bottom:0')=env.t('modalAchievement') + +achievementAvatar('guild',0) + p=env.t('joinedGuildText') + br + button.btn.btn-primary(ng-click='$close()')=env.t('huzzah') + +achievementFooter diff --git a/website/views/shared/modals/index.jade b/website/views/shared/modals/index.jade index afd31bb60e..d3ae56c8ad 100644 --- a/website/views/shared/modals/index.jade +++ b/website/views/shared/modals/index.jade @@ -27,6 +27,7 @@ include ./generic.jade include ./tasks-edit.jade include ./task-notes.jade include ./task-extra-notes.jade +include ./testing.jade //- Settings script(type='text/ng-template', id='modals/change-day-start.html') diff --git a/website/views/shared/modals/testing.jade b/website/views/shared/modals/testing.jade new file mode 100644 index 0000000000..be4e8c979c --- /dev/null +++ b/website/views/shared/modals/testing.jade @@ -0,0 +1,31 @@ +script(type='text/ng-template', id='modals/testing.html') + .modal-content(style='min-width:28em') + .modal-body.text-center(style='padding-bottom:0') + h3=env.t('guildReminderTitle') + br + .scene_guilds.center-block + br + h4=env.t('guildReminderText1') + .modal-footer(style='padding-top:0') + .container-fluid + .row + .col-xs-6.text-center + button.btn-lg.btn-default(ng-click='$close()')=env.t('guildReminderDismiss') + .col-xs-6.text-center + button.btn-lg.btn-primary(ui-sref='options.social.guilds.public', href='/#/options/groups/guilds/public', ng-click='$close()')=env.t('guildReminderCTA') + +script(type='text/ng-template', id='modals/testingVariant.html') + .modal-content(style='min-width:28em') + .modal-body.text-center(style='padding-bottom:0') + h3=env.t('guildReminderTitle') + br + .scene_guilds.center-block + br + h4=env.t('guildReminderText2') + .modal-footer(style='padding-top:0') + .container-fluid + .row + .col-xs-6.text-center + button.btn-lg.btn-default(ng-click='$close()')=env.t('guildReminderDismiss') + .col-xs-6.text-center + button.btn-lg.btn-primary(ui-sref='options.social.guilds.public', href='/#/options/groups/guilds/public', ng-click='$close()')=env.t('guildReminderCTA')