habitica-self-host/website/client/components/inventory/equipment/index.vue
Alys 7a6bf8b870 new client - various fixes: Earnable Quests removed, equipment type names and sort order changed (#8977)
* remove Earnable Quests from quests shop

Earnable Quests could be put back later but with an explanation
for how you can earn them. The Quest Shop isn't well set up for
that at the moment.

* change names of some equipment items

NOTE: The `...Capitalized` strings all have lower-case versions
(e.g., "weapon" and "weaponCapitalized"). I suspect we should
change or (perhaps better) get rid of the uncapitalized version
but I'm not sure what other effects that might have (e.g., haven't
searched for exactly where it's used; don't know what it would do
to the mobile apps). Suggest looking into that after launch. If
the old, uncapitalized strings appear elsewhere during our site
testing, we should probably change them to the Capitalized version
then, or if lowercase is essential, create new strings (e.g.,
"weaponUncapitalized" - that will be much easier later than just
"weapon" for searching for where it's used!)

* change order of Equipment types on the inventory page

http://i.imgur.com/64D5jEk.png
2017-08-23 15:17:11 +10:00

354 lines
12 KiB
Vue

<template lang="pug">
.row
.standard-sidebar
.form-group
input.form-control.input-search(type="text", v-model="searchText", :placeholder="$t('search')")
.form
h2(v-once) {{ $t('filter') }}
h3 {{ this.groupBy === 'type' ? $t('equipmentType') : $t('class') }}
.form-group
.form-check(
v-for="group in itemsGroups",
:key="group.key",
)
label.custom-control.custom-checkbox
input.custom-control-input(type="checkbox", v-model="viewOptions[group.key].selected")
span.custom-control-indicator
span.custom-control-description(v-once) {{ group.label }}
.standard-page
.clearfix
h1.float-left.mb-0.page-header(v-once) {{ $t('equipment') }}
.float-right
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) }}
span.dropdown-label {{ $t('groupBy2') }}
b-dropdown(:text="$t(groupBy === 'type' ? 'equipmentType' : 'class')", right=true)
b-dropdown-item(@click="groupBy = 'type'", :active="groupBy === 'type'") {{ $t('equipmentType') }}
b-dropdown-item(@click="groupBy = 'class'", :active="groupBy === 'class'") {{ $t('class') }}
drawer(
:title="$t('equipment')",
:errorMessage="(costume && !user.preferences.costume) ? $t('costumeDisabled') : null",
)
div(slot="drawer-header")
.drawer-tab-container
.drawer-tab.text-right
a.drawer-tab-text(
@click="costume = false",
:class="{'drawer-tab-text-active': costume === false}",
) {{ $t('equipment') }}
.clearfix
.drawer-tab.float-left
a.drawer-tab-text(
@click="costume = true",
:class="{'drawer-tab-text-active': costume === true}",
) {{ $t('costume') }}
b-popover(
:triggers="['hover']",
:placement="'top'"
)
span(slot="content")
.popover-content-text {{ $t(drawerPreference+'PopoverText') }}
toggle-switch.float-right(
:label="$t(costume ? 'useCostume' : 'autoEquipBattleGear')",
:checked="user.preferences[drawerPreference]",
@change="changeDrawerPreference",
)
.items.items-one-line(slot="drawer-slider")
item(
v-for="(label, group) in gearTypesToStrings",
:key="group",
:item="flatGear[activeItems[group]]",
:itemContentClass="flatGear[activeItems[group]] ? 'shop_' + flatGear[activeItems[group]].key : null",
:emptyItem="!flatGear[activeItems[group]] || flatGear[activeItems[group]].key.indexOf('_base_0') !== -1",
:label="label",
:popoverPosition="'top'",
)
template(slot="popoverContent", scope="context")
equipmentAttributesPopover(:item="context.item")
template(slot="itemBadge", scope="context")
starBadge(
:selected="true",
:show="!costume || user.preferences.costume",
@click="equip(context.item)",
)
div(
v-for="group in itemsGroups",
v-if="viewOptions[group.key].selected",
:key="group.key",
:class='group.key',
)
h2
| {{ group.label }}
|
span.badge.badge-pill.badge-default {{items[group.key].length}}
itemRows(
:items="sortItems(items[group.key], selectedSortGearBy)",
:itemWidth=94,
:itemMargin=24,
:type="group.key",
:noItemsLabel="$t('noGearItemsOfType', { type: group.label })"
)
template(slot="item", scope="context")
item(
:item="context.item",
:itemContentClass="'shop_' + context.item.key",
:emptyItem="!context.item || context.item.key.indexOf('_base_0') !== -1",
:key="context.item.key",
@click="openEquipDialog(context.item)"
)
template(slot="itemBadge", scope="context")
starBadge(
:selected="activeItems[context.item.type] === context.item.key",
:show="!costume || user.preferences.costume",
@click="openEquipDialog(context.item)",
)
template(slot="popoverContent", scope="context")
equipmentAttributesPopover(:item="context.item")
equipGearModal(
:item="gearToEquip",
@equipItem="equipItem($event)",
@change="changeModalState($event)",
:costumeMode="costume",
:isEquipped="gearToEquip == null ? false : activeItems[gearToEquip.type] === gearToEquip.key"
)
</template>
<script>
import { mapState } from 'client/libs/store';
import each from 'lodash/each';
import map from 'lodash/map';
import throttle from 'lodash/throttle';
import _sortBy from 'lodash/sortBy';
import _reverse from 'lodash/reverse';
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
import bPopover from 'bootstrap-vue/lib/components/popover';
import toggleSwitch from 'client/components/ui/toggleSwitch';
import Item from 'client/components/inventory/item';
import ItemRows from 'client/components/ui/itemRows';
import EquipmentAttributesPopover from 'client/components/inventory/equipment/attributesPopover';
import StarBadge from 'client/components/ui/starBadge';
import Drawer from 'client/components/ui/drawer';
import i18n from 'common/script/i18n';
import EquipGearModal from './equipGearModal';
const sortGearTypes = ['sortByName', 'sortByCon', 'sortByPer', 'sortByStr', 'sortByInt'];
const sortGearTypeMap = {
sortByName: (i) => i.text(),
sortByCon: 'con',
sortByStr: 'str',
sortByInt: 'int',
};
export default {
name: 'Equipment',
components: {
Item,
ItemRows,
EquipmentAttributesPopover,
StarBadge,
Drawer,
bDropdown,
bDropdownItem,
bPopover,
toggleSwitch,
EquipGearModal,
},
data () {
return {
itemsPerLine: 9,
searchText: null,
searchTextThrottled: null,
costume: false,
groupBy: 'type', // or 'class'
gearTypesToStrings: Object.freeze({ // TODO use content.itemList?
weapon: i18n.t('weaponCapitalized'),
shield: i18n.t('offhandCapitalized'),
head: i18n.t('headgearCapitalized'),
armor: i18n.t('armorCapitalized'),
headAccessory: i18n.t('headAccessoryCapitalized'),
eyewear: i18n.t('eyewear'),
body: i18n.t('body'),
back: i18n.t('back'),
}),
gearClassesToStrings: Object.freeze({
warrior: i18n.t('warrior'),
wizard: i18n.t('mage'),
rogue: i18n.t('rogue'),
healer: i18n.t('healer'),
special: i18n.t('special'),
mystery: i18n.t('mystery'),
armoire: i18n.t('armoireText'),
}),
viewOptions: {},
gearToEquip: null,
sortGearBy: sortGearTypes,
selectedSortGearBy: 'sortByName',
};
},
watch: {
searchText: throttle(function throttleSearch () {
this.searchTextThrottled = this.searchText;
}, 250),
},
methods: {
openEquipDialog (item) {
this.gearToEquip = item;
},
changeModalState (visible) {
if (!visible) {
this.gearToEquip = null;
}
},
equipItem (item) {
this.$store.dispatch('common:equip', {key: item.key, type: this.costume ? 'costume' : 'equipped'});
this.gearToEquip = null;
},
changeDrawerPreference (newVal) {
this.$store.dispatch('user:set', {
[`preferences.${this.drawerPreference}`]: newVal,
});
},
sortItems (items, sortBy) {
return _reverse(_sortBy(items, sortGearTypeMap[sortBy]));
},
},
computed: {
...mapState({
content: 'content',
user: 'user.data',
ownedItems: 'user.data.items.gear.owned',
equippedItems: 'user.data.items.gear.equipped',
costumeItems: 'user.data.items.gear.costume',
flatGear: 'content.gear.flat',
}),
drawerPreference () {
return this.costume === true ? 'costume' : 'autoEquip';
},
activeItems () {
return this.costume === true ? this.costumeItems : this.equippedItems;
},
gearItemsByType () {
const searchText = this.searchTextThrottled;
const gearItemsByType = {};
each(this.gearTypesToStrings, (string, type) => {
gearItemsByType[type] = [];
});
each(this.ownedItems, (isOwned, gearKey) => {
if (isOwned === true) {
const ownedItem = this.flatGear[gearKey];
const isSearched = !searchText || ownedItem.text().toLowerCase().indexOf(searchText) !== -1;
if (ownedItem.klass !== 'base' && isSearched) {
const type = ownedItem.type;
const isEquipped = this.activeItems[type] === ownedItem.key;
const viewOptions = this.viewOptions[type];
const firstRender = viewOptions.firstRender;
const itemsInFirstPosition = viewOptions.itemsInFirstPosition;
// Render selected items in first postion only for the first render
if (itemsInFirstPosition.indexOf(ownedItem.key) !== -1 && firstRender === false) {
gearItemsByType[type].unshift(ownedItem);
} else if (isEquipped === true && firstRender === true) {
gearItemsByType[type].unshift(ownedItem);
itemsInFirstPosition.push(ownedItem.key);
} else {
gearItemsByType[type].push(ownedItem);
}
}
}
});
each(this.gearTypesToStrings, (string, type) => {
this.viewOptions[type].firstRender = false;
});
return gearItemsByType;
},
gearItemsByClass () {
const searchText = this.searchTextThrottled;
const gearItemsByClass = {};
each(this.gearClassesToStrings, (string, klass) => {
gearItemsByClass[klass] = [];
});
each(this.ownedItems, (isOwned, gearKey) => {
if (isOwned === true) {
const ownedItem = this.flatGear[gearKey];
const klass = ownedItem.klass;
const isSearched = !searchText || ownedItem.text().toLowerCase().indexOf(searchText) !== -1;
if (klass !== 'base' && isSearched) {
const isEquipped = this.activeItems[ownedItem.type] === ownedItem.key;
const viewOptions = this.viewOptions[klass];
const firstRender = viewOptions.firstRender;
const itemsInFirstPosition = viewOptions.itemsInFirstPosition;
// Render selected items in first postion only for the first render
if (itemsInFirstPosition.indexOf(ownedItem.key) !== -1 && firstRender === false) {
gearItemsByClass[klass].unshift(ownedItem);
} else if (isEquipped === true && firstRender === true) {
gearItemsByClass[klass].unshift(ownedItem);
itemsInFirstPosition.push(ownedItem.key);
} else {
gearItemsByClass[klass].push(ownedItem);
}
}
}
});
each(this.gearClassesToStrings, (string, klass) => {
this.viewOptions[klass].firstRender = false;
});
return gearItemsByClass;
},
groups () {
return this.groupBy === 'type' ? this.gearTypesToStrings : this.gearClassesToStrings;
},
items () {
return this.groupBy === 'type' ? this.gearItemsByType : this.gearItemsByClass;
},
itemsGroups () {
return map(this.groups, (label, group) => {
this.$set(this.viewOptions, group, {
selected: true,
open: false,
itemsInFirstPosition: [],
firstRender: true,
});
return {
key: group,
label,
};
});
},
},
};
</script>