Refactor/market vue (#10601)

* extract inventoryDrawer from market

* show scrollbar only if needed

* extract featuredItemsHeader / pinUtils

* extract pageLayout

* extract layoutSection / filterDropdown - fix sortByNumber

* rollback sortByNumber order-fix

* move equipment lists out of the layout-section (for now)

* refactor sellModal

* extract checkbox

* extract equipment section

* extract category row

* revert scroll - remove sellModal item template

* fix(lint): commas and semis

* Created category item component (#10613)

* extract filter sidebar

* fix gemCount - fix raising the item count if the item wasn't previously owned

* fixes #10659

* remove unneeded method
This commit is contained in:
negue
2018-09-09 12:05:33 +02:00
committed by Matteo Pagliazzi
parent a18e9b3b18
commit 92e4d5cd68
23 changed files with 1048 additions and 662 deletions

View File

@@ -29,7 +29,6 @@ div
buyModal( buyModal(
:item="selectedItemToBuy || {}", :item="selectedItemToBuy || {}",
:withPin="true", :withPin="true",
@change="resetItemToBuy($event)",
@buyPressed="customPurchase($event)", @buyPressed="customPurchase($event)",
:genericPurchase="genericPurchase(selectedItemToBuy)", :genericPurchase="genericPurchase(selectedItemToBuy)",
@@ -567,13 +566,6 @@ export default {
}); });
} }
}, },
resetItemToBuy ($event) {
// @TODO: Do we need this? I think selecting a new item
// overwrites. @negue might know
if (!$event && this.selectedItemToBuy.purchaseType !== 'card') {
this.selectedItemToBuy = null;
}
},
itemSelected (item) { itemSelected (item) {
this.selectedItemToBuy = item; this.selectedItemToBuy = item;
}, },

View File

@@ -17,7 +17,7 @@ div
triggers="hover", triggers="hover",
placement="top", placement="top",
) )
h4.popover-content-title {{ item.text() }} h4.popover-content-title {{ itemName || item.text() }}
div.popover-content-text(v-html="item.notes()") div.popover-content-text(v-html="item.notes()")
</template> </template>
@@ -45,6 +45,9 @@ export default {
itemContentClass: { itemContentClass: {
type: String, type: String,
}, },
itemName: {
type: String,
},
active: { active: {
type: Boolean, type: Boolean,
}, },

View File

@@ -145,47 +145,17 @@
.btn.btn-flat.btn-show-more(@click="setShowMore(mountGroup.key)", v-if='mountGroup.key !== "specialMounts"') .btn.btn-flat.btn-show-more(@click="setShowMore(mountGroup.key)", v-if='mountGroup.key !== "specialMounts"')
| {{ $_openedItemRows_isToggled(mountGroup.key) ? $t('showLess') : $t('showMore') }} | {{ $_openedItemRows_isToggled(mountGroup.key) ? $t('showLess') : $t('showMore') }}
drawer(:title="$t('quickInventory')", inventoryDrawer
:errorMessage="(!hasDrawerTabItems(selectedDrawerTab)) ? ((selectedDrawerTab === 0) ? $t('noFoodAvailable') : $t('noSaddlesAvailable')) : null") template(slot="item", slot-scope="ctx")
div(slot="drawer-header") foodItem(
.drawer-tab-container :item="ctx.item",
.drawer-tab.text-right :itemCount="ctx.itemCount",
a.drawer-tab-text( :itemContentClass="ctx.itemClass",
@click="selectedDrawerTab = 0", :active="currentDraggingFood === ctx.item",
:class="{'drawer-tab-text-active': selectedDrawerTab === 0}", @itemDragEnd="onDragEnd()",
) {{ drawerTabs[0].label }} @itemDragStart="onDragStart($event, ctx.item)",
.clearfix @itemClick="onFoodClicked($event, ctx.item)"
.drawer-tab.float-left )
a.drawer-tab-text(
@click="selectedDrawerTab = 1",
:class="{ 'drawer-tab-text-active': selectedDrawerTab === 1 }",
) {{ drawerTabs[1].label }}
#petLikeToEatStable.drawer-help-text(v-once)
| {{ $t('petLikeToEat') + ' ' }}
span.svg-icon.inline.icon-16(v-html="icons.information")
b-popover(
target="petLikeToEatStable"
placement="top"
)
.popover-content-text(v-html="$t('petLikeToEatText')", v-once)
drawer-slider(
:items="drawerTabs[selectedDrawerTab].items",
:scrollButtonsVisible="hasDrawerTabItems(selectedDrawerTab)",
slot="drawer-slider",
:itemWidth=94,
:itemMargin=24,
:itemType="selectedDrawerTab"
)
template(slot="item", slot-scope="context")
foodItem(
:item="context.item",
:itemCount="userItems.food[context.item.key]",
:active="currentDraggingFood == context.item",
@itemDragEnd="onDragEnd()",
@itemDragStart="onDragStart($event, context.item)",
@itemClick="onFoodClicked($event, context.item)"
)
hatchedPetDialog(:hideText="true") hatchedPetDialog(:hideText="true")
div.foodInfo(ref="dragginFoodInfo") div.foodInfo(ref="dragginFoodInfo")
div(v-if="currentDraggingFood != null") div(v-if="currentDraggingFood != null")
@@ -284,9 +254,6 @@
} }
} }
.drawer-slider .items {
height: 114px;
}
.modal-backdrop.fade.show { .modal-backdrop.fade.show {
background-color: $purple-50; background-color: $purple-50;
@@ -386,6 +353,7 @@
import StarBadge from 'client/components/ui/starBadge'; import StarBadge from 'client/components/ui/starBadge';
import CountBadge from 'client/components/ui/countBadge'; import CountBadge from 'client/components/ui/countBadge';
import DrawerSlider from 'client/components/ui/drawerSlider'; import DrawerSlider from 'client/components/ui/drawerSlider';
import InventoryDrawer from 'client/components/shared/inventoryDrawer';
import ResizeDirective from 'client/directives/resize.directive'; import ResizeDirective from 'client/directives/resize.directive';
import DragDropDirective from 'client/directives/dragdrop.directive'; import DragDropDirective from 'client/directives/dragdrop.directive';
@@ -425,6 +393,7 @@
MountRaisedModal, MountRaisedModal,
WelcomeModal, WelcomeModal,
HatchingModal, HatchingModal,
InventoryDrawer,
}, },
directives: { directives: {
resize: ResizeDirective, resize: ResizeDirective,

View File

@@ -0,0 +1,169 @@
<template lang="pug">
drawer.inventoryDrawer(
:title="$t('quickInventory')"
:errorMessage="inventoryDrawerErrorMessage(selectedDrawerItemType)"
)
div(slot="drawer-header")
drawer-header-tabs(
:tabs="filteredTabs",
@changedPosition="tabSelected($event)"
)
div(slot="right-item")
#petLikeToEatMarket.drawer-help-text(v-once)
| {{ $t('petLikeToEat') + ' ' }}
span.svg-icon.inline.icon-16(v-html="icons.information")
b-popover(
target="petLikeToEatMarket",
:placement="'top'",
)
.popover-content-text(v-html="$t('petLikeToEatText')", v-once)
drawer-slider(
v-if="hasOwnedItemsForType(selectedDrawerItemType)"
:items="ownedItems(selectedDrawerItemType) || []",
slot="drawer-slider",
:itemWidth=94,
:itemMargin=24,
:itemType="selectedDrawerTab"
)
template(slot="item", slot-scope="ctx")
slot(
name="item",
:item="ctx.item",
:itemClass="getItemClass(selectedDrawerContentType, ctx.item.key)",
:itemCount="userItems[selectedDrawerContentType][ctx.item.key] || 0",
:itemName="getItemName(selectedDrawerItemType, ctx.item)",
:itemType="selectedDrawerItemType"
)
</template>
<script>
import {mapState} from 'client/libs/store';
import inventoryUtils from 'client/mixins/inventoryUtils';
import svgInformation from 'assets/svg/information.svg';
import _filter from 'lodash/filter';
import CountBadge from 'client/components/ui/countBadge';
import Item from 'client/components/inventory/item';
import Drawer from 'client/components/ui/drawer';
import DrawerSlider from 'client/components/ui/drawerSlider';
import DrawerHeaderTabs from 'client/components/ui/drawerHeaderTabs';
export default {
mixins: [inventoryUtils],
components: {
Item,
CountBadge,
Drawer,
DrawerSlider,
DrawerHeaderTabs,
},
props: {
defaultSelectedTab: {
type: Number,
default: 0,
},
showEggs: Boolean,
showPotions: Boolean,
},
data () {
return {
drawerTabs: [
{
key: 'eggs',
label: this.$t('eggs'),
show: () => this.showEggs,
},
{
key: 'food',
label: this.$t('foodTitle'),
show: () => true,
},
{
key: 'hatchingPotions',
label: this.$t('hatchingPotions'),
show: () => this.showPotions,
},
{
key: 'special',
contentType: 'food',
label: this.$t('special'),
show: () => true,
},
],
selectedDrawerTab: this.defaultSelectedTab,
icons: Object.freeze({
information: svgInformation,
}),
};
},
computed: {
...mapState({
content: 'content',
userItems: 'user.data.items',
}),
selectedDrawerItemType () {
return this.filteredTabs[this.selectedDrawerTab].key;
},
selectedDrawerContentType () {
return this.filteredTabs[this.selectedDrawerTab].contentType ||
this.selectedDrawerItemType;
},
filteredTabs () {
return this.drawerTabs.filter(t => t.show());
},
},
methods: {
ownedItems (type) {
let mappedItems = _filter(this.content[type], i => {
return this.userItems[type][i.key] > 0;
});
switch (type) {
case 'food':
return _filter(mappedItems, f => {
return f.key !== 'Saddle';
});
case 'special':
if (this.userItems.food.Saddle) {
return _filter(this.content.food, f => {
return f.key === 'Saddle';
});
} else {
return [];
}
default:
return mappedItems;
}
},
tabSelected ($event) {
this.selectedDrawerTab = $event;
},
hasOwnedItemsForType (type) {
return this.ownedItems(type).length > 0;
},
inventoryDrawerErrorMessage (type) {
if (!this.hasOwnedItemsForType(type)) {
switch (type) {
case 'food': return this.$t('noFoodAvailable');
case 'special': return this.$t('noSaddlesAvailable');
default:
// @TODO: Change any places using similar locales from `pets.json` and use these new locales from 'inventory.json'
return this.$t('noItemsAvailableForType', {type: this.$t(`${type}ItemType`)});
}
}
},
},
};
</script>
<style lang="scss">
.inventoryDrawer {
.drawer-slider {
height: 126px;
}
}
</style>

View File

@@ -0,0 +1,126 @@
<template lang="pug">
div.featuredItems
.background(:class="{broken: broken}")
.background(:class="{cracked: broken, broken: broken}")
div.npc
div.featured-label
span.rectangle
span.text {{npcName}}
span.rectangle
div.content
div.featured-label.with-border
span.rectangle
span.text {{ featuredText }}
span.rectangle
div.items.margin-center
shopItem(
v-for="item in featuredItems",
:key="item.key",
:item="item",
:price="item.value",
:itemContentClass="'shop_'+item.key",
:emptyItem="false",
:popoverPosition="'top'",
@click="featuredItemSelected(item)"
)
template(slot="itemBadge", slot-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")
</template>
<script>
import ShopItem from './shopItem';
import pinUtils from 'client/mixins/pinUtils';
import svgPin from 'assets/svg/pin.svg';
export default {
mixins: [pinUtils],
props: {
broken: Boolean,
npcName: String,
featuredText: String,
featuredItems: Array,
},
components: {
ShopItem,
},
data () {
return {
icons: Object.freeze({
pin: svgPin,
}),
};
},
methods: {
featuredItemSelected (item) {
this.$emit('featuredItemSelected', item);
},
},
};
</script>
<style lang="scss" scoped>
.featuredItems {
height: 216px;
.background {
width: 100%;
height: 216px;
position: absolute;
top: 0;
left: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.content {
display: flex;
flex-direction: column;
z-index: 1; // Always cover background.
}
.npc {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 216px;
}
.background.broken {
background: url('~assets/images/npc/broken/market_broken_background.png');
background-repeat: repeat-x;
}
.background.cracked {
background: url('~assets/images/npc/broken/market_broken_layer.png');
background-repeat: repeat-x;
}
.broken .npc {
background: url('~assets/images/npc/broken/market_broken_npc.png');
background-repeat: no-repeat;
}
}
.featured-label {
margin: 24px auto;
}
@media only screen and (max-width: 768px) {
.featuredItems .content {
display: none !important;
}
}
</style>

View File

@@ -11,17 +11,11 @@
<script> <script>
import SecondaryMenu from 'client/components/secondaryMenu'; import SecondaryMenu from 'client/components/secondaryMenu';
import notifications from 'client/mixins/notifications';
export default { export default {
mixins: [notifications],
components: { components: {
SecondaryMenu, SecondaryMenu,
}, },
methods: { methods: {},
showUnpinNotification (item) {
this.text(this.$t('unpinnedItem', {item: item.text}));
},
},
}; };
</script> </script>

View File

@@ -0,0 +1,52 @@
<template lang="pug">
div
countBadge(
v-if="item.showCount !== false",
:show="true",
:count="count"
)
.badge.badge-pill.badge-purple.gems-left(v-if='item.key === "gem"')
| {{ gemsLeft }}
span.badge.badge-pill.badge-item.badge-svg(
:class="{'item-selected-badge': item.pinned, 'hide': !item.pinned}",
@click.prevent.stop="togglePinned(item)"
)
span.svg-icon.inline.icon-12.color(v-html="icons.pin")
</template>
<script>
import { mapState } from 'client/libs/store';
import CountBadge from 'client/components/ui/countBadge';
import svgPin from 'assets/svg/pin.svg';
import planGemLimits from 'common/script/libs/planGemLimits';
import pinUtils from '../../../mixins/pinUtils';
export default {
mixins: [pinUtils],
props: ['item'],
components: {
CountBadge,
},
data () {
return {
icons: Object.freeze({
pin: svgPin,
}),
};
},
computed: {
...mapState({
user: 'user.data',
userItems: 'user.data.items',
}),
count () {
return this.userItems[this.item.purchaseType][this.item.key];
},
gemsLeft () {
if (!this.user.purchased.plan) return 0;
return planGemLimits.convCap + this.user.purchased.plan.consecutive.gemCapExtra - this.user.purchased.plan.gemsBought;
},
},
};
</script>

View File

@@ -0,0 +1,94 @@
<template lang="pug">
div.items
shopItem(v-for="item in sortedMarketItems",
:key="item.key",
:item="item",
:emptyItem="false",
:popoverPosition="'top'",
@click="itemSelected(item)")
span(slot="popoverContent")
strong(v-if='item.key === "gem" && gemsLeft === 0') {{ $t('maxBuyGems') }}
h4.popover-content-title {{ item.text }}
template(slot="itemBadge", slot-scope="ctx")
category-item(:item='ctx.item')
</template>
<script>
import { mapState } from 'client/libs/store';
import pinUtils from 'client/mixins/pinUtils';
import planGemLimits from 'common/script/libs/planGemLimits';
import ShopItem from '../shopItem';
import CategoryItem from './CategoryItem';
import _filter from 'lodash/filter';
import _sortBy from 'lodash/sortBy';
import _map from 'lodash/map';
export default {
mixins: [pinUtils],
props: ['hideLocked', 'hidePinned', 'searchBy', 'sortBy', 'category'],
components: {
CategoryItem,
ShopItem,
},
computed: {
...mapState({
content: 'content',
user: 'user.data',
userItems: 'user.data.items',
userStats: 'user.data.stats',
}),
gemsLeft () {
if (!this.user.purchased.plan) return 0;
return planGemLimits.convCap + this.user.purchased.plan.consecutive.gemCapExtra - this.user.purchased.plan.gemsBought;
},
sortedMarketItems () {
let result = _map(this.category.items, (e) => {
return {
...e,
pinned: this.isPinned(e),
};
});
result = _filter(result, (item) => {
if (this.hidePinned && item.pinned) {
return false;
}
if (this.searchBy) {
let foundPosition = item.text.toLowerCase().indexOf(this.searchBy);
if (foundPosition === -1) {
return false;
}
}
return true;
});
switch (this.sortBy) {
case 'AZ': {
result = _sortBy(result, ['text']);
break;
}
case 'sortByNumber': {
result = _sortBy(result, item => {
if (item.showCount === false) return 0;
return this.userItems[item.purchaseType][item.key] || 0;
});
break;
}
}
return result;
},
},
methods: {
itemSelected (item) {
this.$root.$emit('buyModal::showItem', item);
},
},
};
</script>

View File

@@ -0,0 +1,167 @@
<template lang="pug">
layout-section(:title="$t('equipment')")
div(slot="filters")
filter-dropdown(
:label="$t('class')",
:initialItem="selectedGearCategory",
:items="marketGearCategories",
:withIcon="true",
@selected="selectedGroupGearByClass = $event.id"
)
span(slot="item", slot-scope="ctx")
span.svg-icon.inline.icon-16(v-html="icons[ctx.item.id]")
span.text {{ getClassName(ctx.item.id) }}
filter-dropdown(
:label="$t('sortBy')",
:initialItem="selectedSortGearBy",
:items="sortGearBy",
@selected="selectedSortGearBy = $event"
)
span(slot="item", slot-scope="ctx")
span.text {{ $t(ctx.item.id) }}
itemRows(
:items="sortedGearItems",
:itemWidth=94,
:itemMargin=24,
:type="'gear'",
:noItemsLabel="$t('noGearItemsOfClass')",
slot="content"
)
template(slot="item", slot-scope="ctx")
shopItem(
:key="ctx.item.key",
:item="ctx.item",
:emptyItem="userItems.gear[ctx.item.key] === undefined",
:popoverPosition="'top'",
@click="gearSelected(ctx.item)"
)
template(slot="itemBadge", slot-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")
</template>
<script>
import {mapState} from 'client/libs/store';
import LayoutSection from 'client/components/ui/layoutSection';
import FilterDropdown from 'client/components/ui/filterDropdown';
import ItemRows from 'client/components/ui/itemRows';
import ShopItem from '../shopItem';
import shops from 'common/script/libs/shops';
import svgPin from 'assets/svg/pin.svg';
import svgWarrior from 'assets/svg/warrior.svg';
import svgWizard from 'assets/svg/wizard.svg';
import svgRogue from 'assets/svg/rogue.svg';
import svgHealer from 'assets/svg/healer.svg';
import _filter from 'lodash/filter';
import _sortBy from 'lodash/sortBy';
const sortGearTypes = ['sortByType', 'sortByPrice', 'sortByCon', 'sortByPer', 'sortByStr', 'sortByInt'].map(g => ({id: g}));
const sortGearTypeMap = {
sortByType: 'type',
sortByPrice: 'value',
sortByCon: 'con',
sortByStr: 'str',
sortByInt: 'int',
};
export default {
props: ['hideLocked', 'hidePinned', 'searchBy'],
components: {
LayoutSection,
FilterDropdown,
ItemRows,
ShopItem,
},
data () {
return {
sortGearBy: sortGearTypes,
selectedSortGearBy: sortGearTypes[0],
selectedGroupGearByClass: '',
icons: Object.freeze({
pin: svgPin,
warrior: svgWarrior,
wizard: svgWizard,
rogue: svgRogue,
healer: svgHealer,
}),
};
},
computed: {
...mapState({
content: 'content',
user: 'user.data',
userItems: 'user.data.items',
userStats: 'user.data.stats',
}),
marketGearCategories () {
return shops.getMarketGearCategories(this.user).map(c => {
c.id = c.identifier;
return c;
});
},
selectedGearCategory () {
return this.marketGearCategories.filter(c => c.id === this.selectedGroupGearByClass)[0];
},
sortedGearItems () {
let category = _filter(this.marketGearCategories, ['identifier', this.selectedGroupGearByClass]);
let result = _filter(category[0].items, (gear) => {
if (this.hideLocked && gear.locked) {
return false;
}
if (this.hidePinned && gear.pinned) {
return false;
}
if (this.searchBy) {
let foundPosition = gear.text.toLowerCase().indexOf(this.searchBy);
if (foundPosition === -1) {
return false;
}
}
// hide already owned
return !this.userItems.gear.owned[gear.key];
});
// first all unlocked
// then the selected sort
result = _sortBy(result, [(item) => item.locked, sortGearTypeMap[this.selectedSortGearBy.id]]);
return result;
},
},
methods: {
getClassName (classType) {
if (classType === 'wizard') {
return this.$t('mage');
} else {
return this.$t(classType);
}
},
gearSelected (item) {
if (!item.locked) {
this.$root.$emit('buyModal::showItem', item);
}
},
},
created () {
this.selectedGroupGearByClass = this.userStats.class;
},
};
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,46 @@
<template lang="pug">
.form
h2(v-once) {{ $t('filter') }}
.form-group
checkbox(
v-for="category in categories",
:key="category.identifier",
:id="`category-${category.identifier}`",
:checked.sync="viewOptions[category.identifier].selected",
:text="category.text"
)
div.form-group.clearfix
h3.float-left(v-once) {{ $t('hideLocked') }}
toggle-switch.float-right(
v-model="lockedChecked",
@change="$emit('update:hideLocked', $event)"
)
div.form-group.clearfix
h3.float-left(v-once) {{ $t('hidePinned') }}
toggle-switch.float-right(
v-model="pinnedChecked",
@change="$emit('update:hidePinned', $event)"
)
</template>
<script>
import Checkbox from 'client/components/ui/checkbox';
import toggleSwitch from 'client/components/ui/toggleSwitch';
export default {
props: ['hidePinned', 'hideLocked', 'categories', 'viewOptions'],
components: {
Checkbox,
toggleSwitch,
},
data () {
return {
lockedChecked: this.hideLocked,
pinnedChecked: this.hidePinned,
};
},
};
</script>
<style scoped>
</style>

View File

@@ -1,245 +1,79 @@
<template lang="pug"> <template lang="pug">
.row.market page-layout.market
.standard-sidebar.d-none.d-sm-block div(slot="sidebar")
.form-group .form-group
input.form-control.input-search(type="text", v-model="searchText", :placeholder="$t('search')") input.form-control.input-search(type="text", v-model="searchText", :placeholder="$t('search')")
.form market-filter(
h2(v-once) {{ $t('filter') }} :categories="categories",
.form-group :hideLocked.sync="hideLocked",
.form-check( :hidePinned.sync="hidePinned",
v-for="category in categories", :viewOptions="viewOptions"
:key="category.identifier", )
) div(slot="page")
.custom-control.custom-checkbox featured-items-header(
input.custom-control-input(type="checkbox", v-model="viewOptions[category.identifier].selected", :id="`category-${category.identifier}`") :broken="broken",
label.custom-control-label(v-once, :for="`category-${category.identifier}`") {{ category.text }} :npcName="'Alex'",
div.form-group.clearfix :featuredText="market.featured.text",
h3.float-left(v-once) {{ $t('hideLocked') }} :featuredItems="market.featured.items"
toggle-switch.float-right( @featuredItemSelected="featuredItemSelected($event)"
v-model="hideLocked", )
)
div.form-group.clearfix
h3.float-left(v-once) {{ $t('hidePinned') }}
toggle-switch.float-right(
v-model="hidePinned",
)
.standard-page
div.featuredItems
.background(:class="{broken: broken}")
.background(:class="{cracked: broken, broken: broken}")
div.npc
div.featured-label
span.rectangle
span.text Alex
span.rectangle
div.content
div.featured-label.with-border
span.rectangle
span.text {{ market.featured.text }}
span.rectangle
div.items.margin-center
shopItem(
v-for="item in market.featured.items",
:key="item.key",
:item="item",
:price="item.value",
:itemContentClass="'shop_'+item.key",
:emptyItem="false",
:popoverPosition="'top'",
@click="featuredItemSelected(item)"
)
template(slot="itemBadge", slot-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-4.page-header(v-once) {{ $t('market') }} h1.mb-4.page-header(v-once) {{ $t('market') }}
.clearfix(v-if="viewOptions['equipment'].selected") equipment-section(
h2.float-left.mb-3.filters-title v-if="viewOptions['equipment'].selected",
| {{ $t('equipment') }} :hidePinned="hidePinned",
:hideLocked="hideLocked",
.filters.float-right :searchBy="searchTextThrottled"
span.dropdown-label {{ $t('class') }}
b-dropdown(right=true)
span.dropdown-icon-item(slot="text")
span.svg-icon.inline.icon-16(v-html="icons[selectedGroupGearByClass]")
span.text {{ getClassName(selectedGroupGearByClass) }}
b-dropdown-item(
v-for="gearCategory in marketGearCategories",
@click="selectedGroupGearByClass = gearCategory.identifier",
:active="selectedGroupGearByClass === gearCategory.identifier",
:key="gearCategory.identifier"
)
span.dropdown-icon-item
span.svg-icon.inline.icon-16(v-html="icons[gearCategory.identifier]")
span.text {{ gearCategory.text }}
span.dropdown-label {{ $t('sortBy') }}
b-dropdown(:text="$t(selectedSortGearBy)", right=true)
b-dropdown-item(
v-for="sort in sortGearBy",
@click="selectedSortGearBy = sort",
:active="selectedSortGearBy === sort",
:key="sort"
) {{ $t(sort) }}
br
itemRows(
:items="filteredGear(selectedGroupGearByClass, searchTextThrottled, selectedSortGearBy, hideLocked, hidePinned)",
:itemWidth=94,
:itemMargin=24,
:type="'gear'",
:noItemsLabel="$t('noGearItemsOfClass')",
v-if="viewOptions['equipment'].selected"
) )
template(slot="item", slot-scope="ctx")
shopItem( layout-section(:title="$t('items')")
:key="ctx.item.key", div(slot="filters")
:item="ctx.item", filter-dropdown(
:emptyItem="userItems.gear[ctx.item.key] === undefined", :label="$t('sortBy')",
:popoverPosition="'top'", :initialItem="selectedSortItemsBy",
@click="gearSelected(ctx.item)" :items="sortItemsBy",
@selected="selectedSortItemsBy = $event"
) )
span(slot="item", slot-scope="ctx")
template(slot="itemBadge", slot-scope="ctx") span.text {{ $t(ctx.item.id) }}
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")
.clearfix
h2.float-left.mb-3
| {{ $t('items') }}
div.float-right
span.dropdown-label {{ $t('sortBy') }}
b-dropdown(:text="$t(selectedSortItemsBy)", right=true)
b-dropdown-item(
v-for="sort in sortItemsBy",
@click="selectedSortItemsBy = sort",
:active="selectedSortItemsBy === sort",
:key="sort"
) {{ $t(sort) }}
div( div(
v-for="category in categories", v-for="category in categories",
v-if="viewOptions[category.identifier].selected && category.identifier !== 'equipment'" v-if="viewOptions[category.identifier].selected && category.identifier !== 'equipment'"
) )
h4 {{ category.text }} h4 {{ category.text }}
category-row(
div.items :hidePinned="hidePinned",
shopItem( :hideLocked="hideLocked",
v-for="item in sortedMarketItems(category, selectedSortItemsBy, searchTextThrottled, hidePinned)", :searchBy="searchTextThrottled",
:key="item.key", :sortBy="selectedSortItemsBy.id",
:item="item", :category="category"
:emptyItem="false",
:popoverPosition="'top'",
@click="itemSelected(item)"
) )
span(slot="popoverContent")
strong(v-if='item.key === "gem" && gemsLeft === 0') {{ $t('maxBuyGems') }}
h4.popover-content-title {{ item.text }}
template(slot="itemBadge", slot-scope="ctx")
countBadge(
v-if="item.showCount != false",
:show="userItems[item.purchaseType][item.key] != 0",
:count="userItems[item.purchaseType][item.key] || 0"
)
.badge.badge-pill.badge-purple.gems-left(v-if='item.key === "gem"')
| {{ gemsLeft }}
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")
keys-to-kennel(v-if='category.identifier === "special"') keys-to-kennel(v-if='category.identifier === "special"')
div.fill-height div.fill-height
//- @TODO: Create new InventoryDrawer component and re-use in 'inventory/stable' component. inventoryDrawer(:showEggs="true", :showPotions="true")
drawer(
:title="$t('quickInventory')"
:errorMessage="inventoryDrawerErrorMessage(selectedDrawerItemType)"
)
div(slot="drawer-header")
drawer-header-tabs(
:tabs="drawerTabs",
@changedPosition="tabSelected($event)"
)
div(slot="right-item")
#petLikeToEatMarket.drawer-help-text(v-once)
| {{ $t('petLikeToEat') + ' ' }}
span.svg-icon.inline.icon-16(v-html="icons.information")
b-popover(
target="petLikeToEatMarket",
:placement="'top'",
)
.popover-content-text(v-html="$t('petLikeToEatText')", v-once)
drawer-slider(
v-if="hasOwnedItemsForType(selectedDrawerItemType)"
:items="ownedItems(selectedDrawerItemType) || []",
slot="drawer-slider",
:itemWidth=94,
:itemMargin=24,
:itemType="selectedDrawerTab"
)
template(slot="item", slot-scope="ctx")
item(
:item="ctx.item",
:itemContentClass="getItemClass(selectedDrawerItemType, ctx.item.key)",
popoverPosition="top",
@click="selectedItemToSell = ctx.item"
)
template(slot="itemBadge", slot-scope="ctx")
countBadge(
:show="true",
:count="userItems[drawerTabs[selectedDrawerTab].contentType][ctx.item.key] || 0"
)
span(slot="popoverContent")
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", slot-scope="ctx") template(slot="item", slot-scope="ctx")
item.flat( item(
:item="ctx.item", :item="ctx.item",
:itemContentClass="getItemClass(selectedDrawerItemType, ctx.item.key)", :itemContentClass="ctx.itemClass",
:showPopover="false" popoverPosition="top",
@click="sellItem(ctx)"
) )
template(slot="itemBadge", slot-scope="ctx") countBadge(
countBadge( slot="itemBadge"
:show="true", :show="true",
:count="userItems[drawerTabs[selectedDrawerTab].contentType][ctx.item.key] || 0" :count="ctx.itemCount"
) )
h4.popover-content-title(slot="popoverContent") {{ ctx.itemName }}
sellModal
</template> </template>
<style lang="scss"> <style lang="scss">
@import '~client/assets/scss/colors.scss'; @import '~client/assets/scss/colors.scss';
@import '~client/assets/scss/variables.scss'; @import '~client/assets/scss/variables.scss';
.market .drawer-slider {
min-height: 60px;
.message {
top: 10px;
}
}
.fill-height { .fill-height {
height: 38px; // button + margin + padding height: 38px; // button + margin + padding
} }
@@ -250,10 +84,6 @@
height: 48px; height: 48px;
} }
.featured-label {
margin: 24px auto;
}
.item-wrapper.bordered-item .item { .item-wrapper.bordered-item .item {
width: 112px; width: 112px;
height: 112px; height: 112px;
@@ -265,43 +95,15 @@
margin: 0 auto; margin: 0 auto;
} }
.standard-page {
position: relative;
}
.featuredItems { .featuredItems {
height: 216px;
.background { .background {
background: url('~assets/images/npc/#{$npc_market_flavor}/market_background.png'); background: url('~assets/images/npc/#{$npc_market_flavor}/market_background.png');
background-repeat: repeat-x; background-repeat: repeat-x;
width: 100%;
height: 216px;
position: absolute;
top: 0;
left: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.content {
display: flex;
flex-direction: column;
z-index: 1; // Always cover background.
} }
.npc { .npc {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 216px;
background: url('~assets/images/npc/#{$npc_market_flavor}/market_banner_npc.png'); background: url('~assets/images/npc/#{$npc_market_flavor}/market_banner_npc.png');
background-repeat: no-repeat; background-repeat: no-repeat;
@@ -312,23 +114,6 @@
left: 80px; left: 80px;
} }
} }
.background.broken {
background: url('~assets/images/npc/broken/market_broken_background.png');
background-repeat: repeat-x;
}
.background.cracked {
background: url('~assets/images/npc/broken/market_broken_layer.png');
background-repeat: repeat-x;
}
.broken .npc {
background: url('~assets/images/npc/broken/market_broken_npc.png');
background-repeat: no-repeat;
}
} }
} }
@@ -336,20 +121,6 @@
right: -.5em; right: -.5em;
top: -.5em; top: -.5em;
} }
@media only screen and (max-width: 768px) {
.featuredItems .content {
display: none !important;
}
.filters, .filters-title {
float: none;
button {
margin-right: 4em;
margin-bottom: 1em;
}
}
}
</style> </style>
@@ -358,14 +129,18 @@
import ShopItem from '../shopItem'; import ShopItem from '../shopItem';
import KeysToKennel from './keysToKennel'; import KeysToKennel from './keysToKennel';
import EquipmentSection from './equipmentSection';
import CategoryRow from './categoryRow';
import Item from 'client/components/inventory/item'; import Item from 'client/components/inventory/item';
import CountBadge from 'client/components/ui/countBadge'; import CountBadge from 'client/components/ui/countBadge';
import Drawer from 'client/components/ui/drawer';
import DrawerSlider from 'client/components/ui/drawerSlider';
import DrawerHeaderTabs from 'client/components/ui/drawerHeaderTabs';
import ItemRows from 'client/components/ui/itemRows'; import ItemRows from 'client/components/ui/itemRows';
import toggleSwitch from 'client/components/ui/toggleSwitch';
import Avatar from 'client/components/avatar'; import Avatar from 'client/components/avatar';
import InventoryDrawer from 'client/components/shared/inventoryDrawer';
import FeaturedItemsHeader from '../featuredItemsHeader';
import PageLayout from 'client/components/ui/pageLayout';
import LayoutSection from 'client/components/ui/layoutSection';
import FilterDropdown from 'client/components/ui/filterDropdown';
import MarketFilter from './filter';
import SellModal from './sellModal.vue'; import SellModal from './sellModal.vue';
import EquipmentAttributesGrid from '../../inventory/equipment/attributesGrid.vue'; import EquipmentAttributesGrid from '../../inventory/equipment/attributesGrid.vue';
@@ -374,52 +149,45 @@
import svgPin from 'assets/svg/pin.svg'; import svgPin from 'assets/svg/pin.svg';
import svgGem from 'assets/svg/gem.svg'; import svgGem from 'assets/svg/gem.svg';
import svgInformation from 'assets/svg/information.svg'; import svgInformation from 'assets/svg/information.svg';
import svgWarrior from 'assets/svg/warrior.svg';
import svgWizard from 'assets/svg/wizard.svg';
import svgRogue from 'assets/svg/rogue.svg';
import svgHealer from 'assets/svg/healer.svg';
import getItemInfo from 'common/script/libs/getItemInfo'; import getItemInfo from 'common/script/libs/getItemInfo';
import isPinned from 'common/script/libs/isPinned';
import shops from 'common/script/libs/shops'; import shops from 'common/script/libs/shops';
import planGemLimits from 'common/script/libs/planGemLimits';
import _filter from 'lodash/filter'; import _filter from 'lodash/filter';
import _sortBy from 'lodash/sortBy';
import _map from 'lodash/map'; import _map from 'lodash/map';
import _throttle from 'lodash/throttle'; import _throttle from 'lodash/throttle';
const sortGearTypes = ['sortByType', 'sortByPrice', 'sortByCon', 'sortByPer', 'sortByStr', 'sortByInt']; const sortItems = ['AZ', 'sortByNumber'].map(g => ({id: g}));
import notifications from 'client/mixins/notifications'; import notifications from 'client/mixins/notifications';
import buyMixin from 'client/mixins/buy'; import buyMixin from 'client/mixins/buy';
import currencyMixin from '../_currencyMixin'; import currencyMixin from '../_currencyMixin';
import inventoryUtils from 'client/mixins/inventoryUtils';
const sortGearTypeMap = { import pinUtils from 'client/mixins/pinUtils';
sortByType: 'type',
sortByPrice: 'value',
sortByCon: 'con',
sortByStr: 'str',
sortByInt: 'int',
};
export default { export default {
mixins: [notifications, buyMixin, currencyMixin], mixins: [notifications, buyMixin, currencyMixin, inventoryUtils, pinUtils],
components: { components: {
ShopItem, ShopItem,
KeysToKennel, KeysToKennel,
Item, Item,
CountBadge, CountBadge,
Drawer,
DrawerSlider,
DrawerHeaderTabs,
ItemRows, ItemRows,
toggleSwitch,
SellModal, SellModal,
EquipmentAttributesGrid, EquipmentAttributesGrid,
Avatar, Avatar,
InventoryDrawer,
FeaturedItemsHeader,
PageLayout,
LayoutSection,
FilterDropdown,
EquipmentSection,
CategoryRow,
MarketFilter,
SelectMembersModal, SelectMembersModal,
}, },
watch: { watch: {
@@ -438,24 +206,10 @@ export default {
pin: svgPin, pin: svgPin,
gem: svgGem, gem: svgGem,
information: svgInformation, information: svgInformation,
warrior: svgWarrior,
wizard: svgWizard,
rogue: svgRogue,
healer: svgHealer,
}), }),
selectedDrawerTab: 0, sortItemsBy: sortItems,
selectedDrawerItemType: 'eggs', selectedSortItemsBy: sortItems[0],
selectedGroupGearByClass: '',
sortGearBy: sortGearTypes,
selectedSortGearBy: 'sortByType',
sortItemsBy: ['AZ', 'sortByNumber'],
selectedSortItemsBy: 'AZ',
selectedItemToSell: null,
hideLocked: false, hideLocked: false,
hidePinned: false, hidePinned: false,
@@ -474,120 +228,79 @@ export default {
userStats: 'user.data.stats', userStats: 'user.data.stats',
userItems: 'user.data.items', userItems: 'user.data.items',
}), }),
marketGearCategories () {
return shops.getMarketGearCategories(this.user);
},
market () { market () {
return shops.getMarketShop(this.user); return shops.getMarketShop(this.user);
}, },
categories () { categories () {
if (this.market) { if (!this.market) return [];
let categories = [
...this.market.categories,
];
categories.push({ let categories = [
identifier: 'equipment', ...this.market.categories,
text: this.$t('equipment'),
});
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),
showCount: false,
};
}),
});
let specialItems = [{
...getItemInfo(this.user, 'fortify'),
showCount: false,
}];
if (this.user.purchased.plan.customerId) {
let gemItem = getItemInfo(this.user, 'gem');
specialItems.push({
...gemItem,
showCount: false,
});
}
if (this.user.flags.rebirthEnabled) {
let rebirthItem = getItemInfo(this.user, 'rebirth_orb');
specialItems.push({
showCount: false,
...rebirthItem,
});
}
if (specialItems.length > 0) {
categories.push({
identifier: 'special',
text: this.$t('special'),
items: specialItems,
});
}
categories.map((category) => {
if (!this.viewOptions[category.identifier]) {
this.$set(this.viewOptions, category.identifier, {
selected: true,
});
}
});
return categories;
} else {
return [];
}
},
drawerTabs () {
return [
{
key: 'eggs',
contentType: 'eggs',
label: this.$t('eggs'),
},
{
key: 'food',
contentType: 'food',
label: this.$t('foodTitle'),
},
{
key: 'hatchingPotions',
contentType: 'hatchingPotions',
label: this.$t('hatchingPotions'),
},
{
key: 'special',
contentType: 'food',
label: this.$t('special'),
},
]; ];
},
gemsLeft () { categories.push({
if (!this.user.purchased.plan) return 0; identifier: 'equipment',
return planGemLimits.convCap + this.user.purchased.plan.consecutive.gemCapExtra - this.user.purchased.plan.gemsBought; text: this.$t('equipment'),
});
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),
showCount: false,
};
}),
});
let specialItems = [{
...getItemInfo(this.user, 'fortify'),
showCount: false,
}];
if (this.user.purchased.plan.customerId) {
let gemItem = getItemInfo(this.user, 'gem');
specialItems.push({
...gemItem,
showCount: false,
});
}
if (this.user.flags.rebirthEnabled) {
let rebirthItem = getItemInfo(this.user, 'rebirth_orb');
specialItems.push({
showCount: false,
...rebirthItem,
});
}
if (specialItems.length > 0) {
categories.push({
identifier: 'special',
text: this.$t('special'),
items: specialItems,
});
}
categories.map((category) => {
if (!this.viewOptions[category.identifier]) {
this.$set(this.viewOptions, category.identifier, {
selected: true,
});
}
});
return categories;
}, },
}, },
methods: { methods: {
getClassName (classType) { sellItem (itemScope) {
if (classType === 'wizard') { this.$root.$emit('sellItem', itemScope);
return this.$t('mage');
} else {
return this.$t(classType);
}
},
tabSelected ($event) {
this.selectedDrawerTab = $event;
this.selectedDrawerItemType = this.drawerTabs[$event].key;
}, },
ownedItems (type) { ownedItems (type) {
let mappedItems = _filter(this.content[type], i => { let mappedItems = _filter(this.content[type], i => {
@@ -620,133 +333,18 @@ export default {
return this.$t('noItemsAvailableForType', { type: this.$t(`${type}ItemType`) }); return this.$t('noItemsAvailableForType', { type: this.$t(`${type}ItemType`) });
} }
}, },
getItemClass (type, itemKey) {
switch (type) {
case 'food':
case 'special':
return `Pet_Food_${itemKey}`;
case 'eggs':
return `Pet_Egg_${itemKey}`;
case 'hatchingPotions':
return `Pet_HatchingPotion_${itemKey}`;
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 category = _filter(this.marketGearCategories, ['identifier', groupByClass]);
let result = _filter(category[0].items, (gear) => {
if (hideLocked && gear.locked) {
return false;
}
if (hidePinned && gear.pinned) {
return false;
}
if (searchBy) {
let foundPosition = gear.text.toLowerCase().indexOf(searchBy);
if (foundPosition === -1) {
return false;
}
}
// hide already owned
return !this.userItems.gear.owned[gear.key];
});
// first all unlocked
// then the selected sort
result = _sortBy(result, [(item) => item.locked, sortGearTypeMap[sortBy]]);
return result;
},
sortedMarketItems (category, sortBy, searchBy, hidePinned) {
let result = _map(category.items, (e) => {
return {
...e,
pinned: isPinned(this.user, e),
};
});
result = _filter(result, (item) => {
if (hidePinned && item.pinned) {
return false;
}
if (searchBy) {
let foundPosition = item.text.toLowerCase().indexOf(searchBy);
if (foundPosition === -1) {
return false;
}
}
return true;
});
switch (sortBy) {
case 'AZ': {
result = _sortBy(result, ['text']);
break;
}
case 'sortByNumber': {
result = _sortBy(result, item => {
if (item.showCount === false) return 0;
return this.userItems[item.purchaseType][item.key] || 0;
});
break;
}
}
return result;
},
resetItemToSell ($event) {
if (!$event) {
this.selectedItemToSell = null;
}
},
isGearLocked (gear) {
if (gear.klass !== this.userStats.class) {
return true;
}
return false;
},
togglePinned (item) {
if (!this.$store.dispatch('user:togglePinnedItem', {type: item.pinType, path: item.path})) {
this.$parent.showUnpinNotification(item);
}
},
itemSelected (item) { itemSelected (item) {
this.$root.$emit('buyModal::showItem', item); this.$root.$emit('buyModal::showItem', item);
}, },
featuredItemSelected (item) { featuredItemSelected (item) {
if (item.purchaseType === 'gear') { if (item.purchaseType === 'gear') {
this.gearSelected(item); if (!item.locked) {
this.itemSelected(item);
}
} else { } else {
this.itemSelected(item); this.itemSelected(item);
} }
}, },
gearSelected (item) {
if (!item.locked) {
this.$root.$emit('buyModal::showItem', item);
}
},
},
created () {
this.selectedGroupGearByClass = this.userStats.class;
}, },
}; };
</script> </script>

View File

@@ -1,6 +1,5 @@
<template lang="pug"> <template lang="pug">
b-modal#sell-modal( b-modal#sell-modal(
:visible="item != null",
:hide-header="true", :hide-header="true",
@change="onChange($event)" @change="onChange($event)"
) )
@@ -8,30 +7,37 @@
span.svg-icon.inline.icon-10(aria-hidden="true", v-html="icons.close", @click="hideDialog()") span.svg-icon.inline.icon-10(aria-hidden="true", v-html="icons.close", @click="hideDialog()")
div.content(v-if="item") div.content(v-if="item")
div.inner-content
item.flat(
:item="item",
:itemContentClass="itemContextToSell.itemClass",
:showPopover="false"
)
countBadge(
slot="itemBadge",
:show="true",
:count="itemContextToSell.itemCount"
)
div.inner-content(v-if="item.sellWarningNote") h4.title {{ itemContextToSell.itemName }}
slot(name="item", :item="item")
h4.title {{ text ? text : item.text() }} div(v-if="item.sellWarningNote")
div.text {{ item.sellWarningNote() }} div.text {{ item.sellWarningNote() }}
br br
div.inner-content(v-else) div(v-once)
slot(name="item", :item="item") div.text {{ item.notes() }}
h4.title {{ text ? text : item.text() }} div
div.text {{ item.notes() }} b.how-many-to-sell {{ $t('howManyToSell') }}
div div
b.how-many-to-sell {{ $t('howManyToSell') }} b-input.itemsToSell(type="number", v-model="selectedAmountToSell", :max="itemContextToSell.itemCount", min="1", @keyup.native="preventNegative($event)", step="1")
div span.svg-icon.inline.icon-32(aria-hidden="true", v-html="icons.gold")
b-input.itemsToSell(type="number", v-model="selectedAmountToSell", :max="itemCount", min="1", @keyup.native="preventNegative($event)", step="1") span.value {{ item.value }}
span.svg-icon.inline.icon-32(aria-hidden="true", v-html="icons.gold") button.btn.btn-primary(@click="sellItems()") {{ $t('sell') }}
span.value {{ item.value }}
button.btn.btn-primary(@click="sellItems()") {{ $t('sell') }}
div.clearfix(slot="modal-footer") div.clearfix(slot="modal-footer")
span.balance.float-left {{ $t('yourBalance') }} span.balance.float-left {{ $t('yourBalance') }}
@@ -119,14 +125,19 @@
import svgGem from 'assets/svg/gem.svg'; import svgGem from 'assets/svg/gem.svg';
import BalanceInfo from '../balanceInfo.vue'; import BalanceInfo from '../balanceInfo.vue';
import Item from 'client/components/inventory/item';
import CountBadge from 'client/components/ui/countBadge';
export default { export default {
components: { components: {
BalanceInfo, BalanceInfo,
Item,
CountBadge,
}, },
data () { data () {
return { return {
selectedAmountToSell: 1, selectedAmountToSell: 1,
itemContextToSell: null,
icons: Object.freeze({ icons: Object.freeze({
close: svgClose, close: svgClose,
@@ -135,6 +146,20 @@
}), }),
}; };
}, },
computed: {
item () {
return this.itemContextToSell && this.itemContextToSell.item;
},
},
mounted () {
this.$root.$on('sellItem', (itemCtx) => {
this.itemContextToSell = itemCtx;
this.$root.$emit('bv::show::modal', 'sell-modal');
});
},
destroyed () {
this.$root.$off('sellItem');
},
methods: { methods: {
onChange ($event) { onChange ($event) {
this.$emit('change', $event); this.$emit('change', $event);
@@ -155,7 +180,7 @@
} }
this.$store.dispatch('shops:sellItems', { this.$store.dispatch('shops:sellItems', {
type: this.itemType, type: this.itemContextToSell.itemType,
key: this.item.key, key: this.item.key,
amount: this.selectedAmountToSell, amount: this.selectedAmountToSell,
}); });
@@ -165,19 +190,5 @@
this.$root.$emit('bv::hide::modal', 'sell-modal'); this.$root.$emit('bv::hide::modal', 'sell-modal');
}, },
}, },
props: {
item: {
type: Object,
},
itemType: {
type: String,
},
text: {
type: String,
},
itemCount: {
type: Number,
},
},
}; };
</script> </script>

View File

@@ -339,6 +339,7 @@
import toggleSwitch from 'client/components/ui/toggleSwitch'; import toggleSwitch from 'client/components/ui/toggleSwitch';
import Avatar from 'client/components/avatar'; import Avatar from 'client/components/avatar';
import buyMixin from 'client/mixins/buy'; import buyMixin from 'client/mixins/buy';
import pinUtils from 'client/mixins/pinUtils';
import currencyMixin from '../_currencyMixin'; import currencyMixin from '../_currencyMixin';
import BuyModal from './buyQuestModal.vue'; import BuyModal from './buyQuestModal.vue';
@@ -357,7 +358,7 @@
import _map from 'lodash/map'; import _map from 'lodash/map';
export default { export default {
mixins: [buyMixin, currencyMixin], mixins: [buyMixin, currencyMixin, pinUtils],
components: { components: {
ShopItem, ShopItem,
Item, Item,
@@ -474,11 +475,6 @@ export default {
return false; return false;
}, },
togglePinned (item) {
if (!this.$store.dispatch('user:togglePinnedItem', {type: item.pinType, path: item.path})) {
this.$parent.showUnpinNotification(item);
}
},
selectItem (item) { selectItem (item) {
if (item.locked) return; if (item.locked) return;

View File

@@ -294,6 +294,7 @@
import Avatar from 'client/components/avatar'; import Avatar from 'client/components/avatar';
import buyMixin from 'client/mixins/buy'; import buyMixin from 'client/mixins/buy';
import currencyMixin from '../_currencyMixin'; import currencyMixin from '../_currencyMixin';
import pinUtils from 'client/mixins/pinUtils';
import svgPin from 'assets/svg/pin.svg'; import svgPin from 'assets/svg/pin.svg';
import svgWarrior from 'assets/svg/warrior.svg'; import svgWarrior from 'assets/svg/warrior.svg';
@@ -318,7 +319,7 @@
import shops from 'common/script/libs/shops'; import shops from 'common/script/libs/shops';
export default { export default {
mixins: [buyMixin, currencyMixin], mixins: [buyMixin, currencyMixin, pinUtils],
components: { components: {
ShopItem, ShopItem,
Item, Item,
@@ -514,11 +515,6 @@
return false; return false;
}, },
togglePinned (item) {
if (!this.$store.dispatch('user:togglePinnedItem', {type: item.pinType, path: item.path})) {
this.$parent.showUnpinNotification(item);
}
},
itemSelected (item) { itemSelected (item) {
if (item.locked) return; if (item.locked) return;
this.$root.$emit('buyModal::showItem', item); this.$root.$emit('buyModal::showItem', item);

View File

@@ -240,8 +240,10 @@
import isPinned from 'common/script/libs/isPinned'; import isPinned from 'common/script/libs/isPinned';
import shops from 'common/script/libs/shops'; import shops from 'common/script/libs/shops';
import pinUtils from 'client/mixins/pinUtils';
export default { export default {
mixins: [pinUtils],
components: { components: {
ShopItem, ShopItem,
Item, Item,
@@ -369,11 +371,6 @@
getGrouped (entries) { getGrouped (entries) {
return _groupBy(entries, 'group'); return _groupBy(entries, 'group');
}, },
togglePinned (item) {
if (!this.$store.dispatch('user:togglePinnedItem', {type: item.pinType, path: item.path})) {
this.$parent.showUnpinNotification(item);
}
},
selectItemToBuy (item) { selectItemToBuy (item) {
this.$root.$emit('buyModal::showItem', item); this.$root.$emit('buyModal::showItem', item);
}, },

View File

@@ -0,0 +1,26 @@
<template lang="pug">
.form-check
.custom-control.custom-checkbox
input.custom-control-input(type="checkbox", v-model="isChecked", :id="id")
label.custom-control-label(v-once, :for="id") {{ text }}
</template>
<script>
export default {
props: {
checked: Boolean,
id: String,
text: String,
},
data () {
return {
isChecked: this.checked,
};
},
watch: {
isChecked (after) {
this.$emit('update:checked', after);
},
},
};
</script>

View File

@@ -1,6 +1,6 @@
<template lang="pug"> <template lang="pug">
span.badge.badge-pill.badge-item.badge-count( span.badge.badge-pill.badge-item.badge-count(
v-if="show && count != 0", v-if="show && count > 0",
) {{ count }} ) {{ count }}
</template> </template>

View File

@@ -0,0 +1,45 @@
<template lang="pug">
span
span.dropdown-label {{ label }}
b-dropdown(right=true)
span(slot="text", :class="{'dropdown-icon-item': withIcon}")
slot(name="item", :item="selectedItem")
b-dropdown-item(
v-for="item in items",
@click="selectItem(item)",
:active="selectedItem.id === item.id",
:key="item.id"
)
span(:class="{'dropdown-icon-item': withIcon}")
slot(name="item", :item="item")
</template>
<script>
export default {
props: {
label: String,
items: Array,
initialItem: Object,
withIcon: {
type: Boolean,
default: false,
},
},
data () {
return {
selectedItem: this.initialItem,
};
},
methods: {
selectItem (item) {
this.selectedItem = item;
this.$emit('selected', item);
},
},
};
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,33 @@
<template lang="pug">
div
.clearfix
h2.float-left.mb-3.filters-title {{ title }}
.filters.float-right
slot(name="filters")
br
slot(name="content")
</template>
<script>
export default {
props: {
title: String,
},
};
</script>
<style scoped lang="scss">
@media only screen and (max-width: 768px) {
.filters, .filters-title {
float: none;
button {
margin-right: 4em;
margin-bottom: 1em;
}
}
}
</style>

View File

@@ -0,0 +1,25 @@
<template lang="pug">
.row
.standard-sidebar.d-none.d-sm-block(v-if="showSidebar")
slot(name="sidebar")
.standard-page
slot(name="page")
</template>
<script>
export default {
props: {
showSidebar: {
type: Boolean,
default: true,
},
},
};
</script>
<style scoped lang="scss">
.standard-page {
position: relative;
}
</style>

View File

@@ -0,0 +1,27 @@
export default {
methods: {
getItemClass (type, itemKey) {
switch (type) {
case 'food':
case 'special':
return `Pet_Food_${itemKey}`;
case 'eggs':
return `Pet_Egg_${itemKey}`;
case 'hatchingPotions':
return `Pet_HatchingPotion_${itemKey}`;
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();
}
},
},
};

View File

@@ -0,0 +1,19 @@
import notifications from 'client/mixins/notifications';
import isPinned from 'common/script/libs/isPinned';
export default {
mixins: [notifications],
methods: {
isPinned (item) {
return isPinned(this.user, item);
},
togglePinned (item) {
if (!this.$store.dispatch('user:togglePinnedItem', {type: item.pinType, path: item.path})) {
this.showUnpinNotification(item);
}
},
showUnpinNotification (item) {
this.text(this.$t('unpinnedItem', {item: item.text}));
},
},
};

View File

@@ -104,6 +104,7 @@ function getClassName (classType, language) {
} }
} }
// TODO Refactor the `.locked` logic
shops.checkMarketGearLocked = function checkMarketGearLocked (user, items) { shops.checkMarketGearLocked = function checkMarketGearLocked (user, items) {
let result = filter(items, ['pinType', 'marketGear']); let result = filter(items, ['pinType', 'marketGear']);
let availableGear = map(updateStore(user), (item) => getItemInfo(user, 'marketGear', item).path); let availableGear = map(updateStore(user), (item) => getItemInfo(user, 'marketGear', item).path);
@@ -116,12 +117,6 @@ shops.checkMarketGearLocked = function checkMarketGearLocked (user, items) {
gear.locked = true; gear.locked = true;
} }
// @TODO: I'm not sure what the logic for locking is supposed to be
// But, I am pretty sure if we pin an armoire item, it needs to be unlocked
if (gear.klass === 'armoire') {
gear.locked = false;
}
if (Boolean(gear.specialClass) && Boolean(gear.set)) { if (Boolean(gear.specialClass) && Boolean(gear.set)) {
let currentSet = gear.set === seasonalShopConfig.pinnedSets[gear.specialClass]; let currentSet = gear.set === seasonalShopConfig.pinnedSets[gear.specialClass];
@@ -132,7 +127,6 @@ shops.checkMarketGearLocked = function checkMarketGearLocked (user, items) {
gear.locked = !gear.canOwn(user); gear.locked = !gear.canOwn(user);
} }
let itemOwned = user.items.gear.owned[gear.key]; let itemOwned = user.items.gear.owned[gear.key];
if (itemOwned === false && !availableGear.includes(gear.path)) { if (itemOwned === false && !availableGear.includes(gear.path)) {
@@ -140,6 +134,12 @@ shops.checkMarketGearLocked = function checkMarketGearLocked (user, items) {
} }
gear.owned = itemOwned; gear.owned = itemOwned;
// @TODO: I'm not sure what the logic for locking is supposed to be
// But, I am pretty sure if we pin an armoire item, it needs to be unlocked
if (gear.klass === 'armoire') {
gear.locked = false;
}
} }
}; };
@@ -188,7 +188,8 @@ shops.getMarketGearCategories = function getMarketGear (user, language) {
}; };
let specialNonClassGear = filter(content.gear.flat, (gear) => { let specialNonClassGear = filter(content.gear.flat, (gear) => {
return !user.items.gear.owned[gear.key] && return user.items.gear.owned[gear.key] === false ||
!user.items.gear.owned[gear.key] &&
content.classes.indexOf(gear.klass) === -1 && content.classes.indexOf(gear.klass) === -1 &&
content.classes.indexOf(gear.specialClass) === -1 && content.classes.indexOf(gear.specialClass) === -1 &&
(gear.canOwn && gear.canOwn(user)); (gear.canOwn && gear.canOwn(user));