From f5cf27a79e69a93b5f2074c70228e530f07c3c1e Mon Sep 17 00:00:00 2001 From: negue Date: Thu, 17 Aug 2017 00:34:25 +0200 Subject: [PATCH] [WIP] Client/multiple fixes shops (#8956) * reposition FlyingPigs and Hydra * hide count-badge if 0 * fix sortBy hatchable (ignore already hatched pets) * always show animal name * featuredItems, use shopItem objects * fix egg / potion names in market * buyModals: check for price, mark it if not enough available / change buy-button opacity / show purchaseGems button * save itemRows open/collapsed state during session, refactor itemRows for some more performance * pin featured items * show bordered items in market buyModal * fix popover margins / paddings * position pinned items popovers to the left --- website/client/assets/scss/item.scss | 6 ++ website/client/assets/scss/popover.scss | 9 ++- .../components/inventory/equipment/index.vue | 1 + .../components/inventory/items/index.vue | 3 + .../components/inventory/stable/index.vue | 26 +++++--- .../client/components/shops/_currencyMixin.js | 25 +++++++ .../client/components/shops/balanceInfo.vue | 65 ++++++++++++++----- website/client/components/shops/buyModal.vue | 35 ++++++++-- .../client/components/shops/market/index.vue | 37 +++++++++-- .../components/shops/market/sellModal.vue | 5 +- .../components/shops/quests/buyQuestModal.vue | 29 ++++++++- .../client/components/shops/quests/index.vue | 15 ++++- .../components/shops/timeTravelers/index.vue | 1 + website/client/components/tasks/column.vue | 3 +- website/client/components/ui/itemRows.vue | 63 +++++++++++++----- website/client/libs/createAnimal.js | 2 +- website/client/store/index.js | 1 + .../script/content/shop-featuredItems.js | 49 ++++++++++---- 18 files changed, 302 insertions(+), 73 deletions(-) create mode 100644 website/client/components/shops/_currencyMixin.js diff --git a/website/client/assets/scss/item.scss b/website/client/assets/scss/item.scss index d9468d9f9d..17688fdeb0 100644 --- a/website/client/assets/scss/item.scss +++ b/website/client/assets/scss/item.scss @@ -54,6 +54,12 @@ .flat .item { box-shadow: none; + border: none; +} + +.bordered-item .item { + background: $gray-700 !important; + margin: 0 auto; } .drawer-content .item:hover { diff --git a/website/client/assets/scss/popover.scss b/website/client/assets/scss/popover.scss index 85a93232a6..b1758cea03 100644 --- a/website/client/assets/scss/popover.scss +++ b/website/client/assets/scss/popover.scss @@ -21,10 +21,17 @@ line-height: 1.14; } -.popover-content-text { +.popover-content-text:not(:last-child) { margin-bottom: 16px; } +.popover-content-title:last-child { + margin-bottom: 0px; +} +.popover-content-text:last-child { + margin-bottom: 0px; +} + .popover-content-attr { width: 50%; display: inline-block; diff --git a/website/client/components/inventory/equipment/index.vue b/website/client/components/inventory/equipment/index.vue index a5b0aa12ee..b133c32246 100644 --- a/website/client/components/inventory/equipment/index.vue +++ b/website/client/components/inventory/equipment/index.vue @@ -99,6 +99,7 @@ :items="sortItems(items[group.key], selectedSortGearBy)", :itemWidth=94, :itemMargin=24, + :type="group.key", :noItemsLabel="$t('noGearItemsOfType', { type: group.label })" ) template(slot="item", scope="context") diff --git a/website/client/components/inventory/items/index.vue b/website/client/components/inventory/items/index.vue index eac6f96bc5..d720c62fd9 100644 --- a/website/client/components/inventory/items/index.vue +++ b/website/client/components/inventory/items/index.vue @@ -40,6 +40,7 @@ :items="items[group.key]", :itemWidth=94, :itemMargin=24, + :type="group.key", :noItemsLabel="$t('noGearItemsOfType', { type: $t(group.key) })" ) template(slot="item", scope="context") @@ -67,6 +68,7 @@ :items="items[group.key]", :itemWidth=94, :itemMargin=24, + :type="group.key", :noItemsLabel="$t('noGearItemsOfType', { type: $t(group.key) })" ) template(slot="item", scope="context") @@ -94,6 +96,7 @@ :items="items[group.key]", :itemWidth=94, :itemMargin=24, + :type="group.key", :noItemsLabel="$t('noGearItemsOfType', { type: $t(group.key) })" ) template(slot="item", scope="context") diff --git a/website/client/components/inventory/stable/index.vue b/website/client/components/inventory/stable/index.vue index 94ce565936..49f14c5a12 100644 --- a/website/client/components/inventory/stable/index.vue +++ b/website/client/components/inventory/stable/index.vue @@ -84,6 +84,7 @@ :items="pets(petGroup, hideMissing, selectedSortBy, searchTextThrottled)", :itemWidth=94, :itemMargin=24, + :type="petGroup.key", ) template(slot="item", scope="context") div( @@ -99,14 +100,12 @@ :popoverPosition="'top'", :progress="context.item.progress", :emptyItem="!context.item.isOwned()", - :showPopover="context.item.isOwned() || context.item.isHatchable()", + :showPopover="true", :highlightBorder="highlightPet == context.item.key", @click="petClicked(context.item)" ) span(slot="popoverContent") - div(v-if="context.item.isOwned()") - h4.popover-content-title {{ context.item.name }} - div.hatchablePopover(v-else-if="context.item.isHatchable()") + div.hatchablePopover(v-if="context.item.isHatchable()") h4.popover-content-title {{ context.item.name }} div.popover-content-text(v-html="$t('haveHatchablePet', { potion: context.item.potionName, egg: context.item.eggName })") div.potionEggGroup @@ -115,6 +114,9 @@ div.potionEggBackground div(:class="'Pet_Egg_'+context.item.eggKey") + div(v-else) + h4.popover-content-title {{ context.item.name }} + template(slot="itemBadge", scope="context") starBadge( :selected="context.item.key === currentPet", @@ -138,6 +140,7 @@ :items="mounts(mountGroup, hideMissing, selectedSortBy, searchTextThrottled)", :itemWidth=94, :itemMargin=24, + :type="mountGroup.key", ) template(slot="item", scope="context") mountItem( @@ -146,7 +149,7 @@ :key="context.item.key", :popoverPosition="'top'", :emptyItem="!context.item.isOwned()", - :showPopover="context.item.isOwned()", + :showPopover="true", @click="selectMount(context.item)" ) span(slot="popoverContent") @@ -273,11 +276,18 @@ display: inline-block; } - .stable .item .item-content.Pet { - position: absolute; + .stable .item .item-content.Pet:not(.FlyingPig) { top: -28px; } + .stable .item .item-content.FlyingPig { + top: 7px; + } + + .stable .item .item-content.Pet-Dragon-Hydra { + top: -16px !important; + } + .hatchablePopover { width: 180px } @@ -833,7 +843,7 @@ getPetItemClass (pet) { if (pet.isOwned()) { - return `Pet Pet-${pet.key}`; + return `Pet Pet-${pet.key} ${pet.eggKey}`; } if (pet.mountOwned()) { diff --git a/website/client/components/shops/_currencyMixin.js b/website/client/components/shops/_currencyMixin.js new file mode 100644 index 0000000000..21f4190825 --- /dev/null +++ b/website/client/components/shops/_currencyMixin.js @@ -0,0 +1,25 @@ +import { mapState, mapGetters } from 'client/libs/store'; + +export default { + computed: { + ...mapGetters({ + userGems: 'user:gems', + }), + ...mapState({ + userHourglasses: 'user.data.purchased.plan.consecutive.trinkets', + userGold: 'user.data.stats.gp', + }), + }, + methods: { + enoughCurrency (currency, amount) { + switch (currency) { + case 'gold': + return this.userGold >= amount; + case 'gems': + return this.userGems >= amount; + case 'hourglasses': + return this.userHourglasses >= amount; + } + }, + }, +}; diff --git a/website/client/components/shops/balanceInfo.vue b/website/client/components/shops/balanceInfo.vue index a4955c2f7c..80ba7dbae3 100644 --- a/website/client/components/shops/balanceInfo.vue +++ b/website/client/components/shops/balanceInfo.vue @@ -1,12 +1,11 @@ diff --git a/website/client/components/shops/buyModal.vue b/website/client/components/shops/buyModal.vue index 40c96594fc..c61382914f 100644 --- a/website/client/components/shops/buyModal.vue +++ b/website/client/components/shops/buyModal.vue @@ -25,15 +25,28 @@ slot(name="additionalInfo", :item="item") - div - span.svg-icon.inline.icon-32(aria-hidden="true", v-html="icons[getSvgClass()]") - span.value(:class="getSvgClass()") {{ item.value }} + div(:class="{'notEnough': !this.enoughCurrency(getPriceClass(), item.value)}") + span.svg-icon.inline.icon-32(aria-hidden="true", v-html="icons[getPriceClass()]") + span.value(:class="getPriceClass()") {{ item.value }} - button.btn.btn-primary(@click="buyItem()") {{ $t('buyNow') }} + button.btn.btn-primary( + @click="purchaseGems()", + v-if="getPriceClass() === 'gems' && !this.enoughCurrency(getPriceClass(), item.value)" + ) {{ $t('purchaseGems') }} + + button.btn.btn-primary( + @click="buyItem()", + v-else, + :class="{'notEnough': !this.enoughCurrency(getPriceClass(), item.value)}" + ) {{ $t('buyNow') }} div.clearfix(slot="modal-footer") span.balance.float-left {{ $t('yourBalance') }} - balanceInfo(:withHourglass="getSvgClass() === 'hourglasses'").float-right + balanceInfo( + :withHourglass="getPriceClass() === 'hourglasses'", + :currencyNeeded="getPriceClass()", + :amountNeeded="item.value" + ).float-right @@ -136,7 +149,10 @@ } } - + .notEnough { + pointer-events: none; + opacity: 0.55; + } } @@ -150,8 +166,10 @@ import svgPin from 'assets/svg/pin.svg'; import BalanceInfo from './balanceInfo.vue'; + import currencyMixin from './_currencyMixin'; export default { + mixins: [currencyMixin], components: { bModal, BalanceInfo, @@ -191,13 +209,16 @@ this.$emit('buyPressed', this.item); this.hideDialog(); }, + purchaseGems () { + this.$root.$emit('show::modal', 'buy-gems'); + }, togglePinned () { this.$emit('togglePinned', this.item); }, hideDialog () { this.$root.$emit('hide::modal', 'buy-modal'); }, - getSvgClass () { + getPriceClass () { if (this.priceType && this.icons[this.priceType]) { return this.priceType; } else { diff --git a/website/client/components/shops/market/index.vue b/website/client/components/shops/market/index.vue index 0685ea8fc4..0e4d101ef7 100644 --- a/website/client/components/shops/market/index.vue +++ b/website/client/components/shops/market/index.vue @@ -53,6 +53,12 @@ :popoverPosition="'top'", @click="selectedGearToBuy = item" ) + template(slot="itemBadge", scope="ctx") + span.badge.badge-pill.badge-item.badge-svg( + :class="{'item-selected-badge': ctx.item.pinned, 'hide': !ctx.item.pinned}", + @click.prevent.stop="togglePinned(ctx.item)" + ) + span.svg-icon.inline.icon-12.color(v-html="icons.pin") h1.mb-0.page-header(v-once) {{ $t('market') }} @@ -92,8 +98,7 @@ :items="filteredGear(selectedGroupGearByClass, searchTextThrottled, selectedSortGearBy, hideLocked, hidePinned)", :itemWidth=94, :itemMargin=24, - :showAllLabel="$t('showAllGeneric', { type: getClassName(selectedGroupGearByClass) + ' '+$t('equipment') })", - :showLessLabel="$t('showLessGeneric', { type: getClassName(selectedGroupGearByClass) + ' '+$t('equipment') })" + :type="'gear'", ) template(slot="item", scope="ctx") shopItem( @@ -146,7 +151,7 @@ template(slot="itemBadge", scope="ctx") countBadge( - :show="true", + :show="userItems[item.purchaseType][item.key] != 0", :count="userItems[item.purchaseType][item.key] || 0" ) @@ -196,12 +201,13 @@ :count="userItems[drawerTabs[selectedDrawerTab].contentType][ctx.item.key] || 0" ) span(slot="popoverContent") - h4.popover-content-title {{ ctx.item.text() }} + h4.popover-content-title {{ getItemName(selectedDrawerItemType, ctx.item) }} sellModal( :item="selectedItemToSell", :itemType="selectedDrawerItemType", :itemCount="selectedItemToSell != null ? userItems[drawerTabs[selectedDrawerTab].contentType][selectedItemToSell.key] : 0", + :text="selectedItemToSell != null ? getItemName(selectedDrawerItemType, selectedItemToSell) : ''", @change="resetItemToSell($event)" ) template(slot="item", scope="ctx") @@ -244,7 +250,7 @@ @togglePinned="togglePinned($event)" ) template(slot="item", scope="ctx") - item.flat( + item.flat.bordered-item( :item="ctx.item", :itemContentClass="ctx.item.class", :showPopover="false" @@ -302,6 +308,11 @@ padding: 24px 24px 10px; } + .item-wrapper.bordered-item .item { + width: 112px; + height: 112px; + } + .market { .avatar { cursor: default; @@ -398,6 +409,7 @@ import _filter from 'lodash/filter'; import _sortBy from 'lodash/sortBy'; import _map from 'lodash/map'; + import _get from 'lodash/get'; import _throttle from 'lodash/throttle'; import _isPinned from '../_isPinned'; @@ -520,7 +532,10 @@ export default { featuredItems () { return featuredItems.market.map(i => { - return getItemInfo(this.user, 'marketGear', this.content.gear.flat[i]); + let newItem = getItemInfo(this.user, i.type, _get(this.content, i.path)); + newItem.pinned = _isPinned(this.user, newItem); + + return newItem; }); }, }, @@ -571,6 +586,16 @@ export default { return ''; } }, + getItemName (type, item) { + switch (type) { + case 'eggs': + return this.$t('egg', {eggType: item.text()}); + case 'hatchingPotions': + return this.$t('potion', {potionType: item.text()}); + default: + return item.text(); + } + }, filteredGear (groupByClass, searchBy, sortBy, hideLocked, hidePinned) { let result = _filter(this.content.gear.flat, ['klass', groupByClass]); result = _map(result, (e) => { diff --git a/website/client/components/shops/market/sellModal.vue b/website/client/components/shops/market/sellModal.vue index dbb80e1c5c..a26ca32584 100644 --- a/website/client/components/shops/market/sellModal.vue +++ b/website/client/components/shops/market/sellModal.vue @@ -12,7 +12,7 @@ div.inner-content slot(name="item", :item="item") - h4.title {{ item.text() }} + h4.title {{ text ? text : item.text() }} div.text {{ item.notes() }} div @@ -176,6 +176,9 @@ itemType: { type: String, }, + text: { + type: String, + }, itemCount: { type: Number, }, diff --git a/website/client/components/shops/quests/buyQuestModal.vue b/website/client/components/shops/quests/buyQuestModal.vue index 2b8d5671e8..ccc156a5e7 100644 --- a/website/client/components/shops/quests/buyQuestModal.vue +++ b/website/client/components/shops/quests/buyQuestModal.vue @@ -29,7 +29,17 @@ span.svg-icon.inline.icon-32(aria-hidden="true", v-html="(priceType === 'gems') ? icons.gem : icons.gold") span.value(:class="priceType") {{ item.value }} - button.btn.btn-primary(@click="buyItem()") {{ $t('buyNow') }} + button.btn.btn-primary( + @click="purchaseGems()", + v-if="priceType === 'gems' && !this.enoughCurrency(priceType, item.value)" + ) {{ $t('purchaseGems') }} + + + button.btn.btn-primary( + @click="buyItem()", + v-else, + :class="{'notEnough': !this.enoughCurrency(priceType, item.value)}" + ) {{ $t('buyNow') }} div.right-sidebar(v-if="item.drop") h3(v-once) {{ $t('rewards') }} @@ -46,7 +56,10 @@ div.clearfix(slot="modal-footer") span.balance.float-left {{ $t('yourBalance') }} - balanceInfo.float-right + balanceInfo( + :currencyNeeded="priceType", + :amountNeeded="item.value" + ).float-right @@ -186,6 +199,12 @@ color: $white; } } + + + .notEnough { + pointer-events: none; + opacity: 0.55; + } } @@ -202,8 +221,10 @@ import svgExperience from 'assets/svg/experience.svg'; import BalanceInfo from '../balanceInfo.vue'; + import currencyMixin from '../_currencyMixin'; export default { + mixins: [currencyMixin], components: { bModal, BalanceInfo, @@ -284,6 +305,10 @@ return `Unknown type: ${drop.type}`; } }, + + purchaseGems () { + this.$root.$emit('show::modal', 'buy-gems'); + }, }, props: { item: { diff --git a/website/client/components/shops/quests/index.vue b/website/client/components/shops/quests/index.vue index 205d8a7fb8..4d3cb9b544 100644 --- a/website/client/components/shops/quests/index.vue +++ b/website/client/components/shops/quests/index.vue @@ -59,6 +59,14 @@ h4.popover-content-title {{ item.text }} .popover-content-text(v-html="item.notes") + template(slot="itemBadge", scope="ctx") + span.badge.badge-pill.badge-item.badge-svg( + :class="{'item-selected-badge': ctx.item.pinned, 'hide': !ctx.item.pinned}", + @click.prevent.stop="togglePinned(ctx.item)" + ) + span.svg-icon.inline.icon-12.color(v-html="icons.pin") + + h1.mb-0.page-header(v-once) {{ $t('quests') }} .clearfix @@ -84,6 +92,7 @@ :items="questItems(category, selectedSortItemsBy, searchTextThrottled, hideLocked, hidePinned)", :itemWidth=94, :itemMargin=24, + :type="'pet_quests'", ) template(slot="item", scope="ctx") shopItem( @@ -338,6 +347,7 @@ import _throttle from 'lodash/throttle'; import _groupBy from 'lodash/groupBy'; import _map from 'lodash/map'; + import _get from 'lodash/get'; export default { components: { @@ -403,7 +413,10 @@ export default { featuredItems () { return featuredItems.quests.map(i => { - return getItemInfo(this.user, 'quest', this.content.quests[i]); + let newItem = getItemInfo(this.user, i.type, _get(this.content, i.path)); + newItem.pinned = _isPinned(this.user, newItem); + + return newItem; }); }, }, diff --git a/website/client/components/shops/timeTravelers/index.vue b/website/client/components/shops/timeTravelers/index.vue index 9e190da844..32dfcc375a 100644 --- a/website/client/components/shops/timeTravelers/index.vue +++ b/website/client/components/shops/timeTravelers/index.vue @@ -78,6 +78,7 @@ :items="travelersItems(category, selectedSortItemsBy, searchTextThrottled, hidePinned)", :itemWidth=94, :itemMargin=24, + :type="category.identifier", ) template(slot="item", scope="ctx") shopItem( diff --git a/website/client/components/tasks/column.vue b/website/client/components/tasks/column.vue index 931bf8bb05..f87c7ce4bb 100644 --- a/website/client/components/tasks/column.vue +++ b/website/client/components/tasks/column.vue @@ -25,7 +25,8 @@ :item="reward", :key="reward.key", :highlightBorder="reward.isSuggested", - @click="openBuyDialog(reward)" + @click="openBuyDialog(reward)", + :popoverPosition="'left'" ) .column-background( diff --git a/website/client/components/ui/itemRows.vue b/website/client/components/ui/itemRows.vue index c05b24a683..034f21225e 100644 --- a/website/client/components/ui/itemRows.vue +++ b/website/client/components/ui/itemRows.vue @@ -1,18 +1,18 @@