diff --git a/package-lock.json b/package-lock.json index 60f888006e..eb6663f175 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "habitica", - "version": "4.230.0", + "version": "4.230.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 975d6cec9b..5945998e47 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "habitica", "description": "A habit tracker app which treats your goals like a Role Playing Game.", - "version": "4.230.0", + "version": "4.230.2", "main": "./website/server/index.js", "dependencies": { "@babel/core": "^7.17.10", diff --git a/test/api/unit/libs/xmlMarshaller.test.js b/test/api/unit/libs/xmlMarshaller.test.js index 73633a4c30..a1d201df29 100644 --- a/test/api/unit/libs/xmlMarshaller.test.js +++ b/test/api/unit/libs/xmlMarshaller.test.js @@ -42,3 +42,109 @@ describe('xml marshaller marshalls user data', () => { `); }); }); + +describe('xml marshaller marshalls user data (with purchases)', () => { + const minimumUser = { + pinnedItems: [], + unpinnedItems: [], + inbox: {}, + }; + + function userDataWith (fields) { + return { ...minimumUser, ...fields }; + } + + it('maps the purchases field with data that begins with a number', () => { + const userData = userDataWith({ + purchased: { + ads: false, + txnCount: 0, + skin: { + eb052b: true, + '0ff591': true, + '2b43f6': true, + d7a9f7: true, + '800ed0': true, + rainbow: true, + }, + }, + }); + + const xml = xmlMarshaller.marshallUserData(userData); + + expect(xml).to.equal(` + + + false + 0 + eb052b + 0ff591 + 2b43f6 + d7a9f7 + 800ed0 + rainbow + +`); + }); +}); + +describe('xml marshaller marshalls user data (with purchases nested)', () => { + const minimumUser = { + pinnedItems: [], + unpinnedItems: [], + inbox: {}, + }; + + function userDataWith (fields) { + return { ...minimumUser, ...fields }; + } + + it('maps the purchases field with data that begins with a number and nested objects', () => { + const userData = userDataWith({ + purchased: { + ads: false, + txnCount: 0, + skin: { + eb052b: true, + '0ff591': true, + '2b43f6': true, + d7a9f7: true, + '800ed0': true, + rainbow: true, + }, + plan: { + consecutive: { + count: 0, + offset: 0, + gemCapExtra: 0, + trinkets: 0, + }, + }, + }, + }); + + const xml = xmlMarshaller.marshallUserData(userData); + + expect(xml).to.equal(` + + + false + 0 + eb052b + 0ff591 + 2b43f6 + d7a9f7 + 800ed0 + rainbow + + + 0 + 0 + 0 + 0 + + + +`); + }); +}); diff --git a/test/api/unit/models/user.test.js b/test/api/unit/models/user.test.js index 06307e39a3..7061baf7ca 100644 --- a/test/api/unit/models/user.test.js +++ b/test/api/unit/models/user.test.js @@ -634,7 +634,7 @@ describe('User Model', () => { user = await user.save(); // verify that it's been awarded expect(user.achievements.beastMaster).to.equal(true); - expect(user.notifications.find(notification => notification.type === 'ACHIEVEMENT_BEAST_MASTER')).to.exist; + expect(user.notifications.find(notification => notification.type === 'ACHIEVEMENT_STABLE')).to.exist; // reset the user user.achievements.beastMasterCount = 0; @@ -683,9 +683,9 @@ describe('User Model', () => { user = await user.save(); // verify that it's been awarded - expect(user.notifications.find(notification => notification.type === 'ACHIEVEMENT_BEAST_MASTER')).to.exist; - expect(user.notifications.find(notification => notification.type === 'ACHIEVEMENT_MOUNT_MASTER')).to.exist; - expect(user.notifications.find(notification => notification.type === 'ACHIEVEMENT_TRIAD_BINGO')).to.exist; + expect(user.notifications.find( + notification => notification.type === 'ACHIEVEMENT_STABLE', + )).to.exist; }); context('manage unallocated stats points notifications', () => { diff --git a/website/client/src/assets/svg/big-gift.svg b/website/client/src/assets/svg/big-gift.svg new file mode 100644 index 0000000000..1d756e4dfa --- /dev/null +++ b/website/client/src/assets/svg/big-gift.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/client/src/assets/svg/close.svg b/website/client/src/assets/svg/close.svg index 5070218c06..398440da99 100644 --- a/website/client/src/assets/svg/close.svg +++ b/website/client/src/assets/svg/close.svg @@ -1,5 +1,11 @@ - - - + + + Icon/Close + + + + + + - + \ No newline at end of file diff --git a/website/client/src/components/achievements/justAddWater.vue b/website/client/src/components/achievements/justAddWater.vue deleted file mode 100644 index 012902aae0..0000000000 --- a/website/client/src/components/achievements/justAddWater.vue +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - {{ $t('achievementJustAddWaterModalText') }} - - {{ $t('huzzah') }} - - - - - - - - - - diff --git a/website/client/src/components/achievements/lostMasterclasser.vue b/website/client/src/components/achievements/lostMasterclasser.vue deleted file mode 100644 index 2b35e66826..0000000000 --- a/website/client/src/components/achievements/lostMasterclasser.vue +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - {{ $t('achievementLostMasterclasserModalText') }} - - {{ $t('huzzah') }} - - - - - - - - - - diff --git a/website/client/src/components/achievements/mindOverMatter.vue b/website/client/src/components/achievements/mindOverMatter.vue deleted file mode 100644 index 35931a0a08..0000000000 --- a/website/client/src/components/achievements/mindOverMatter.vue +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - {{ $t('achievementMindOverMatterModalText') }} - - {{ $t('huzzah') }} - - - - - - - - - - diff --git a/website/client/src/components/header/menu.vue b/website/client/src/components/header/menu.vue index d77c6f8e0d..fbfdaed141 100644 --- a/website/client/src/components/header/menu.vue +++ b/website/client/src/components/header/menu.vue @@ -3,7 +3,7 @@ - + - - - - - - diff --git a/website/client/src/components/header/notifications/lostMasterclasser.vue b/website/client/src/components/header/notifications/lostMasterclasser.vue deleted file mode 100644 index 3570055765..0000000000 --- a/website/client/src/components/header/notifications/lostMasterclasser.vue +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - diff --git a/website/client/src/components/header/notifications/mindOverMatter.vue b/website/client/src/components/header/notifications/mindOverMatter.vue deleted file mode 100644 index 700185a198..0000000000 --- a/website/client/src/components/header/notifications/mindOverMatter.vue +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - diff --git a/website/client/src/components/header/notificationsDropdown.vue b/website/client/src/components/header/notificationsDropdown.vue index fc1ac8ef57..4c075820cb 100644 --- a/website/client/src/components/header/notificationsDropdown.vue +++ b/website/client/src/components/header/notificationsDropdown.vue @@ -140,9 +140,6 @@ import NEW_INBOX_MESSAGE from './notifications/newPrivateMessage'; import NEW_CHAT_MESSAGE from './notifications/newChatMessage'; import WORLD_BOSS from './notifications/worldBoss'; import VERIFY_USERNAME from './notifications/verifyUsername'; -import ACHIEVEMENT_JUST_ADD_WATER from './notifications/justAddWater'; -import ACHIEVEMENT_LOST_MASTERCLASSER from './notifications/lostMasterclasser'; -import ACHIEVEMENT_MIND_OVER_MATTER from './notifications/mindOverMatter'; import ONBOARDING_COMPLETE from './notifications/onboardingComplete'; import GIFT_ONE_GET_ONE from './notifications/g1g1'; import OnboardingGuide from './onboardingGuide'; @@ -167,9 +164,6 @@ export default { CARD_RECEIVED, NEW_INBOX_MESSAGE, NEW_CHAT_MESSAGE, - ACHIEVEMENT_JUST_ADD_WATER, - ACHIEVEMENT_LOST_MASTERCLASSER, - ACHIEVEMENT_MIND_OVER_MATTER, WorldBoss: WORLD_BOSS, VERIFY_USERNAME, OnboardingGuide, @@ -194,13 +188,24 @@ export default { // listed in the order they should appear in the notifications panel. // NOTE: Those not listed here won't be shown in the notification panel! handledNotifications: [ - 'NEW_STUFF', 'GIFT_ONE_GET_ONE', 'GROUP_TASK_NEEDS_WORK', - 'GUILD_INVITATION', 'PARTY_INVITATION', 'CHALLENGE_INVITATION', - 'QUEST_INVITATION', 'GROUP_TASK_ASSIGNED', 'GROUP_TASK_APPROVAL', 'GROUP_TASK_APPROVED', - 'GROUP_TASK_CLAIMED', 'NEW_MYSTERY_ITEMS', 'CARD_RECEIVED', - 'NEW_INBOX_MESSAGE', 'NEW_CHAT_MESSAGE', 'UNALLOCATED_STATS_POINTS', - 'ACHIEVEMENT_JUST_ADD_WATER', 'ACHIEVEMENT_LOST_MASTERCLASSER', 'ACHIEVEMENT_MIND_OVER_MATTER', - 'VERIFY_USERNAME', 'ONBOARDING_COMPLETE', + 'NEW_STUFF', + 'GIFT_ONE_GET_ONE', + 'GROUP_TASK_NEEDS_WORK', + 'GUILD_INVITATION', + 'PARTY_INVITATION', + 'CHALLENGE_INVITATION', + 'QUEST_INVITATION', + 'GROUP_TASK_ASSIGNED', + 'GROUP_TASK_APPROVAL', + 'GROUP_TASK_APPROVED', + 'GROUP_TASK_CLAIMED', + 'NEW_MYSTERY_ITEMS', + 'CARD_RECEIVED', + 'NEW_INBOX_MESSAGE', + 'NEW_CHAT_MESSAGE', + 'UNALLOCATED_STATS_POINTS', + 'VERIFY_USERNAME', + 'ONBOARDING_COMPLETE', ], }; }, diff --git a/website/client/src/components/notifications.vue b/website/client/src/components/notifications.vue index ab66886618..f4d702d8db 100644 --- a/website/client/src/components/notifications.vue +++ b/website/client/src/components/notifications.vue @@ -30,9 +30,6 @@ v-if="notificationData && notificationData.achievement" :data="notificationData" /> - - - @@ -141,25 +138,34 @@ import streak from './achievements/streak'; import ultimateGear from './achievements/ultimateGear'; import wonChallenge from './achievements/wonChallenge'; import genericAchievement from './achievements/genericAchievement'; -import justAddWater from './achievements/justAddWater'; -import lostMasterclasser from './achievements/lostMasterclasser'; -import mindOverMatter from './achievements/mindOverMatter'; import loginIncentives from './achievements/login-incentives'; import onboardingComplete from './achievements/onboardingComplete'; import verifyUsername from './settings/verifyUsername'; import firstDrops from './achievements/firstDrops'; const NOTIFICATIONS = { + // general notifications + NEW_CONTRIBUTOR_LEVEL: { + achievement: true, + label: $t => $t('modalContribAchievement'), + modalId: 'contributor', + sticky: true, + }, + // achievement notifications + ACHIEVEMENT: { // null data filled in handleUserNotifications + achievement: true, + modalId: 'generic-achievement', + label: null, + data: { + message: $t => $t('achievement'), + modalText: null, + }, + }, CHALLENGE_JOINED_ACHIEVEMENT: { achievement: true, label: $t => `${$t('achievement')}: ${$t('joinedChallenge')}`, modalId: 'joined-challenge', }, - ULTIMATE_GEAR_ACHIEVEMENT: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('gearAchievementNotification')}`, - modalId: 'ultimate-gear', - }, GUILD_JOINED_ACHIEVEMENT: { label: $t => `${$t('achievement')}: ${$t('joinedGuild')}`, achievement: true, @@ -170,42 +176,14 @@ const NOTIFICATIONS = { label: $t => `${$t('achievement')}: ${$t('invitedFriend')}`, modalId: 'invited-friend', }, - NEW_CONTRIBUTOR_LEVEL: { + ACHIEVEMENT_PARTY_ON: { achievement: true, - label: $t => $t('modalContribAchievement'), - modalId: 'contributor', - sticky: true, - }, - ACHIEVEMENT_ALL_YOUR_BASE: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementAllYourBase')}`, + label: $t => `${$t('achievement')}: ${$t('achievementPartyOn')}`, modalId: 'generic-achievement', data: { - achievement: 'allYourBase', // defined manually until the server sends all the necessary data - }, - }, - ACHIEVEMENT_BACK_TO_BASICS: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementBackToBasics')}`, - modalId: 'generic-achievement', - data: { - achievement: 'backToBasics', - }, - }, - ACHIEVEMENT_DUST_DEVIL: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementDustDevil')}`, - modalId: 'generic-achievement', - data: { - achievement: 'dustDevil', - }, - }, - ACHIEVEMENT_ARID_AUTHORITY: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementAridAuthority')}`, - modalId: 'generic-achievement', - data: { - achievement: 'aridAuthority', + message: $t => $t('achievement'), + modalText: $t => $t('achievementPartyOn'), + achievement: 'partyOn', }, }, ACHIEVEMENT_PARTY_UP: { @@ -218,245 +196,47 @@ const NOTIFICATIONS = { achievement: 'partyUp', }, }, - ACHIEVEMENT_PARTY_ON: { + ULTIMATE_GEAR_ACHIEVEMENT: { + achievement: true, + label: $t => `${$t('achievement')}: ${$t('gearAchievementNotification')}`, + modalId: 'ultimate-gear', + }, + ACHIEVEMENT_STABLE: { achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementPartyOn')}`, modalId: 'generic-achievement', data: { - message: $t => $t('achievement'), - modalText: $t => $t('achievementPartyOn'), - achievement: 'partyOn', + achievement: 'stableAchievs', }, }, - ACHIEVEMENT_BEAST_MASTER: { + ACHIEVEMENT_QUESTS: { achievement: true, - label: $t => `${$t('achievement')}: ${$t('beastAchievement')}`, modalId: 'generic-achievement', data: { - message: $t => $t('achievement'), - modalText: $t => $t('beastAchievement'), - achievement: 'beastMaster', + achievement: 'questSeriesAchievs', }, }, - ACHIEVEMENT_MOUNT_MASTER: { + ACHIEVEMENT_ANIMAL_SET: { achievement: true, - label: $t => `${$t('achievement')}: ${$t('mountAchievement')}`, + label: $t => `${$t('achievement')}: ${$t('achievementAnimalSet')}`, modalId: 'generic-achievement', data: { - message: $t => $t('achievement'), - modalText: $t => $t('mountAchievement'), - achievement: 'mountMaster', + achievement: 'animalSetAchievs', }, }, - ACHIEVEMENT_TRIAD_BINGO: { + ACHIEVEMENT_PET_COLOR: { achievement: true, - label: $t => `${$t('achievement')}: ${$t('triadBingoAchievement')}`, + label: $t => `${$t('achievement')}: ${$t('achievementPetColor')}`, modalId: 'generic-achievement', data: { - message: $t => $t('achievement'), - modalText: $t => $t('triadBingoAchievement'), - achievement: 'triadBingo', + achievement: 'petColorAchievs', }, }, - ACHIEVEMENT_MONSTER_MAGUS: { + ACHIEVEMENT_MOUNT_COLOR: { achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementMonsterMagus')}`, + label: $t => `${$t('achievement')}: ${$t('achievementMountColor')}`, modalId: 'generic-achievement', data: { - achievement: 'monsterMagus', - }, - }, - ACHIEVEMENT_UNDEAD_UNDERTAKER: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementUndeadUndertaker')}`, - modalId: 'generic-achievement', - data: { - achievement: 'undeadUndertaker', - }, - }, - ACHIEVEMENT: { // data filled in handleUserNotifications - achievement: true, - modalId: 'generic-achievement', - label: null, // data filled in handleUserNotifications - data: { - message: $t => $t('achievement'), - modalText: null, // data filled in handleUserNotifications - }, - }, - ACHIEVEMENT_PRIMED_FOR_PAINTING: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementPrimedForPainting')}`, - modalId: 'generic-achievement', - data: { - achievement: 'primedForPainting', - }, - }, - ACHIEVEMENT_PEARLY_PRO: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementPearlyPro')}`, - modalId: 'generic-achievement', - data: { - achievement: 'pearlyPro', - }, - }, - ACHIEVEMENT_TICKLED_PINK: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementTickledPink')}`, - modalId: 'generic-achievement', - data: { - achievement: 'tickledPink', - }, - }, - ACHIEVEMENT_ROSY_OUTLOOK: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementRosyOutlook')}`, - modalId: 'generic-achievement', - data: { - achievement: 'rosyOutlook', - }, - }, - ACHIEVEMENT_BUG_BONANZA: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementBugBonanza')}`, - modalId: 'generic-achievement', - data: { - achievement: 'bugBonanza', - }, - }, - ACHIEVEMENT_BARE_NECESSITIES: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementBareNecessities')}`, - modalId: 'generic-achievement', - data: { - achievement: 'bareNecessities', - }, - }, - ACHIEVEMENT_FRESHWATER_FRIENDS: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementFreshwaterFriends')}`, - modalId: 'generic-achievement', - data: { - achievement: 'freshwaterFriends', - }, - }, - ACHIEVEMENT_GOOD_AS_GOLD: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementGoodAsGold')}`, - modalId: 'generic-achievement', - data: { - achievement: 'goodAsGold', - }, - }, - ACHIEVEMENT_ALL_THAT_GLITTERS: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementAllThatGlitters')}`, - modalId: 'generic-achievement', - data: { - achievement: 'allThatGlitters', - }, - }, - ACHIEVEMENT_BONE_COLLECTOR: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementBoneCollector')}`, - modalId: 'generic-achievement', - data: { - achievement: 'boneCollector', - }, - }, - ACHIEVEMENT_SKELETON_CREW: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementSkeletonCrew')}`, - modalId: 'generic-achievement', - data: { - achievement: 'skeletonCrew', - }, - }, - ACHIEVEMENT_SEEING_RED: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementSeeingRed')}`, - modalId: 'generic-achievement', - data: { - achievement: 'seeingRed', - }, - }, - ACHIEVEMENT_RED_LETTER_DAY: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementRedLetterDay')}`, - modalId: 'generic-achievement', - data: { - achievement: 'redLetterDay', - }, - }, - ACHIEVEMENT_LEGENDARY_BESTIARY: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementLegendaryBestiary')}`, - modalId: 'generic-achievement', - data: { - achievement: 'legendaryBestiary', - }, - }, - ACHIEVEMENT_SEASONAL_SPECIALIST: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementSeasonalSpecialist')}`, - modalId: 'generic-achievement', - data: { - achievement: 'seasonalSpecialist', - }, - }, - ACHIEVEMENT_VIOLETS_ARE_BLUE: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementVioletsAreBlue')}`, - modalId: 'generic-achievement', - data: { - achievement: 'violetsAreBlue', - }, - }, - ACHIEVEMENT_WILD_BLUE_YONDER: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementWildBlueYonder')}`, - modalId: 'generic-achievement', - data: { - achievement: 'wildBlueYonder', - }, - }, - ACHIEVEMENT_DOMESTICATED: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementDomesticated')}`, - modalId: 'generic-achievement', - data: { - achievement: 'domesticated', - }, - }, - ACHIEVEMENT_SHADY_CUSTOMER: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementShadyCustomer')}`, - modalId: 'generic-achievement', - data: { - achievement: 'shadyCustomer', - }, - }, - ACHIEVEMENT_SHADE_OF_IT_ALL: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementShadeOfItAll')}`, - modalId: 'generic-achievement', - data: { - achievement: 'shadeOfItAll', - }, - }, - ACHIEVEMENT_ZODIAC_ZOOKEEPER: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementZodiacZookeeper')}`, - modalId: 'generic-achievement', - data: { - achievement: 'zodiacZookeeper', - }, - }, - ACHIEVEMENT_BIRDS_OF_A_FEATHER: { - achievement: true, - label: $t => `${$t('achievement')}: ${$t('achievementBirdsOfAFeather')}`, - modalId: 'generic-achievement', - data: { - achievement: 'birdsOfAFeather', + achievement: 'mountColorAchievs', }, }, }; @@ -486,9 +266,6 @@ export default { loginIncentives, verifyUsername, genericAchievement, - lostMasterclasser, - mindOverMatter, - justAddWater, onboardingComplete, firstDrops, }, @@ -509,21 +286,30 @@ export default { const handledNotifications = {}; [ - 'GUILD_PROMPT', 'REBIRTH_ENABLED', 'WON_CHALLENGE', 'STREAK_ACHIEVEMENT', - 'ULTIMATE_GEAR_ACHIEVEMENT', 'REBIRTH_ACHIEVEMENT', 'GUILD_JOINED_ACHIEVEMENT', - 'CHALLENGE_JOINED_ACHIEVEMENT', 'INVITED_FRIEND_ACHIEVEMENT', 'NEW_CONTRIBUTOR_LEVEL', - 'CRON', 'LOGIN_INCENTIVE', 'ACHIEVEMENT_ALL_YOUR_BASE', 'ACHIEVEMENT_BACK_TO_BASICS', - 'GENERIC_ACHIEVEMENT', 'ACHIEVEMENT_PARTY_UP', 'ACHIEVEMENT_PARTY_ON', 'ACHIEVEMENT_BEAST_MASTER', - 'ACHIEVEMENT_MOUNT_MASTER', 'ACHIEVEMENT_TRIAD_BINGO', 'ACHIEVEMENT_DUST_DEVIL', 'ACHIEVEMENT_ARID_AUTHORITY', - 'ACHIEVEMENT_MONSTER_MAGUS', 'ACHIEVEMENT_UNDEAD_UNDERTAKER', 'ACHIEVEMENT_PRIMED_FOR_PAINTING', - 'ACHIEVEMENT_PEARLY_PRO', 'ACHIEVEMENT_TICKLED_PINK', 'ACHIEVEMENT_ROSY_OUTLOOK', 'ACHIEVEMENT', - 'ONBOARDING_COMPLETE', 'FIRST_DROPS', 'ACHIEVEMENT_BUG_BONANZA', 'ACHIEVEMENT_BARE_NECESSITIES', - 'ACHIEVEMENT_FRESHWATER_FRIENDS', 'ACHIEVEMENT_GOOD_AS_GOLD', 'ACHIEVEMENT_ALL_THAT_GLITTERS', - 'ACHIEVEMENT_BONE_COLLECTOR', 'ACHIEVEMENT_SKELETON_CREW', 'ACHIEVEMENT_SEEING_RED', - 'ACHIEVEMENT_RED_LETTER_DAY', 'ACHIEVEMENT_LEGENDARY_BESTIARY', 'ACHIEVEMENT_SEASONAL_SPECIALIST', - 'ACHIEVEMENT_VIOLETS_ARE_BLUE', 'ACHIEVEMENT_WILD_BLUE_YONDER', 'ACHIEVEMENT_DOMESTICATED', - 'ACHIEVEMENT_SHADY_CUSTOMER', 'ACHIEVEMENT_SHADE_OF_IT_ALL', 'ACHIEVEMENT_ZODIAC_ZOOKEEPER', - 'ACHIEVEMENT_BIRDS_OF_A_FEATHER', + // general notifications + 'CRON', + 'FIRST_DROPS', + 'GUILD_PROMPT', + 'LOGIN_INCENTIVE', + 'NEW_CONTRIBUTOR_LEVEL', + 'ONBOARDING_COMPLETE', + 'REBIRTH_ENABLED', + 'WON_CHALLENGE', + // achievement notifications + 'ACHIEVEMENT', + 'CHALLENGE_JOINED_ACHIEVEMENT', + 'GUILD_JOINED_ACHIEVEMENT', + 'INVITED_FRIEND_ACHIEVEMENT', + 'ACHIEVEMENT_PARTY_ON', + 'ACHIEVEMENT_PARTY_UP', + 'REBIRTH_ACHIEVEMENT', + 'STREAK_ACHIEVEMENT', + 'ULTIMATE_GEAR_ACHIEVEMENT', + 'ACHIEVEMENT_STABLE', + 'ACHIEVEMENT_QUESTS', + 'ACHIEVEMENT_ANIMAL_SET', + 'ACHIEVEMENT_PET_COLOR', + 'ACHIEVEMENT_MOUNT_COLOR', ].forEach(type => { handledNotifications[type] = true; }); @@ -921,57 +707,68 @@ export default { case 'WON_CHALLENGE': this.$root.$emit('habitica:won-challenge', notification); break; + case 'REBIRTH_ACHIEVEMENT': + this.playSound('Achievement_Unlocked'); + this.$root.$emit('bv::show::modal', 'rebirth'); + break; case 'STREAK_ACHIEVEMENT': this.text(`${this.$t('streaks')}: ${this.user.achievements.streak}`, () => { this.$root.$emit('bv::show::modal', 'streak'); }, this.user.preferences.suppressModals.streak); this.playSound('Achievement_Unlocked'); break; - case 'REBIRTH_ACHIEVEMENT': - this.playSound('Achievement_Unlocked'); - this.$root.$emit('bv::show::modal', 'rebirth'); - break; - case 'ULTIMATE_GEAR_ACHIEVEMENT': - case 'GUILD_JOINED_ACHIEVEMENT': - case 'CHALLENGE_JOINED_ACHIEVEMENT': - case 'INVITED_FRIEND_ACHIEVEMENT': case 'NEW_CONTRIBUTOR_LEVEL': - case 'ACHIEVEMENT_ALL_YOUR_BASE': - case 'ACHIEVEMENT_BACK_TO_BASICS': - case 'ACHIEVEMENT_DUST_DEVIL': - case 'ACHIEVEMENT_ARID_AUTHORITY': - case 'ACHIEVEMENT_PARTY_UP': + case 'CHALLENGE_JOINED_ACHIEVEMENT': + case 'GUILD_JOINED_ACHIEVEMENT': + case 'INVITED_FRIEND_ACHIEVEMENT': case 'ACHIEVEMENT_PARTY_ON': - case 'ACHIEVEMENT_BEAST_MASTER': - case 'ACHIEVEMENT_MOUNT_MASTER': - case 'ACHIEVEMENT_TRIAD_BINGO': - case 'ACHIEVEMENT_MONSTER_MAGUS': - case 'ACHIEVEMENT_UNDEAD_UNDERTAKER': - case 'ACHIEVEMENT_PRIMED_FOR_PAINTING': - case 'ACHIEVEMENT_PEARLY_PRO': - case 'ACHIEVEMENT_TICKLED_PINK': - case 'ACHIEVEMENT_ROSY_OUTLOOK': - case 'ACHIEVEMENT_BUG_BONANZA': - case 'ACHIEVEMENT_BARE_NECESSITIES': - case 'ACHIEVEMENT_FRESHWATER_FRIENDS': - case 'ACHIEVEMENT_GOOD_AS_GOLD': - case 'ACHIEVEMENT_ALL_THAT_GLITTERS': - case 'ACHIEVEMENT_BONE_COLLECTOR': - case 'ACHIEVEMENT_SKELETON_CREW': - case 'ACHIEVEMENT_SEEING_RED': - case 'ACHIEVEMENT_RED_LETTER_DAY': - case 'ACHIEVEMENT_LEGENDARY_BESTIARY': - case 'ACHIEVEMENT_SEASONAL_SPECIALIST': - case 'ACHIEVEMENT_VIOLETS_ARE_BLUE': - case 'ACHIEVEMENT_WILD_BLUE_YONDER': - case 'ACHIEVEMENT_DOMESTICATED': - case 'ACHIEVEMENT_SHADY_CUSTOMER': - case 'ACHIEVEMENT_SHADE_OF_IT_ALL': - case 'ACHIEVEMENT_ZODIAC_ZOOKEEPER': - case 'ACHIEVEMENT_BIRDS_OF_A_FEATHER': - case 'GENERIC_ACHIEVEMENT': + case 'ACHIEVEMENT_PARTY_UP': + case 'ULTIMATE_GEAR_ACHIEVEMENT': this.showNotificationWithModal(notification); break; + case 'ACHIEVEMENT_QUESTS': { + const { achievement } = notification.data; + const upperCaseAchievement = achievement.charAt(0).toUpperCase() + achievement.slice(1); + const achievementTitleKey = `achievement${upperCaseAchievement}`; + NOTIFICATIONS.ACHIEVEMENT_QUESTS.label = $t => `${$t('achievement')}: ${$t(achievementTitleKey)}`; + this.showNotificationWithModal(notification); + Vue.set(this.user.achievements, achievement, true); + break; + } + case 'ACHIEVEMENT_STABLE': { + const { achievement, achievementNotification } = notification.data; + NOTIFICATIONS.ACHIEVEMENT_STABLE.label = $t => `${$t('achievement')}: ${$t(achievementNotification)}`; + this.showNotificationWithModal(notification); + Vue.set(this.user.achievements, achievement, true); + break; + } + case 'ACHIEVEMENT_ANIMAL_SET': { + const { achievement } = notification.data; + const upperCaseAchievement = achievement.charAt(0).toUpperCase() + achievement.slice(1); + const achievementTitleKey = `achievement${upperCaseAchievement}`; + NOTIFICATIONS.ACHIEVEMENT_ANIMAL_SET.label = $t => `${$t('achievement')}: ${$t(achievementTitleKey)}`; + this.showNotificationWithModal(notification); + Vue.set(this.user.achievements, achievement, true); + break; + } + case 'ACHIEVEMENT_PET_COLOR': { + const { achievement } = notification.data; + const upperCaseAchievement = achievement.charAt(0).toUpperCase() + achievement.slice(1); + const achievementTitleKey = `achievement${upperCaseAchievement}`; + NOTIFICATIONS.ACHIEVEMENT_PET_COLOR.label = $t => `${$t('achievement')}: ${$t(achievementTitleKey)}`; + this.showNotificationWithModal(notification); + Vue.set(this.user.achievements, achievement, true); + break; + } + case 'ACHIEVEMENT_MOUNT_COLOR': { + const { achievement } = notification.data; + const upperCaseAchievement = achievement.charAt(0).toUpperCase() + achievement.slice(1); + const achievementTitleKey = `achievement${upperCaseAchievement}`; + NOTIFICATIONS.ACHIEVEMENT_MOUNT_COLOR.label = $t => `${$t('achievement')}: ${$t(achievementTitleKey)}`; + this.showNotificationWithModal(notification); + Vue.set(this.user.achievements, achievement, true); + break; + } case 'ACHIEVEMENT': { // generic achievement const { achievement } = notification.data; const upperCaseAchievement = achievement.charAt(0).toUpperCase() + achievement.slice(1); @@ -984,10 +781,6 @@ export default { Vue.set(this.user.achievements, achievement, true); break; } - case 'CRON': - // Not needed because it's shown already by the userHp and userMp watchers - // Keeping an empty block so that it gets read - break; case 'LOGIN_INCENTIVE': if (this.user.flags.tour.intro === this.TOUR_END && this.user.flags.welcomed) { this.notificationData = notification.data; diff --git a/website/client/src/components/payments/buttons/list.vue b/website/client/src/components/payments/buttons/list.vue index 5f06500061..d8e5f7952d 100644 --- a/website/client/src/components/payments/buttons/list.vue +++ b/website/client/src/components/payments/buttons/list.vue @@ -1,5 +1,6 @@ + {{ $t('choosePaymentMethod') }} diff --git a/website/client/src/components/settings/subscription.vue b/website/client/src/components/settings/subscription.vue index 1bc59834d0..5a02860fc8 100644 --- a/website/client/src/components/settings/subscription.vue +++ b/website/client/src/components/settings/subscription.vue @@ -450,10 +450,6 @@ background-color: $white; } - .subscribe-option { - border-bottom: 1px solid $gray-600; - } - .svg-amazon-pay { width: 208px; } diff --git a/website/client/src/components/userMenu/profile.vue b/website/client/src/components/userMenu/profile.vue index 83a7f308fb..1432a85f3a 100644 --- a/website/client/src/components/userMenu/profile.vue +++ b/website/client/src/components/userMenu/profile.vue @@ -969,7 +969,8 @@ export default { axios.post(`/api/v4/user/block/${this.user._id}`); }, openSendGemsModal () { - this.$root.$emit('habitica::send-gems', this.user); + this.$store.state.giftModalOptions.startingPage = 'buyGems'; + this.$root.$emit('habitica::send-gift', this.user); }, adminTurnOnShadowMuting () { if (!this.hero.flags) { diff --git a/website/client/src/store/index.js b/website/client/src/store/index.js index 67e5978306..dd3a0e28dd 100644 --- a/website/client/src/store/index.js +++ b/website/client/src/store/index.js @@ -124,6 +124,9 @@ export default function () { profileOptions: { startingPage: '', }, + giftModalOptions: { + startingPage: '', + }, rageModalOptions: { npc: '', }, diff --git a/website/common/locales/en/groups.json b/website/common/locales/en/groups.json index a73cd62a37..294abf1855 100644 --- a/website/common/locales/en/groups.json +++ b/website/common/locales/en/groups.json @@ -129,6 +129,7 @@ "sendGiftHeading": "Send Gift to <%= name %>", "sendGiftGemsBalance": "From <%= number %> Gems", "sendGiftCost": "Total: $<%= cost %> USD", + "sendGiftTotal": "Total:", "sendGiftFromBalance": "From Balance", "sendGiftPurchase": "Purchase", "sendGiftMessagePlaceholder": "Personal message (optional)", diff --git a/website/common/locales/en/questsContent.json b/website/common/locales/en/questsContent.json index c969c30ef0..d0fce2dd93 100644 --- a/website/common/locales/en/questsContent.json +++ b/website/common/locales/en/questsContent.json @@ -780,7 +780,7 @@ "rockingReptilesNotes": "Contains 'The Insta-Gator,' 'The Serpent of Distraction,' and 'The Veloci-Rapper.' Available until September 30.", "delightfulDinosText": "Delightful Dinos Quest Bundle", - "delightfulDinosNotes": "Contains 'The Pterror-dactyl,' 'The Trampling Triceratops,' and 'The Dinosaur Unearthed.' Available until November 30.", + "delightfulDinosNotes": "Contains 'The Pterror-dactyl,' 'The Trampling Triceratops,' and 'The Dinosaur Unearthed.' Available until May 31.", "questAmberText": "The Amber Alliance", "questAmberNotes": "You’re sitting in the Tavern with @beffymaroo and @-Tyr- when @Vikte bursts through the door and excitedly tells you about the rumors of another type of Magic Hatching Potion hidden in the Taskwoods. Having completed your Dailies, the three of you immediately agree to help @Vikte on their search. After all, what’s the harm in a little adventure?After walking through the Taskwoods for hours, you’re beginning to regret joining such a wild chase. You’re about to head home, when you hear a surprised yelp and turn to see a huge lizard with shiny amber scales coiled around a tree, clutching @Vikte in her claws. @beffymaroo reaches for her sword.“Wait!” cries @-Tyr-. “It’s the Trerezin! She’s not dangerous, just dangerously clingy!”", diff --git a/website/common/locales/en/subscriber.json b/website/common/locales/en/subscriber.json index a3dfea7c61..d4da76797a 100644 --- a/website/common/locales/en/subscriber.json +++ b/website/common/locales/en/subscriber.json @@ -3,6 +3,10 @@ "subscriptions": "Subscriptions", "viewSubscriptions": "View Subscriptions", "sendGems": "Send Gems", + "howManyGemsPurchase": "How many Gems would you like to purchase?", + "howManyGemsSend":"How many Gems would you like to send?", + "needToPurchaseGems": "Need to purchase Gems as a gift?", + "wantToSendOwnGems": "Want to send your own Gems?", "buyGemsGold": "Buy Gems with Gold", "mustSubscribeToPurchaseGems": "Must subscribe to purchase gems with GP", "reachedGoldToGemCapQuantity": "Your requested amount <%= quantity %> exceeds the amount you can buy for this month (<%= convCap %>). The full amount becomes available within the first three days of each month. Thanks for subscribing!", @@ -196,5 +200,6 @@ "needToUpdateCard": "Need to update your card?", "readyToResubscribe": "Are you ready to resubscribe?", "cancelYourSubscription": "Cancel your subscription?", - "cancelSubAlternatives": "If you're having technical problems or Habitica doesn't seem to be working out for you, please consider contacting us. We want to help you get the most from Habitica." + "cancelSubAlternatives": "If you're having technical problems or Habitica doesn't seem to be working out for you, please consider contacting us. We want to help you get the most from Habitica.", + "sendAGift": "Send Gift" } diff --git a/website/common/script/content/achievements.js b/website/common/script/content/achievements.js index b8f7ba8aab..f4746f86b1 100644 --- a/website/common/script/content/achievements.js +++ b/website/common/script/content/achievements.js @@ -55,27 +55,30 @@ const seasonalSpellAchievs = { }; Object.assign(achievementsData, seasonalSpellAchievs); -const masterAchievs = { +const stableAchievs = { beastMaster: { icon: 'achievement-rat', titleKey: 'beastMasterName', textKey: 'beastMasterText', text2Key: 'beastMasterText2', + notificationText: 'beastAchievement', }, mountMaster: { icon: 'achievement-wolf', titleKey: 'mountMasterName', textKey: 'mountMasterText', text2Key: 'mountMasterText2', + notificationText: 'mountAchievement', }, triadBingo: { icon: 'achievement-triadbingo', titleKey: 'triadBingoName', textKey: 'triadBingoText', text2Key: 'triadBingoText2', + notificationText: 'triadBingoAchievement', }, }; -Object.assign(achievementsData, masterAchievs); +Object.assign(achievementsData, stableAchievs); const basicAchievs = { partyUp: { @@ -122,146 +125,173 @@ const basicAchievs = { titleKey: 'invitedFriend', textKey: 'invitedFriendText', }, +}; +Object.assign(achievementsData, basicAchievs); + +const questSeriesAchievs = { lostMasterclasser: { icon: 'achievement-lostMasterclasser', titleKey: 'achievementLostMasterclasser', textKey: 'achievementLostMasterclasserText', }, - mindOverMatter: { - icon: 'achievement-mindOverMatter', - titleKey: 'achievementMindOverMatter', - textKey: 'achievementMindOverMatterText', - }, - justAddWater: { - icon: 'achievement-justAddWater', - titleKey: 'achievementJustAddWater', - textKey: 'achievementJustAddWaterText', - }, - backToBasics: { - icon: 'achievement-backToBasics', - titleKey: 'achievementBackToBasics', - textKey: 'achievementBackToBasicsText', - }, - allYourBase: { - icon: 'achievement-allYourBase', - titleKey: 'achievementAllYourBase', - textKey: 'achievementAllYourBaseText', - }, - dustDevil: { - icon: 'achievement-dustDevil', - titleKey: 'achievementDustDevil', - textKey: 'achievementDustDevilText', - }, - aridAuthority: { - icon: 'achievement-aridAuthority', - titleKey: 'achievementAridAuthority', - textKey: 'achievementAridAuthorityText', - }, - monsterMagus: { - icon: 'achievement-monsterMagus', - titleKey: 'achievementMonsterMagus', - textKey: 'achievementMonsterMagusText', - }, - undeadUndertaker: { - icon: 'achievement-undeadUndertaker', - titleKey: 'achievementUndeadUndertaker', - textKey: 'achievementUndeadUndertakerText', - }, - primedForPainting: { - icon: 'achievement-primedForPainting', - titleKey: 'achievementPrimedForPainting', - textKey: 'achievementPrimedForPaintingText', - }, - pearlyPro: { - icon: 'achievement-pearlyPro', - titleKey: 'achievementPearlyPro', - textKey: 'achievementPearlyProText', - }, - tickledPink: { - icon: 'achievement-tickledPink', - titleKey: 'achievementTickledPink', - textKey: 'achievementTickledPinkText', - }, - rosyOutlook: { - icon: 'achievement-rosyOutlook', - titleKey: 'achievementRosyOutlook', - textKey: 'achievementRosyOutlookText', - }, - bugBonanza: { - icon: 'achievement-bugBonanza', - titleKey: 'achievementBugBonanza', - textKey: 'achievementBugBonanzaText', - }, bareNecessities: { icon: 'achievement-bareNecessities', titleKey: 'achievementBareNecessities', textKey: 'achievementBareNecessitiesText', }, + bugBonanza: { + icon: 'achievement-bugBonanza', + titleKey: 'achievementBugBonanza', + textKey: 'achievementBugBonanzaText', + }, freshwaterFriends: { icon: 'achievement-freshwaterFriends', titleKey: 'achievementFreshwaterFriends', textKey: 'achievementFreshwaterFriendsText', }, - goodAsGold: { - icon: 'achievement-goodAsGold', - titleKey: 'achievementGoodAsGold', - textKey: 'achievementGoodAsGoldText', + justAddWater: { + icon: 'achievement-justAddWater', + titleKey: 'achievementJustAddWater', + textKey: 'achievementJustAddWaterText', }, - allThatGlitters: { - icon: 'achievement-allThatGlitters', - titleKey: 'achievementAllThatGlitters', - textKey: 'achievementAllThatGlittersText', - }, - boneCollector: { - icon: 'achievement-boneCollector', - titleKey: 'achievementBoneCollector', - textKey: 'achievementBoneCollectorText', - }, - skeletonCrew: { - icon: 'achievement-skeletonCrew', - titleKey: 'achievementSkeletonCrew', - textKey: 'achievementSkeletonCrewText', - }, - seeingRed: { - icon: 'achievement-seeingRed', - titleKey: 'achievementSeeingRed', - textKey: 'achievementSeeingRedText', - }, - redLetterDay: { - icon: 'achievement-redLetterDay', - titleKey: 'achievementRedLetterDay', - textKey: 'achievementRedLetterDayText', - }, - legendaryBestiary: { - icon: 'achievement-legendaryBestiary', - titleKey: 'achievementLegendaryBestiary', - textKey: 'achievementLegendaryBestiaryText', + mindOverMatter: { + icon: 'achievement-mindOverMatter', + titleKey: 'achievementMindOverMatter', + textKey: 'achievementMindOverMatterText', }, seasonalSpecialist: { icon: 'achievement-seasonalSpecialist', titleKey: 'achievementSeasonalSpecialist', textKey: 'achievementSeasonalSpecialistText', }, - violetsAreBlue: { - icon: 'achievement-violetsAreBlue', - titleKey: 'achievementVioletsAreBlue', - textKey: 'achievementVioletsAreBlueText', +}; +Object.assign(achievementsData, questSeriesAchievs); + +const animalSetAchievs = { + legendaryBestiary: { + icon: 'achievement-legendaryBestiary', + titleKey: 'achievementLegendaryBestiary', + textKey: 'achievementLegendaryBestiaryText', }, - wildBlueYonder: { - icon: 'achievement-wildBlueYonder', - titleKey: 'achievementWildBlueYonder', - textKey: 'achievementWildBlueYonderText', + birdsOfAFeather: { + icon: 'achievement-birdsOfAFeather', + titleKey: 'achievementBirdsOfAFeather', + textKey: 'achievementBirdsOfAFeatherText', }, domesticated: { icon: 'achievement-domesticated', titleKey: 'achievementDomesticated', textKey: 'achievementDomesticatedText', }, + zodiacZookeeper: { + icon: 'achievement-zodiac', + titleKey: 'achievementZodiacZookeeper', + textKey: 'achievementZodiacZookeeperText', + }, +}; +Object.assign(achievementsData, animalSetAchievs); + +const petColorAchievs = { + backToBasics: { + icon: 'achievement-backToBasics', + titleKey: 'achievementBackToBasics', + textKey: 'achievementBackToBasicsText', + }, + dustDevil: { + icon: 'achievement-dustDevil', + titleKey: 'achievementDustDevil', + textKey: 'achievementDustDevilText', + }, + monsterMagus: { + icon: 'achievement-monsterMagus', + titleKey: 'achievementMonsterMagus', + textKey: 'achievementMonsterMagusText', + }, + primedForPainting: { + icon: 'achievement-primedForPainting', + titleKey: 'achievementPrimedForPainting', + textKey: 'achievementPrimedForPaintingText', + }, + tickledPink: { + icon: 'achievement-tickledPink', + titleKey: 'achievementTickledPink', + textKey: 'achievementTickledPinkText', + }, + goodAsGold: { + icon: 'achievement-goodAsGold', + titleKey: 'achievementGoodAsGold', + textKey: 'achievementGoodAsGoldText', + }, + boneCollector: { + icon: 'achievement-boneCollector', + titleKey: 'achievementBoneCollector', + textKey: 'achievementBoneCollectorText', + }, + seeingRed: { + icon: 'achievement-seeingRed', + titleKey: 'achievementSeeingRed', + textKey: 'achievementSeeingRedText', + modalTextKey: 'achievementSeeingRedModalText', + }, + violetsAreBlue: { + icon: 'achievement-violetsAreBlue', + titleKey: 'achievementVioletsAreBlue', + textKey: 'achievementVioletsAreBlueText', + }, shadyCustomer: { icon: 'achievement-shadyCustomer', titleKey: 'achievementShadyCustomer', textKey: 'achievementShadyCustomerText', }, +}; +Object.assign(achievementsData, petColorAchievs); + +const mountColorAchievs = { + allYourBase: { + icon: 'achievement-allYourBase', + titleKey: 'achievementAllYourBase', + textKey: 'achievementAllYourBaseText', + }, + aridAuthority: { + icon: 'achievement-aridAuthority', + titleKey: 'achievementAridAuthority', + textKey: 'achievementAridAuthorityText', + }, + undeadUndertaker: { + icon: 'achievement-undeadUndertaker', + titleKey: 'achievementUndeadUndertaker', + textKey: 'achievementUndeadUndertakerText', + }, + pearlyPro: { + icon: 'achievement-pearlyPro', + titleKey: 'achievementPearlyPro', + textKey: 'achievementPearlyProText', + }, + rosyOutlook: { + icon: 'achievement-rosyOutlook', + titleKey: 'achievementRosyOutlook', + textKey: 'achievementRosyOutlookText', + }, + allThatGlitters: { + icon: 'achievement-allThatGlitters', + titleKey: 'achievementAllThatGlitters', + textKey: 'achievementAllThatGlittersText', + }, + skeletonCrew: { + icon: 'achievement-skeletonCrew', + titleKey: 'achievementSkeletonCrew', + textKey: 'achievementSkeletonCrewText', + }, + redLetterDay: { + icon: 'achievement-redLetterDay', + titleKey: 'achievementRedLetterDay', + textKey: 'achievementRedLetterDayText', + }, + wildBlueYonder: { + icon: 'achievement-wildBlueYonder', + titleKey: 'achievementWildBlueYonder', + textKey: 'achievementWildBlueYonderText', + }, shadeOfItAll: { icon: 'achievement-shadeOfItAll', titleKey: 'achievementShadeOfItAll', @@ -278,7 +308,7 @@ const basicAchievs = { textKey: 'achievementBirdsOfAFeatherText', }, }; -Object.assign(achievementsData, basicAchievs); +Object.assign(achievementsData, mountColorAchievs); const onboardingAchievs = { createdTask: { diff --git a/website/common/script/content/bundles.js b/website/common/script/content/bundles.js index bfb7429b5a..5179cb1cd5 100644 --- a/website/common/script/content/bundles.js +++ b/website/common/script/content/bundles.js @@ -220,7 +220,7 @@ const bundles = { 'trex_undead', ], canBuy () { - return moment().isBetween('2019-11-14', '2019-12-02'); + return moment().isBetween('2022-05-16', '2022-05-31'); }, type: 'quests', value: 7, diff --git a/website/common/script/content/constants/animalColorAchievements.js b/website/common/script/content/constants/animalColorAchievements.js index d16883d612..62eafd83c2 100644 --- a/website/common/script/content/constants/animalColorAchievements.js +++ b/website/common/script/content/constants/animalColorAchievements.js @@ -2,72 +2,72 @@ const ANIMAL_COLOR_ACHIEVEMENTS = [ { color: 'Base', petAchievement: 'backToBasics', - petNotificationType: 'ACHIEVEMENT_BACK_TO_BASICS', + petNotificationType: 'ACHIEVEMENT_PET_COLOR', mountAchievement: 'allYourBase', - mountNotificationType: 'ACHIEVEMENT_ALL_YOUR_BASE', + mountNotificationType: 'ACHIEVEMENT_MOUNT_COLOR', }, { color: 'Desert', petAchievement: 'dustDevil', - petNotificationType: 'ACHIEVEMENT_DUST_DEVIL', + petNotificationType: 'ACHIEVEMENT_PET_COLOR', mountAchievement: 'aridAuthority', - mountNotificationType: 'ACHIEVEMENT_ARID_AUTHORITY', + mountNotificationType: 'ACHIEVEMENT_MOUNT_COLOR', }, { color: 'Zombie', petAchievement: 'monsterMagus', - petNotificationType: 'ACHIEVEMENT_MONSTER_MAGUS', + petNotificationType: 'ACHIEVEMENT_PET_COLOR', mountAchievement: 'undeadUndertaker', - mountNotificationType: 'ACHIEVEMENT_UNDEAD_UNDERTAKER', + mountNotificationType: 'ACHIEVEMENT_MOUNT_COLOR', }, { color: 'White', petAchievement: 'primedForPainting', - petNotificationType: 'ACHIEVEMENT_PRIMED_FOR_PAINTING', + petNotificationType: 'ACHIEVEMENT_PET_COLOR', mountAchievement: 'pearlyPro', - mountNotificationType: 'ACHIEVEMENT_PEARLY_PRO', + mountNotificationType: 'ACHIEVEMENT_MOUNT_COLOR', }, { color: 'CottonCandyPink', petAchievement: 'tickledPink', - petNotificationType: 'ACHIEVEMENT_TICKLED_PINK', + petNotificationType: 'ACHIEVEMENT_PET_COLOR', mountAchievement: 'rosyOutlook', - mountNotificationType: 'ACHIEVEMENT_ROSY_OUTLOOK', + mountNotificationType: 'ACHIEVEMENT_MOUNT_COLOR', }, { color: 'Golden', petAchievement: 'goodAsGold', - petNotificationType: 'ACHIEVEMENT_GOOD_AS_GOLD', + petNotificationType: 'ACHIEVEMENT_PET_COLOR', mountAchievement: 'allThatGlitters', - mountNotificationType: 'ACHIEVEMENT_ALL_THAT_GLITTERS', + mountNotificationType: 'ACHIEVEMENT_MOUNT_COLOR', }, { color: 'Skeleton', petAchievement: 'boneCollector', - petNotificationType: 'ACHIEVEMENT_BONE_COLLECTOR', + petNotificationType: 'ACHIEVEMENT_PET_COLOR', mountAchievement: 'skeletonCrew', - mountNotificationType: 'ACHIEVEMENT_SKELETON_CREW', + mountNotificationType: 'ACHIEVEMENT_MOUNT_COLOR', }, { color: 'Red', petAchievement: 'seeingRed', - petNotificationType: 'ACHIEVEMENT_SEEING_RED', + petNotificationType: 'ACHIEVEMENT_PET_COLOR', mountAchievement: 'redLetterDay', - mountNotificationType: 'ACHIEVEMENT_RED_LETTER_DAY', + mountNotificationType: 'ACHIEVEMENT_MOUNT_COLOR', }, { color: 'CottonCandyBlue', petAchievement: 'violetsAreBlue', - petNotificationType: 'ACHIEVEMENT_VIOLETS_ARE_BLUE', + petNotificationType: 'ACHIEVEMENT_PET_COLOR', mountAchievement: 'wildBlueYonder', - mountNotificationType: 'ACHIEVEMENT_WILD_BLUE_YONDER', + mountNotificationType: 'ACHIEVEMENT_MOUNT_COLOR', }, { color: 'Shade', petAchievement: 'shadyCustomer', - petNotificationType: 'ACHIEVEMENT_SHADY_CUSTOMER', + petNotificationType: 'ACHIEVEMENT_PET_COLOR', mountAchievement: 'shadeOfItAll', - mountNotificationType: 'ACHIEVEMENT_SHADE_OF_IT_ALL', + mountNotificationType: 'ACHIEVEMENT_MOUNT_COLOR', }, ]; diff --git a/website/common/script/content/constants/animalSetAchievements.js b/website/common/script/content/constants/animalSetAchievements.js index 55e89301c4..25f9018983 100644 --- a/website/common/script/content/constants/animalSetAchievements.js +++ b/website/common/script/content/constants/animalSetAchievements.js @@ -9,7 +9,7 @@ const ANIMAL_SET_ACHIEVEMENTS = { 'Unicorn', ], achievementKey: 'legendaryBestiary', - notificationType: 'ACHIEVEMENT_LEGENDARY_BESTIARY', + notificationType: 'ACHIEVEMENT_ANIMAL_SET', }, birdsOfAFeather: { type: 'pet', @@ -24,7 +24,7 @@ const ANIMAL_SET_ACHIEVEMENTS = { 'Peacock', ], achievementKey: 'birdsOfAFeather', - notificationType: 'ACHIEVEMENT_BIRDS_OF_A_FEATHER', + notificationType: 'ACHIEVEMENT_ANIMAL_SET', }, domesticated: { type: 'pet', @@ -39,7 +39,7 @@ const ANIMAL_SET_ACHIEVEMENTS = { 'Cow', ], achievementKey: 'domesticated', - notificationType: 'ACHIEVEMENT_DOMESTICATED', + notificationType: 'ACHIEVEMENT_ANIMAL_SET', }, zodiacZookeeper: { type: 'pet', @@ -53,12 +53,12 @@ const ANIMAL_SET_ACHIEVEMENTS = { 'Monkey', 'Rooster', 'Wolf', - 'TigerCub', + 'Tiger', 'FlyingPig', 'Dragon', ], achievementKey: 'zodiacZookeeper', - notificationType: 'ACHIEVEMENT_ZODIAC_ZOOKEEPER', + notificationType: 'ACHIEVEMENT_ANIMAL_SET', }, }; diff --git a/website/common/script/content/constants/events.js b/website/common/script/content/constants/events.js index 32fdcf3734..9ed33d8edb 100644 --- a/website/common/script/content/constants/events.js +++ b/website/common/script/content/constants/events.js @@ -10,11 +10,17 @@ const gemsPromo = { export const EVENTS = { noCurrentEvent: { - start: '2022-04-30T20:00-05:00', + start: '2022-05-31T20:00-04:00', end: '2022-06-30T20:00-05:00', season: 'normal', npcImageSuffix: '', }, + potions202205: { + start: '2022-05-17T08:00-04:00', + end: '2022-05-31T20:00-04:00', + season: 'normal', + npcImageSuffix: '', + }, spring2022: { start: '2022-03-22T08:00-05:00', end: '2022-04-30T20:00-05:00', diff --git a/website/common/script/content/constants/index.js b/website/common/script/content/constants/index.js index 044bb68039..b9200cac6b 100644 --- a/website/common/script/content/constants/index.js +++ b/website/common/script/content/constants/index.js @@ -32,6 +32,7 @@ export { default as SEASONAL_SETS } from './seasonalSets'; export { default as ANIMAL_COLOR_ACHIEVEMENTS } from './animalColorAchievements'; export { default as ANIMAL_SET_ACHIEVEMENTS } from './animalSetAchievements'; export { default as QUEST_SERIES_ACHIEVEMENTS } from './questSeriesAchievements'; +export { default as STABLE_ACHIEVEMENTS } from './stableAchievements'; export { default as ITEM_LIST } from './itemList'; export { default as QUEST_SERIES } from '../quests/series'; export { default as QUEST_MASTERCLASSER } from '../quests/masterclasser'; diff --git a/website/common/script/content/constants/questSeriesAchievements.js b/website/common/script/content/constants/questSeriesAchievements.js index f9ca87a489..a91334a172 100644 --- a/website/common/script/content/constants/questSeriesAchievements.js +++ b/website/common/script/content/constants/questSeriesAchievements.js @@ -17,10 +17,21 @@ const QUEST_SERIES_ACHIEVEMENTS = { 'lostMasterclasser3', 'lostMasterclasser4', ], - mindOverMatter: [ - 'rock', - 'slime', - 'yarn', + bareNecessities: [ + 'monkey', + 'sloth', + 'treeling', + ], + bugBonanza: [ + 'beetle', + 'butterfly', + 'snail', + 'spider', + ], + freshwaterFriends: [ + 'axolotl', + 'frog', + 'hippo', ], justAddWater: [ 'octopus', @@ -32,21 +43,10 @@ const QUEST_SERIES_ACHIEVEMENTS = { 'seaserpent', 'dolphin', ], - bugBonanza: [ - 'beetle', - 'butterfly', - 'snail', - 'spider', - ], - bareNecessities: [ - 'monkey', - 'sloth', - 'treeling', - ], - freshwaterFriends: [ - 'axolotl', - 'frog', - 'hippo', + mindOverMatter: [ + 'rock', + 'slime', + 'yarn', ], seasonalSpecialist: [ 'egg', diff --git a/website/common/script/content/constants/stableAchievements.js b/website/common/script/content/constants/stableAchievements.js new file mode 100644 index 0000000000..b069ea0dbd --- /dev/null +++ b/website/common/script/content/constants/stableAchievements.js @@ -0,0 +1,16 @@ +const STABLE_ACHIEVEMENTS = { + ACHIEVEMENT_BEAST_MASTER: { + masterAchievement: 'beastMasterName', + masterNotificationType: 'ACHIEVEMENT_STABLE', + }, + ACHIEVEMENT_MOUNT_MASTER: { + masterAchievement: 'mountMasterName', + masterNotificationType: 'ACHIEVEMENT_STABLE', + }, + ACHIEVEMENT_TRIAD_BINGO: { + masterAchievement: 'triadBingoName', + masterNotificationType: 'ACHIEVEMENT_STABLE', + }, +}; + +export default STABLE_ACHIEVEMENTS; diff --git a/website/common/script/content/hatching-potions.js b/website/common/script/content/hatching-potions.js index f50ef98f7f..0f7f67d859 100644 --- a/website/common/script/content/hatching-potions.js +++ b/website/common/script/content/hatching-potions.js @@ -96,13 +96,13 @@ const premium = { value: 2, text: t('hatchingPotionFloral'), limited: true, - event: EVENTS.potions202105, + event: EVENTS.potions202205, _addlNotes: t('eventAvailabilityReturning', { availableDate: t('dateEndMay'), - previousDate: t('mayYYYY', { year: 2019 }), + previousDate: t('mayYYYY', { year: 2021 }), }), canBuy () { - return moment().isBefore(EVENTS.potions202105.end); + return moment().isBefore(EVENTS.potions202205.end); }, }, Aquatic: { @@ -297,12 +297,13 @@ const premium = { value: 2, text: t('hatchingPotionSunshine'), limited: true, + event: EVENTS.potions202205, _addlNotes: t('eventAvailabilityReturning', { availableDate: t('dateEndMay'), - previousDate: t('mayYYYY', { year: 2019 }), + previousDate: t('mayYYYY', { year: 2020 }), }), canBuy () { - return moment().isBefore('2020-06-02'); + return moment().isBefore(EVENTS.potions202205.end); }, }, Bronze: { diff --git a/website/common/script/content/index.js b/website/common/script/content/index.js index 9d28943e22..474a9f64db 100644 --- a/website/common/script/content/index.js +++ b/website/common/script/content/index.js @@ -12,6 +12,7 @@ import { QUEST_SERIES_ACHIEVEMENTS, ANIMAL_COLOR_ACHIEVEMENTS, ANIMAL_SET_ACHIEVEMENTS, + STABLE_ACHIEVEMENTS, } from './constants'; import achievements from './achievements'; @@ -41,6 +42,7 @@ api.achievements = achievements; api.questSeriesAchievements = QUEST_SERIES_ACHIEVEMENTS; api.animalColorAchievements = ANIMAL_COLOR_ACHIEVEMENTS; api.animalSetAchievements = ANIMAL_SET_ACHIEVEMENTS; +api.stableAchievements = STABLE_ACHIEVEMENTS; api.quests = quests; api.questsByLevel = questsByLevel; diff --git a/website/common/script/content/shop-featuredItems.js b/website/common/script/content/shop-featuredItems.js index 7cecd12cf3..8d6bb0ec14 100644 --- a/website/common/script/content/shop-featuredItems.js +++ b/website/common/script/content/shop-featuredItems.js @@ -1,11 +1,11 @@ import moment from 'moment'; -import { EVENTS } from './constants'; +// import { EVENTS } from './constants'; // Magic Hatching Potions are configured like this: // type: 'premiumHatchingPotion', // note no "s" at the end // path: 'premiumHatchingPotions.Rainbow', const featuredItems = { market () { - if (moment().isBefore(EVENTS.spring2022.end)) { + if (moment().isBefore('2022-05-31T20:00-04:00')) { return [ { type: 'armoire', @@ -13,15 +13,15 @@ const featuredItems = { }, { type: 'premiumHatchingPotion', - path: 'premiumHatchingPotions.Shimmer', + path: 'premiumHatchingPotions.Sunshine', }, { type: 'premiumHatchingPotion', - path: 'premiumHatchingPotions.Celestial', + path: 'premiumHatchingPotions.Floral', }, { - type: 'premiumHatchingPotion', - path: 'premiumHatchingPotions.PolkaDot', + type: 'hatchingPotions', + path: 'hatchingPotions.Golden', }, ]; } @@ -45,19 +45,19 @@ const featuredItems = { ]; }, quests () { - if (moment().isBefore('2022-03-31T20:00-04:00')) { + if (moment().isBefore('2022-05-31T20:00-04:00')) { return [ { type: 'bundles', - path: 'bundles.cuddleBuddies', + path: 'bundles.delightfulDinos', }, { type: 'quests', - path: 'quests.egg', + path: 'quests.alligator', }, { type: 'quests', - path: 'quests.ghost_stag', + path: 'quests.turtle', }, ]; } diff --git a/website/common/script/libs/shops.js b/website/common/script/libs/shops.js index a278069192..662f461750 100644 --- a/website/common/script/libs/shops.js +++ b/website/common/script/libs/shops.js @@ -108,12 +108,6 @@ shops.checkMarketGearLocked = function checkMarketGearLocked (user, items) { gear.locked = true; } - if (Boolean(gear.specialClass) && Boolean(gear.set)) { - const currentSet = gear.set === seasonalShopConfig.pinnedSets[gear.specialClass]; - - gear.locked = currentSet && user.stats.class !== gear.specialClass; - } - if (gear.canOwn) { gear.locked = !gear.canOwn(user); } @@ -124,6 +118,12 @@ shops.checkMarketGearLocked = function checkMarketGearLocked (user, items) { gear.locked = true; } + if (Boolean(gear.specialClass) && Boolean(gear.set)) { + const currentSet = gear.set === seasonalShopConfig.pinnedSets[gear.specialClass]; + + gear.locked = currentSet && user.stats.class !== gear.specialClass; + } + gear.owned = itemOwned; // @TODO: I'm not sure what the logic for locking is supposed to be diff --git a/website/common/script/ops/buy/buyMarketGear.js b/website/common/script/ops/buy/buyMarketGear.js index 8c87ef6fa2..83e638360d 100644 --- a/website/common/script/ops/buy/buyMarketGear.js +++ b/website/common/script/ops/buy/buyMarketGear.js @@ -28,7 +28,7 @@ export class BuyMarketGearOperation extends AbstractGoldItemOperation { // eslin const checkSpecialClass = item.klass === 'special' && item.specialClass && item.specialClass !== user.stats.class; // check for different class gear - if (checkKlass || checkSpecialClass) { + if ((checkKlass || checkSpecialClass) && user.items.gear.owned[item.key] !== false) { throw new NotAuthorized(this.i18n('cannotBuyItem')); } } diff --git a/website/common/script/ops/feed.js b/website/common/script/ops/feed.js index 79bd7489cb..9170b728dc 100644 --- a/website/common/script/ops/feed.js +++ b/website/common/script/ops/feed.js @@ -130,6 +130,7 @@ export default function feed (user, req = {}, analytics) { if (user.addNotification) { const achievementString = `achievement${upperFirst(achievement.mountAchievement)}`; user.addNotification(achievement.mountNotificationType, { + label: `${'achievement'}: ${achievementString}`, achievement: achievement.mountAchievement, message: `${i18n.t('modalAchievement')} ${i18n.t(achievementString)}`, modalText: i18n.t(`${achievementString}ModalText`), diff --git a/website/common/script/ops/hatch.js b/website/common/script/ops/hatch.js index 542312729c..0fec8d3833 100644 --- a/website/common/script/ops/hatch.js +++ b/website/common/script/ops/hatch.js @@ -72,6 +72,7 @@ export default function hatch (user, req = {}, analytics) { if (user.addNotification) { const achievementString = `achievement${upperFirst(achievement.petAchievement)}`; user.addNotification(achievement.petNotificationType, { + label: `${'achievement'}: ${achievementString}`, achievement: achievement.petAchievement, message: `${i18n.t('modalAchievement')} ${i18n.t(achievementString)}`, modalText: i18n.t(`${achievementString}ModalText`), @@ -100,6 +101,7 @@ export default function hatch (user, req = {}, analytics) { if (user.addNotification) { const achievementString = `achievement${upperFirst(achievement.achievementKey)}`; user.addNotification(achievement.notificationType, { + label: `${'achievement'}: ${achievementString}`, achievement: achievement.achievementKey, message: `${i18n.t('modalAchievement')} ${i18n.t(achievementString)}`, modalText: i18n.t(`${achievementString}ModalText`), diff --git a/website/common/script/ops/scoreTask.js b/website/common/script/ops/scoreTask.js index c450e34756..0912b7d5a5 100644 --- a/website/common/script/ops/scoreTask.js +++ b/website/common/script/ops/scoreTask.js @@ -420,5 +420,5 @@ export default function scoreTask (options = {}, req = {}, analytics) { checkOnboardingStatus(user, req, analytics); } - return [delta]; + return delta; } diff --git a/website/server/libs/xmlMarshaller.js b/website/server/libs/xmlMarshaller.js index e2a4b14c40..c1cbbd1788 100644 --- a/website/server/libs/xmlMarshaller.js +++ b/website/server/libs/xmlMarshaller.js @@ -23,7 +23,33 @@ export function marshallUserData (userData) { type: i.type, })); - return js2xml.parse('user', userData, { + const copyUserData = JSON.parse(JSON.stringify(userData)); + + const newPurchased = {}; + if (userData.purchased != null) { + for (const itemType in userData.purchased) { + if (userData.purchased[itemType] != null) { + if (typeof userData.purchased[itemType] === 'object') { + const fixedData = []; + for (const item in userData.purchased[itemType]) { + if (item != null) { + if (typeof userData.purchased[itemType][item] === 'object') { + fixedData.push({ item: userData.purchased[itemType][item] }); + } else { + fixedData.push(item); + } + } + } + newPurchased[itemType] = fixedData; + } else { + newPurchased[itemType] = userData.purchased[itemType]; + } + } + } + copyUserData.purchased = newPurchased; + } + + return js2xml.parse('user', copyUserData, { cdataInvalidChars: true, replaceInvalidChars: true, declaration: { diff --git a/website/server/models/group.js b/website/server/models/group.js index 8f915e92a8..b2e9edc6fd 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -1002,10 +1002,9 @@ schema.methods.finishQuest = async function finishQuest (quest) { const questAchievementUpdate = { $set: {}, $push: {} }; questAchievementUpdate.$set[`achievements.${achievement}`] = true; const achievementTitleCase = `${achievement.slice(0, 1).toUpperCase()}${achievement.slice(1, achievement.length)}`; - const achievementSnakeCase = `ACHIEVEMENT_${_.snakeCase(achievement).toUpperCase()}`; questAchievementUpdate.$push = { notifications: new UserNotification({ - type: achievementSnakeCase, + type: 'ACHIEVEMENT_QUESTS', data: { achievement, message: `${shared.i18n.t('modalAchievement')} ${shared.i18n.t(`achievement${achievementTitleCase}`)}`, diff --git a/website/server/models/user/hooks.js b/website/server/models/user/hooks.js index 597db34706..93a4df68b9 100644 --- a/website/server/models/user/hooks.js +++ b/website/server/models/user/hooks.js @@ -258,7 +258,13 @@ schema.pre('save', true, function preSaveUser (next, done) { && this.achievements.beastMaster !== true ) { this.achievements.beastMaster = true; - this.addNotification('ACHIEVEMENT_BEAST_MASTER'); + this.addNotification( + 'ACHIEVEMENT_STABLE', + { + achievement: 'beastMaster', + achievementNotification: 'beastAchievement', + }, + ); } // Determines if Mount Master should be awarded @@ -269,7 +275,13 @@ schema.pre('save', true, function preSaveUser (next, done) { && this.achievements.mountMaster !== true ) { this.achievements.mountMaster = true; - this.addNotification('ACHIEVEMENT_MOUNT_MASTER'); + this.addNotification( + 'ACHIEVEMENT_STABLE', + { + achievement: 'mountMaster', + achievementNotification: 'mountAchievement', + }, + ); } // Determines if Triad Bingo should be awarded @@ -281,7 +293,13 @@ schema.pre('save', true, function preSaveUser (next, done) { && this.achievements.triadBingo !== true ) { this.achievements.triadBingo = true; - this.addNotification('ACHIEVEMENT_TRIAD_BINGO'); + this.addNotification( + 'ACHIEVEMENT_STABLE', + { + achievement: 'triadBingo', + achievementNotification: 'triadBingoAchievement', + }, + ); } // EXAMPLE CODE for allowing all existing and new players to be diff --git a/website/server/models/userNotification.js b/website/server/models/userNotification.js index c6b12351e7..325fc672f5 100644 --- a/website/server/models/userNotification.js +++ b/website/server/models/userNotification.js @@ -5,75 +5,85 @@ import validator from 'validator'; import baseModel from '../libs/baseModel'; const NOTIFICATION_TYPES = [ - 'DROPS_ENABLED', // unused - 'REBIRTH_ENABLED', - 'WON_CHALLENGE', - 'STREAK_ACHIEVEMENT', - 'ULTIMATE_GEAR_ACHIEVEMENT', - 'REBIRTH_ACHIEVEMENT', - 'NEW_CONTRIBUTOR_LEVEL', + // general notifications + 'CARD_RECEIVED', 'CRON', + 'DROP_CAP_REACHED', + 'DROPS_ENABLED', // unused + 'FIRST_DROPS', + 'GIFT_ONE_GET_ONE', + 'GROUP_INVITE_ACCEPTED', 'GROUP_TASK_APPROVAL', 'GROUP_TASK_APPROVED', 'GROUP_TASK_ASSIGNED', 'GROUP_TASK_CLAIMED', 'GROUP_TASK_NEEDS_WORK', - 'LOGIN_INCENTIVE', - 'GROUP_INVITE_ACCEPTED', - 'SCORED_TASK', - 'BOSS_DAMAGE', // Not used currently but kept to avoid validation errors - 'GIFT_ONE_GET_ONE', 'GUILD_PROMPT', - 'GUILD_JOINED_ACHIEVEMENT', - 'CHALLENGE_JOINED_ACHIEVEMENT', - 'INVITED_FRIEND_ACHIEVEMENT', - 'CARD_RECEIVED', - 'NEW_MYSTERY_ITEMS', - 'UNALLOCATED_STATS_POINTS', - 'NEW_INBOX_MESSAGE', - 'NEW_STUFF', + 'LEVELED_UP', // not in use + 'LOGIN_INCENTIVE', 'NEW_CHAT_MESSAGE', - 'LEVELED_UP', // Not in use - 'FIRST_DROPS', + 'NEW_CONTRIBUTOR_LEVEL', + 'NEW_INBOX_MESSAGE', + 'NEW_MYSTERY_ITEMS', + 'NEW_STUFF', 'ONBOARDING_COMPLETE', - 'ACHIEVEMENT_ALL_YOUR_BASE', - 'ACHIEVEMENT_BACK_TO_BASICS', - 'ACHIEVEMENT_JUST_ADD_WATER', - 'ACHIEVEMENT_LOST_MASTERCLASSER', - 'ACHIEVEMENT_MIND_OVER_MATTER', - 'ACHIEVEMENT_DUST_DEVIL', - 'ACHIEVEMENT_ARID_AUTHORITY', - 'ACHIEVEMENT_PARTY_UP', - 'ACHIEVEMENT_PARTY_ON', - 'ACHIEVEMENT_BEAST_MASTER', - 'ACHIEVEMENT_MOUNT_MASTER', - 'ACHIEVEMENT_TRIAD_BINGO', - 'ACHIEVEMENT_MONSTER_MAGUS', - 'ACHIEVEMENT_UNDEAD_UNDERTAKER', - 'ACHIEVEMENT_PRIMED_FOR_PAINTING', - 'ACHIEVEMENT_PEARLY_PRO', - 'ACHIEVEMENT_TICKLED_PINK', - 'ACHIEVEMENT_ROSY_OUTLOOK', - 'ACHIEVEMENT_BUG_BONANZA', - 'ACHIEVEMENT_BARE_NECESSITIES', - 'ACHIEVEMENT_FRESHWATER_FRIENDS', - 'ACHIEVEMENT_GOOD_AS_GOLD', - 'ACHIEVEMENT_ALL_THAT_GLITTERS', - 'ACHIEVEMENT_BONE_COLLECTOR', - 'ACHIEVEMENT_SKELETON_CREW', - 'ACHIEVEMENT_SEEING_RED', - 'ACHIEVEMENT_RED_LETTER_DAY', - 'ACHIEVEMENT_LEGENDARY_BESTIARY', - 'ACHIEVEMENT_SEASONAL_SPECIALIST', - 'ACHIEVEMENT_VIOLETS_ARE_BLUE', - 'ACHIEVEMENT_WILD_BLUE_YONDER', - 'ACHIEVEMENT_DOMESTICATED', - 'ACHIEVEMENT_SHADY_CUSTOMER', - 'ACHIEVEMENT_SHADE_OF_IT_ALL', - 'ACHIEVEMENT_ZODIAC_ZOOKEEPER', - 'ACHIEVEMENT_BIRDS_OF_A_FEATHER', + 'REBIRTH_ENABLED', + 'SCORED_TASK', + 'UNALLOCATED_STATS_POINTS', + 'WON_CHALLENGE', + // achievement notifications 'ACHIEVEMENT', // generic achievement notification, details inside `notification.data` - 'DROP_CAP_REACHED', + 'CHALLENGE_JOINED_ACHIEVEMENT', + 'GUILD_JOINED_ACHIEVEMENT', + 'ACHIEVEMENT_PARTY_ON', + 'ACHIEVEMENT_PARTY_UP', + 'INVITED_FRIEND_ACHIEVEMENT', + 'REBIRTH_ACHIEVEMENT', + 'STREAK_ACHIEVEMENT', + 'ULTIMATE_GEAR_ACHIEVEMENT', + 'ACHIEVEMENT_STABLE', + 'ACHIEVEMENT_QUESTS', + 'ACHIEVEMENT_ANIMAL_SET', + 'ACHIEVEMENT_PET_COLOR', + 'ACHIEVEMENT_MOUNT_COLOR', + // Deprecated notification types. Can be removed once old data is cleaned out + 'BOSS_DAMAGE', // deprecated + 'ACHIEVEMENT_ALL_YOUR_BASE', // deprecated + 'ACHIEVEMENT_BACK_TO_BASICS', // deprecated + 'ACHIEVEMENT_JUST_ADD_WATER', // deprecated + 'ACHIEVEMENT_LOST_MASTERCLASSER', // deprecated + 'ACHIEVEMENT_MIND_OVER_MATTER', // deprecated + 'ACHIEVEMENT_DUST_DEVIL', // deprecated + 'ACHIEVEMENT_ARID_AUTHORITY', // deprecated + 'ACHIEVEMENT_PARTY_UP', // deprecated + 'ACHIEVEMENT_PARTY_ON', // deprecated + 'ACHIEVEMENT_BEAST_MASTER', // deprecated + 'ACHIEVEMENT_MOUNT_MASTER', // deprecated + 'ACHIEVEMENT_TRIAD_BINGO', // deprecated + 'ACHIEVEMENT_MONSTER_MAGUS', // deprecated + 'ACHIEVEMENT_UNDEAD_UNDERTAKER', // deprecated + 'ACHIEVEMENT_PRIMED_FOR_PAINTING', // deprecated + 'ACHIEVEMENT_PEARLY_PRO', // deprecated + 'ACHIEVEMENT_TICKLED_PINK', // deprecated + 'ACHIEVEMENT_ROSY_OUTLOOK', // deprecated + 'ACHIEVEMENT_BUG_BONANZA', // deprecated + 'ACHIEVEMENT_BARE_NECESSITIES', // deprecated + 'ACHIEVEMENT_FRESHWATER_FRIENDS', // deprecated + 'ACHIEVEMENT_GOOD_AS_GOLD', // deprecated + 'ACHIEVEMENT_ALL_THAT_GLITTERS', // deprecated + 'ACHIEVEMENT_BONE_COLLECTOR', // deprecated + 'ACHIEVEMENT_SKELETON_CREW', // deprecated + 'ACHIEVEMENT_SEEING_RED', // deprecated + 'ACHIEVEMENT_RED_LETTER_DAY', // deprecated + 'ACHIEVEMENT_LEGENDARY_BESTIARY', // deprecated + 'ACHIEVEMENT_SEASONAL_SPECIALIST', // deprecated + 'ACHIEVEMENT_VIOLETS_ARE_BLUE', // deprecated + 'ACHIEVEMENT_WILD_BLUE_YONDER', // deprecated + 'ACHIEVEMENT_DOMESTICATED', // deprecated + 'ACHIEVEMENT_SHADY_CUSTOMER', // deprecated + 'ACHIEVEMENT_SHADE_OF_IT_ALL', // deprecated + 'ACHIEVEMENT_ZODIAC_ZOOKEEPER', // deprecated + 'ACHIEVEMENT_BIRDS_OF_A_FEATHER', // deprecated ]; const { Schema } = mongoose;
{{ $t('achievementJustAddWaterModalText') }}
{{ $t('achievementLostMasterclasserModalText') }}
{{ $t('achievementMindOverMatterModalText') }}