multiplefixes quest (#8964)

* add hourglass to header

* multiple quest fixes - show quest info in popover/modal

* pin backgrounds

* unpin gem-purchable items / change pinType of quest to quests

* check if hatching potion is allowed on egg - wide hatching info

* fix (perf): items - request text()/notes() only once

* change items margin to 23px

* list cards + open modal to choose a target + add space between market items

* buy card from task-list

* fix tests - unpin items on purchasing eggs / hatchingPotions
This commit is contained in:
negue
2017-08-21 00:32:32 +02:00
committed by GitHub
parent 2b922508c5
commit 876552b922
21 changed files with 562 additions and 58 deletions

View File

@@ -2,7 +2,7 @@
.items > div {
display: inline-block;
margin-right: 24px;
margin-right: 23px;
}
.items > div:last-of-type {
@@ -87,3 +87,7 @@
color: $gray-400;
margin-top: 4px;
}
.questPopover {
width: 200px;
}

View File

@@ -14,7 +14,7 @@
.modal-dialog {
.title {
height: 24px;
min-height: 24px;
margin-top: 24px;
font-family: 'Roboto Condensed';
font-size: 20px;

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="#E1E0E3" fill-rule="evenodd" d="M10.667 10.667L16 8l-5.333-2.667L8 0 5.333 5.333 0 8l5.333 2.667L8 16z"/>
</svg>

After

Width:  |  Height:  |  Size: 213 B

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd">
<path fill="#E1E0E3" d="M10.667 10.667L16 8l-5.333-2.667L8 0 5.333 5.333 0 8l5.333 2.667L8 16z"/>
<path fill="#FFB445" d="M8 0L5.333 5.333 0 8l5.333 2.667L8 16z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 320 B

View File

@@ -52,6 +52,9 @@ div
a.dropdown-item(href="https://trello.com/c/odmhIqyW/440-read-first-table-of-contents", target='_blank') {{ $t('requestAF') }}
a.dropdown-item(href="http://habitica.wikia.com/wiki/Contributing_to_Habitica", target='_blank') {{ $t('contributing') }}
a.dropdown-item(href="http://habitica.wikia.com/wiki/Habitica_Wiki", target='_blank') {{ $t('wiki') }}
.item-with-icon(v-if="userHourglasses != 0")
.svg-icon(v-html="icons.hourglasses")
span {{ userHourglasses }}
.item-with-icon
.svg-icon(v-html="icons.gem")
span {{userGems | roundBigNumber}}
@@ -219,6 +222,7 @@ import { mapState, mapGetters } from 'client/libs/store';
import gemIcon from 'assets/svg/gem.svg';
import goldIcon from 'assets/svg/gold.svg';
import userIcon from 'assets/svg/user.svg';
import svgHourglasses from 'assets/svg/hourglass.svg';
import logo from 'assets/svg/logo.svg';
import InboxModal from './userMenu/inbox.vue';
import notificationMenu from './notificationMenu';
@@ -236,6 +240,7 @@ export default {
gem: gemIcon,
gold: goldIcon,
user: userIcon,
hourglasses: svgHourglasses,
logo,
}),
groupPlans: [],
@@ -245,7 +250,10 @@ export default {
...mapGetters({
userGems: 'user:gems',
}),
...mapState({user: 'user.data'}),
...mapState({
user: 'user.data',
userHourglasses: 'user.data.purchased.plan.consecutive.trinkets',
}),
},
mounted () {
this.getUserGroupPlans();

View File

@@ -157,7 +157,7 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
.col-6.offset-3.text-center.set-title
strong {{set.text}}
.col-12(v-if='showPlainBackgroundBlurb(set.identifier, set.items)') {{ $t('incentiveBackgroundsUnlockedWithCheckins') }}
.col-4.text-center.customize-option(v-for='bg in set.items',
.col-4.text-center.customize-option.background-button(v-for='bg in set.items',
@click='unlock("background." + bg.key)',
:popover-title='bg.text',
:popover='bg.notes',
@@ -167,6 +167,11 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
.purchase-single(v-if='!user.purchased.background[bg.key]')
.svg-icon.gem(v-html='icons.gem')
span 7
span.badge.badge-pill.badge-item.badge-svg(
:class="{'item-selected-badge': isBackgroundPinned(bg), 'hide': !isBackgroundPinned(bg)}",
@click.prevent.stop="togglePinned(bg)"
)
span.svg-icon.inline.icon-12.color(v-html="icons.pin")
.col-12.text-center(v-if='!ownsSet("background", set.items) && set.identifier !== "incentiveBackgrounds"')
.gem-amount
.svg-icon.gem(v-html='icons.gem')
@@ -488,6 +493,39 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
vertical-align: bottom;
}
}
.badge-svg {
left: calc((100% - 18px) / 2);
cursor: pointer;
color: $gray-400;
background: $white;
padding: 4.5px 6px;
&.item-selected-badge {
background: $purple-300;
color: $white;
}
}
.icon-12 {
width: 12px;
height: 12px;
}
span.badge.badge-pill.badge-item.badge-svg:not(.item-selected-badge) {
color: #a5a1ac;
}
span.badge.badge-pill.badge-item.badge-svg.hide {
display: none;
}
.background-button:hover {
span.badge.badge-pill.badge-item.badge-svg.hide {
display: block;
}
}
</style>
<script>
@@ -499,6 +537,7 @@ import avatar from './avatar';
import { getBackgroundShopSets } from '../../common/script/libs/shops';
import unlock from '../../common/script/ops/unlock';
import guide from 'client/mixins/guide';
import notifications from 'client/mixins/notifications';
import bModal from 'bootstrap-vue/lib/components/modal';
@@ -509,9 +548,12 @@ import skinIcon from 'assets/svg/skin.svg';
import hairIcon from 'assets/svg/hair.svg';
import backgroundsIcon from 'assets/svg/backgrounds.svg';
import gem from 'assets/svg/gem.svg';
import pin from 'assets/svg/pin.svg';
import _isPinned from './shops/_isPinned';
export default {
mixins: [guide],
mixins: [guide, notifications],
components: {
avatar,
bModal,
@@ -546,6 +588,7 @@ export default {
hairIcon,
backgroundsIcon,
gem,
pin,
}),
modalPage: 1,
activeTopPage: 'body',
@@ -674,6 +717,14 @@ export default {
if (this.user.purchased.background[bgKey]) backgroundClass = 'background-unlocked';
return backgroundClass;
},
isBackgroundPinned (bg) {
return _isPinned(this.user, bg);
},
togglePinned (bg) {
if (!this.$store.dispatch('user:togglePinnedItem', {type: bg.pinType, path: bg.path})) {
this.text(this.$t('unpinnedItem', {item: bg.text}));
}
},
},
};
</script>

View File

@@ -48,7 +48,7 @@
:item="context.item",
:key="context.item.key",
:itemContentClass="`${group.classPrefix}${context.item.key}`",
:highlightBorder="currentDraggingPotion != null",
:highlightBorder="isHatchable(currentDraggingPotion, context.item.key)",
v-drag.drop.hatch="context.item.key",
@itemDragOver="onDragOver($event, context.item)",
@@ -58,8 +58,8 @@
@click="onEggClicked($event, context.item)",
)
template(slot="popoverContent", scope="context")
h4.popover-content-title {{ context.item.text() }}
.popover-content-text(v-if="currentDraggingPotion == null") {{ context.item.notes() }}
h4.popover-content-title {{ context.item.text }}
.popover-content-text(v-if="currentDraggingPotion == null") {{ context.item.notes }}
template(slot="itemBadge", scope="context")
span.badge.badge-pill.badge-item.badge-quantity {{ context.item.quantity }}
@@ -86,8 +86,8 @@
@click="onPotionClicked($event, context.item)"
)
template(slot="popoverContent", scope="context")
h4.popover-content-title {{ context.item.text() }}
.popover-content-text {{ context.item.notes() }}
h4.popover-content-title {{ context.item.text }}
.popover-content-text {{ context.item.notes }}
template(slot="itemBadge", scope="context")
span.badge.badge-pill.badge-item.badge-quantity {{ context.item.quantity }}
@@ -107,8 +107,8 @@
:showPopover="currentDraggingPotion == null"
)
template(slot="popoverContent", scope="context")
h4.popover-content-title {{ context.item.text() }}
.popover-content-text {{ context.item.notes() }}
h4.popover-content-title {{ context.item.text }}
.popover-content-text {{ context.item.notes }}
template(slot="itemBadge", scope="context")
span.badge.badge-pill.badge-item.badge-quantity {{ context.item.quantity }}
@@ -121,13 +121,13 @@
div(v-if="currentDraggingPotion != null")
div.potion-icon(:class="'Pet_HatchingPotion_'+currentDraggingPotion.key")
div.popover
div.popover-content {{ $t('dragThisPotion', {potionName: currentDraggingPotion.text() }) }}
div.popover-content {{ $t('dragThisPotion', {potionName: currentDraggingPotion.text }) }}
div.hatchingPotionInfo.mouse(ref="clickPotionInfo", v-if="potionClickMode")
div(v-if="currentDraggingPotion != null")
div.potion-icon(:class="'Pet_HatchingPotion_'+currentDraggingPotion.key")
div.popover
div.popover-content {{ $t('clickOnEggToHatch', {potionName: currentDraggingPotion.text() }) }}
div.popover-content {{ $t('clickOnEggToHatch', {potionName: currentDraggingPotion.text }) }}
</template>
<style lang="scss" scoped>
@@ -143,12 +143,12 @@
}
.potion-icon {
margin: 0 auto;
margin: 0 auto 8px;
}
.popover {
position: inherit;
width: 100px;
width: 180px;
}
}
</style>
@@ -243,6 +243,8 @@ export default {
if (isSearched) {
itemsArray.push({
...item,
text: item.text(),
notes: item.notes(),
quantity: itemQuantity,
});
@@ -255,7 +257,7 @@ export default {
if (this.sortBy === 'quantity') {
return b.quantity - a.quantity;
} else { // AZ
return a.data.text().localeCompare(b.data.text());
return a.text.localeCompare(b.text);
}
});
});
@@ -264,7 +266,7 @@ export default {
},
},
methods: {
petExists (potionKey, eggKey) {
userHasPet (potionKey, eggKey) {
let animalKey = `${eggKey}-${potionKey}`;
let result = this.user.items.pets[animalKey] > 0;
@@ -290,10 +292,20 @@ export default {
dragEvent.dataTransfer.setDragImage(itemRef, -20, -20);
},
onDragOver ($event, egg) {
let potionKey = this.currentDraggingPotion.key;
isHatchable (potion, eggKey) {
if (potion === null)
return false;
if (this.petExists(potionKey, egg.key)) {
let petKey = `${eggKey}-${potion.key}`;
if (!this.content.petInfo[petKey])
return false;
return !this.userHasPet(potion.key, eggKey);
},
onDragOver ($event, egg) {
if (this.isHatchable(this.currentDraggingPotion, egg.key)) {
$event.dropable = false;
}
},
@@ -309,7 +321,7 @@ export default {
return;
}
if (!this.petExists(this.currentDraggingPotion.key, egg.key)) {
if (this.isHatchable(this.currentDraggingPotion, egg.key)) {
this.hatchPet(this.currentDraggingPotion, egg);
}

View File

@@ -144,13 +144,14 @@
:item="item",
:emptyItem="false",
:popoverPosition="'top'",
@click="selectedItemToBuy = item"
@click="itemSelected(item)"
)
span(slot="popoverContent")
h4.popover-content-title {{ item.text }}
template(slot="itemBadge", scope="ctx")
countBadge(
v-if="item.purchaseType !== 'card'",
:show="userItems[item.purchaseType][item.key] != 0",
:count="userItems[item.purchaseType][item.key] || 0"
)
@@ -161,6 +162,7 @@
)
span.svg-icon.inline.icon-12.color(v-html="icons.pin")
div.fill-height
drawer(
:title="$t('quickInventory')"
@@ -255,11 +257,22 @@
:itemContentClass="ctx.item.class",
:showPopover="false"
)
selectMembersModal(
:card="selectedCardToBuy",
:group="user.party",
@change="resetCardToBuy($event)",
@memberSelected="memberSelected($event)",
)
</template>
<style lang="scss">
@import '~client/assets/scss/colors.scss';
.fill-height {
height: 38px; // button + margin + padding
}
.badge-svg {
left: calc((100% - 18px) / 2);
cursor: pointer;
@@ -391,6 +404,7 @@
import SellModal from './sellModal.vue';
import BuyModal from '../buyModal.vue';
import EquipmentAttributesGrid from './equipmentAttributesGrid.vue';
import SelectMembersModal from './selectMembersModal.vue';
import bPopover from 'bootstrap-vue/lib/components/popover';
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
@@ -416,6 +430,8 @@
const sortGearTypes = ['sortByType', 'sortByPrice', 'sortByCon', 'sortByPer', 'sortByStr', 'sortByInt'];
import notifications from 'client/mixins/notifications';
const sortGearTypeMap = {
sortByType: 'type',
sortByPrice: 'value',
@@ -425,6 +441,7 @@
};
export default {
mixins: [notifications],
components: {
ShopItem,
Item,
@@ -443,6 +460,8 @@ export default {
BuyModal,
EquipmentAttributesGrid,
Avatar,
SelectMembersModal,
},
watch: {
searchText: _throttle(function throttleSearch () {
@@ -480,6 +499,8 @@ export default {
selectedGearToBuy: null,
selectedItemToBuy: null,
selectedCardToBuy: null,
hideLocked: false,
hidePinned: false,
};
@@ -494,13 +515,26 @@ export default {
}),
categories () {
if (this.market) {
this.market.categories.map((category) => {
let categories = [
...this.market.categories,
];
categories.push({
identifier: 'cards',
text: this.$t('cards'),
items: _map(_filter(this.content.cardTypes, (value) => {
return value.yearRound;
}), (value) => {
return getItemInfo(this.user, 'card', value);
}),
});
categories.map((category) => {
this.$set(this.viewOptions, category.identifier, {
selected: true,
});
});
return this.market.categories;
return categories;
} else {
return [];
}
@@ -684,6 +718,11 @@ export default {
this.selectedItemToBuy = null;
}
},
resetCardToBuy ($event) {
if (!$event) {
this.selectedCardToBuy = null;
}
},
isGearLocked (gear) {
if (gear.value > this.userStats.gp) {
return true;
@@ -707,6 +746,21 @@ export default {
buyItem (item) {
this.$store.dispatch('shops:purchase', {type: item.purchaseType, key: item.key});
},
itemSelected (item) {
if (item.purchaseType === 'card') {
if (this.user.party._id) {
this.selectedCardToBuy = item;
} else {
this.error(this.$t('errorNotInParty'));
}
} else {
this.selectedItemToBuy = item;
}
},
memberSelected (member) {
this.$store.dispatch('user:castSpell', {key: this.selectedCardToBuy.key, targetId: member.id});
this.selectedCardToBuy = null;
},
},
created () {
this.$store.dispatch('shops:fetchMarket');

View File

@@ -0,0 +1,194 @@
<template lang="pug">
div
b-modal#select-member-modal(
size='lg',
:visible="true",
v-if="card != null",
:hideFooter="true",
@change="onChange($event)"
)
.header-wrap(slot="modal-header")
.row
.col-6
h1(v-once) {{$t('selectPartyMember')}}
.col-6
button(type="button" aria-label="Close" class="close", @click='close()')
span(aria-hidden="true") ×
.row
.form-group.col-6
input.form-control.search(type="text", :placeholder="$t('search')", v-model='searchTerm')
.col-4.offset-2
span.dropdown-label {{ $t('sortBy') }}
b-dropdown(:text="$t('sort')", right=true)
b-dropdown-item(v-for='sortOption in sortOptions', @click='sort(sortOption.value)', :key='sortOption.value') {{sortOption.text}}
.row(v-for='member in sortedMembers')
.col-10
member-details(:member='member')
.col-2.actions
button.btn.btn-primary(@click="selectMember(member)") {{ $t('select') }}
.row.gradient(v-if='members.length > 3')
</template>
<style lang='scss'>
#select-member-modal {
header {
background-color: #edecee;
border-radius: 4px 4px 0 0;
}
.header-wrap {
width: 100%;
}
h1 {
color: #4f2a93;
}
.actions {
padding-top: 5em;
.action-icon {
margin-right: 1em;
}
}
#select-member-modal_modal_body {
padding: 0;
max-height: 450px;
.col-8 {
margin-left: 0;
}
.member-details {
margin: 0;
}
.member-stats {
width: 382px;
height: 147px;
}
.gradient {
background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0), #ffffff);
height: 50px;
width: 100%;
position: absolute;
bottom: 0px;
margin-left: -15px;
}
}
.dropdown-icon-item .svg-icon {
width: 20px;
}
}
</style>
<script>
// @TODO: Move this under members directory
import sortBy from 'lodash/sortBy';
import bModal from 'bootstrap-vue/lib/components/modal';
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
import MemberDetails from '../../memberDetails';
import removeIcon from 'assets/members/remove.svg';
import messageIcon from 'assets/members/message.svg';
import starIcon from 'assets/members/star.svg';
export default {
props: ['group', 'hideBadge', 'card'],
components: {
bModal,
bDropdown,
bDropdownItem,
MemberDetails,
},
mounted () {
this.$root.$on('shown::modal', () => {
this.getMembers();
});
},
data () {
return {
sortOption: '',
members: [],
memberToRemove: '',
sortOptions: [
{
value: 'level',
text: this.$t('tier'),
},
{
value: 'name',
text: this.$t('name'),
},
{
value: 'lvl',
text: this.$t('level'),
},
{
value: 'class',
text: this.$t('class'),
},
],
searchTerm: '',
icons: Object.freeze({
removeIcon,
messageIcon,
starIcon,
}),
};
},
computed: {
sortedMembers () {
let sortedMembers = this.members;
if (!this.sortOption) return sortedMembers;
sortedMembers = sortBy(this.members, [(member) => {
if (this.sortOption === 'tier') {
if (!member.contributor) return;
return member.contributor.level;
} else if (this.sortOption === 'name') {
return member.profile.name;
} else if (this.sortOption === 'lvl') {
return member.stats.lvl;
} else if (this.sortOption === 'class') {
return member.stats.class;
}
}]);
return this.members;
},
},
methods: {
async getMembers () {
let groupId = this.$store.state.groupId || this.group._id;
if (groupId && groupId !== 'challenge') {
let members = await this.$store.dispatch('members:getGroupMembers', {
groupId,
includeAllPublicFields: true,
});
this.members = members;
}
if (this.$store.state.viewingMembers.length > 0) {
this.members = this.$store.state.viewingMembers;
}
},
close () {
this.$root.$emit('hide::modal', 'select-member-modal');
},
sort (option) {
this.sortOption = option;
},
selectMember (member) {
this.$emit('memberSelected', member);
},
onChange ($event) {
this.$emit('change', $event);
},
},
};
</script>

View File

@@ -23,7 +23,7 @@
h4.title {{ itemText }}
div.text(v-html="itemNotes")
slot(name="additionalInfo", :item="item")
questInfo.questInfo(:quest="item")
div
span.svg-icon.inline.icon-32(aria-hidden="true", v-html="(priceType === 'gems') ? icons.gem : icons.gold")
@@ -81,7 +81,19 @@
.inner-content {
margin: 33px auto auto;
width: 282px;
width: 400px;
}
.text {
max-height: 220px;
margin-bottom: 8px;
overflow-y: scroll;
text-overflow: ellipsis;
}
.questInfo {
width: 70%;
margin: 0 auto;
margin-bottom: 10px;
}
.content-text {
@@ -222,12 +234,14 @@
import BalanceInfo from '../balanceInfo.vue';
import currencyMixin from '../_currencyMixin';
import QuestInfo from './questInfo.vue';
export default {
mixins: [currencyMixin],
components: {
bModal,
BalanceInfo,
QuestInfo,
},
data () {
return {

View File

@@ -55,9 +55,9 @@
@click="selectedItemToBuy = item"
)
template(slot="popoverContent", scope="ctx")
div
div.questPopover
h4.popover-content-title {{ item.text }}
.popover-content-text(v-html="item.notes")
questInfo(:quest="item")
template(slot="itemBadge", scope="ctx")
span.badge.badge-pill.badge-item.badge-svg(
@@ -105,9 +105,9 @@
@click="selectedItemToBuy = ctx.item"
)
span(slot="popoverContent", scope="ctx")
div
div.questPopover
h4.popover-content-title {{ ctx.item.text }}
.popover-content-text(v-html="ctx.item.notes")
questInfo(:quest="ctx.item")
template(slot="itemBadge", scope="ctx")
span.badge.badge-pill.badge-item.badge-svg(
@@ -135,9 +135,9 @@
@click="selectedItemToBuy = item"
)
span(slot="popoverContent")
div
div.questPopover
h4.popover-content-title {{ item.text }}
.popover-content-text(v-html="item.notes")
questInfo(:quest="item")
template(slot="itemBadge", scope="ctx")
span.badge.badge-pill.badge-item.badge-svg(
@@ -162,9 +162,9 @@
@click="selectedItemToBuy = item"
)
span(slot="popoverContent")
div
div.questPopover
h4.popover-content-title {{ item.text }}
.popover-content-text(v-html="item.notes")
questInfo(:quest="item")
template(slot="itemBadge", scope="ctx")
span.badge.badge-pill.badge-item.badge-svg(
@@ -331,6 +331,7 @@
import Avatar from 'client/components/avatar';
import BuyModal from './buyQuestModal.vue';
import QuestInfo from './questInfo.vue';
import bPopover from 'bootstrap-vue/lib/components/popover';
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
@@ -363,6 +364,7 @@ export default {
Avatar,
BuyModal,
QuestInfo,
},
watch: {
searchText: _throttle(function throttleSearch () {

View File

@@ -0,0 +1,88 @@
<template lang="pug">
div.row
span.col-4(v-if="quest.collect") {{ $t('collect') }}
span.col-8(v-if="quest.collect")
div(v-for="(collect, key) of quest.collect")
span {{ collect.count }} {{ collect.text }}
span.col-4 {{ $t('difficulty') }}
span.col-8
span.svg-icon.inline.icon-16(v-for="star of stars()", v-html="icons[star]")
</template>
<style lang="scss" scoped>
@import '~client/assets/scss/colors.scss';
.col-4{
text-align: left;
font-weight: bold;
white-space: nowrap;
height: 16px;
width: 80px;
}
.col-8 {
text-align: right;
}
.col-8:not(:last-child) {
margin-bottom: 4px;
}
span.svg-icon.inline.icon-16 {
margin-right: 4px;
}
</style>
<script>
import svgStar from 'assets/svg/difficulty-star.svg';
import svgStarHalf from 'assets/svg/difficulty-star-half.svg';
import svgStarEmpty from 'assets/svg/difficulty-star-empty.svg';
export default {
data () {
return {
icons: Object.freeze({
star: svgStar,
starHalf: svgStarHalf,
starEmpty: svgStarEmpty,
}),
};
},
computed: {
difficulty () {
if (this.quest.boss) {
return this.quest.boss.str;
}
return 1;
},
},
methods: {
stars () {
let result = [];
let difficulty = this.difficulty;
for (let i = 1; i <= 4; i++) {
let diff = difficulty - i;
if (diff >= 0) {
result.push('star');
} else if (diff <= -1) {
result.push('starEmpty');
} else {
result.push('starHalf');
}
}
return result;
},
},
props: {
quest: {
type: Object,
},
},
};
</script>

View File

@@ -105,6 +105,14 @@
:item="ctx.item",
v-if="ctx.item.purchaseType === 'gear'"
)
selectMembersModal(
:card="selectedCardToBuy",
:group="user.party",
@change="resetCardToBuy($event)",
@memberSelected="memberSelectedToSendCard($event)",
)
spells
</template>
@@ -292,6 +300,7 @@ import BuyModal from 'client/components/shops/buyModal.vue';
import Item from 'client/components/inventory/item.vue';
import Avatar from 'client/components/avatar';
import EquipmentAttributesGrid from 'client/components/shops/market/equipmentAttributesGrid.vue';
import SelectMembersModal from 'client/components/shops/market/selectMembersModal.vue';
export default {
components: {
@@ -304,6 +313,7 @@ export default {
Avatar,
EquipmentAttributesGrid,
spells,
SelectMembersModal,
},
data () {
return {
@@ -329,6 +339,7 @@ export default {
creatingTask: null,
selectedItemToBuy: null,
selectedCardToBuy: null,
};
},
computed: {
@@ -452,13 +463,20 @@ export default {
this.selectedItemToBuy = null;
}
},
resetCardToBuy ($event) {
if (!$event) {
this.selectedCardToBuy = null;
}
},
memberOverrideAvatarGear (gear) {
return {
[gear.type]: gear.key,
};
},
buyItem (item) {
if (item.currency === 'gold') {
if (item.purchaseType === 'card') {
this.selectedCardToBuy = item;
} else if (item.currency === 'gold') {
this.$store.dispatch('shops:buyItem', {key: item.key});
} else {
this.$store.dispatch('shops:purchase', {type: item.purchaseType, key: item.key});
@@ -467,6 +485,10 @@ export default {
openBuyDialog (rewardItem) {
this.selectedItemToBuy = rewardItem;
},
memberSelectedToSendCard (member) {
this.$store.dispatch('user:castSpell', {key: this.selectedCardToBuy.key, targetId: member.id});
this.selectedCardToBuy = null;
},
},
};
</script>

View File

@@ -10,7 +10,7 @@ span.badge.badge-pill.badge-item.badge-count(
.badge-count {
right: -9px;
color: $white;
background: $orange-100;
background: $gray-200;
padding: 4.5px 6px;
min-width: 24px;
height: 24px;

View File

@@ -71,3 +71,10 @@ export function togglePinnedItem (store, params) {
return addedItem;
}
export function castSpell (store, params) {
let spellUrl = `/api/v3/user/class/cast/${params.key}`;
if (params.targetId) spellUrl += `?targetId=${params.targetId}`;
return axios.post(spellUrl);
}

View File

@@ -47,7 +47,7 @@
"dragThisFood": "Drag this <%= foodName %> to a Pet and watch it grow!",
"clickOnPetToFeed": "Click on a Pet to feed <%= foodName %> and watch it grow!",
"dragThisPotion": "Drag this <%= potionName %> to an Egg and hatch a new pet!",
"clickOnEggToHatch": "Click on an Egg to use your <%= potionName %> and hatch a new pet!",
"clickOnEggToHatch": "Click on an Egg to use your <%= potionName %> hatching potion and hatch a new pet!",
"hatchDialogText": "Pour your <%= potionName %> hatching potion on your <%= eggName %> egg, and it will hatch into a <%= petName %> .",
"editAvatar": "Edit Avatar",
"sort": "Sort",
@@ -285,5 +285,7 @@
"sortByName": "Name",
"haveHatchablePet": "You have a <%= potion %> hatching potion and <%= egg %> egg to hatch this pet! <b>Click</b> the paw print to hatch.",
"welcomeBack": "Welcome back!",
"checkOffYesterDailies": "Check off any Dailies you did yesterday:"
"checkOffYesterDailies": "Check off any Dailies you did yesterday:",
"selectPartyMember": "Select a Party Member",
"errorNotInParty": "You are not in a Party"
}

View File

@@ -71,6 +71,7 @@ api.bundles = {
return moment().isBetween('2017-05-16', '2017-05-31');
},
type: 'quests',
class: 'quest_bundle_featheredFriends',
value: 7,
},
splashyPals: {
@@ -86,6 +87,7 @@ api.bundles = {
return moment().isBetween('2017-07-11', '2017-08-02');
},
type: 'quests',
class: 'quest_bundle_splashyPals',
value: 7,
},
};

View File

@@ -5,29 +5,33 @@ const featuredItems = {
path: 'armoire',
},
{
type: 'hatchingPotion',
type: 'hatchingPotions',
path: 'hatchingPotions.Golden',
},
{
type: 'food',
path: 'food.Saddle',
},
{
type: 'card',
path: 'cardTypes.greeting',
},
],
quests: [
{
type: 'quest',
type: 'quests',
path: 'quests.gryphon',
},
{
type: 'quest',
type: 'quests',
path: 'quests.dilatoryDistress1',
},
{
type: 'quest',
type: 'quests',
path: 'quests.nudibranch',
},
{
type: 'quest',
type: 'quests',
path: 'quests.taskwoodsTerror1',
},
],

View File

@@ -3,6 +3,8 @@ import content from '../content/index';
import { BadRequest } from './errors';
import count from '../count';
import _mapValues from 'lodash/mapValues';
function lockQuest (quest, user) {
if (quest.lvl && user.stats.lvl < quest.lvl) return true;
if (quest.unlockCondition && (quest.key === 'moon1' || quest.key === 'moon2' || quest.key === 'moon3')) {
@@ -42,7 +44,7 @@ module.exports = function getItemInfo (user, type, item, language = 'en') {
let itemInfo;
switch (type) {
case 'egg':
case 'eggs':
itemInfo = {
key: item.key,
text: i18n.t('egg', {eggType: item.text(language)}, language),
@@ -53,10 +55,10 @@ module.exports = function getItemInfo (user, type, item, language = 'en') {
currency: 'gems',
purchaseType: 'eggs',
path: `eggs.${item.key}`,
pinType: 'egg',
pinType: 'eggs',
};
break;
case 'hatchingPotion':
case 'hatchingPotions':
itemInfo = {
key: item.key,
text: i18n.t('potion', {potionType: item.text(language)}),
@@ -67,7 +69,7 @@ module.exports = function getItemInfo (user, type, item, language = 'en') {
currency: 'gems',
purchaseType: 'hatchingPotions',
path: `hatchingPotions.${item.key}`,
pinType: 'hatchingPotion',
pinType: 'hatchingPotions',
};
break;
case 'premiumHatchingPotion':
@@ -98,20 +100,20 @@ module.exports = function getItemInfo (user, type, item, language = 'en') {
pinType: 'food',
};
break;
case 'questBundle':
case 'bundles':
itemInfo = {
key: item.key,
text: item.text(language),
notes: item.notes(language),
value: item.value,
currency: 'gems',
class: `quest_bundle_${item.key}`,
class: item.class,
purchaseType: 'bundles',
path: `bundles.${item.key}`,
pinType: 'questBundle',
pinType: 'bundles',
};
break;
case 'quest': // eslint-disable-line no-case-declarations
case 'quests': // eslint-disable-line no-case-declarations
const locked = lockQuest(item, user);
itemInfo = {
@@ -125,13 +127,19 @@ module.exports = function getItemInfo (user, type, item, language = 'en') {
unlockCondition: item.unlockCondition,
drop: item.drop,
boss: item.boss,
collect: item.collect,
collect: item.collect ? _mapValues(item.collect, (o) => {
return {
count: o.count,
text: o.text(),
};
}) : undefined,
lvl: item.lvl,
class: locked ? `inventory_quest_scroll_locked inventory_quest_scroll_${item.key}_locked` : `inventory_quest_scroll inventory_quest_scroll_${item.key}`,
purchaseType: 'quests',
path: `quests.${item.key}`,
pinType: 'quest',
pinType: 'quests',
};
break;
case 'timeTravelers':
// TODO
@@ -235,6 +243,23 @@ module.exports = function getItemInfo (user, type, item, language = 'en') {
path: 'armoire',
pinType: 'armoire',
};
break;
case 'card': {
let spellInfo = content.spells.special[item.key];
itemInfo = {
key: item.key,
purchaseType: 'card',
class: `inventory_special_${item.key}`,
text: spellInfo.text(),
notes: spellInfo.notes(),
value: spellInfo.value,
currency: 'gold',
path: `cardTypes.${item.key}`,
pinType: 'card',
};
break;
}
}
if (itemInfo) {

View File

@@ -24,7 +24,7 @@ shops.getMarketCategories = function getMarket (user, language) {
.filter(egg => egg.canBuy(user))
.concat(values(content.dropEggs))
.map(egg => {
return getItemInfo(user, 'egg', egg, language);
return getItemInfo(user, 'eggs', egg, language);
}), 'key');
categories.push(eggsCategory);
@@ -36,7 +36,7 @@ shops.getMarketCategories = function getMarket (user, language) {
hatchingPotionsCategory.items = sortBy(values(content.hatchingPotions)
.filter(hp => !hp.limited)
.map(hatchingPotion => {
return getItemInfo(user, 'hatchingPotion', hatchingPotion, language);
return getItemInfo(user, 'hatchingPotions', hatchingPotion, language);
}), 'key');
categories.push(hatchingPotionsCategory);
@@ -134,7 +134,7 @@ shops.getQuestShopCategories = function getQuestShopCategories (user, language)
bundleCategory.items = sortBy(values(content.bundles)
.filter(bundle => bundle.type === 'quests' && bundle.canBuy())
.map(bundle => {
return getItemInfo(user, 'questBundle', bundle, language);
return getItemInfo(user, 'bundles', bundle, language);
}));
if (bundleCategory.items.length > 0) {
@@ -150,7 +150,7 @@ shops.getQuestShopCategories = function getQuestShopCategories (user, language)
category.items = content.questsByLevel
.filter(quest => quest.canBuy(user) && quest.category === type)
.map(quest => {
return getItemInfo(user, 'quest', quest, language);
return getItemInfo(user, 'quests', quest, language);
});
categories.push(category);

View File

@@ -11,6 +11,9 @@ import {
BadRequest,
} from '../libs/errors';
import { removeItemByPath } from './pinnedGearUtils';
import getItemInfo from '../libs/getItemInfo';
module.exports = function purchase (user, req = {}, analytics) {
let type = get(req.params, 'type');
let key = get(req.params, 'key');
@@ -103,6 +106,9 @@ module.exports = function purchase (user, req = {}, analytics) {
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
}
let itemInfo = getItemInfo(user, type, item);
removeItemByPath(user, itemInfo.path);
user.balance -= price;
if (type === 'gear') {