mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 22:57:21 +01:00
WIP(shops): first full test version
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
id="body"
|
id="body"
|
||||||
class="section customize-section"
|
class="customize-section d-flex flex-column justify-content-between"
|
||||||
>
|
>
|
||||||
<sub-menu
|
<sub-menu
|
||||||
class="text-center"
|
class="text-center"
|
||||||
@@ -17,17 +17,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="activeSubPage === 'shirt'">
|
<div v-if="activeSubPage === 'shirt'">
|
||||||
<customize-options
|
<customize-options
|
||||||
:items="freeShirts"
|
:items="userShirts"
|
||||||
:current-value="user.preferences.shirt"
|
:current-value="user.preferences.shirt"
|
||||||
/>
|
/>
|
||||||
<customize-options
|
|
||||||
v-if="editing"
|
|
||||||
:items="specialShirts"
|
|
||||||
:current-value="user.preferences.shirt"
|
|
||||||
:full-set="!userOwnsSet('shirt', specialShirtKeys)"
|
|
||||||
@unlock="unlock(`shirt.${specialShirtKeys.join(',shirt.')}`)"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<customize-banner />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -36,16 +30,14 @@ import appearance from '@/../../common/script/content/appearance';
|
|||||||
import { subPageMixin } from '../../mixins/subPage';
|
import { subPageMixin } from '../../mixins/subPage';
|
||||||
import { userStateMixin } from '../../mixins/userState';
|
import { userStateMixin } from '../../mixins/userState';
|
||||||
import { avatarEditorUtilities } from '../../mixins/avatarEditUtilities';
|
import { avatarEditorUtilities } from '../../mixins/avatarEditUtilities';
|
||||||
import subMenu from './sub-menu';
|
import customizeBanner from './customize-banner.vue';
|
||||||
import customizeOptions from './customize-options';
|
import customizeOptions from './customize-options';
|
||||||
import gem from '@/assets/svg/gem.svg';
|
import subMenu from './sub-menu';
|
||||||
|
|
||||||
const freeShirtKeys = Object.keys(appearance.shirt).filter(k => appearance.shirt[k].price === 0);
|
|
||||||
const specialShirtKeys = Object.keys(appearance.shirt).filter(k => appearance.shirt[k].price !== 0);
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
subMenu,
|
subMenu,
|
||||||
|
customizeBanner,
|
||||||
customizeOptions,
|
customizeOptions,
|
||||||
},
|
},
|
||||||
mixins: [
|
mixins: [
|
||||||
@@ -58,10 +50,6 @@ export default {
|
|||||||
],
|
],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
specialShirtKeys,
|
|
||||||
icons: Object.freeze({
|
|
||||||
gem,
|
|
||||||
}),
|
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: 'size',
|
id: 'size',
|
||||||
@@ -78,25 +66,19 @@ export default {
|
|||||||
sizes () {
|
sizes () {
|
||||||
return ['slim', 'broad'].map(s => this.mapKeysToFreeOption(s, 'size'));
|
return ['slim', 'broad'].map(s => this.mapKeysToFreeOption(s, 'size'));
|
||||||
},
|
},
|
||||||
freeShirts () {
|
userShirts () {
|
||||||
return freeShirtKeys.map(s => this.mapKeysToFreeOption(s, 'shirt'));
|
const freeShirts = Object.keys(appearance.shirt)
|
||||||
},
|
.filter(k => appearance.shirt[k].price === 0)
|
||||||
specialShirts () {
|
.map(s => this.mapKeysToFreeOption(s, 'shirt'));
|
||||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
const ownedShirts = Object.keys(this.user.purchased.shirt)
|
||||||
const keys = this.specialShirtKeys;
|
.filter(k => this.user.purchased.shirt[k])
|
||||||
const options = keys.map(key => this.mapKeysToOption(key, 'shirt'));
|
.map(s => this.mapKeysToFreeOption(s, 'shirt'));
|
||||||
return options;
|
|
||||||
|
return [...freeShirts, ...ownedShirts];
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.changeSubPage('size');
|
this.changeSubPage('size');
|
||||||
},
|
},
|
||||||
methods: {
|
|
||||||
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<div class="bottom-banner">
|
||||||
|
<div class="d-flex justify-content-center align-items-center mt-3">
|
||||||
|
<span
|
||||||
|
class="svg svg-icon sparkles"
|
||||||
|
v-html="icons.sparkles"
|
||||||
|
></span>
|
||||||
|
<strong
|
||||||
|
v-once
|
||||||
|
class="mx-2"
|
||||||
|
> {{ $t('lookingForMore') }}
|
||||||
|
</strong>
|
||||||
|
<span
|
||||||
|
v-once
|
||||||
|
class="svg svg-icon sparkles mirror"
|
||||||
|
v-html="icons.sparkles"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="check-link"
|
||||||
|
>
|
||||||
|
<span>Check out the </span>
|
||||||
|
<a href='/shops/customizations'>Customizations Shop</a>
|
||||||
|
<span> for even more ways to customize your avatar!</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '~@/assets/scss/colors.scss';
|
||||||
|
|
||||||
|
.bottom-banner {
|
||||||
|
background: linear-gradient(114.26deg, $purple-300 0%, $purple-200 100%);
|
||||||
|
border-bottom-left-radius: 5px;
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
color: $white;
|
||||||
|
height: 80px;
|
||||||
|
line-height: 24px;
|
||||||
|
|
||||||
|
.check-link, a {
|
||||||
|
color: $purple-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sparkles {
|
||||||
|
width: 32px;
|
||||||
|
|
||||||
|
&.mirror {
|
||||||
|
transform: scaleX(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import sparkles from '@/assets/svg/sparkles-left.svg';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
icons: Object.freeze({
|
||||||
|
sparkles,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="customize-options"
|
class="customize-options"
|
||||||
:class="{'background-set': fullSet}"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-for="option in items"
|
v-for="option in items"
|
||||||
@@ -28,38 +27,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
v-if="option.gemLocked"
|
|
||||||
class="gem-lock"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="svg-icon gem"
|
|
||||||
v-html="icons.gem"
|
|
||||||
></div>
|
|
||||||
<span>{{ option.gem }}</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="option.goldLocked"
|
|
||||||
class="gold-lock"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="svg-icon gold"
|
|
||||||
v-html="icons.gold"
|
|
||||||
></div>
|
|
||||||
<span>{{ option.gold }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="fullSet"
|
|
||||||
class="purchase-set"
|
|
||||||
@click="unlock()"
|
|
||||||
>
|
|
||||||
<span class="label">{{ $t('purchaseAll') }}</span>
|
|
||||||
<div
|
|
||||||
class="svg-icon gem"
|
|
||||||
v-html="icons.gem"
|
|
||||||
></div>
|
|
||||||
<span class="price">5</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -73,7 +40,7 @@ export default {
|
|||||||
mixins: [
|
mixins: [
|
||||||
avatarEditorUtilities,
|
avatarEditorUtilities,
|
||||||
],
|
],
|
||||||
props: ['items', 'currentValue', 'fullSet'],
|
props: ['items', 'currentValue'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
icons: Object.freeze({
|
icons: Object.freeze({
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
id="extra"
|
id="extra"
|
||||||
class="section container customize-section"
|
class="customize-section d-flex flex-column"
|
||||||
|
:class="{ 'justify-content-between': !showEmptySection}"
|
||||||
>
|
>
|
||||||
<sub-menu
|
<sub-menu
|
||||||
class="text-center"
|
class="text-center"
|
||||||
@@ -20,9 +21,8 @@
|
|||||||
id="animal-ears"
|
id="animal-ears"
|
||||||
>
|
>
|
||||||
<customize-options
|
<customize-options
|
||||||
|
v-if="animalItems('back').length > 1"
|
||||||
:items="animalItems('headAccessory')"
|
:items="animalItems('headAccessory')"
|
||||||
:full-set="!animalItemsOwned('headAccessory')"
|
|
||||||
@unlock="unlock(animalItemsUnlockString('headAccessory'))"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -30,9 +30,8 @@
|
|||||||
id="animal-tails"
|
id="animal-tails"
|
||||||
>
|
>
|
||||||
<customize-options
|
<customize-options
|
||||||
|
v-if="animalItems('back').length > 1"
|
||||||
:items="animalItems('back')"
|
:items="animalItems('back')"
|
||||||
:full-set="!animalItemsOwned('back')"
|
|
||||||
@unlock="unlock(animalItemsUnlockString('back'))"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -53,6 +52,22 @@
|
|||||||
>
|
>
|
||||||
<customize-options :items="flowers" />
|
<customize-options :items="flowers" />
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="showEmptySection"
|
||||||
|
class="my-5"
|
||||||
|
>
|
||||||
|
<h3
|
||||||
|
v-once
|
||||||
|
> {{ $t('noItemsOwned') }} </h3>
|
||||||
|
<p
|
||||||
|
v-once
|
||||||
|
v-html="$t('visitCustomizationsShop')"
|
||||||
|
class="w-50 mx-auto"
|
||||||
|
></p>
|
||||||
|
</div>
|
||||||
|
<customize-banner
|
||||||
|
v-else
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -61,17 +76,18 @@ import appearance from '@/../../common/script/content/appearance';
|
|||||||
import { subPageMixin } from '../../mixins/subPage';
|
import { subPageMixin } from '../../mixins/subPage';
|
||||||
import { userStateMixin } from '../../mixins/userState';
|
import { userStateMixin } from '../../mixins/userState';
|
||||||
import { avatarEditorUtilities } from '../../mixins/avatarEditUtilities';
|
import { avatarEditorUtilities } from '../../mixins/avatarEditUtilities';
|
||||||
import subMenu from './sub-menu';
|
import customizeBanner from './customize-banner';
|
||||||
import customizeOptions from './customize-options';
|
import customizeOptions from './customize-options';
|
||||||
import gem from '@/assets/svg/gem.svg';
|
import subMenu from './sub-menu';
|
||||||
|
|
||||||
const freeShirtKeys = Object.keys(appearance.shirt).filter(k => appearance.shirt[k].price === 0);
|
const freeShirtKeys = Object.keys(appearance.shirt).filter(k => appearance.shirt[k].price === 0);
|
||||||
const specialShirtKeys = Object.keys(appearance.shirt).filter(k => appearance.shirt[k].price !== 0);
|
const specialShirtKeys = Object.keys(appearance.shirt).filter(k => appearance.shirt[k].price !== 0);
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
subMenu,
|
customizeBanner,
|
||||||
customizeOptions,
|
customizeOptions,
|
||||||
|
subMenu,
|
||||||
},
|
},
|
||||||
mixins: [
|
mixins: [
|
||||||
subPageMixin,
|
subPageMixin,
|
||||||
@@ -89,9 +105,6 @@ export default {
|
|||||||
},
|
},
|
||||||
chairKeys: ['none', 'black', 'blue', 'green', 'pink', 'red', 'yellow', 'handleless_black', 'handleless_blue', 'handleless_green', 'handleless_pink', 'handleless_red', 'handleless_yellow'],
|
chairKeys: ['none', 'black', 'blue', 'green', 'pink', 'red', 'yellow', 'handleless_black', 'handleless_blue', 'handleless_green', 'handleless_pink', 'handleless_red', 'handleless_yellow'],
|
||||||
specialShirtKeys,
|
specialShirtKeys,
|
||||||
icons: Object.freeze({
|
|
||||||
gem,
|
|
||||||
}),
|
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: 'size',
|
id: 'size',
|
||||||
@@ -178,7 +191,7 @@ export default {
|
|||||||
return freeShirtKeys.map(s => this.mapKeysToFreeOption(s, 'shirt'));
|
return freeShirtKeys.map(s => this.mapKeysToFreeOption(s, 'shirt'));
|
||||||
},
|
},
|
||||||
specialShirts () {
|
specialShirts () {
|
||||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||||
const keys = this.specialShirtKeys;
|
const keys = this.specialShirtKeys;
|
||||||
const options = keys.map(key => this.mapKeysToOption(key, 'shirt'));
|
const options = keys.map(key => this.mapKeysToOption(key, 'shirt'));
|
||||||
return options;
|
return options;
|
||||||
@@ -228,6 +241,16 @@ export default {
|
|||||||
});
|
});
|
||||||
return options;
|
return options;
|
||||||
},
|
},
|
||||||
|
showEmptySection () {
|
||||||
|
switch (this.activeSubPage) {
|
||||||
|
case 'ears':
|
||||||
|
return this.editing && this.animalItems('headAccessory').length === 1;
|
||||||
|
case 'tails':
|
||||||
|
return this.editing && this.animalItems('back').length === 1;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.changeSubPage(this.extraSubMenuItems[0].id);
|
this.changeSubPage(this.extraSubMenuItems[0].id);
|
||||||
@@ -236,7 +259,7 @@ export default {
|
|||||||
animalItems (category) {
|
animalItems (category) {
|
||||||
// @TODO: For some resonse when I use $set on the
|
// @TODO: For some resonse when I use $set on the
|
||||||
// user purchases object, this is not recomputed. Hack for now
|
// user purchases object, this is not recomputed. Hack for now
|
||||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||||
const keys = this.animalItemKeys[category];
|
const keys = this.animalItemKeys[category];
|
||||||
|
|
||||||
const noneOption = this.createGearItem(0, category, 'base', category);
|
const noneOption = this.createGearItem(0, category, 'base', category);
|
||||||
@@ -248,36 +271,22 @@ export default {
|
|||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
const newKey = `${category}_special_${key}`;
|
const newKey = `${category}_special_${key}`;
|
||||||
const userPurchased = this.user.items.gear.owned[newKey];
|
const userPurchased = this.user.items.gear.owned[newKey];
|
||||||
|
if (userPurchased) {
|
||||||
const option = {};
|
const option = {};
|
||||||
option.key = key;
|
option.key = key;
|
||||||
option.active = this.user.preferences.costume
|
option.active = this.user.preferences.costume
|
||||||
? this.user.items.gear.costume[category] === newKey
|
? this.user.items.gear.costume[category] === newKey
|
||||||
: this.user.items.gear.equipped[category] === newKey;
|
: this.user.items.gear.equipped[category] === newKey;
|
||||||
option.class = `headAccessory_special_${option.key} ${category}`;
|
option.class = `headAccessory_special_${option.key} ${category}`;
|
||||||
if (category === 'back') {
|
if (category === 'back') {
|
||||||
option.class = `icon_back_special_${option.key} back`;
|
option.class = `icon_back_special_${option.key} back`;
|
||||||
}
|
|
||||||
option.gemLocked = userPurchased === undefined;
|
|
||||||
option.goldLocked = userPurchased === false;
|
|
||||||
if (option.goldLocked) {
|
|
||||||
option.gold = 20;
|
|
||||||
}
|
|
||||||
if (option.gemLocked) {
|
|
||||||
option.gem = 2;
|
|
||||||
}
|
|
||||||
option.locked = option.gemLocked || option.goldLocked;
|
|
||||||
option.click = () => {
|
|
||||||
if (option.gemLocked) {
|
|
||||||
return this.unlock(`items.gear.owned.${newKey}`);
|
|
||||||
} if (option.goldLocked) {
|
|
||||||
return this.buy(newKey);
|
|
||||||
}
|
}
|
||||||
const type = this.user.preferences.costume ? 'costume' : 'equipped';
|
option.click = () => {
|
||||||
return this.equip(newKey, type);
|
const type = this.user.preferences.costume ? 'costume' : 'equipped';
|
||||||
};
|
return this.equip(newKey, type);
|
||||||
|
};
|
||||||
options.push(option);
|
options.push(option);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
@@ -287,17 +296,6 @@ export default {
|
|||||||
|
|
||||||
return keys.join(',');
|
return keys.join(',');
|
||||||
},
|
},
|
||||||
animalItemsOwned (category) {
|
|
||||||
// @TODO: For some resonse when I use $set on the user purchases object,
|
|
||||||
// this is not recomputed. Hack for now
|
|
||||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
|
||||||
|
|
||||||
let own = true;
|
|
||||||
this.animalItemKeys[category].forEach(key => {
|
|
||||||
if (this.user.items.gear.owned[`${category}_special_${key}`] === undefined) own = false;
|
|
||||||
});
|
|
||||||
return own;
|
|
||||||
},
|
|
||||||
createGearItem (key, gearType, subGearType, additionalClass) {
|
createGearItem (key, gearType, subGearType, additionalClass) {
|
||||||
const newKey = `${gearType}_${subGearType ? `${subGearType}_` : ''}${key}`;
|
const newKey = `${gearType}_${subGearType ? `${subGearType}_` : ''}${key}`;
|
||||||
const option = {};
|
const option = {};
|
||||||
@@ -339,7 +337,3 @@ export default {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
id="hair"
|
id="hair"
|
||||||
class="section customize-section"
|
class="customize-section d-flex flex-column"
|
||||||
|
:class="{ 'justify-content-between': !showEmptySection}"
|
||||||
>
|
>
|
||||||
<sub-menu
|
<sub-menu
|
||||||
class="text-center"
|
class="text-center"
|
||||||
@@ -14,37 +15,9 @@
|
|||||||
id="hair-color"
|
id="hair-color"
|
||||||
>
|
>
|
||||||
<customize-options
|
<customize-options
|
||||||
:items="freeHairColors"
|
:items="userHairColors"
|
||||||
:current-value="user.preferences.hair.color"
|
:current-value="user.preferences.hair.color"
|
||||||
/>
|
/>
|
||||||
<!-- eslint-disable vue/no-use-v-if-with-v-for -->
|
|
||||||
<div
|
|
||||||
v-for="set in seasonalHairColors"
|
|
||||||
v-if="editing && set.key !== 'undefined'"
|
|
||||||
:key="set.key"
|
|
||||||
>
|
|
||||||
<!-- eslint-enable vue/no-use-v-if-with-v-for -->
|
|
||||||
<customize-options
|
|
||||||
:items="set.options"
|
|
||||||
:current-value="user.preferences.hair.color"
|
|
||||||
:full-set="!hideSet(set.key) && !userOwnsSet('hair', set.keys, 'color')"
|
|
||||||
@unlock="unlock(`hair.color.${set.keys.join(',hair.color.')}`)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="activeSubPage === 'style'"
|
|
||||||
id="style"
|
|
||||||
>
|
|
||||||
<!-- eslint-disable vue/require-v-for-key NO KEY AVAILABLE HERE -->
|
|
||||||
<div v-for="set in styleSets">
|
|
||||||
<customize-options
|
|
||||||
:items="set.options"
|
|
||||||
:full-set="set.fullSet"
|
|
||||||
@unlock="set.unlock()"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- eslint-enable vue/require-v-for-key -->
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="activeSubPage === 'bangs'"
|
v-if="activeSubPage === 'bangs'"
|
||||||
@@ -55,44 +28,62 @@
|
|||||||
:current-value="user.preferences.hair.bangs"
|
:current-value="user.preferences.hair.bangs"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="activeSubPage === 'style'"
|
||||||
|
id="style"
|
||||||
|
>
|
||||||
|
<customize-options
|
||||||
|
:items="userHairStyles"
|
||||||
|
:current-value="user.preferences.hair.base"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="activeSubPage === 'facialhair'"
|
v-if="activeSubPage === 'facialhair'"
|
||||||
id="facialhair"
|
id="facialhair"
|
||||||
>
|
>
|
||||||
<customize-options
|
<customize-options
|
||||||
v-if="editing"
|
v-if="editing && userMustaches.length > 1"
|
||||||
:items="mustacheList"
|
:items="userMustaches"
|
||||||
/>
|
/>
|
||||||
<!-- eslint-disable max-len -->
|
|
||||||
<customize-options
|
<customize-options
|
||||||
v-if="editing"
|
v-if="editing && userBeards.length > 1"
|
||||||
:items="beardList"
|
:items="userBeards"
|
||||||
:full-set="isPurchaseAllNeeded('hair', ['baseHair5', 'baseHair6'], ['mustache', 'beard'])"
|
|
||||||
@unlock="unlock(`hair.mustache.${baseHair5Keys.join(',hair.mustache.')},hair.beard.${baseHair6Keys.join(',hair.beard.')}`)"
|
|
||||||
/>
|
/>
|
||||||
<!-- eslint-enable max-len -->
|
<div
|
||||||
|
class="my-5"
|
||||||
|
v-if="showEmptySection"
|
||||||
|
>
|
||||||
|
<h3
|
||||||
|
v-once
|
||||||
|
> {{ $t('noItemsOwned') }} </h3>
|
||||||
|
<p
|
||||||
|
v-once
|
||||||
|
v-html="$t('visitCustomizationsShop')"
|
||||||
|
class="w-50 mx-auto"
|
||||||
|
></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<customize-banner
|
||||||
|
v-if="!showEmptySection"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import groupBy from 'lodash/groupBy';
|
import groupBy from 'lodash/groupBy';
|
||||||
import appearance from '@/../../common/script/content/appearance';
|
import appearance from '@/../../common/script/content/appearance';
|
||||||
import appearanceSets from '@/../../common/script/content/appearance/sets';
|
|
||||||
import { subPageMixin } from '../../mixins/subPage';
|
import { subPageMixin } from '../../mixins/subPage';
|
||||||
import { userStateMixin } from '../../mixins/userState';
|
import { userStateMixin } from '../../mixins/userState';
|
||||||
import { avatarEditorUtilities } from '../../mixins/avatarEditUtilities';
|
import { avatarEditorUtilities } from '../../mixins/avatarEditUtilities';
|
||||||
import subMenu from './sub-menu';
|
import customizeBanner from './customize-banner';
|
||||||
import customizeOptions from './customize-options';
|
import customizeOptions from './customize-options';
|
||||||
import gem from '@/assets/svg/gem.svg';
|
import subMenu from './sub-menu';
|
||||||
|
|
||||||
const hairColorBySet = groupBy(appearance.hair.color, 'set.key');
|
|
||||||
const freeHairColorKeys = hairColorBySet[undefined].map(s => s.key);
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
subMenu,
|
customizeBanner,
|
||||||
customizeOptions,
|
customizeOptions,
|
||||||
|
subMenu,
|
||||||
},
|
},
|
||||||
mixins: [
|
mixins: [
|
||||||
subPageMixin,
|
subPageMixin,
|
||||||
@@ -102,20 +93,6 @@ export default {
|
|||||||
props: [
|
props: [
|
||||||
'editing',
|
'editing',
|
||||||
],
|
],
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
freeHairColorKeys,
|
|
||||||
icons: Object.freeze({
|
|
||||||
gem,
|
|
||||||
}),
|
|
||||||
baseHair1: [1, 3],
|
|
||||||
baseHair2Keys: [2, 4, 5, 6, 7, 8],
|
|
||||||
baseHair3Keys: [9, 10, 11, 12, 13, 14],
|
|
||||||
baseHair4Keys: [15, 16, 17, 18, 19, 20],
|
|
||||||
baseHair5Keys: [1, 2],
|
|
||||||
baseHair6Keys: [1, 2, 3],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
hairSubMenuItems () {
|
hairSubMenuItems () {
|
||||||
const items = [
|
const items = [
|
||||||
@@ -142,91 +119,46 @@ export default {
|
|||||||
|
|
||||||
return items;
|
return items;
|
||||||
},
|
},
|
||||||
freeHairColors () {
|
userHairColors () {
|
||||||
return freeHairColorKeys.map(s => this.mapKeysToFreeOption(s, 'hair', 'color'));
|
const freeHairColors = groupBy(appearance.hair.color, 'set.key')[undefined]
|
||||||
|
.map(s => s.key).map(s => this.mapKeysToFreeOption(s, 'hair', 'color'));
|
||||||
|
const ownedHairColors = Object.keys(this.user.purchased.hair.color || {})
|
||||||
|
.filter(k => this.user.purchased.hair.color[k])
|
||||||
|
.map(h => this.mapKeysToFreeOption(h, 'hair', 'color'));
|
||||||
|
return [...freeHairColors, ...ownedHairColors];
|
||||||
},
|
},
|
||||||
seasonalHairColors () {
|
userHairStyles () {
|
||||||
// @TODO: For some resonse when I use $set on the user purchases object,
|
const emptyHairStyle = {
|
||||||
// this is not recomputed. Hack for now
|
...this.mapKeysToFreeOption(0, 'hair', 'base'),
|
||||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
none: true,
|
||||||
|
};
|
||||||
|
const freeHairStyles = [1, 3].map(s => this.mapKeysToFreeOption(s, 'hair', 'base'));
|
||||||
|
const ownedHairStyles = Object.keys(this.user.purchased.hair.base || {})
|
||||||
|
.filter(k => this.user.purchased.hair.base[k])
|
||||||
|
.map(h => this.mapKeysToFreeOption(h, 'hair', 'base'));
|
||||||
|
return [emptyHairStyle, ...freeHairStyles, ...ownedHairStyles];
|
||||||
|
},
|
||||||
|
userMustaches () {
|
||||||
|
const emptyMustache = {
|
||||||
|
...this.mapKeysToFreeOption(0, 'hair', 'mustache'),
|
||||||
|
none: true,
|
||||||
|
};
|
||||||
|
const ownedMustaches = Object.keys(this.user.purchased.hair.mustache || {})
|
||||||
|
.filter(k => this.user.purchased.hair.mustache[k])
|
||||||
|
.map(h => this.mapKeysToFreeOption(h, 'hair', 'mustache'));
|
||||||
|
|
||||||
const seasonalHairColors = [];
|
return [emptyMustache, ...ownedMustaches];
|
||||||
for (const key of Object.keys(hairColorBySet)) {
|
},
|
||||||
const set = hairColorBySet[key];
|
userBeards () {
|
||||||
|
const emptyBeard = {
|
||||||
|
...this.mapKeysToFreeOption(0, 'hair', 'beard'),
|
||||||
|
none: true,
|
||||||
|
};
|
||||||
|
const ownedBeards = Object.keys(this.user.purchased.hair.beard || {})
|
||||||
|
.filter(k => this.user.purchased.hair.beard[k])
|
||||||
|
.map(h => this.mapKeysToFreeOption(h, 'hair', 'beard'));
|
||||||
|
|
||||||
const keys = set.map(item => item.key);
|
return [emptyBeard, ...ownedBeards];
|
||||||
|
|
||||||
const options = keys.map(optionKey => {
|
|
||||||
const option = this.mapKeysToOption(optionKey, 'hair', 'color', key);
|
|
||||||
return option;
|
|
||||||
});
|
|
||||||
|
|
||||||
let text = this.$t(key);
|
|
||||||
if (appearanceSets[key] && appearanceSets[key].text) {
|
|
||||||
text = appearanceSets[key].text();
|
|
||||||
}
|
|
||||||
|
|
||||||
const compiledSet = {
|
|
||||||
key,
|
|
||||||
options,
|
|
||||||
keys,
|
|
||||||
text,
|
|
||||||
};
|
|
||||||
seasonalHairColors.push(compiledSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
return seasonalHairColors;
|
|
||||||
},
|
|
||||||
premiumHairColors () {
|
|
||||||
// @TODO: For some resonse when I use $set on the user purchases object,
|
|
||||||
// this is not recomputed. Hack for now
|
|
||||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
|
||||||
const keys = this.premiumHairColorKeys;
|
|
||||||
const options = keys.map(key => this.mapKeysToOption(key, 'hair', 'color'));
|
|
||||||
return options;
|
|
||||||
},
|
|
||||||
baseHair2 () {
|
|
||||||
// @TODO: For some resonse when I use $set on the user purchases object,
|
|
||||||
// this is not recomputed. Hack for now
|
|
||||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
|
||||||
const keys = this.baseHair2Keys;
|
|
||||||
const options = keys.map(key => this.mapKeysToOption(key, 'hair', 'base'));
|
|
||||||
return options;
|
|
||||||
},
|
|
||||||
baseHair3 () {
|
|
||||||
// @TODO: For some resonse when I use $set on the user purchases object,
|
|
||||||
// this is not recomputed. Hack for now
|
|
||||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
|
||||||
const keys = this.baseHair3Keys;
|
|
||||||
const options = keys.map(key => {
|
|
||||||
const option = this.mapKeysToOption(key, 'hair', 'base');
|
|
||||||
return option;
|
|
||||||
});
|
|
||||||
return options;
|
|
||||||
},
|
|
||||||
baseHair4 () {
|
|
||||||
// @TODO: For some resonse when I use $set on the user purchases object,
|
|
||||||
// this is not recomputed. Hack for now
|
|
||||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
|
||||||
const keys = this.baseHair4Keys;
|
|
||||||
const options = keys.map(key => this.mapKeysToOption(key, 'hair', 'base'));
|
|
||||||
return options;
|
|
||||||
},
|
|
||||||
baseHair5 () {
|
|
||||||
// @TODO: For some resonse when I use $set on the user purchases object,
|
|
||||||
// this is not recomputed. Hack for now
|
|
||||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
|
||||||
const keys = this.baseHair5Keys;
|
|
||||||
const options = keys.map(key => this.mapKeysToOption(key, 'hair', 'mustache'));
|
|
||||||
return options;
|
|
||||||
},
|
|
||||||
baseHair6 () {
|
|
||||||
// @TODO: For some resonse when I use $set on the user purchases object,
|
|
||||||
// this is not recomputed. Hack for now
|
|
||||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
|
||||||
const keys = this.baseHair6Keys;
|
|
||||||
const options = keys.map(key => this.mapKeysToOption(key, 'hair', 'beard'));
|
|
||||||
return options;
|
|
||||||
},
|
},
|
||||||
hairBangs () {
|
hairBangs () {
|
||||||
const none = this.mapKeysToFreeOption(0, 'hair', 'bangs');
|
const none = this.mapKeysToFreeOption(0, 'hair', 'bangs');
|
||||||
@@ -236,136 +168,13 @@ export default {
|
|||||||
|
|
||||||
return [none, ...options];
|
return [none, ...options];
|
||||||
},
|
},
|
||||||
mustacheList () {
|
showEmptySection () {
|
||||||
const noneOption = this.mapKeysToFreeOption(0, 'hair', 'mustache');
|
return this.editing && this.activeSubPage === 'facialhair'
|
||||||
noneOption.none = true;
|
&& this.userMustaches.length === 1 && this.userBeards.length === 1;
|
||||||
|
|
||||||
return [noneOption, ...this.baseHair5];
|
|
||||||
},
|
|
||||||
beardList () {
|
|
||||||
const noneOption = this.mapKeysToFreeOption(0, 'hair', 'beard');
|
|
||||||
noneOption.none = true;
|
|
||||||
|
|
||||||
return [noneOption, ...this.baseHair6];
|
|
||||||
},
|
|
||||||
styleSets () {
|
|
||||||
const sets = [];
|
|
||||||
|
|
||||||
const emptyHairBase = {
|
|
||||||
...this.mapKeysToFreeOption(0, 'hair', 'base'),
|
|
||||||
none: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
sets.push({
|
|
||||||
options: [
|
|
||||||
emptyHairBase,
|
|
||||||
...this.baseHair1.map(key => this.mapKeysToFreeOption(key, 'hair', 'base')),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.editing) {
|
|
||||||
sets.push({
|
|
||||||
fullSet: !this.userOwnsSet('hair', this.baseHair3Keys, 'base'),
|
|
||||||
unlock: () => this.unlock(`hair.base.${this.baseHair3Keys.join(',hair.base.')}`),
|
|
||||||
options: [
|
|
||||||
...this.baseHair3,
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
sets.push({
|
|
||||||
fullSet: !this.userOwnsSet('hair', this.baseHair4Keys, 'base'),
|
|
||||||
unlock: () => this.unlock(`hair.base.${this.baseHair4Keys.join(',hair.base.')}`),
|
|
||||||
options: [
|
|
||||||
...this.baseHair4,
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.editing) {
|
|
||||||
sets.push({
|
|
||||||
fullSet: !this.userOwnsSet('hair', this.baseHair2Keys, 'base'),
|
|
||||||
unlock: () => this.unlock(`hair.base.${this.baseHair2Keys.join(',hair.base.')}`),
|
|
||||||
options: [
|
|
||||||
...this.baseHair2,
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return sets;
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.changeSubPage('color');
|
this.changeSubPage('color');
|
||||||
},
|
},
|
||||||
methods: {
|
|
||||||
/**
|
|
||||||
* Allows you to find out whether you need the "Purchase All" button or not.
|
|
||||||
* If there are more than 2 unpurchased items, returns true, otherwise returns false.
|
|
||||||
* @param {string} category - The selected category.
|
|
||||||
* @param {string[]} keySets - The items keySets.
|
|
||||||
* @param {string[]} [types] - The items types (subcategories). Optional.
|
|
||||||
* @returns {boolean} - Determines whether the "Purchase All" button
|
|
||||||
* is needed (true) or not (false).
|
|
||||||
*/
|
|
||||||
isPurchaseAllNeeded (category, keySets, types) {
|
|
||||||
const purchasedItemsLengths = [];
|
|
||||||
// If item types are specified, count them
|
|
||||||
if (types && types.length > 0) {
|
|
||||||
// Types can be undefined, so we must check them.
|
|
||||||
types.forEach(type => {
|
|
||||||
if (this.user.purchased[category][type]) {
|
|
||||||
purchasedItemsLengths
|
|
||||||
.push(Object.keys(this.user.purchased[category][type]).length);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
let purchasedItemsCounter = 0;
|
|
||||||
|
|
||||||
// If types are not specified, recursively
|
|
||||||
// search for purchased items in the category
|
|
||||||
const findPurchasedItems = item => {
|
|
||||||
if (typeof item === 'object') {
|
|
||||||
Object.values(item)
|
|
||||||
.forEach(innerItem => {
|
|
||||||
if (typeof innerItem === 'boolean' && innerItem === true) {
|
|
||||||
purchasedItemsCounter += 1;
|
|
||||||
}
|
|
||||||
return findPurchasedItems(innerItem);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return purchasedItemsCounter;
|
|
||||||
};
|
|
||||||
|
|
||||||
findPurchasedItems(this.user.purchased[category]);
|
|
||||||
if (purchasedItemsCounter > 0) {
|
|
||||||
purchasedItemsLengths.push(purchasedItemsCounter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't need to count the key sets (below)
|
|
||||||
// if there are no purchased items at all.
|
|
||||||
if (purchasedItemsLengths.length === 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const allItemsLengths = [];
|
|
||||||
// Key sets must be specify correctly.
|
|
||||||
keySets.forEach(keySet => {
|
|
||||||
allItemsLengths.push(Object.keys(this[keySet]).length);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Simply sum all the length values and
|
|
||||||
// write them into variables for the convenience.
|
|
||||||
const allItems = allItemsLengths.reduce((acc, val) => acc + val);
|
|
||||||
const purchasedItems = purchasedItemsLengths.reduce((acc, val) => acc + val);
|
|
||||||
|
|
||||||
const unpurchasedItems = allItems - purchasedItems;
|
|
||||||
return unpurchasedItems > 2;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
id="skin"
|
id="skin"
|
||||||
class="section customize-section"
|
class="customize-section d-flex flex-column justify-content-between"
|
||||||
>
|
>
|
||||||
<sub-menu
|
<sub-menu
|
||||||
class="text-center"
|
class="text-center"
|
||||||
@@ -10,47 +10,27 @@
|
|||||||
@changeSubPage="changeSubPage($event)"
|
@changeSubPage="changeSubPage($event)"
|
||||||
/>
|
/>
|
||||||
<customize-options
|
<customize-options
|
||||||
:items="freeSkins"
|
:items="userSkins"
|
||||||
:current-value="user.preferences.skin"
|
:current-value="user.preferences.skin"
|
||||||
/>
|
/>
|
||||||
<!-- eslint-disable vue/no-use-v-if-with-v-for -->
|
<customize-banner />
|
||||||
<div
|
|
||||||
v-for="set in seasonalSkins"
|
|
||||||
v-if="editing && set.key !== 'undefined'"
|
|
||||||
:key="set.key"
|
|
||||||
>
|
|
||||||
<!-- eslint-enable vue/no-use-v-if-with-v-for -->
|
|
||||||
<customize-options
|
|
||||||
:items="set.options"
|
|
||||||
:current-value="user.preferences.skin"
|
|
||||||
:full-set="!hideSet(set.key) && !userOwnsSet('skin', set.keys)"
|
|
||||||
@unlock="unlock(`skin.${set.keys.join(',skin.')}`)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import groupBy from 'lodash/groupBy';
|
import groupBy from 'lodash/groupBy';
|
||||||
import appearance from '@/../../common/script/content/appearance';
|
import appearance from '@/../../common/script/content/appearance';
|
||||||
import appearanceSets from '@/../../common/script/content/appearance/sets';
|
|
||||||
import { subPageMixin } from '../../mixins/subPage';
|
import { subPageMixin } from '../../mixins/subPage';
|
||||||
import { userStateMixin } from '../../mixins/userState';
|
import { userStateMixin } from '../../mixins/userState';
|
||||||
import { avatarEditorUtilities } from '../../mixins/avatarEditUtilities';
|
import { avatarEditorUtilities } from '../../mixins/avatarEditUtilities';
|
||||||
import subMenu from './sub-menu';
|
import customizeBanner from './customize-banner.vue';
|
||||||
import customizeOptions from './customize-options';
|
import customizeOptions from './customize-options';
|
||||||
import gem from '@/assets/svg/gem.svg';
|
import subMenu from './sub-menu';
|
||||||
|
|
||||||
const skinsBySet = groupBy(appearance.skin, 'set.key');
|
|
||||||
|
|
||||||
const freeSkinKeys = skinsBySet[undefined].map(s => s.key);
|
|
||||||
|
|
||||||
// const specialSkinKeys = Object.keys(appearance.shirt)
|
|
||||||
// .filter(k => appearance.shirt[k].price !== 0);
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
subMenu,
|
subMenu,
|
||||||
|
customizeBanner,
|
||||||
customizeOptions,
|
customizeOptions,
|
||||||
},
|
},
|
||||||
mixins: [
|
mixins: [
|
||||||
@@ -63,10 +43,6 @@ export default {
|
|||||||
],
|
],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
freeSkinKeys,
|
|
||||||
icons: Object.freeze({
|
|
||||||
gem,
|
|
||||||
}),
|
|
||||||
skinSubMenuItems: [
|
skinSubMenuItems: [
|
||||||
{
|
{
|
||||||
id: 'color',
|
id: 'color',
|
||||||
@@ -76,41 +52,13 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
freeSkins () {
|
userSkins () {
|
||||||
return freeSkinKeys.map(s => this.mapKeysToFreeOption(s, 'skin'));
|
const freeSkins = groupBy(appearance.skin, 'set.key')[undefined]
|
||||||
},
|
.map(s => s.key).map(s => this.mapKeysToFreeOption(s, 'skin'));
|
||||||
seasonalSkins () {
|
const ownedSkins = Object.keys(this.user.purchased.skin)
|
||||||
// @TODO: For some resonse when I use $set on the user purchases object,
|
.filter(k => this.user.purchased.skin[k])
|
||||||
// this is not recomputed. Hack for now
|
.map(s => this.mapKeysToFreeOption(s, 'skin'));
|
||||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
return [...freeSkins, ...ownedSkins];
|
||||||
|
|
||||||
const seasonalSkins = [];
|
|
||||||
for (const setKey of Object.keys(skinsBySet)) {
|
|
||||||
const set = skinsBySet[setKey];
|
|
||||||
|
|
||||||
const keys = set.map(item => item.key);
|
|
||||||
|
|
||||||
const options = keys.map(optionKey => {
|
|
||||||
const option = this.mapKeysToOption(optionKey, 'skin', '', setKey);
|
|
||||||
|
|
||||||
return option;
|
|
||||||
});
|
|
||||||
|
|
||||||
let text = this.$t(setKey);
|
|
||||||
if (appearanceSets[setKey] && appearanceSets[setKey].text) {
|
|
||||||
text = appearanceSets[setKey].text();
|
|
||||||
}
|
|
||||||
|
|
||||||
const compiledSet = {
|
|
||||||
key: setKey,
|
|
||||||
options,
|
|
||||||
keys,
|
|
||||||
text,
|
|
||||||
};
|
|
||||||
seasonalSkins.push(compiledSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
return seasonalSkins;
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
|
|||||||
@@ -151,14 +151,14 @@
|
|||||||
<div
|
<div
|
||||||
v-if="activeTopPage === 'backgrounds'"
|
v-if="activeTopPage === 'backgrounds'"
|
||||||
id="backgrounds"
|
id="backgrounds"
|
||||||
class="section container customize-section"
|
class="section customize-section"
|
||||||
>
|
>
|
||||||
<div class="row text-center title-row">
|
<div class="row text-center title-row">
|
||||||
<strong>{{ $t('incentiveBackgrounds') }}</strong>
|
<strong>{{ $t('incentiveBackgrounds') }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="row title-row"
|
class="row title-row"
|
||||||
v-if="!ownsSet('background', standardBackgrounds)"
|
v-if="standardBackgrounds.length < standardBackgroundMax"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="col-12"
|
class="col-12"
|
||||||
@@ -274,12 +274,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="row text-center title-row mt-2"
|
||||||
|
>
|
||||||
|
<strong>{{ $t('monthlyBackgrounds') }}</strong>
|
||||||
|
</div>
|
||||||
<div v-if="monthlyBackgrounds.length > 0">
|
<div v-if="monthlyBackgrounds.length > 0">
|
||||||
<div
|
|
||||||
class="row text-center title-row mt-2"
|
|
||||||
>
|
|
||||||
<strong>{{ $t('monthlyBackgrounds') }}</strong>
|
|
||||||
</div>
|
|
||||||
<div class="row title-row">
|
<div class="row title-row">
|
||||||
<div
|
<div
|
||||||
v-for="(bg) in monthlyBackgrounds"
|
v-for="(bg) in monthlyBackgrounds"
|
||||||
@@ -301,6 +301,15 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<customize-banner />
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<h3 v-once> {{ $t('noItemsOwned') }} </h3>
|
||||||
|
<p
|
||||||
|
v-once
|
||||||
|
v-html="$t('visitCustomizationsShop')"
|
||||||
|
class="w-50 mx-auto"
|
||||||
|
></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -605,7 +614,10 @@
|
|||||||
|
|
||||||
.customize-section {
|
.customize-section {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding-bottom: 2em;
|
background-color: #f9f9f9;
|
||||||
|
border-bottom-left-radius: 12px;
|
||||||
|
border-bottom-right-radius: 12px;
|
||||||
|
min-height: 256px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#creator-background {
|
#creator-background {
|
||||||
@@ -626,9 +638,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 20px;
|
color: $gray-100;
|
||||||
font-weight: normal;
|
font-weight: 700;
|
||||||
color: $gray-200;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.purchase-all {
|
.purchase-all {
|
||||||
@@ -819,11 +831,6 @@
|
|||||||
color: $yellow-10
|
color: $yellow-10
|
||||||
}
|
}
|
||||||
|
|
||||||
.customize-section {
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
min-height: 280px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interests-section {
|
.interests-section {
|
||||||
margin-top: 3em;
|
margin-top: 3em;
|
||||||
margin-bottom: 60px;
|
margin-bottom: 60px;
|
||||||
@@ -1076,6 +1083,7 @@ import usernameForm from './settings/usernameForm';
|
|||||||
import guide from '@/mixins/guide';
|
import guide from '@/mixins/guide';
|
||||||
import notifications from '@/mixins/notifications';
|
import notifications from '@/mixins/notifications';
|
||||||
import PinBadge from '@/components/ui/pinBadge';
|
import PinBadge from '@/components/ui/pinBadge';
|
||||||
|
import customizeBanner from './avatarModal/customize-banner';
|
||||||
import bodySettings from './avatarModal/body-settings';
|
import bodySettings from './avatarModal/body-settings';
|
||||||
import skinSettings from './avatarModal/skin-settings';
|
import skinSettings from './avatarModal/skin-settings';
|
||||||
import hairSettings from './avatarModal/hair-settings';
|
import hairSettings from './avatarModal/hair-settings';
|
||||||
@@ -1098,6 +1106,7 @@ import { avatarEditorUtilities } from '../mixins/avatarEditUtilities';
|
|||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
avatar,
|
avatar,
|
||||||
|
customizeBanner,
|
||||||
bodySettings,
|
bodySettings,
|
||||||
extraSettings,
|
extraSettings,
|
||||||
hairSettings,
|
hairSettings,
|
||||||
@@ -1112,6 +1121,7 @@ export default {
|
|||||||
allBackgrounds: content.backgroundsFlat,
|
allBackgrounds: content.backgroundsFlat,
|
||||||
monthlyBackgrounds: [],
|
monthlyBackgrounds: [],
|
||||||
standardBackgrounds: [],
|
standardBackgrounds: [],
|
||||||
|
standardBackgroundMax: 0,
|
||||||
timeTravelBackgrounds: [],
|
timeTravelBackgrounds: [],
|
||||||
backgroundUpdate: new Date(),
|
backgroundUpdate: new Date(),
|
||||||
|
|
||||||
@@ -1173,6 +1183,9 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
forEach(this.allBackgrounds, bg => {
|
forEach(this.allBackgrounds, bg => {
|
||||||
|
if (bg.set === 'incentiveBackgrounds') {
|
||||||
|
this.standardBackgroundMax += 1;
|
||||||
|
}
|
||||||
if (this.user.purchased.background[bg.key]) {
|
if (this.user.purchased.background[bg.key]) {
|
||||||
if (bg.set === 'incentiveBackgrounds') {
|
if (bg.set === 'incentiveBackgrounds') {
|
||||||
this.standardBackgrounds.push(bg);
|
this.standardBackgrounds.push(bg);
|
||||||
|
|||||||
@@ -126,5 +126,7 @@
|
|||||||
"zombie2": "Undead",
|
"zombie2": "Undead",
|
||||||
"allCustomizationsOwned": "You own all of these items. You can try them on by <a href=''>customizing your avatar</a>.",
|
"allCustomizationsOwned": "You own all of these items. You can try them on by <a href=''>customizing your avatar</a>.",
|
||||||
"checkNextMonth": "Be sure to check back later for next month's options!",
|
"checkNextMonth": "Be sure to check back later for next month's options!",
|
||||||
"checkNextSeason": "Be sure to check back later for next season's options!"
|
"checkNextSeason": "Be sure to check back later for next season's options!",
|
||||||
|
"noItemsOwned": "You don't own any of these items",
|
||||||
|
"visitCustomizationsShop": "Head over to the <a href='/shops/customizations'>Customizations Shop</a> to browse the many ways you can customize your avatar!"
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
"checkinEarned": "Your Check-In Counter went up!",
|
"checkinEarned": "Your Check-In Counter went up!",
|
||||||
"unlockedCheckInReward": "You unlocked a Check-In Prize!",
|
"unlockedCheckInReward": "You unlocked a Check-In Prize!",
|
||||||
"checkinProgressTitle": "Progress until next",
|
"checkinProgressTitle": "Progress until next",
|
||||||
"incentiveBackgroundsUnlockedWithCheckins": "Locked Plain Backgrounds will unlock with Daily Check-Ins.",
|
"incentiveBackgroundsUnlockedWithCheckins": "More Standard Backgrounds will unlock with Daily Check-Ins.",
|
||||||
"oneOfAllPetEggs": "one of each standard Pet Egg",
|
"oneOfAllPetEggs": "one of each standard Pet Egg",
|
||||||
"twoOfAllPetEggs": "two of each standard Pet Egg",
|
"twoOfAllPetEggs": "two of each standard Pet Egg",
|
||||||
"threeOfAllPetEggs": "three of each standard Pet Egg",
|
"threeOfAllPetEggs": "three of each standard Pet Egg",
|
||||||
|
|||||||
Reference in New Issue
Block a user