mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 07:07:35 +01:00
Merge branch 'develop' into sabrecat/teams-rebase
This commit is contained in:
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"version": "4.230.0",
|
||||
"version": "4.230.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -42,3 +42,109 @@ describe('xml marshaller marshalls user data', () => {
|
||||
</user>`);
|
||||
});
|
||||
});
|
||||
|
||||
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(`<user>
|
||||
<inbox/>
|
||||
<purchased>
|
||||
<ads>false</ads>
|
||||
<txnCount>0</txnCount>
|
||||
<skin>eb052b</skin>
|
||||
<skin>0ff591</skin>
|
||||
<skin>2b43f6</skin>
|
||||
<skin>d7a9f7</skin>
|
||||
<skin>800ed0</skin>
|
||||
<skin>rainbow</skin>
|
||||
</purchased>
|
||||
</user>`);
|
||||
});
|
||||
});
|
||||
|
||||
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(`<user>
|
||||
<inbox/>
|
||||
<purchased>
|
||||
<ads>false</ads>
|
||||
<txnCount>0</txnCount>
|
||||
<skin>eb052b</skin>
|
||||
<skin>0ff591</skin>
|
||||
<skin>2b43f6</skin>
|
||||
<skin>d7a9f7</skin>
|
||||
<skin>800ed0</skin>
|
||||
<skin>rainbow</skin>
|
||||
<plan>
|
||||
<item>
|
||||
<count>0</count>
|
||||
<offset>0</offset>
|
||||
<gemCapExtra>0</gemCapExtra>
|
||||
<trinkets>0</trinkets>
|
||||
</item>
|
||||
</plan>
|
||||
</purchased>
|
||||
</user>`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
27
website/client/src/assets/svg/big-gift.svg
Normal file
27
website/client/src/assets/svg/big-gift.svg
Normal file
@@ -0,0 +1,27 @@
|
||||
<svg width="176" height="67" viewBox="0 0 176 67" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path fill="#77F4C7" d="M35.667 11.667 40 9.5l-4.333-2.167L33.5 3l-2.167 4.333L27 9.5l4.333 2.167L33.5 16z"/>
|
||||
<path fill="#BDA8FF" d="M24.667 38.667 30 36l-5.333-2.667L22 28l-2.667 5.333L14 36l5.333 2.667L22 44z"/>
|
||||
<path fill="#8EEDF6" d="M35.667 63.667 39 62l-3.333-1.667L34 57l-1.667 3.333L29 62l3.333 1.667L34 67z"/>
|
||||
<path fill="#FFBE5D" d="M6.667 49.667 10 48l-3.333-1.667L5 43l-1.667 3.333L0 48l3.333 1.667L5 53z"/>
|
||||
<path fill="#FFB6B8" d="M5.667 20.667 8 19.5l-2.333-1.167L4.5 16l-1.167 2.333L1 19.5l2.333 1.167L4.5 23z"/>
|
||||
<g>
|
||||
<path fill="#77F4C7" d="M140.333 11.667 136 9.5l4.333-2.167L142.5 3l2.167 4.333L149 9.5l-4.333 2.167L142.5 16z"/>
|
||||
<path fill="#BDA8FF" d="M151.333 38.667 146 36l5.333-2.667L154 28l2.667 5.333L162 36l-5.333 2.667L154 44z"/>
|
||||
<path fill="#8EEDF6" d="M140.333 63.667 137 62l3.333-1.667L142 57l1.667 3.333L147 62l-3.333 1.667L142 67z"/>
|
||||
<path fill="#FFBE5D" d="M169.333 49.667 166 48l3.333-1.667L171 43l1.667 3.333L176 48l-3.333 1.667L171 53z"/>
|
||||
<path fill="#FFB6B8" d="M170.333 20.667 168 19.5l2.333-1.167L171.5 16l1.167 2.333L175 19.5l-2.333 1.167L171.5 23z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M81.117 13.904c-2.139-4.838-6.274-9.113-11.25-9.324-4.976-.211-7.828 3.779-6.367 7.309 1.461 3.53 4.94 4.177 16.227 7.202 3.204.858 3.528-.35 1.39-5.187z" stroke="#22AEB7" stroke-width="4"/>
|
||||
<path d="M93.833 13.904c2.138-4.838 6.273-9.113 11.25-9.324 4.975-.211 7.828 3.779 6.367 7.309-1.462 3.53-4.94 4.177-16.227 7.202-3.205.858-3.528-.35-1.39-5.187z" stroke="#38C9C6" stroke-width="4"/>
|
||||
<path d="M87.128 11c-9.738 0-3.907 11.145 0 11.145 3.908 0 9.74-11.145 0-11.145z" fill="#46DDDA"/>
|
||||
<path fill="#6133B4" d="M62 33h52v34H62zM56 21h64v12H56z"/>
|
||||
<path fill-opacity=".5" fill="#FFF" style="mix-blend-mode:soft-light" d="M32 30h26v34H32z" transform="translate(56 3)"/>
|
||||
<path fill="#8EEDF6" d="M88 33h6v34h-6z"/>
|
||||
<path fill="#3BCAD7" d="M82 33h6v34h-6zM76 21h12v12H76z"/>
|
||||
<path fill="#8EEDF6" d="M88 21h12v12H88z"/>
|
||||
<path fill-opacity=".2" fill="#000" style="mix-blend-mode:multiply" d="M6 30h26v6H6zM20 18h12v6H20zM0 24h20v6H0zM44 24h20v6H44zM32 18h12v6H32zM6 58h26v6H6zM32 30h26v6H32zM32 58h26v6H32z" transform="translate(56 3)"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -1,5 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
|
||||
<g fill="none" fill-rule="evenodd" stroke="#A5A1AC" stroke-width="2">
|
||||
<path d="M1 11L11 1M11 11L1 1"/>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Icon/Close</title>
|
||||
<g id="Modals" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Shop-Modals" transform="translate(-183.000000, -655.000000)" fill="#878190" fill-rule="nonzero">
|
||||
<g id="Icon/Close" transform="translate(183.000000, 655.000000)">
|
||||
<polygon id="Mask" points="12.1973467 2 14 3.80265326 9.80187117 8 14 12.1973467 12.1973467 14 8 9.80187117 3.80265326 14 2 12.1973467 6.19812883 8 2 3.80265326 3.80265326 2 8 6.19812883"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 215 B After Width: | Height: | Size: 747 B |
@@ -1,60 +0,0 @@
|
||||
<template>
|
||||
<b-modal
|
||||
id="just-add-water"
|
||||
:title="title"
|
||||
size="md"
|
||||
:hide-footer="true"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<div class="col-12">
|
||||
<achievement-avatar class="avatar" />
|
||||
</div>
|
||||
<div class="col-6 offset-3 text-center">
|
||||
<p>{{ $t('achievementJustAddWaterModalText') }}</p>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="close()"
|
||||
>
|
||||
{{ $t('huzzah') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<achievement-footer />
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.avatar {
|
||||
width: 140px;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 1.5em;
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import achievementFooter from './achievementFooter';
|
||||
import achievementAvatar from './achievementAvatar';
|
||||
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
achievementFooter,
|
||||
achievementAvatar,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
title: `${this.$t('modalAchievement')} ${this.$t('achievementJustAddWater')}`,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'just-add-water');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,60 +0,0 @@
|
||||
<template>
|
||||
<b-modal
|
||||
id="lost-masterclasser"
|
||||
:title="title"
|
||||
size="md"
|
||||
:hide-footer="true"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<div class="col-12">
|
||||
<achievement-avatar class="avatar" />
|
||||
</div>
|
||||
<div class="col-6 offset-3 text-center">
|
||||
<p>{{ $t('achievementLostMasterclasserModalText') }}</p>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="close()"
|
||||
>
|
||||
{{ $t('huzzah') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<achievement-footer />
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.avatar {
|
||||
width: 140px;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 1.5em;
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import achievementFooter from './achievementFooter';
|
||||
import achievementAvatar from './achievementAvatar';
|
||||
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
achievementFooter,
|
||||
achievementAvatar,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
title: `${this.$t('modalAchievement')} ${this.$t('achievementLostMasterclasser')}`,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'lost-masterclasser');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,60 +0,0 @@
|
||||
<template>
|
||||
<b-modal
|
||||
id="mind-over-matter"
|
||||
:title="title"
|
||||
size="md"
|
||||
:hide-footer="true"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<div class="col-12">
|
||||
<achievement-avatar class="avatar" />
|
||||
</div>
|
||||
<div class="col-6 offset-3 text-center">
|
||||
<p>{{ $t('achievementMindOverMatterModalText') }}</p>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="close()"
|
||||
>
|
||||
{{ $t('huzzah') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<achievement-footer />
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.avatar {
|
||||
width: 140px;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 1.5em;
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import achievementFooter from './achievementFooter';
|
||||
import achievementAvatar from './achievementAvatar';
|
||||
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
achievementFooter,
|
||||
achievementAvatar,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
title: `${this.$t('modalAchievement')} ${this.$t('achievementMindOverMatter')}`,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'mind-over-matter');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -3,7 +3,7 @@
|
||||
<creator-intro />
|
||||
<profileModal />
|
||||
<report-flag-modal />
|
||||
<send-gems-modal />
|
||||
<send-gift-modal />
|
||||
<select-user-modal />
|
||||
<b-navbar
|
||||
id="habitica-menu"
|
||||
@@ -747,7 +747,7 @@ import creatorIntro from '../creatorIntro';
|
||||
import notificationMenu from './notificationsDropdown';
|
||||
import profileModal from '../userMenu/profileModal';
|
||||
import reportFlagModal from '../chat/reportFlagModal';
|
||||
import sendGemsModal from '@/components/payments/sendGemsModal';
|
||||
import sendGiftModal from '@/components/payments/sendGiftModal';
|
||||
import selectUserModal from '@/components/payments/selectUserModal';
|
||||
import sync from '@/mixins/sync';
|
||||
import userDropdown from './userDropdown';
|
||||
@@ -759,7 +759,7 @@ export default {
|
||||
notificationMenu,
|
||||
profileModal,
|
||||
reportFlagModal,
|
||||
sendGemsModal,
|
||||
sendGiftModal,
|
||||
selectUserModal,
|
||||
userDropdown,
|
||||
},
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
<template>
|
||||
<base-notification
|
||||
:can-remove="canRemove"
|
||||
:notification="notification"
|
||||
:read-after-click="true"
|
||||
@click="action"
|
||||
>
|
||||
<div
|
||||
slot="content"
|
||||
v-html="achievementString"
|
||||
></div>
|
||||
</base-notification>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseNotification from './base';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BaseNotification,
|
||||
},
|
||||
props: ['notification', 'canRemove'],
|
||||
computed: {
|
||||
achievementString () {
|
||||
return `<strong>${this.$t('achievement')}</strong>: ${this.$t('achievementJustAddWater')}`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
action () {
|
||||
this.$root.$emit('bv::show::modal', 'just-add-water');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,34 +0,0 @@
|
||||
<template>
|
||||
<base-notification
|
||||
:can-remove="canRemove"
|
||||
:notification="notification"
|
||||
:read-after-click="true"
|
||||
@click="action"
|
||||
>
|
||||
<div
|
||||
slot="content"
|
||||
v-html="achievementString"
|
||||
></div>
|
||||
</base-notification>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseNotification from './base';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BaseNotification,
|
||||
},
|
||||
props: ['notification', 'canRemove'],
|
||||
computed: {
|
||||
achievementString () {
|
||||
return `<strong>${this.$t('achievement')}</strong>: ${this.$t('achievementLostMasterclasser')}`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
action () {
|
||||
this.$root.$emit('bv::show::modal', 'lost-masterclasser');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,34 +0,0 @@
|
||||
<template>
|
||||
<base-notification
|
||||
:can-remove="canRemove"
|
||||
:notification="notification"
|
||||
:read-after-click="true"
|
||||
@click="action"
|
||||
>
|
||||
<div
|
||||
slot="content"
|
||||
v-html="achievementString"
|
||||
></div>
|
||||
</base-notification>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseNotification from './base';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BaseNotification,
|
||||
},
|
||||
props: ['notification', 'canRemove'],
|
||||
computed: {
|
||||
achievementString () {
|
||||
return `<strong>${this.$t('achievement')}</strong>: ${this.$t('achievementMindOverMatter')}`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
action () {
|
||||
this.$root.$emit('bv::show::modal', 'mind-over-matter');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -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',
|
||||
],
|
||||
};
|
||||
},
|
||||
|
||||
@@ -30,9 +30,6 @@
|
||||
v-if="notificationData && notificationData.achievement"
|
||||
:data="notificationData"
|
||||
/>
|
||||
<just-add-water />
|
||||
<lost-masterclasser />
|
||||
<mind-over-matter />
|
||||
<onboarding-complete />
|
||||
<first-drops />
|
||||
</div>
|
||||
@@ -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('achievementPartyOn')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
message: $t => $t('achievement'),
|
||||
modalText: $t => $t('achievementPartyOn'),
|
||||
achievement: 'partyOn',
|
||||
label: $t => `${$t('achievement')}: ${$t('gearAchievementNotification')}`,
|
||||
modalId: 'ultimate-gear',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_BEAST_MASTER: {
|
||||
achievement: true,
|
||||
label: $t => `${$t('achievement')}: ${$t('beastAchievement')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
message: $t => $t('achievement'),
|
||||
modalText: $t => $t('beastAchievement'),
|
||||
achievement: 'beastMaster',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_MOUNT_MASTER: {
|
||||
achievement: true,
|
||||
label: $t => `${$t('achievement')}: ${$t('mountAchievement')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
message: $t => $t('achievement'),
|
||||
modalText: $t => $t('mountAchievement'),
|
||||
achievement: 'mountMaster',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_TRIAD_BINGO: {
|
||||
achievement: true,
|
||||
label: $t => `${$t('achievement')}: ${$t('triadBingoAchievement')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
message: $t => $t('achievement'),
|
||||
modalText: $t => $t('triadBingoAchievement'),
|
||||
achievement: 'triadBingo',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_MONSTER_MAGUS: {
|
||||
achievement: true,
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementMonsterMagus')}`,
|
||||
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_STABLE: {
|
||||
achievement: true,
|
||||
modalId: 'generic-achievement',
|
||||
label: null, // data filled in handleUserNotifications
|
||||
data: {
|
||||
message: $t => $t('achievement'),
|
||||
modalText: null, // data filled in handleUserNotifications
|
||||
achievement: 'stableAchievs',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_PRIMED_FOR_PAINTING: {
|
||||
ACHIEVEMENT_QUESTS: {
|
||||
achievement: true,
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementPrimedForPainting')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'primedForPainting',
|
||||
achievement: 'questSeriesAchievs',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_PEARLY_PRO: {
|
||||
ACHIEVEMENT_ANIMAL_SET: {
|
||||
achievement: true,
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementPearlyPro')}`,
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementAnimalSet')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'pearlyPro',
|
||||
achievement: 'animalSetAchievs',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_TICKLED_PINK: {
|
||||
ACHIEVEMENT_PET_COLOR: {
|
||||
achievement: true,
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementTickledPink')}`,
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementPetColor')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'tickledPink',
|
||||
achievement: 'petColorAchievs',
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_ROSY_OUTLOOK: {
|
||||
ACHIEVEMENT_MOUNT_COLOR: {
|
||||
achievement: true,
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementRosyOutlook')}`,
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementMountColor')}`,
|
||||
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;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div class="payments-column mx-auto mt-auto">
|
||||
<h4>{{ $t('choosePaymentMethod') }}</h4>
|
||||
<button
|
||||
v-if="stripeAvailable"
|
||||
class="btn btn-primary payment-button payment-item with-icon"
|
||||
@@ -80,6 +81,13 @@
|
||||
cursor: default !important;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 0.875rem;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -14,15 +14,44 @@
|
||||
</div>
|
||||
<h2
|
||||
v-else
|
||||
class="ml-2"
|
||||
class="d-flex flex-column mx-auto align-items-center"
|
||||
>
|
||||
{{ $t('sendGift') }}
|
||||
{{ $t('sendAGift') }}
|
||||
</h2>
|
||||
<div
|
||||
v-if="currentEvent && currentEvent.promo === 'g1g1'"
|
||||
class="g1g1-margin d-flex flex-column align-items-center"
|
||||
>
|
||||
<div
|
||||
class="svg-big-gift"
|
||||
v-once
|
||||
v-html="icons.bigGift"
|
||||
></div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="d-flex flex-column align-items-center">
|
||||
<div
|
||||
class="svg-big-gift"
|
||||
v-once
|
||||
v-html="icons.bigGift"
|
||||
></div>
|
||||
</div>
|
||||
<div class="d-flex flex-column align-items-center">
|
||||
<div
|
||||
class="modal-close"
|
||||
v-if="currentEvent && currentEvent.promo === 'g1g1'"
|
||||
class="g1g1-modal-close"
|
||||
@click="close()"
|
||||
>
|
||||
<div
|
||||
class="g1g1-svg-icon"
|
||||
v-html="icons.close"
|
||||
></div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="modal-close"
|
||||
@click="close()">
|
||||
<div
|
||||
class="svg-icon"
|
||||
v-html="icons.close"
|
||||
@@ -42,6 +71,7 @@
|
||||
v-model="userSearchTerm"
|
||||
class="form-control"
|
||||
type="text"
|
||||
ref="textBox"
|
||||
:placeholder="$t('usernameOrUserId')"
|
||||
:class="{
|
||||
'input-valid': foundUser._id,
|
||||
@@ -70,15 +100,20 @@
|
||||
<div
|
||||
v-else
|
||||
>
|
||||
{{ $t('selectGift') }}
|
||||
{{ $t('next') }}
|
||||
</div>
|
||||
</button>
|
||||
<a
|
||||
class="cancel-link mx-auto mt-3"
|
||||
<div
|
||||
v-if="currentEvent && currentEvent.promo ==='g1g1'"
|
||||
class="g1g1-cancel d-flex justify-content-center"
|
||||
v-html="$t('cancel')"
|
||||
@click="close()"
|
||||
>
|
||||
{{ $t('cancel') }}
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
v-else>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -110,13 +145,16 @@
|
||||
@import '~@/assets/scss/mixins.scss';
|
||||
|
||||
#select-user-modal {
|
||||
.modal-content {
|
||||
width:448px;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
margin-top: 0rem;
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
width: 29.5rem;
|
||||
margin-top: 25vh;
|
||||
width: 448px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
@@ -126,6 +164,15 @@
|
||||
margin: 0rem 0.25rem 0.25rem 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
body.modal-open .modal {
|
||||
display: flex !important;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body.modal-open .modal .modal-dialog {
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -146,12 +193,12 @@
|
||||
|
||||
.g1g1 {
|
||||
background-image: url('~@/assets/images/g1g1-send.png');
|
||||
background-size: 472px 152px;
|
||||
width: 470px;
|
||||
background-size: 446px 152px;
|
||||
width: 446px;
|
||||
height: 152px;
|
||||
margin: -1rem 0rem 0rem -1rem;
|
||||
border-radius: 0.3rem 0.3rem 0rem 0rem;
|
||||
padding: 1.5rem;
|
||||
margin: -16px 0px 0px -16px;
|
||||
border-radius: 4.8px 4.8px 0px 0px;
|
||||
padding: 24px;
|
||||
color: $white;
|
||||
|
||||
h1 {
|
||||
@@ -169,6 +216,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
.g1g1-margin {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.g1g1-cancel {
|
||||
margin-top: 16px;
|
||||
color: $blue-10;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.g1g1-fine-print {
|
||||
color: $gray-100;
|
||||
background-color: $gray-700;
|
||||
@@ -176,6 +233,29 @@
|
||||
line-height: 1.33;
|
||||
}
|
||||
|
||||
.g1g1-modal-close {
|
||||
position: absolute;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
padding: 4px;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
.g1g1-svg-icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
|
||||
& ::v-deep svg path {
|
||||
fill: #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.g1g1-modal-dialog {
|
||||
margin-top: 10vh;
|
||||
}
|
||||
|
||||
.input-error {
|
||||
color: $red-50;
|
||||
font-size: 90%;
|
||||
@@ -192,6 +272,18 @@
|
||||
border-color: $purple-500;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.75rem;
|
||||
color: $purple-300;
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.svg-big-gift {
|
||||
width: 176px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
position: absolute;
|
||||
width: 18px;
|
||||
@@ -206,14 +298,17 @@
|
||||
height: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// import { nextTick } from 'vue'; // may not need this? I don't know!
|
||||
import debounce from 'lodash/debounce';
|
||||
import find from 'lodash/find';
|
||||
import isUUID from 'validator/lib/isUUID';
|
||||
import { mapState } from '@/libs/store';
|
||||
import closeIcon from '@/assets/svg/close.svg';
|
||||
import bigGiftIcon from '@/assets/svg/big-gift.svg';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
@@ -223,6 +318,7 @@ export default {
|
||||
foundUser: {},
|
||||
icons: Object.freeze({
|
||||
close: closeIcon,
|
||||
bigGift: bigGiftIcon,
|
||||
}),
|
||||
};
|
||||
},
|
||||
@@ -281,7 +377,7 @@ export default {
|
||||
this.foundUser = result;
|
||||
}, 500),
|
||||
selectUser () {
|
||||
this.$root.$emit('habitica::send-gems', this.foundUser);
|
||||
this.$root.$emit('habitica::send-gift', this.foundUser);
|
||||
this.close();
|
||||
},
|
||||
onHide () {
|
||||
|
||||
634
website/client/src/components/payments/sendGiftModal.vue
Normal file
634
website/client/src/components/payments/sendGiftModal.vue
Normal file
@@ -0,0 +1,634 @@
|
||||
<template>
|
||||
<b-modal
|
||||
id="send-gift"
|
||||
:hide-footer="true"
|
||||
:hide-header="true"
|
||||
size="md"
|
||||
@hide="onHide()"
|
||||
>
|
||||
<div>
|
||||
<!-- header -->
|
||||
<div
|
||||
class="modal-close"
|
||||
@click="close()"
|
||||
>
|
||||
<div
|
||||
class="icon-close"
|
||||
v-html="icons.closeIcon"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="d-flex flex-column mx-auto align-items-center">
|
||||
{{ $t('sendAGift') }}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<!-- user avatar -->
|
||||
<div
|
||||
v-if="userReceivingGift"
|
||||
class="modal-body"
|
||||
>
|
||||
<avatar
|
||||
:member="userReceivingGift"
|
||||
:hideClassBadge="true"
|
||||
class="d-flex flex-column mx-auto align-items-center"
|
||||
/>
|
||||
<div class="avatar-spacer"></div>
|
||||
<div class="d-flex flex-column mx-auto align-items-center display-name">
|
||||
<!-- user display name and username -->
|
||||
<user-link
|
||||
:user-id="displayName"
|
||||
:name="displayName"
|
||||
:backer="userBacker"
|
||||
:contributor="userContributor"
|
||||
:class="display-name"
|
||||
/>
|
||||
</div>
|
||||
<div class="d-flex flex-column mx-auto align-items-center user-name">
|
||||
@{{ userName }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- menu area -->
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-8 offset-md-2 text-center nav">
|
||||
<div
|
||||
class="nav-item"
|
||||
:class="{active: selectedPage === 'subscription'}"
|
||||
@click="selectPage('subscription')"
|
||||
>
|
||||
{{ $t('subscription') }}
|
||||
</div>
|
||||
<div
|
||||
class="nav-item"
|
||||
:class="{active: selectedPage !== 'subscription'}"
|
||||
@click="selectPage('buyGems')"
|
||||
>
|
||||
{{ $t('gems') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- subscriber block -->
|
||||
<subscription-options
|
||||
v-show="selectedPage === 'subscription'"
|
||||
class="subscribe-option"
|
||||
/>
|
||||
|
||||
<!-- gem block -->
|
||||
<div
|
||||
v-show="selectedPage === 'buyGems'"
|
||||
>
|
||||
<div class="gem-group">
|
||||
<!-- buy gems with money -->
|
||||
<label v-once>
|
||||
{{ $t('howManyGemsPurchase') }}
|
||||
</label>
|
||||
<div class="d-flex flex-row align-items-center justify-content-center">
|
||||
<div
|
||||
class="gray-circle"
|
||||
@click="gift.gems.amount <= 0 ? gift.gems.amount = 0 : gift.gems.amount--"
|
||||
>
|
||||
<div
|
||||
class="icon-negative"
|
||||
v-html="icons.negativeIcon"
|
||||
></div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend input-group-icon align-items-center">
|
||||
<div
|
||||
class="icon-gem"
|
||||
v-html="icons.gemIcon"
|
||||
></div>
|
||||
</div>
|
||||
<input
|
||||
id="gemsForm"
|
||||
v-model.number="gift.gems.amount"
|
||||
class="form-control"
|
||||
min="0"
|
||||
max="9999"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="gray-circle"
|
||||
@click="gift.gems.amount++"
|
||||
>
|
||||
<div
|
||||
class="icon-positive"
|
||||
v-html="icons.positiveIcon"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- the word "total" -->
|
||||
<div class="buy-gem-total">
|
||||
{{ $t('sendGiftTotal') }}
|
||||
</div>
|
||||
|
||||
<!-- the actual dollar amount -->
|
||||
<div class="buy-gem-amount">
|
||||
<span>
|
||||
{{formatter.format(totalGems)}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- change to sending own gems page -->
|
||||
<div
|
||||
:class="{active: selectedPage === 'ownGems'}"
|
||||
@click="selectPage('ownGems')"
|
||||
class="gem-state-change"
|
||||
>
|
||||
{{ $t('wantToSendOwnGems') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- paying for gems -->
|
||||
<payments-buttons
|
||||
class="payment-buttons"
|
||||
:stripe-fn="() => redirectToStripe({gift, uuid: userReceivingGift._id, receiverName})"
|
||||
:paypal-fn="() => openPaypalGift({
|
||||
gift: gift, giftedTo: userReceivingGift._id, receiverName,
|
||||
})"
|
||||
:amazon-data="{type: 'single', gift, giftedTo: userReceivingGift._id, receiverName}"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- send gems from balance -->
|
||||
<div
|
||||
v-show="selectedPage === 'ownGems'"
|
||||
>
|
||||
<div class="gem-group">
|
||||
<label v-once>
|
||||
{{ $t('howManyGemsSend') }}
|
||||
</label>
|
||||
<div class="d-flex align-items-center justify-content-center">
|
||||
<div
|
||||
class="gray-circle"
|
||||
@click="gift.gems.amount <= 0 ? gift.gems.amount = 0 : gift.gems.amount--"
|
||||
>
|
||||
<div
|
||||
class="icon-negative"
|
||||
v-html="icons.negativeIcon"
|
||||
></div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend input-group-icon align-items-center">
|
||||
<div
|
||||
class="icon-gem"
|
||||
v-html="icons.gemIcon"
|
||||
></div>
|
||||
</div>
|
||||
<input
|
||||
id="gemsForm"
|
||||
v-model="gift.gems.amount"
|
||||
class="form-control"
|
||||
min="0"
|
||||
:max="maxGems"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="gray-circle"
|
||||
@click="gift.gems.amount < maxGems ? gift.gems.amount++ : gift.gems.amount = maxGems"
|
||||
>
|
||||
<div
|
||||
class="icon-positive"
|
||||
v-html="icons.positiveIcon"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="align-items-middle">
|
||||
<div class="d-flex justify-content-center align-items-middle">
|
||||
<span class="balance-text">
|
||||
{{ $t('yourBalance') }}
|
||||
</span>
|
||||
<span
|
||||
class="icon-gem balance-gem-margin"
|
||||
v-html="icons.gemIcon"
|
||||
style="display: inline-block;"
|
||||
></span>
|
||||
<span
|
||||
class="balance-gems">
|
||||
{{ maxGems }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex flex-column justify-content-center align-items-middle mt-3">
|
||||
<button
|
||||
v-if="fromBal"
|
||||
class="btn btn-primary mx-auto mt-2"
|
||||
type="submit"
|
||||
:disabled="sendingInProgress"
|
||||
@click="sendGift()"
|
||||
>
|
||||
{{ $t("send") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- change to buying gems page -->
|
||||
<div
|
||||
:class="{active: selectedPage === 'buyGems'}"
|
||||
@click="selectPage('buyGems')"
|
||||
class="gem-state-change"
|
||||
>
|
||||
{{ $t('needToPurchaseGems') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~@/assets/scss/mixins.scss';
|
||||
#send-gift {
|
||||
.modal-dialog {
|
||||
max-width: 448px;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
width: 448px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 14px 28px 0 rgba(26, 24, 29, 0.24), 0 10px 10px 0 rgba(26, 24, 29, 0.28);
|
||||
}
|
||||
.modal-body{
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
position: absolute;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
padding: 4px;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
.icon-close {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
|
||||
& ::v-deep svg path {
|
||||
fill: #878190;
|
||||
}
|
||||
}
|
||||
}
|
||||
#subscription-form .subscribe-option {
|
||||
background: #F9F9F9;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style scoped lang="scss">
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
h2 {
|
||||
color: $purple-300;
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
||||
.avatar-spacer {
|
||||
height: 9px;
|
||||
}
|
||||
|
||||
.display-name {
|
||||
font-size: 0.875rem;
|
||||
font-weight: bold;
|
||||
line-height: 1.71;
|
||||
margin: 0px 6px 0 20px;
|
||||
}
|
||||
|
||||
.display-name a:hover{
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.33;
|
||||
text-align: center;
|
||||
color: $gray-100;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.row {
|
||||
background-color: $gray-700;
|
||||
margin: 0 0 0 0;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.nav {
|
||||
font-weight: bold;
|
||||
font-size: 0.75rem;
|
||||
min-height: 32px;
|
||||
padding: 16px 0 0 0;
|
||||
color: $purple-300;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
padding: 0px 8px 6px 8px;
|
||||
}
|
||||
|
||||
.nav-item:hover, .nav-item.active {
|
||||
color: $purple-300;
|
||||
border-bottom: 2px solid $purple-400;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nav-item.inactive {
|
||||
color: $purple-300;
|
||||
border-bottom: 0px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.gem-group {
|
||||
padding: 0 0 24px 0;
|
||||
background-color: $gray-700;
|
||||
margin: 0 0 0 0;
|
||||
border-bottom-right-radius: 8px;
|
||||
border-bottom-left-radius: 8px
|
||||
}
|
||||
|
||||
label {
|
||||
color: $gray-50;
|
||||
font-size: 0.875rem;
|
||||
font-weight: bold;
|
||||
line-height: 1.71;
|
||||
margin: 12px 0 16px 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
width: 94px;
|
||||
height: 32px;
|
||||
margin: 0px 16px 0px 16px;
|
||||
padding: 0;
|
||||
border-radius: 2px;
|
||||
border: solid 1px $gray-400;
|
||||
background-color: $white;
|
||||
}
|
||||
|
||||
.gray-circle {
|
||||
border-radius: 100%;
|
||||
border: solid 2px $gray-300;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border-color: $purple-400;
|
||||
}
|
||||
}
|
||||
|
||||
.gray-circle:hover{
|
||||
.icon-positive, .icon-negative {
|
||||
& ::v-deep svg path {
|
||||
fill: $purple-400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon-gem {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.icon-positive, .icon-negative {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin: 4px auto;
|
||||
|
||||
& ::v-deep svg path {
|
||||
fill: $gray-300;
|
||||
}
|
||||
}
|
||||
|
||||
.buy-gem-total {
|
||||
font-size: 0.875rem;
|
||||
font-weight: bold;
|
||||
line-height: 1.71;
|
||||
padding-top: 24px;
|
||||
text-align: center;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.buy-gem-amount {
|
||||
font-size: 1.25rem;
|
||||
font-weight: bold;
|
||||
line-height: 1.4;
|
||||
margin: 16px 0 24px 0;
|
||||
text-align: center;
|
||||
height: 28px;
|
||||
color: $green-10;
|
||||
}
|
||||
|
||||
.balance-text {
|
||||
font-size: 0.75rem;
|
||||
font-weight: bold;
|
||||
color: $gray-100;
|
||||
line-height: 1.33;
|
||||
margin: 12px 0px 0px 70px;
|
||||
}
|
||||
|
||||
.balance-gem-margin {
|
||||
margin: 8px 4px 0px 8px;
|
||||
}
|
||||
|
||||
.balance-gems {
|
||||
font-size: 0.75rem;
|
||||
color: $gray-100;
|
||||
line-height: 1.33;
|
||||
margin: 12px 71px 0px 4px;
|
||||
}
|
||||
|
||||
.gem-state-change {
|
||||
color: $blue-10;
|
||||
font-size: 0.875rem;
|
||||
min-height: 24px;
|
||||
margin: 16px 0 0;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.subscribe-option {
|
||||
border-bottom-left-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.payment-buttons {
|
||||
padding: 24px 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
// libs imports
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
// mixins imports
|
||||
import paymentsMixin from '../../mixins/payments';
|
||||
|
||||
// component imports
|
||||
import avatar from '../avatar';
|
||||
import userLink from '../userLink';
|
||||
import subscriptionOptions from '../settings/subscriptionOptions.vue';
|
||||
import paymentsButtons from '@/components/payments/buttons/list';
|
||||
|
||||
// svg imports
|
||||
import closeIcon from '@/assets/svg/close.svg';
|
||||
import gemIcon from '@/assets/svg/gem.svg';
|
||||
import positiveIcon from '@/assets/svg/positive.svg';
|
||||
import negativeIcon from '@/assets/svg/negative.svg';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
avatar,
|
||||
subscriptionOptions,
|
||||
paymentsButtons,
|
||||
userLink,
|
||||
},
|
||||
mixins: [
|
||||
paymentsMixin,
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
subscription: {
|
||||
key: '',
|
||||
},
|
||||
icons: Object.freeze({
|
||||
closeIcon,
|
||||
gemIcon,
|
||||
positiveIcon,
|
||||
negativeIcon,
|
||||
}),
|
||||
userReceivingGift: {
|
||||
profile: '',
|
||||
},
|
||||
name: '',
|
||||
display: '',
|
||||
selectedPage: 'subscription',
|
||||
gift: {
|
||||
type: 'gems',
|
||||
gems: {
|
||||
amount: 0,
|
||||
fromBalance: true,
|
||||
},
|
||||
},
|
||||
sendingInProgress: false,
|
||||
amazonPayments: {},
|
||||
gemCost: 1,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'send-gift');
|
||||
},
|
||||
selectPage (page) {
|
||||
this.selectedPage = page || 'subscription';
|
||||
},
|
||||
async sendGift () {
|
||||
this.sendingInProgress = true;
|
||||
await this.$store.dispatch('members:transferGems', {
|
||||
toUserId: this.userReceivingGift._id,
|
||||
gemAmount: this.gift.gems.amount,
|
||||
});
|
||||
this.close();
|
||||
setTimeout(() => { // wait for the send gem modal to be closed
|
||||
this.$root.$emit('habitica:payment-success', {
|
||||
paymentMethod: 'balance',
|
||||
paymentCompleted: true,
|
||||
paymentType: 'gift-gems-balance',
|
||||
gift: {
|
||||
gems: {
|
||||
amount: this.gift.gems.amount,
|
||||
},
|
||||
},
|
||||
giftReceiver: this.receiverName,
|
||||
});
|
||||
}, 500);
|
||||
},
|
||||
onHide () {
|
||||
this.sendingInProgress = false;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
userLoggedIn: 'user.data',
|
||||
}),
|
||||
userName () {
|
||||
const userName = this.userReceivingGift.auth
|
||||
&& this.userReceivingGift.auth.local
|
||||
&& this.userReceivingGift.auth.local.username;
|
||||
return userName;
|
||||
},
|
||||
displayName () {
|
||||
const displayName = this.userReceivingGift.profile.name;
|
||||
return displayName;
|
||||
},
|
||||
userBacker () {
|
||||
const userBacker = this.userReceivingGift.backer;
|
||||
return userBacker;
|
||||
},
|
||||
userContributor () {
|
||||
const userContributor = this.userReceivingGift.contributor;
|
||||
return userContributor;
|
||||
},
|
||||
tierIcon () {
|
||||
if (this.isNPC) {
|
||||
return this.icons.tierNPC;
|
||||
}
|
||||
return this.icons[`tier${this.level}`];
|
||||
},
|
||||
fromBal () {
|
||||
return this.gift.type === 'gems' && this.gift.gems.fromBalance;
|
||||
},
|
||||
maxGems () {
|
||||
const maxGems = this.fromBal ? this.userLoggedIn.balance * 4 : 9999;
|
||||
return maxGems;
|
||||
},
|
||||
formatter () {
|
||||
const formatter = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
minimumFractionDigits: 2,
|
||||
});
|
||||
return formatter;
|
||||
},
|
||||
totalGems () {
|
||||
const totalGems = this.gift.gems.amount * 0.25;
|
||||
return totalGems;
|
||||
},
|
||||
receiverName () {
|
||||
if (
|
||||
this.userReceivingGift.auth
|
||||
&& this.userReceivingGift.auth.local
|
||||
&& this.userReceivingGift.auth.local.username
|
||||
) {
|
||||
return this.userReceivingGift.auth.local.username;
|
||||
}
|
||||
return this.userReceivingGift.profile.name;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
startingPage () {
|
||||
this.selectedPage = this.startingPage;
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.$root.$on('habitica::send-gift', data => {
|
||||
this.userReceivingGift = data;
|
||||
if (this.$store.state.giftModalOptions.startingPage) {
|
||||
this.selectedPage = this.$store.state.giftModalOptions.startingPage;
|
||||
this.$store.state.giftModalOptions.startingPage = '';
|
||||
this.selectPage(this.selectedPage);
|
||||
} else {
|
||||
this.selectPage(this.startingPage);
|
||||
}
|
||||
this.$root.$emit('bv::show::modal', 'send-gift');
|
||||
});
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -450,10 +450,6 @@
|
||||
background-color: $white;
|
||||
}
|
||||
|
||||
.subscribe-option {
|
||||
border-bottom: 1px solid $gray-600;
|
||||
}
|
||||
|
||||
.svg-amazon-pay {
|
||||
width: 208px;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -124,6 +124,9 @@ export default function () {
|
||||
profileOptions: {
|
||||
startingPage: '',
|
||||
},
|
||||
giftModalOptions: {
|
||||
startingPage: '',
|
||||
},
|
||||
rageModalOptions: {
|
||||
npc: '',
|
||||
},
|
||||
|
||||
@@ -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)",
|
||||
|
||||
@@ -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?<br><br>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.<br><br>“Wait!” cries @-Tyr-. “It’s the Trerezin! She’s not dangerous, just dangerously clingy!”",
|
||||
|
||||
@@ -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 <a href='mailto:admin@habitica.com'>contacting us</a>. 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 <a href='mailto:admin@habitica.com'>contacting us</a>. We want to help you get the most from Habitica.",
|
||||
"sendAGift": "Send Gift"
|
||||
}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
@@ -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: {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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`),
|
||||
|
||||
@@ -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`),
|
||||
|
||||
@@ -420,5 +420,5 @@ export default function scoreTask (options = {}, req = {}, analytics) {
|
||||
checkOnboardingStatus(user, req, analytics);
|
||||
}
|
||||
|
||||
return [delta];
|
||||
return delta;
|
||||
}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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}`)}`,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user