WIP(shops): first full test version

This commit is contained in:
Sabe Jones 2024-04-24 00:02:41 -05:00
parent 7a50b2d2ff
commit 3bf323032c
9 changed files with 258 additions and 474 deletions

View file

@ -1,7 +1,7 @@
<template>
<div
id="body"
class="section customize-section"
class="customize-section d-flex flex-column justify-content-between"
>
<sub-menu
class="text-center"
@ -17,17 +17,11 @@
</div>
<div v-if="activeSubPage === 'shirt'">
<customize-options
:items="freeShirts"
:items="userShirts"
: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>
<customize-banner />
</div>
</template>
@ -36,16 +30,14 @@ import appearance from '@/../../common/script/content/appearance';
import { subPageMixin } from '../../mixins/subPage';
import { userStateMixin } from '../../mixins/userState';
import { avatarEditorUtilities } from '../../mixins/avatarEditUtilities';
import subMenu from './sub-menu';
import customizeBanner from './customize-banner.vue';
import customizeOptions from './customize-options';
import gem from '@/assets/svg/gem.svg';
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);
import subMenu from './sub-menu';
export default {
components: {
subMenu,
customizeBanner,
customizeOptions,
},
mixins: [
@ -58,10 +50,6 @@ export default {
],
data () {
return {
specialShirtKeys,
icons: Object.freeze({
gem,
}),
items: [
{
id: 'size',
@ -78,25 +66,19 @@ export default {
sizes () {
return ['slim', 'broad'].map(s => this.mapKeysToFreeOption(s, 'size'));
},
freeShirts () {
return freeShirtKeys.map(s => this.mapKeysToFreeOption(s, 'shirt'));
},
specialShirts () {
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
const keys = this.specialShirtKeys;
const options = keys.map(key => this.mapKeysToOption(key, 'shirt'));
return options;
userShirts () {
const freeShirts = Object.keys(appearance.shirt)
.filter(k => appearance.shirt[k].price === 0)
.map(s => this.mapKeysToFreeOption(s, 'shirt'));
const ownedShirts = Object.keys(this.user.purchased.shirt)
.filter(k => this.user.purchased.shirt[k])
.map(s => this.mapKeysToFreeOption(s, 'shirt'));
return [...freeShirts, ...ownedShirts];
},
},
mounted () {
this.changeSubPage('size');
},
methods: {
},
};
</script>
<style scoped>
</style>

View file

@ -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>

View file

@ -1,7 +1,6 @@
<template>
<div
class="customize-options"
:class="{'background-set': fullSet}"
>
<div
v-for="option in items"
@ -28,38 +27,6 @@
</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>
</template>
@ -73,7 +40,7 @@ export default {
mixins: [
avatarEditorUtilities,
],
props: ['items', 'currentValue', 'fullSet'],
props: ['items', 'currentValue'],
data () {
return {
icons: Object.freeze({

View file

@ -1,7 +1,8 @@
<template>
<div
id="extra"
class="section container customize-section"
class="customize-section d-flex flex-column"
:class="{ 'justify-content-between': !showEmptySection}"
>
<sub-menu
class="text-center"
@ -20,9 +21,8 @@
id="animal-ears"
>
<customize-options
v-if="animalItems('back').length > 1"
:items="animalItems('headAccessory')"
:full-set="!animalItemsOwned('headAccessory')"
@unlock="unlock(animalItemsUnlockString('headAccessory'))"
/>
</div>
<div
@ -30,9 +30,8 @@
id="animal-tails"
>
<customize-options
v-if="animalItems('back').length > 1"
:items="animalItems('back')"
:full-set="!animalItemsOwned('back')"
@unlock="unlock(animalItemsUnlockString('back'))"
/>
</div>
<div
@ -53,6 +52,22 @@
>
<customize-options :items="flowers" />
</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>
</template>
@ -61,17 +76,18 @@ import appearance from '@/../../common/script/content/appearance';
import { subPageMixin } from '../../mixins/subPage';
import { userStateMixin } from '../../mixins/userState';
import { avatarEditorUtilities } from '../../mixins/avatarEditUtilities';
import subMenu from './sub-menu';
import customizeBanner from './customize-banner';
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 {
components: {
subMenu,
customizeBanner,
customizeOptions,
subMenu,
},
mixins: [
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'],
specialShirtKeys,
icons: Object.freeze({
gem,
}),
items: [
{
id: 'size',
@ -178,7 +191,7 @@ export default {
return freeShirtKeys.map(s => this.mapKeysToFreeOption(s, 'shirt'));
},
specialShirts () {
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
const keys = this.specialShirtKeys;
const options = keys.map(key => this.mapKeysToOption(key, 'shirt'));
return options;
@ -228,6 +241,16 @@ export default {
});
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 () {
this.changeSubPage(this.extraSubMenuItems[0].id);
@ -236,7 +259,7 @@ export default {
animalItems (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 backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
const keys = this.animalItemKeys[category];
const noneOption = this.createGearItem(0, category, 'base', category);
@ -248,36 +271,22 @@ export default {
for (const key of keys) {
const newKey = `${category}_special_${key}`;
const userPurchased = this.user.items.gear.owned[newKey];
const option = {};
option.key = key;
option.active = this.user.preferences.costume
? this.user.items.gear.costume[category] === newKey
: this.user.items.gear.equipped[category] === newKey;
option.class = `headAccessory_special_${option.key} ${category}`;
if (category === '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);
if (userPurchased) {
const option = {};
option.key = key;
option.active = this.user.preferences.costume
? this.user.items.gear.costume[category] === newKey
: this.user.items.gear.equipped[category] === newKey;
option.class = `headAccessory_special_${option.key} ${category}`;
if (category === 'back') {
option.class = `icon_back_special_${option.key} back`;
}
const type = this.user.preferences.costume ? 'costume' : 'equipped';
return this.equip(newKey, type);
};
options.push(option);
option.click = () => {
const type = this.user.preferences.costume ? 'costume' : 'equipped';
return this.equip(newKey, type);
};
options.push(option);
}
}
return options;
@ -287,17 +296,6 @@ export default {
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) {
const newKey = `${gearType}_${subGearType ? `${subGearType}_` : ''}${key}`;
const option = {};
@ -339,7 +337,3 @@ export default {
},
};
</script>
<style scoped>
</style>

View file

@ -1,7 +1,8 @@
<template>
<div
id="hair"
class="section customize-section"
class="customize-section d-flex flex-column"
:class="{ 'justify-content-between': !showEmptySection}"
>
<sub-menu
class="text-center"
@ -14,37 +15,9 @@
id="hair-color"
>
<customize-options
:items="freeHairColors"
:items="userHairColors"
: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
v-if="activeSubPage === 'bangs'"
@ -55,44 +28,62 @@
:current-value="user.preferences.hair.bangs"
/>
</div>
<div
v-if="activeSubPage === 'style'"
id="style"
>
<customize-options
:items="userHairStyles"
:current-value="user.preferences.hair.base"
/>
</div>
<div
v-if="activeSubPage === 'facialhair'"
id="facialhair"
>
<customize-options
v-if="editing"
:items="mustacheList"
v-if="editing && userMustaches.length > 1"
:items="userMustaches"
/>
<!-- eslint-disable max-len -->
<customize-options
v-if="editing"
:items="beardList"
:full-set="isPurchaseAllNeeded('hair', ['baseHair5', 'baseHair6'], ['mustache', 'beard'])"
@unlock="unlock(`hair.mustache.${baseHair5Keys.join(',hair.mustache.')},hair.beard.${baseHair6Keys.join(',hair.beard.')}`)"
v-if="editing && userBeards.length > 1"
:items="userBeards"
/>
<!-- 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>
<customize-banner
v-if="!showEmptySection"
/>
</div>
</template>
<script>
import groupBy from 'lodash/groupBy';
import appearance from '@/../../common/script/content/appearance';
import appearanceSets from '@/../../common/script/content/appearance/sets';
import { subPageMixin } from '../../mixins/subPage';
import { userStateMixin } from '../../mixins/userState';
import { avatarEditorUtilities } from '../../mixins/avatarEditUtilities';
import subMenu from './sub-menu';
import customizeBanner from './customize-banner';
import customizeOptions from './customize-options';
import gem from '@/assets/svg/gem.svg';
const hairColorBySet = groupBy(appearance.hair.color, 'set.key');
const freeHairColorKeys = hairColorBySet[undefined].map(s => s.key);
import subMenu from './sub-menu';
export default {
components: {
subMenu,
customizeBanner,
customizeOptions,
subMenu,
},
mixins: [
subPageMixin,
@ -102,20 +93,6 @@ export default {
props: [
'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: {
hairSubMenuItems () {
const items = [
@ -142,91 +119,46 @@ export default {
return items;
},
freeHairColors () {
return freeHairColorKeys.map(s => this.mapKeysToFreeOption(s, 'hair', 'color'));
userHairColors () {
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 () {
// @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
userHairStyles () {
const emptyHairStyle = {
...this.mapKeysToFreeOption(0, 'hair', 'base'),
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 = [];
for (const key of Object.keys(hairColorBySet)) {
const set = hairColorBySet[key];
return [emptyMustache, ...ownedMustaches];
},
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);
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;
return [emptyBeard, ...ownedBeards];
},
hairBangs () {
const none = this.mapKeysToFreeOption(0, 'hair', 'bangs');
@ -236,136 +168,13 @@ export default {
return [none, ...options];
},
mustacheList () {
const noneOption = this.mapKeysToFreeOption(0, 'hair', 'mustache');
noneOption.none = true;
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;
showEmptySection () {
return this.editing && this.activeSubPage === 'facialhair'
&& this.userMustaches.length === 1 && this.userBeards.length === 1;
},
},
mounted () {
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>
<style scoped>
</style>

View file

@ -1,7 +1,7 @@
<template>
<div
id="skin"
class="section customize-section"
class="customize-section d-flex flex-column justify-content-between"
>
<sub-menu
class="text-center"
@ -10,47 +10,27 @@
@changeSubPage="changeSubPage($event)"
/>
<customize-options
:items="freeSkins"
:items="userSkins"
:current-value="user.preferences.skin"
/>
<!-- eslint-disable vue/no-use-v-if-with-v-for -->
<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>
<customize-banner />
</div>
</template>
<script>
import groupBy from 'lodash/groupBy';
import appearance from '@/../../common/script/content/appearance';
import appearanceSets from '@/../../common/script/content/appearance/sets';
import { subPageMixin } from '../../mixins/subPage';
import { userStateMixin } from '../../mixins/userState';
import { avatarEditorUtilities } from '../../mixins/avatarEditUtilities';
import subMenu from './sub-menu';
import customizeBanner from './customize-banner.vue';
import customizeOptions from './customize-options';
import gem from '@/assets/svg/gem.svg';
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);
import subMenu from './sub-menu';
export default {
components: {
subMenu,
customizeBanner,
customizeOptions,
},
mixins: [
@ -63,10 +43,6 @@ export default {
],
data () {
return {
freeSkinKeys,
icons: Object.freeze({
gem,
}),
skinSubMenuItems: [
{
id: 'color',
@ -76,41 +52,13 @@ export default {
};
},
computed: {
freeSkins () {
return freeSkinKeys.map(s => this.mapKeysToFreeOption(s, 'skin'));
},
seasonalSkins () {
// @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 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;
userSkins () {
const freeSkins = groupBy(appearance.skin, 'set.key')[undefined]
.map(s => s.key).map(s => this.mapKeysToFreeOption(s, 'skin'));
const ownedSkins = Object.keys(this.user.purchased.skin)
.filter(k => this.user.purchased.skin[k])
.map(s => this.mapKeysToFreeOption(s, 'skin'));
return [...freeSkins, ...ownedSkins];
},
},
mounted () {

View file

@ -151,14 +151,14 @@
<div
v-if="activeTopPage === 'backgrounds'"
id="backgrounds"
class="section container customize-section"
class="section customize-section"
>
<div class="row text-center title-row">
<strong>{{ $t('incentiveBackgrounds') }}</strong>
</div>
<div
class="row title-row"
v-if="!ownsSet('background', standardBackgrounds)"
v-if="standardBackgrounds.length < standardBackgroundMax"
>
<div
class="col-12"
@ -274,12 +274,12 @@
</div>
</div>
</div>
<div
class="row text-center title-row mt-2"
>
<strong>{{ $t('monthlyBackgrounds') }}</strong>
</div>
<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
v-for="(bg) in monthlyBackgrounds"
@ -301,6 +301,15 @@
/>
</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>
@ -605,7 +614,10 @@
.customize-section {
text-align: center;
padding-bottom: 2em;
background-color: #f9f9f9;
border-bottom-left-radius: 12px;
border-bottom-right-radius: 12px;
min-height: 256px;
}
#creator-background {
@ -626,9 +638,9 @@
}
h3 {
font-size: 20px;
font-weight: normal;
color: $gray-200;
color: $gray-100;
font-weight: 700;
line-height: 24px;
}
.purchase-all {
@ -819,11 +831,6 @@
color: $yellow-10
}
.customize-section {
background-color: #f9f9f9;
min-height: 280px;
}
.interests-section {
margin-top: 3em;
margin-bottom: 60px;
@ -1076,6 +1083,7 @@ import usernameForm from './settings/usernameForm';
import guide from '@/mixins/guide';
import notifications from '@/mixins/notifications';
import PinBadge from '@/components/ui/pinBadge';
import customizeBanner from './avatarModal/customize-banner';
import bodySettings from './avatarModal/body-settings';
import skinSettings from './avatarModal/skin-settings';
import hairSettings from './avatarModal/hair-settings';
@ -1098,6 +1106,7 @@ import { avatarEditorUtilities } from '../mixins/avatarEditUtilities';
export default {
components: {
avatar,
customizeBanner,
bodySettings,
extraSettings,
hairSettings,
@ -1112,6 +1121,7 @@ export default {
allBackgrounds: content.backgroundsFlat,
monthlyBackgrounds: [],
standardBackgrounds: [],
standardBackgroundMax: 0,
timeTravelBackgrounds: [],
backgroundUpdate: new Date(),
@ -1173,6 +1183,9 @@ export default {
},
mounted () {
forEach(this.allBackgrounds, bg => {
if (bg.set === 'incentiveBackgrounds') {
this.standardBackgroundMax += 1;
}
if (this.user.purchased.background[bg.key]) {
if (bg.set === 'incentiveBackgrounds') {
this.standardBackgrounds.push(bg);

View file

@ -126,5 +126,7 @@
"zombie2": "Undead",
"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!",
"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!"
}

View file

@ -7,7 +7,7 @@
"checkinEarned": "Your Check-In Counter went up!",
"unlockedCheckInReward": "You unlocked a Check-In Prize!",
"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",
"twoOfAllPetEggs": "two of each standard Pet Egg",
"threeOfAllPetEggs": "three of each standard Pet Egg",