From 9fca8f783fbd8fd2eafd95112724f3107b8c0c29 Mon Sep 17 00:00:00 2001 From: negue Date: Wed, 12 Jun 2019 18:30:27 +0200 Subject: [PATCH 01/18] wip: createIntro / onboard ui rework --- .../client/components/avatarModal/body.vue | 59 +++++++++++++++++++ .../client/components/avatarModal/shirts.vue | 13 ++++ website/client/components/creatorIntro.vue | 35 +++-------- website/client/mixins/avatarEditUtilities.js | 0 website/client/mixins/subPage.js | 12 ++++ 5 files changed, 91 insertions(+), 28 deletions(-) create mode 100644 website/client/components/avatarModal/body.vue create mode 100644 website/client/components/avatarModal/shirts.vue create mode 100644 website/client/mixins/avatarEditUtilities.js create mode 100644 website/client/mixins/subPage.js diff --git a/website/client/components/avatarModal/body.vue b/website/client/components/avatarModal/body.vue new file mode 100644 index 000000000000..4f97170830f8 --- /dev/null +++ b/website/client/components/avatarModal/body.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/website/client/components/avatarModal/shirts.vue b/website/client/components/avatarModal/shirts.vue new file mode 100644 index 000000000000..990864d1d57b --- /dev/null +++ b/website/client/components/avatarModal/shirts.vue @@ -0,0 +1,13 @@ + + + + + diff --git a/website/client/components/creatorIntro.vue b/website/client/components/creatorIntro.vue index b38986d598bb..dea1fd2adafb 100644 --- a/website/client/components/creatorIntro.vue +++ b/website/client/components/creatorIntro.vue @@ -8,7 +8,7 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true .avatar-section.row(v-if='modalPage > 1', :class='{"page-2": modalPage === 2}') .col-6.offset-3 .user-creation-bg(v-if='!editing') - avatar(:member='user', :avatarOnly='!editing', :class='{"edit-avatar": editing}') + avatar(:member='user', :class='{"edit-avatar": editing}') .section(v-if='modalPage === 2', :class='{"edit-modal": editing}') // @TODO Implement in V2 .section.row @@ -36,33 +36,8 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true .menu-item .svg-icon(v-html='icons.backgroundsIcon') strong(v-once) {{$t('backgrounds')}} - #body.section.customize-section(v-if='activeTopPage === "body"') - .row.sub-menu.text-center - .col-3.offset-3.sub-menu-item(@click='changeSubPage("size")', :class='{active: activeSubPage === "size"}') - strong(v-once) {{$t('size')}} - .col-3.sub-menu-item(@click='changeSubPage("shirt")', :class='{active: activeSubPage === "shirt"}') - strong(v-once) {{$t('shirt')}} - .row(v-if='activeSubPage === "size"') - .col-12.customize-options.size-options - .option(v-for='option in ["slim", "broad"]', :class='{active: user.preferences.size === option}') - .sprite.customize-option(:class="`${option}_shirt_black`", @click='set({"preferences.size": option})') - .row(v-if='activeSubPage === "shirt"') - .col-12.customize-options - .option(v-for='option in ["black", "blue", "green", "pink", "white", "yellow"]', - :class='{active: user.preferences.shirt === option}') - .sprite.customize-option(:class="`slim_shirt_${option}`", @click='set({"preferences.shirt": option})') - .col-12.customize-options(v-if='editing') - .option(v-for='item in specialShirts', - :class='{active: item.active, locked: item.locked}') - .sprite.customize-option(:class="`broad_shirt_${item.key}`", @click='item.click') - .gem-lock(v-if='item.locked') - .svg-icon.gem(v-html='icons.gem') - span 2 - .col-12.text-center(v-if='!userOwnsSet("shirt", specialShirtKeys)') - .gem-lock - .svg-icon.gem(v-html='icons.gem') - span 5 - button.btn.btn-secondary.purchase-all(@click='unlock(`shirt.${specialShirtKeys.join(",shirt.")}`)') {{ $t('purchaseAll') }} + body-settings(v-if='activeTopPage === "body"') + #skin.section.customize-section(v-if='activeTopPage === "skin"') .row.sub-menu.col-6.offset-3.text-center .col-6.offset-3.text-center.sub-menu-item(:class='{active: activeSubPage === "color"}') @@ -901,6 +876,7 @@ import notifications from 'client/mixins/notifications'; import appearance from 'common/script/content/appearance'; import appearanceSets from 'common/script/content/appearance/sets'; import toggleSwitch from 'client/components/ui/toggleSwitch'; +import bodySettings from './avatarModal/body'; import logoPurple from 'assets/svg/logo-purple.svg'; import bodyIcon from 'assets/svg/body.svg'; @@ -1045,12 +1021,15 @@ const tasksByCategory = { ], }; + + export default { mixins: [guide, notifications], components: { avatar, toggleSwitch, usernameForm, + bodySettings, }, mounted () { if (this.editing) this.modalPage = 2; diff --git a/website/client/mixins/avatarEditUtilities.js b/website/client/mixins/avatarEditUtilities.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/website/client/mixins/subPage.js b/website/client/mixins/subPage.js new file mode 100644 index 000000000000..bdae59faeebe --- /dev/null +++ b/website/client/mixins/subPage.js @@ -0,0 +1,12 @@ +export const subPageMixin = { + data () { + return { + activeSubPage: '', + }; + }, + methods: { + changeSubPage (page) { + this.activeSubPage = page; + }, + }, +}; From 7f961db34492e9fa2b06b22c0b4de27db3a5b029 Mon Sep 17 00:00:00 2001 From: negue Date: Sat, 15 Jun 2019 19:40:26 +0200 Subject: [PATCH 02/18] extract more methods - working body settings component --- .../client/components/avatarModal/body.vue | 32 +- .../client/components/avatarModal/tasks.js | 128 +++ website/client/components/creatorIntro.vue | 955 +++++++----------- website/client/mixins/avatarEditUtilities.js | 127 +++ website/client/mixins/userState.js | 7 + 5 files changed, 645 insertions(+), 604 deletions(-) create mode 100644 website/client/components/avatarModal/tasks.js create mode 100644 website/client/mixins/userState.js diff --git a/website/client/components/avatarModal/body.vue b/website/client/components/avatarModal/body.vue index 4f97170830f8..21c292e4b612 100644 --- a/website/client/components/avatarModal/body.vue +++ b/website/client/components/avatarModal/body.vue @@ -1,7 +1,5 @@ diff --git a/website/client/components/avatarModal/tasks.js b/website/client/components/avatarModal/tasks.js new file mode 100644 index 000000000000..3583d1641c99 --- /dev/null +++ b/website/client/components/avatarModal/tasks.js @@ -0,0 +1,128 @@ +export const tasksByCategory = { + work: [ + { + type: 'habit', + text: 'Process email', + up: true, + down: false, + }, + { + type: 'daily', + text: 'Most important task >> Worked on today’s most important task', + notes: 'Tap to specify your most important task', + }, + { + type: 'todo', + text: 'Work project >> Complete work project', + notes: 'Tap to specify the name of your current project + set a due date!', + }, + ], + exercise: [ + { + type: 'habit', + text: '10 min cardio >> + 10 minutes cardio', + up: true, + down: false, + }, + { + type: 'daily', + text: 'Stretching >> Daily workout routine', + notes: 'Tap to choose your schedule and specify exercises!', + }, + { + type: 'todo', + text: 'Set up workout schedule', + notes: 'Tap to add a checklist!', + }, + ], + health_wellness: [ // eslint-disable-line + { + type: 'habit', + text: 'Eat Health/Junk Food', + up: true, + down: true, + }, + { + type: 'daily', + text: 'Floss', + notes: 'Tap to make any changes!', + }, + { + type: 'todo', + text: 'Schedule check-up >> Brainstorm a healthy change', + notes: 'Tap to add checklists!', + }, + ], + school: [ + { + type: 'habit', + text: 'Study/Procrastinate', + up: true, + down: true, + }, + { + type: 'daily', + text: 'Finish homework', + notes: 'Tap to choose your homework schedule!', + }, + { + type: 'todo', + text: 'Finish assignment for class', + notes: 'Tap to name the assignment and choose a due date!]', + }, + ], + self_care: [ // eslint-disable-line + { + type: 'habit', + text: 'Take a short break', + up: true, + down: false, + }, + { + type: 'daily', + text: '5 minutes of quiet breathing', + notes: 'Tap to choose your schedule!', + }, + { + type: 'todo', + text: 'Engage in a fun activity', + notes: 'Tap to specify what you plan to do!', + }, + ], + chores: [ + { + type: 'habit', + text: '10 minutes cleaning', + up: true, + down: false, + }, + { + type: 'daily', + text: 'Wash dishes', + notes: 'Tap to choose your schedule!', + }, + { + type: 'todo', + text: 'Organize closet >> Organize clutter', + notes: 'Tap to specify the cluttered area!', + }, + ], + creativity: [ + { + type: 'habit', + text: 'Study a master of the craft >> + Practiced a new creative technique', + up: true, + down: false, + }, + { + type: 'daily', + text: 'Work on creative project', + notes: 'Tap to specify the name of your current project + set the schedule!', + }, + { + type: 'todo', + text: 'Finish creative project', + notes: 'Tap to specify the name of your project', + }, + ], +}; diff --git a/website/client/components/creatorIntro.vue b/website/client/components/creatorIntro.vue index dea1fd2adafb..91c4ddb03773 100644 --- a/website/client/components/creatorIntro.vue +++ b/website/client/components/creatorIntro.vue @@ -36,7 +36,10 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true .menu-item .svg-icon(v-html='icons.backgroundsIcon') strong(v-once) {{$t('backgrounds')}} - body-settings(v-if='activeTopPage === "body"') + body-settings( + v-if='activeTopPage === "body"', + :editing="editing", + ) #skin.section.customize-section(v-if='activeTopPage === "skin"') .row.sub-menu.col-6.offset-3.text-center @@ -371,7 +374,9 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true button.btn.btn-primary.next(v-once, v-if='!loading') {{$t('done')}} - - - @@ -888,143 +882,14 @@ import gem from 'assets/svg/gem.svg'; import gold from 'assets/svg/gold.svg'; import pin from 'assets/svg/pin.svg'; import isPinned from 'common/script/libs/isPinned'; +import {avatarEditorUtilies} from '../mixins/avatarEditUtilities'; +import {tasksByCategory} from './avatarModal/tasks'; const skinsBySet = groupBy(appearance.skin, 'set.key'); const hairColorBySet = groupBy(appearance.hair.color, 'set.key'); -const tasksByCategory = { - work: [ - { - type: 'habit', - text: 'Process email', - up: true, - down: false, - }, - { - type: 'daily', - text: 'Most important task >> Worked on today’s most important task', - notes: 'Tap to specify your most important task', - }, - { - type: 'todo', - text: 'Work project >> Complete work project', - notes: 'Tap to specify the name of your current project + set a due date!', - }, - ], - exercise: [ - { - type: 'habit', - text: '10 min cardio >> + 10 minutes cardio', - up: true, - down: false, - }, - { - type: 'daily', - text: 'Stretching >> Daily workout routine', - notes: 'Tap to choose your schedule and specify exercises!', - }, - { - type: 'todo', - text: 'Set up workout schedule', - notes: 'Tap to add a checklist!', - }, - ], - health_wellness: [ // eslint-disable-line - { - type: 'habit', - text: 'Eat Health/Junk Food', - up: true, - down: true, - }, - { - type: 'daily', - text: 'Floss', - notes: 'Tap to make any changes!', - }, - { - type: 'todo', - text: 'Schedule check-up >> Brainstorm a healthy change', - notes: 'Tap to add checklists!', - }, - ], - school: [ - { - type: 'habit', - text: 'Study/Procrastinate', - up: true, - down: true, - }, - { - type: 'daily', - text: 'Finish homework', - notes: 'Tap to choose your homework schedule!', - }, - { - type: 'todo', - text: 'Finish assignment for class', - notes: 'Tap to name the assignment and choose a due date!]', - }, - ], - self_care: [ // eslint-disable-line - { - type: 'habit', - text: 'Take a short break', - up: true, - down: false, - }, - { - type: 'daily', - text: '5 minutes of quiet breathing', - notes: 'Tap to choose your schedule!', - }, - { - type: 'todo', - text: 'Engage in a fun activity', - notes: 'Tap to specify what you plan to do!', - }, - ], - chores: [ - { - type: 'habit', - text: '10 minutes cleaning', - up: true, - down: false, - }, - { - type: 'daily', - text: 'Wash dishes', - notes: 'Tap to choose your schedule!', - }, - { - type: 'todo', - text: 'Organize closet >> Organize clutter', - notes: 'Tap to specify the cluttered area!', - }, - ], - creativity: [ - { - type: 'habit', - text: 'Study a master of the craft >> + Practiced a new creative technique', - up: true, - down: false, - }, - { - type: 'daily', - text: 'Work on creative project', - notes: 'Tap to specify the name of your current project + set the schedule!', - }, - { - type: 'todo', - text: 'Finish creative project', - notes: 'Tap to specify the name of your project', - }, - ], -}; - - - export default { - mixins: [guide, notifications], + mixins: [guide, notifications, avatarEditorUtilies], components: { avatar, toggleSwitch, @@ -1328,40 +1193,6 @@ export default { }); this.backgroundUpdate = new Date(); }, - mapKeysToOption (key, type, subType, set) { - let userPreference = subType ? this.user.preferences[type][subType] : this.user.preferences[type]; - let userPurchased = subType ? this.user.purchased[type][subType] : this.user.purchased[type]; - let locked = !userPurchased || !userPurchased[key]; - let pathKey = subType ? `${type}.${subType}` : `${type}`; - let hide = false; - - if (set && appearanceSets[set]) { - if (locked) hide = moment(appearanceSets[set].availableUntil).isBefore(moment()); - } - - let option = {}; - option.key = key; - option.active = userPreference === key; - option.locked = locked; - option.hide = hide; - option.click = () => { - return locked ? this.unlock(`${pathKey}.${key}`) : this.set({[`preferences.${pathKey}`]: key}); - }; - return option; - }, - userOwnsSet (type, setKeys, subType) { - let owns = true; - - setKeys.forEach(key => { - if (subType) { - if (!this.user.purchased[type] || !this.user.purchased[type][subType] || !this.user.purchased[type][subType][key]) owns = false; - return; - } - if (!this.user.purchased[type][key]) owns = false; - }); - - return owns; - }, /** * 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. @@ -1437,12 +1268,6 @@ export default { changeSubPage (page) { this.activeSubPage = page; }, - set (settings) { - this.$store.dispatch('user:set', settings); - }, - equip (key, type) { - this.$store.dispatch('common:equip', {key, type}); - }, async done () { this.loading = true; @@ -1485,74 +1310,6 @@ export default { return setOwnedByUser; }, - /** - * For gem-unlockable preferences, (a) if owned, select preference (b) else, purchase - * @param path: User.preferences <-> User.purchased maps like User.preferences.skin=abc <-> User.purchased.skin.abc. - * Pass in this paramater as "skin.abc". Alternatively, pass as an array ["skin.abc", "skin.xyz"] to unlock sets - */ - async unlock (path) { - let fullSet = path.indexOf(',') !== -1; - let isBackground = path.indexOf('background.') !== -1; - - let cost; - - if (isBackground) { - cost = fullSet ? 3.75 : 1.75; // (Backgrounds) 15G per set, 7G per individual - } else { - cost = fullSet ? 1.25 : 0.5; // (Hair, skin, etc) 5G per set, 2G per individual - } - - let loginIncentives = [ - 'background.blue', - 'background.green', - 'background.red', - 'background.purple', - 'background.yellow', - 'background.violet', - ]; - - if (loginIncentives.indexOf(path) === -1) { - if (fullSet) { - if (confirm(this.$t('purchaseFor', {cost: cost * 4})) !== true) return; - // @TODO: implement gem modal - // if (this.user.balance < cost) return $rootScope.openModal('buyGems'); - } else if (!get(this.user, `purchased.${path}`)) { - if (confirm(this.$t('purchaseFor', {cost: cost * 4})) !== true) return; - // @TODO: implement gem modal - // if (this.user.balance < cost) return $rootScope.openModal('buyGems'); - } - } - - await axios.post(`/api/v4/user/unlock?path=${path}`); - try { - unlock(this.user, { - query: { - path, - }, - }); - this.backgroundUpdate = new Date(); - } catch (e) { - alert(e.message); - } - }, - async buy (item) { - const options = { - currency: 'gold', - key: item, - type: 'marketGear', - quantity: 1, - pinType: 'marketGear', - }; - await axios.post(`/api/v4/user/buy/${item}`, options); - try { - buy(this.user, { - params: options, - }); - this.backgroundUpdate = new Date(); - } catch (e) { - alert(e.message); - } - }, setKeys (type, _set) { return map(_set, (v, k) => { if (type === 'background') k = v.key; diff --git a/website/client/mixins/avatarEditUtilities.js b/website/client/mixins/avatarEditUtilities.js index e69de29bb2d1..1f8fe4b5d97f 100644 --- a/website/client/mixins/avatarEditUtilities.js +++ b/website/client/mixins/avatarEditUtilities.js @@ -0,0 +1,127 @@ +import moment from 'moment'; +import axios from 'axios'; + +import unlock from '../../common/script/ops/unlock'; +import buy from '../../common/script/ops/buy/buy'; + +import get from 'lodash/get'; + +import appearanceSets from 'common/script/content/appearance/sets'; + +export const avatarEditorUtilies = { + data () { + return { + backgroundUpdate: new Date(), + }; + }, + methods: { + mapKeysToOption (key, type, subType, set) { + let userPreference = subType ? this.user.preferences[type][subType] : this.user.preferences[type]; + let userPurchased = subType ? this.user.purchased[type][subType] : this.user.purchased[type]; + let locked = !userPurchased || !userPurchased[key]; + let pathKey = subType ? `${type}.${subType}` : `${type}`; + let hide = false; + + if (set && appearanceSets[set]) { + if (locked) hide = moment(appearanceSets[set].availableUntil).isBefore(moment()); + } + + let option = {}; + option.key = key; + option.active = userPreference === key; + option.locked = locked; + option.hide = hide; + option.click = () => { + return locked ? this.unlock(`${pathKey}.${key}`) : this.set({[`preferences.${pathKey}`]: key}); + }; + return option; + }, + userOwnsSet (type, setKeys, subType) { + let owns = true; + + setKeys.forEach(key => { + if (subType) { + if (!this.user.purchased[type] || !this.user.purchased[type][subType] || !this.user.purchased[type][subType][key]) owns = false; + return; + } + if (!this.user.purchased[type][key]) owns = false; + }); + + return owns; + }, + set (settings) { + this.$store.dispatch('user:set', settings); + }, + equip (key, type) { + this.$store.dispatch('common:equip', {key, type}); + }, + /** + * For gem-unlockable preferences, (a) if owned, select preference (b) else, purchase + * @param path: User.preferences <-> User.purchased maps like User.preferences.skin=abc <-> User.purchased.skin.abc. + * Pass in this paramater as "skin.abc". Alternatively, pass as an array ["skin.abc", "skin.xyz"] to unlock sets + */ + async unlock (path) { + let fullSet = path.indexOf(',') !== -1; + let isBackground = path.indexOf('background.') !== -1; + + let cost; + + if (isBackground) { + cost = fullSet ? 3.75 : 1.75; // (Backgrounds) 15G per set, 7G per individual + } else { + cost = fullSet ? 1.25 : 0.5; // (Hair, skin, etc) 5G per set, 2G per individual + } + + let loginIncentives = [ + 'background.blue', + 'background.green', + 'background.red', + 'background.purple', + 'background.yellow', + 'background.violet', + ]; + + if (loginIncentives.indexOf(path) === -1) { + if (fullSet) { + if (confirm(this.$t('purchaseFor', {cost: cost * 4})) !== true) return; + // @TODO: implement gem modal + // if (this.user.balance < cost) return $rootScope.openModal('buyGems'); + } else if (!get(this.user, `purchased.${path}`)) { + if (confirm(this.$t('purchaseFor', {cost: cost * 4})) !== true) return; + // @TODO: implement gem modal + // if (this.user.balance < cost) return $rootScope.openModal('buyGems'); + } + } + + await axios.post(`/api/v4/user/unlock?path=${path}`); + try { + unlock(this.user, { + query: { + path, + }, + }); + this.backgroundUpdate = new Date(); + } catch (e) { + alert(e.message); + } + }, + async buy (item) { + const options = { + currency: 'gold', + key: item, + type: 'marketGear', + quantity: 1, + pinType: 'marketGear', + }; + await axios.post(`/api/v4/user/buy/${item}`, options); + try { + buy(this.user, { + params: options, + }); + this.backgroundUpdate = new Date(); + } catch (e) { + alert(e.message); + } + }, + }, +}; diff --git a/website/client/mixins/userState.js b/website/client/mixins/userState.js new file mode 100644 index 000000000000..d52d6e2be2ae --- /dev/null +++ b/website/client/mixins/userState.js @@ -0,0 +1,7 @@ +import { mapState } from 'client/libs/store'; + +export const userStateMixin = { + computed: { + ...mapState({user: 'user.data'}), + }, +}; From f579bfda1d63dc77d1b039ede4ed3c05fa9666dd Mon Sep 17 00:00:00 2001 From: negue Date: Mon, 24 Jun 2019 19:38:47 +0200 Subject: [PATCH 03/18] move justin above the dialog --- website/client/components/creatorIntro.vue | 36 ++++++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/website/client/components/creatorIntro.vue b/website/client/components/creatorIntro.vue index 91c4ddb03773..e8ea21393308 100644 --- a/website/client/components/creatorIntro.vue +++ b/website/client/components/creatorIntro.vue @@ -8,7 +8,7 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true .avatar-section.row(v-if='modalPage > 1', :class='{"page-2": modalPage === 2}') .col-6.offset-3 .user-creation-bg(v-if='!editing') - avatar(:member='user', :class='{"edit-avatar": editing}') + avatar(:member='user', :avatarOnly='!editing', :class='{"edit-avatar": editing}') .section(v-if='modalPage === 2', :class='{"edit-modal": editing}') // @TODO Implement in V2 .section.row @@ -377,17 +377,28 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true From 1012e27d0b5989e3a512bb50005a8d63ce7dde9d Mon Sep 17 00:00:00 2001 From: negue Date: Sun, 30 Jun 2019 17:43:00 +0200 Subject: [PATCH 05/18] white background on items, working example of "none" item, item border radius --- website/client/components/creatorIntro.vue | 60 +++++++++++++++++----- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/website/client/components/creatorIntro.vue b/website/client/components/creatorIntro.vue index e8ea21393308..63b0207fe6c0 100644 --- a/website/client/components/creatorIntro.vue +++ b/website/client/components/creatorIntro.vue @@ -92,7 +92,8 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true button.btn.btn-secondary.purchase-all(@click='unlock(`hair.color.${set.keys.join(",hair.color.")}`)') {{ $t('purchaseAll') }} #style.row(v-if='activeSubPage === "style"') .col-12.customize-options(v-if='editing') - .head_0.option(@click='set({"preferences.hair.base": 0})', :class="[{ active: user.preferences.hair.base === 0 }, 'hair_base_0_' + user.preferences.hair.color]") + .option(@click='set({"preferences.hair.base": 0})', :class="{ active: user.preferences.hair.base === 0 }") + .head_0(:class="['hair_base_0_' + user.preferences.hair.color]") .option(v-for='option in baseHair3', :class='{active: option.active, locked: option.locked}') .base.sprite.customize-option(:class="`hair_base_${option.key}_${user.preferences.hair.color}`", @click='option.click') @@ -117,7 +118,8 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true span 5 button.btn.btn-secondary.purchase-all(@click='unlock(`hair.base.${baseHair4Keys.join(",hair.base.")}`)') {{ $t('purchaseAll') }} .col-12.customize-options - .head_0.option(v-if="!editing", @click='set({"preferences.hair.base": 0})', :class="[{ active: user.preferences.hair.base === 0 }, 'hair_base_0_' + user.preferences.hair.color]") + .option(v-if="!editing", @click='set({"preferences.hair.base": 0})', :class="{ active: user.preferences.hair.base === 0 }") + .head_0(:class="['hair_base_0_' + user.preferences.hair.color]") .option(v-for='option in baseHair1', :class='{active: user.preferences.hair.base === option}') .base.sprite.customize-option(:class="`hair_base_${option}_${user.preferences.hair.color}`", @click='set({"preferences.hair.base": option})') @@ -135,8 +137,10 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true button.btn.btn-secondary.purchase-all(@click='unlock(`hair.base.${baseHair2Keys.join(",hair.base.")}`)') {{ $t('purchaseAll') }} #bangs.row(v-if='activeSubPage === "bangs"') .col-12.customize-options - .head_0.option(@click='set({"preferences.hair.bangs": 0})', - :class="[{ active: user.preferences.hair.bangs === 0 }, 'hair_bangs_0_' + user.preferences.hair.color]") + .option.none(@click='set({"preferences.hair.bangs": 0})', :class="{ active: user.preferences.hair.bangs === 0 }") + .bangs.sprite.customize-option.head_0(:class="['hair_bangs_0_' + user.preferences.hair.color]") + .redline-outer + .redline .option(v-for='option in [1, 2, 3, 4]', :class='{active: user.preferences.hair.bangs === option}') .bangs.sprite.customize-option(:class="`hair_bangs_${option}_${user.preferences.hair.color}`", @click='set({"preferences.hair.bangs": option})') @@ -431,7 +435,36 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true width: 90px; margin: 1em .5em .5em 0; border: 4px solid $gray-700; - border-radius: 4px; + border-radius: 10px; + position: relative; + + &.none { + .sprite { + opacity: 0.24; + } + + .redline-outer { + height: 60px; + width: 60px; + position: absolute; + bottom: 0; + margin: 0 auto 0 0; + + .redline { + width: 60px; + height: 4px; + display: block; + background: red; + transform: rotate(-45deg); + position: absolute; + top: 0; + + margin-top: 10px; + margin-bottom: 20px; + margin-left: 6px; + } + } + } &.locked { border: none; @@ -441,6 +474,15 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true margin-top: 0; } + &.active { + background: white; + border: solid 4px $purple-300; + } + + &:hover { + cursor: pointer; + } + .sprite.customize-option { margin: 0 auto; } @@ -638,14 +680,6 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true color: $yellow-10 } - .option.active { - border-color: $purple-200; - } - - .option:hover { - cursor: pointer; - } - .customize-section { background-color: #f9f9f9; padding-top: 1em; From e059aeda9c78bc26a88177581acc2ba15f3ed89e Mon Sep 17 00:00:00 2001 From: negue Date: Tue, 2 Jul 2019 20:25:39 +0200 Subject: [PATCH 06/18] extract options as component --- .../client/components/avatarModal/body.vue | 16 ++- .../avatarModal/customize-options.vue | 107 ++++++++++++++++++ website/client/components/creatorIntro.vue | 30 +++-- website/client/mixins/avatarEditUtilities.js | 4 + 4 files changed, 145 insertions(+), 12 deletions(-) create mode 100644 website/client/components/avatarModal/customize-options.vue diff --git a/website/client/components/avatarModal/body.vue b/website/client/components/avatarModal/body.vue index e54323ddbfcb..7cc9d969e77d 100644 --- a/website/client/components/avatarModal/body.vue +++ b/website/client/components/avatarModal/body.vue @@ -6,10 +6,11 @@ .option(v-for='option in ["slim", "broad"]', :class='{active: user.preferences.size === option}') .sprite.customize-option(:class="`${option}_shirt_black`", @click='set({"preferences.size": option})') .row(v-if='activeSubPage === "shirt"') - .col-12.customize-options - .option(v-for='option in freeShirts', - :class='{active: user.preferences.shirt === option}') - .sprite.customize-option(:class="`slim_shirt_${option}`", @click='set({"preferences.shirt": option})') + customize-options.col-12( + :items="freeShirts", + propertyToChange="preferences.shirt", + :currentValue="user.preferences.shirt" + ) .col-12.customize-options(v-if='editing') .option(v-for='item in specialShirts', :class='{active: item.active, locked: item.locked}') @@ -30,6 +31,7 @@ import {userStateMixin} from '../../mixins/userState'; import {avatarEditorUtilies} from '../../mixins/avatarEditUtilities'; import subMenu from './sub-menu'; + 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); @@ -42,6 +44,7 @@ ], components: { subMenu, + customizeOptions, }, mixins: [ subPageMixin, @@ -50,7 +53,10 @@ ], data () { return { - freeShirts: freeShirtKeys, + freeShirts: freeShirtKeys.map(s => ({ + key: s, + class: `slim_shirt_${s}`, + })), specialShirtKeys, icons: Object.freeze({ gem, diff --git a/website/client/components/avatarModal/customize-options.vue b/website/client/components/avatarModal/customize-options.vue new file mode 100644 index 000000000000..eb0d6039a04c --- /dev/null +++ b/website/client/components/avatarModal/customize-options.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/website/client/components/creatorIntro.vue b/website/client/components/creatorIntro.vue index decfaee1348c..73c8cacf7308 100644 --- a/website/client/components/creatorIntro.vue +++ b/website/client/components/creatorIntro.vue @@ -42,9 +42,7 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true ) #skin.section.customize-section(v-if='activeTopPage === "skin"') - .row.sub-menu.col-6.offset-3.text-center - .col-6.offset-3.text-center.sub-menu-item(:class='{active: activeSubPage === "color"}') - strong(v-once) {{$t('color')}} + sub-menu.text-center(:items="skinSubMenuItem", :activeSubPage="activeSubPage", @changeSubPage="changeSubPage($event)") .row .col-12.customize-options .option(v-for='option in ["ddc994", "f5a76e", "ea8349", "c06534", "98461a", "915533", "c3e1dc", "6bd049"]', @@ -430,9 +428,9 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true .customize-options .option { display: inline-block; vertical-align: bottom; - padding: .5em; - height: 90px; - width: 90px; + //padding: .5em; + height: 64px; + width: 64px; margin: 1em .5em .5em 0; border: 4px solid $gray-700; border-radius: 10px; @@ -484,7 +482,16 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true } .sprite.customize-option { - margin: 0 auto; + // margin: 0 auto; + margin-left: -3px; + margin-top: -7px; + + &.color-bangs { + margin-top: 3px; + } + &.skin { + margin-top: -3px; + } } } @@ -921,6 +928,7 @@ import appearance from 'common/script/content/appearance'; import appearanceSets from 'common/script/content/appearance/sets'; import toggleSwitch from 'client/components/ui/toggleSwitch'; import bodySettings from './avatarModal/body'; +import subMenu from './avatarModal/sub-menu'; import logoPurple from 'assets/svg/logo-purple.svg'; import bodyIcon from 'assets/svg/body.svg'; @@ -945,6 +953,8 @@ export default { toggleSwitch, usernameForm, bodySettings, + + subMenu, }, mounted () { if (this.editing) this.modalPage = 2; @@ -988,6 +998,12 @@ export default { activeTopPage: 'body', activeSubPage: 'size', taskCategories: [], + skinSubMenuItem: [ + { + id: 'color', + label: 'color', + }, + ], }; }, watch: { diff --git a/website/client/mixins/avatarEditUtilities.js b/website/client/mixins/avatarEditUtilities.js index 1f8fe4b5d97f..d599c824cad2 100644 --- a/website/client/mixins/avatarEditUtilities.js +++ b/website/client/mixins/avatarEditUtilities.js @@ -8,7 +8,11 @@ import get from 'lodash/get'; import appearanceSets from 'common/script/content/appearance/sets'; + +import {userStateMixin} from './userState'; + export const avatarEditorUtilies = { + mixins: [userStateMixin], data () { return { backgroundUpdate: new Date(), From f44eec00e3e13d23920cd272692296f0d696a774 Mon Sep 17 00:00:00 2001 From: negue Date: Mon, 8 Jul 2019 20:03:34 +0200 Subject: [PATCH 07/18] move more subMenu's to the component --- .../client/components/avatarModal/body.vue | 4 +- .../avatarModal/customize-options.vue | 4 + .../components/avatarModal/sub-menu.vue | 2 +- website/client/components/creatorIntro.vue | 104 +++++++++++------- 4 files changed, 72 insertions(+), 42 deletions(-) diff --git a/website/client/components/avatarModal/body.vue b/website/client/components/avatarModal/body.vue index 7cc9d969e77d..46875ba9b1c6 100644 --- a/website/client/components/avatarModal/body.vue +++ b/website/client/components/avatarModal/body.vue @@ -64,11 +64,11 @@ items: [ { id: 'size', - label: 'size', + label: this.$t('size'), }, { id: 'shirt', - label: 'shirt', + label: this.$t('shirt'), }, ], }; diff --git a/website/client/components/avatarModal/customize-options.vue b/website/client/components/avatarModal/customize-options.vue index eb0d6039a04c..033769b32727 100644 --- a/website/client/components/avatarModal/customize-options.vue +++ b/website/client/components/avatarModal/customize-options.vue @@ -101,6 +101,10 @@ &.skin { margin-top: -3px; } + &.chair { + margin-left: -2px; + margin-top: -4px; + } } } diff --git a/website/client/components/avatarModal/sub-menu.vue b/website/client/components/avatarModal/sub-menu.vue index a76118f20da6..e715d91d1180 100644 --- a/website/client/components/avatarModal/sub-menu.vue +++ b/website/client/components/avatarModal/sub-menu.vue @@ -6,7 +6,7 @@ @click='$emit("changeSubPage", item.id)', :class='{active: activeSubPage === item.id}' ) - strong(v-once) {{$t(item.label)}} + strong(v-once) {{ item.label }} diff --git a/website/client/components/avatarModal/hair-settings.vue b/website/client/components/avatarModal/hair-settings.vue new file mode 100644 index 000000000000..013dea381ad2 --- /dev/null +++ b/website/client/components/avatarModal/hair-settings.vue @@ -0,0 +1,387 @@ + + + + + diff --git a/website/client/components/avatarModal/shirts.vue b/website/client/components/avatarModal/shirts.vue deleted file mode 100644 index 990864d1d57b..000000000000 --- a/website/client/components/avatarModal/shirts.vue +++ /dev/null @@ -1,13 +0,0 @@ - - - - - diff --git a/website/client/components/avatarModal/skin-settings.vue b/website/client/components/avatarModal/skin-settings.vue new file mode 100644 index 000000000000..062cf2c98d01 --- /dev/null +++ b/website/client/components/avatarModal/skin-settings.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/website/client/components/creatorIntro.vue b/website/client/components/creatorIntro.vue index ace9b63c5e5a..6581af099f13 100644 --- a/website/client/components/creatorIntro.vue +++ b/website/client/components/creatorIntro.vue @@ -47,121 +47,16 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true :editing="editing", ) - #skin.section.customize-section(v-if='activeTopPage === "skin"') - sub-menu.text-center(:items="skinSubMenuItems", :activeSubPage="activeSubPage", @changeSubPage="changeSubPage($event)") - .row - .col-12.customize-options - .option(v-for='option in ["ddc994", "f5a76e", "ea8349", "c06534", "98461a", "915533", "c3e1dc", "6bd049"]', - :class='{active: user.preferences.skin === option}') - .skin.sprite.customize-option(:class="`skin_${option}`", @click='set({"preferences.skin": option})') - .row(v-if='editing && set.key !== "undefined"', v-for='set in seasonalSkins') - .col-12.customize-options - .option(v-for='option in set.options', - :class='{active: option.active, locked: option.locked, hide: option.hide}') - .skin.sprite.customize-option(:class="`skin_${option.key}`", @click='option.click') - .gem-lock(v-if='option.locked') - .svg-icon.gem(v-html='icons.gem') - span 2 - .col-12.text-center(v-if='!hideSet(set) && !userOwnsSet("skin", set.keys)') - .gem-lock - .svg-icon.gem(v-html='icons.gem') - span 5 - button.btn.btn-secondary.purchase-all(@click='unlock(`skin.${set.keys.join(",skin.")}`)') {{ $t('purchaseAll') }} - #hair.section.customize-section(v-if='activeTopPage === "hair"') - sub-menu.text-center(:items="hairSubMenuItems", :activeSubPage="activeSubPage", @changeSubPage="changeSubPage($event)") - #hair-color.row(v-if='activeSubPage === "color"') - .col-12.customize-options - .option(v-for='option in ["white", "brown", "blond", "red", "black"]', - :class='{active: user.preferences.hair.color === option}') - .color-bangs.sprite.customize-option(:class="`hair_bangs_1_${option}`", @click='set({"preferences.hair.color": option})') - .col-12.customize-options(v-if='editing && set.key !== "undefined"', v-for='set in seasonalHairColors') - .option(v-for='option in set.options', - :class='{active: option.active, locked: option.locked, hide: option.hide}') - .skin.sprite.customize-option(:class="`hair_bangs_1_${option.key}`", @click='option.click') - .gem-lock(v-if='option.locked') - .svg-icon.gem(v-html='icons.gem') - span 2 - .col-12.text-center(v-if='!hideSet(set) && !userOwnsSet("hair", set.keys, "color")') - .gem-lock - .svg-icon.gem(v-html='icons.gem') - span 5 - button.btn.btn-secondary.purchase-all(@click='unlock(`hair.color.${set.keys.join(",hair.color.")}`)') {{ $t('purchaseAll') }} - #style.row(v-if='activeSubPage === "style"') - .col-12.customize-options(v-if='editing') - .option(@click='set({"preferences.hair.base": 0})', :class="{ active: user.preferences.hair.base === 0 }") - .head_0(:class="['hair_base_0_' + user.preferences.hair.color]") - .option(v-for='option in baseHair3', - :class='{active: option.active, locked: option.locked}') - .base.sprite.customize-option(:class="`hair_base_${option.key}_${user.preferences.hair.color}`", @click='option.click') - .gem-lock(v-if='option.locked') - .svg-icon.gem(v-html='icons.gem') - span 2 - .col-12.text-center(v-if='!userOwnsSet("hair", baseHair3Keys, "base")') - .gem-lock - .svg-icon.gem(v-html='icons.gem') - span 5 - button.btn.btn-secondary.purchase-all(@click='unlock(`hair.base.${baseHair3Keys.join(",hair.base.")}`)') {{ $t('purchaseAll') }} - .col-12.customize-options(v-if='editing') - .option(v-for='option in baseHair4', - :class='{active: option.active, locked: option.locked}') - .base.sprite.customize-option(:class="`hair_base_${option.key}_${user.preferences.hair.color}`", @click='option.click') - .gem-lock(v-if='option.locked') - .svg-icon.gem(v-html='icons.gem') - span 2 - .col-12.text-center(v-if='!userOwnsSet("hair", baseHair4Keys, "base")') - .gem-lock - .svg-icon.gem(v-html='icons.gem') - span 5 - button.btn.btn-secondary.purchase-all(@click='unlock(`hair.base.${baseHair4Keys.join(",hair.base.")}`)') {{ $t('purchaseAll') }} - .col-12.customize-options - .option(v-if="!editing", @click='set({"preferences.hair.base": 0})', :class="{ active: user.preferences.hair.base === 0 }") - .head_0(:class="['hair_base_0_' + user.preferences.hair.color]") - .option(v-for='option in baseHair1', - :class='{active: user.preferences.hair.base === option}') - .base.sprite.customize-option(:class="`hair_base_${option}_${user.preferences.hair.color}`", @click='set({"preferences.hair.base": option})') - .col-12.customize-options(v-if='editing') - .option(v-for='option in baseHair2', - :class='{active: option.active, locked: option.locked}') - .base.sprite.customize-option(:class="`hair_base_${option.key}_${user.preferences.hair.color}`", @click='option.click') - .gem-lock(v-if='option.locked') - .svg-icon.gem(v-html='icons.gem') - span 2 - .col-12.text-center(v-if='!userOwnsSet("hair", baseHair2Keys, "base")') - .gem-lock - .svg-icon.gem(v-html='icons.gem') - span 5 - button.btn.btn-secondary.purchase-all(@click='unlock(`hair.base.${baseHair2Keys.join(",hair.base.")}`)') {{ $t('purchaseAll') }} - #bangs.row(v-if='activeSubPage === "bangs"') - .col-12.customize-options - .option.none(@click='set({"preferences.hair.bangs": 0})', :class="{ active: user.preferences.hair.bangs === 0 }") - .bangs.sprite.customize-option.head_0(:class="['hair_bangs_0_' + user.preferences.hair.color]") - .redline-outer - .redline - .option(v-for='option in [1, 2, 3, 4]', - :class='{active: user.preferences.hair.bangs === option}') - .bangs.sprite.customize-option(:class="`hair_bangs_${option}_${user.preferences.hair.color}`", @click='set({"preferences.hair.bangs": option})') - #facialhair.row(v-if='activeSubPage === "facialhair"') - .col-12.customize-options(v-if='editing') - .head_0.option(@click='set({"preferences.hair.mustache": 0})', :class="[{ active: user.preferences.hair.mustache === 0 }, 'hair_base_0_' + user.preferences.hair.color]") - .option(v-for='option in baseHair5', - :class='{active: option.active, locked: option.locked}') - .base.sprite.customize-option(:class="`hair_mustache_${option.key}_${user.preferences.hair.color}`", @click='option.click') - .gem-lock(v-if='option.locked') - .svg-icon.gem(v-html='icons.gem') - span 2 - .col-12.customize-options(v-if='editing') - .head_0.option(@click='set({"preferences.hair.beard": 0})', :class="[{ active: user.preferences.hair.beard === 0 }, 'hair_base_0_' + user.preferences.hair.color]") - .option(v-for='option in baseHair6', - :class='{active: option.active, locked: option.locked}') - .base.sprite.customize-option(:class="`hair_beard_${option.key}_${user.preferences.hair.color}`", @click='option.click') - .gem-lock(v-if='option.locked') - .svg-icon.gem(v-html='icons.gem') - span 2 - .col-12.text-center(v-if="isPurchaseAllNeeded('hair', ['baseHair5', 'baseHair6'], ['mustache', 'beard'])") - .gem-lock - .svg-icon.gem(v-html='icons.gem') - span 5 - button.btn.btn-secondary.purchase-all(@click='unlock(`hair.mustache.${baseHair5Keys.join(",hair.mustache.")},hair.beard.${baseHair6Keys.join(",hair.beard.")}`)') {{ $t('purchaseAll') }} + skin-settings( + v-if='activeTopPage === "skin"', + :editing="editing", + ) + + hairSettings( + v-if='activeTopPage === "hair"', + :editing="editing" + ) + #extra.section.container.customize-section(v-if='activeTopPage === "extra"') sub-menu.text-center(:items="extraSubMenuItems", :activeSubPage="activeSubPage", @changeSubPage="changeSubPage($event)") #glasses.row(v-if='activeSubPage === "glasses"') @@ -404,8 +299,8 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true //padding: .5em; height: 64px; width: 64px; - margin: 1em .5em .5em 0; - border: 4px solid $gray-700; + // margin: 1em .5em .5em 0; + // border: 4px solid $gray-700; border-radius: 10px; position: relative; @@ -453,23 +348,6 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true &:hover { cursor: pointer; } - - .sprite.customize-option { - // margin: 0 auto; - margin-left: -3px; - margin-top: -7px; - - &.color-bangs { - margin-top: 3px; - } - &.skin { - margin-top: -3px; - } - &.chair { - margin-left: -2px; - margin-top: -4px; - } - } } @@ -913,7 +791,9 @@ import notifications from 'client/mixins/notifications'; import appearance from 'common/script/content/appearance'; import appearanceSets from 'common/script/content/appearance/sets'; import toggleSwitch from 'client/components/ui/toggleSwitch'; -import bodySettings from './avatarModal/body'; +import bodySettings from './avatarModal/body-settings'; +import skinSettings from './avatarModal/skin-settings'; +import hairSettings from './avatarModal/hair-settings'; import subMenu from './avatarModal/sub-menu'; import logoPurple from 'assets/svg/logo-purple.svg'; @@ -940,6 +820,8 @@ export default { toggleSwitch, usernameForm, bodySettings, + skinSettings, + hairSettings, subMenu, }, @@ -947,6 +829,9 @@ export default { if (this.editing) this.modalPage = 2; // Buy modal is global, so we listen at root. I'd like to not this.$root.$on('buyModal::boughtItem', this.backgroundPurchased); + + + console.info('skins by set', skinsBySet); }, data () { let backgroundShopSets = getBackgroundShopSets(); @@ -961,12 +846,7 @@ export default { rainbowSkinKeys: ['eb052b', 'f69922', 'f5d70f', '0ff591', '2b43f6', 'd7a9f7', '800ed0', 'rainbow'], animalSkinKeys: ['bear', 'cactus', 'fox', 'lion', 'panda', 'pig', 'tiger', 'wolf'], premiumHairColorKeys: ['rainbow', 'yellow', 'green', 'purple', 'blue', 'TRUred'], - 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], + animalItemKeys: { back: ['bearTail', 'cactusTail', 'foxTail', 'lionTail', 'pandaTail', 'pigTail', 'tigerTail', 'wolfTail'], headAccessory: ['bearEars', 'cactusEars', 'foxEars', 'lionEars', 'pandaEars', 'pigEars', 'tigerEars', 'wolfEars'], @@ -993,24 +873,7 @@ export default { label: this.$t('color'), }, ], - hairSubMenuItems: [ - { - id: 'color', - label: this.$t('color'), - }, - { - id: 'bangs', - label: this.$t('bangs'), - }, - { - id: 'style', - label: this.$t('style'), - }, - { - id: 'facialhair', - label: this.$t('facialhair'), - }, - ], + bgSubMenuItems: ['2019', '2018', '2017', '2016', '2015', '2014'].map(y => ({ id: y, @@ -1297,9 +1160,6 @@ export default { }, }, methods: { - hideSet (set) { - return moment(appearanceSets[set.key].availableUntil).isBefore(moment()); - }, purchase (type, key) { this.$store.dispatch('shops:purchase', { type, @@ -1307,68 +1167,6 @@ export default { }); this.backgroundUpdate = new Date(); }, - /** - * 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; - }, prev () { this.modalPage -= 1; }, diff --git a/website/client/mixins/avatarEditUtilities.js b/website/client/mixins/avatarEditUtilities.js index d599c824cad2..c85b598a2adc 100644 --- a/website/client/mixins/avatarEditUtilities.js +++ b/website/client/mixins/avatarEditUtilities.js @@ -19,6 +19,9 @@ export const avatarEditorUtilies = { }; }, methods: { + hideSet (set) { + return moment(appearanceSets[set.key].availableUntil).isBefore(moment()); + }, mapKeysToOption (key, type, subType, set) { let userPreference = subType ? this.user.preferences[type][subType] : this.user.preferences[type]; let userPurchased = subType ? this.user.purchased[type][subType] : this.user.purchased[type]; @@ -35,6 +38,7 @@ export const avatarEditorUtilies = { option.active = userPreference === key; option.locked = locked; option.hide = hide; + option.gem = 2; option.click = () => { return locked ? this.unlock(`${pathKey}.${key}`) : this.set({[`preferences.${pathKey}`]: key}); }; From 426e05f941c534a24abe8b443eaa7a9d4cd53eb3 Mon Sep 17 00:00:00 2001 From: negue Date: Sat, 27 Jul 2019 20:44:43 +0200 Subject: [PATCH 12/18] refactor / fully converted hair-settings --- .../components/avatarModal/body-settings.vue | 17 +- .../avatarModal/customize-options.vue | 20 ++- .../components/avatarModal/hair-settings.vue | 152 +++++++----------- .../components/avatarModal/skin-settings.vue | 20 +-- .../components/avatarModal/sub-menu.vue | 1 + website/client/mixins/avatarEditUtilities.js | 61 +++++-- 6 files changed, 138 insertions(+), 133 deletions(-) diff --git a/website/client/components/avatarModal/body-settings.vue b/website/client/components/avatarModal/body-settings.vue index 2fd039ee8421..0ff9d5d59c74 100644 --- a/website/client/components/avatarModal/body-settings.vue +++ b/website/client/components/avatarModal/body-settings.vue @@ -69,26 +69,15 @@ }, computed: { sizes () { - return ['slim', 'broad'].map(s => ({ - key: s, - class: `${s}_shirt_black`, - })); + return ['slim', 'broad'].map(s => this.mapKeysToFreeOption(s, 'size')); }, freeShirts () { - return freeShirtKeys.map(s => ({ - key: s, - class: `${this.user.preferences.size}_shirt_${s}`, - })); + return freeShirtKeys.map(s => this.mapKeysToFreeOption(s, 'shirt')); }, specialShirts () { let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line let keys = this.specialShirtKeys; - let options = keys.map(key => { - const option = this.mapKeysToOption(key, 'shirt'); - option.class = `${this.user.preferences.size}_shirt_${key}`; - - return option; - }); + let options = keys.map(key => this.mapKeysToOption(key, 'shirt')); return options; }, }, diff --git a/website/client/components/avatarModal/customize-options.vue b/website/client/components/avatarModal/customize-options.vue index 1acf31591cf4..bcab21c18170 100644 --- a/website/client/components/avatarModal/customize-options.vue +++ b/website/client/components/avatarModal/customize-options.vue @@ -104,9 +104,9 @@ position: absolute; top: 0; - margin-top: 10px; - margin-bottom: 20px; - margin-left: 6px; + margin-top: 33px; + margin-bottom: 20px; + margin-left: 0; } } } @@ -143,6 +143,11 @@ margin-top: 0; margin-left: 0; + &.size, &.shirt { + margin-top: -8px; + margin-left: -4px; + } + &.color-bangs { margin-top: 3px; } @@ -154,6 +159,15 @@ margin-left: -2px; margin-top: -4px; } + &.color, &.bangs { + margin-top: 4px; + margin-left: -3px; + } + + &.hair.base { + margin-top: 0px; + margin-left: -5px; + } } } diff --git a/website/client/components/avatarModal/hair-settings.vue b/website/client/components/avatarModal/hair-settings.vue index 013dea381ad2..3bd0a22af5e6 100644 --- a/website/client/components/avatarModal/hair-settings.vue +++ b/website/client/components/avatarModal/hair-settings.vue @@ -19,87 +19,30 @@ @unlock='unlock(`hair.color.${set.keys.join(",hair.color.")}`)' ) - #style.row(v-if='activeSubPage === "style"') + #style(v-if='activeSubPage === "style"') .row(v-if='editing && set.key !== "undefined"', v-for='set in styleSets') customize-options.col-12( :items='set.options', :fullSet='set.fullSet', @unlock='set.unlock()' ) - - - .col-12.customize-options(v-if='editing') - .option(@click='set({"preferences.hair.base": 0})', :class="{ active: user.preferences.hair.base === 0 }") - .head_0(:class="['hair_base_0_' + user.preferences.hair.color]") - .option(v-for='option in baseHair3', - :class='{active: option.active, locked: option.locked}') - .base.sprite.customize-option(:class="`hair_base_${option.key}_${user.preferences.hair.color}`", @click='option.click') - .gem-lock(v-if='option.locked') - .svg-icon.gem(v-html='icons.gem') - span 2 - .col-12.text-center(v-if='!userOwnsSet("hair", baseHair3Keys, "base")') - .gem-lock - .svg-icon.gem(v-html='icons.gem') - span 5 - button.btn.btn-secondary.purchase-all(@click='unlock(`hair.base.${baseHair3Keys.join(",hair.base.")}`)') {{ $t('purchaseAll') }} - .col-12.customize-options(v-if='editing') - .option(v-for='option in baseHair4', - :class='{active: option.active, locked: option.locked}') - .base.sprite.customize-option(:class="`hair_base_${option.key}_${user.preferences.hair.color}`", @click='option.click') - .gem-lock(v-if='option.locked') - .svg-icon.gem(v-html='icons.gem') - span 2 - .col-12.text-center(v-if='!userOwnsSet("hair", baseHair4Keys, "base")') - .gem-lock - .svg-icon.gem(v-html='icons.gem') - span 5 - button.btn.btn-secondary.purchase-all(@click='unlock(`hair.base.${baseHair4Keys.join(",hair.base.")}`)') {{ $t('purchaseAll') }} - .col-12.customize-options - .option(v-if="!editing", @click='set({"preferences.hair.base": 0})', :class="{ active: user.preferences.hair.base === 0 }") - .head_0(:class="['hair_base_0_' + user.preferences.hair.color]") - .option(v-for='option in baseHair1', - :class='{active: user.preferences.hair.base === option}') - .base.sprite.customize-option(:class="`hair_base_${option}_${user.preferences.hair.color}`", @click='set({"preferences.hair.base": option})') - .col-12.customize-options(v-if='editing') - .option(v-for='option in baseHair2', - :class='{active: option.active, locked: option.locked}') - .base.sprite.customize-option(:class="`hair_base_${option.key}_${user.preferences.hair.color}`", @click='option.click') - .gem-lock(v-if='option.locked') - .svg-icon.gem(v-html='icons.gem') - span 2 - .col-12.text-center(v-if='!userOwnsSet("hair", baseHair2Keys, "base")') - .gem-lock - .svg-icon.gem(v-html='icons.gem') - span 5 - button.btn.btn-secondary.purchase-all(@click='unlock(`hair.base.${baseHair2Keys.join(",hair.base.")}`)') {{ $t('purchaseAll') }} - #bangs.row(v-if='activeSubPage === "bangs"') + #bangs(v-if='activeSubPage === "bangs"') customize-options.col-12( :items='hairBangs', propertyToChange="user.preferences.hair.bangs", :currentValue="user.preferences.hair.bangs" ) - #facialhair.row(v-if='activeSubPage === "facialhair"') - .col-12.customize-options(v-if='editing') - .head_0.option(@click='set({"preferences.hair.mustache": 0})', :class="[{ active: user.preferences.hair.mustache === 0 }, 'hair_base_0_' + user.preferences.hair.color]") - .option(v-for='option in baseHair5', - :class='{active: option.active, locked: option.locked}') - .base.sprite.customize-option(:class="`hair_mustache_${option.key}_${user.preferences.hair.color}`", @click='option.click') - .gem-lock(v-if='option.locked') - .svg-icon.gem(v-html='icons.gem') - span 2 - .col-12.customize-options(v-if='editing') - .head_0.option(@click='set({"preferences.hair.beard": 0})', :class="[{ active: user.preferences.hair.beard === 0 }, 'hair_base_0_' + user.preferences.hair.color]") - .option(v-for='option in baseHair6', - :class='{active: option.active, locked: option.locked}') - .base.sprite.customize-option(:class="`hair_beard_${option.key}_${user.preferences.hair.color}`", @click='option.click') - .gem-lock(v-if='option.locked') - .svg-icon.gem(v-html='icons.gem') - span 2 - .col-12.text-center(v-if="isPurchaseAllNeeded('hair', ['baseHair5', 'baseHair6'], ['mustache', 'beard'])") - .gem-lock - .svg-icon.gem(v-html='icons.gem') - span 5 - button.btn.btn-secondary.purchase-all(@click='unlock(`hair.mustache.${baseHair5Keys.join(",hair.mustache.")},hair.beard.${baseHair6Keys.join(",hair.beard.")}`)') {{ $t('purchaseAll') }} + #facialhair(v-if='activeSubPage === "facialhair"') + customize-options( + v-if='editing', + :items='mustacheList' + ) + customize-options( + v-if='editing', + :items='beardList', + :fullSet='isPurchaseAllNeeded(\'hair\', [\'baseHair5\', \'baseHair6\'], [\'mustache\', \'beard\'])', + @unlock='unlock(`hair.mustache.${baseHair5Keys.join(",hair.mustache.")},hair.beard.${baseHair6Keys.join(",hair.beard.")}`)' + ) + + diff --git a/website/client/components/avatarModal/sub-menu.vue b/website/client/components/avatarModal/sub-menu.vue index 8db5ba975b5a..17ebcb873ca5 100644 --- a/website/client/components/avatarModal/sub-menu.vue +++ b/website/client/components/avatarModal/sub-menu.vue @@ -29,8 +29,6 @@ } .sub-menu-item { - flex: 1; - flex-grow: 0; padding: 6px 16px; text-align: center; border-bottom: 2px solid #f9f9f9; diff --git a/website/client/components/creatorIntro.vue b/website/client/components/creatorIntro.vue index 6581af099f13..beca7fa040f2 100644 --- a/website/client/components/creatorIntro.vue +++ b/website/client/components/creatorIntro.vue @@ -57,68 +57,11 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true :editing="editing" ) - #extra.section.container.customize-section(v-if='activeTopPage === "extra"') - sub-menu.text-center(:items="extraSubMenuItems", :activeSubPage="activeSubPage", @changeSubPage="changeSubPage($event)") - #glasses.row(v-if='activeSubPage === "glasses"') - .col-12.customize-options - .option(v-for='option in eyewear', :class='{active: option.active}') - .sprite.customize-option(:class="`eyewear_special_${option.key}`", @click='option.click') - #animal-ears.row(v-if='activeSubPage === "ears"') - .section.col-12.customize-options - .option(v-for='option in animalItems("headAccessory")', - :class='{active: option.active, locked: option.locked}') - .sprite.customize-option(:class="`headAccessory_special_${option.key}`", @click='option.click') - .gem-lock(v-if='option.gemLocked') - .svg-icon.gem(v-html='icons.gem') - span 2 - .gold-lock(v-if='option.goldLocked') - .svg-icon.gold(v-html='icons.gold') - span 20 - .col-12.text-center(v-if='!animalItemsOwned("headAccessory")') - .gem-lock - .svg-icon.gem(v-html='icons.gem') - span 5 - button.btn.btn-secondary.purchase-all(@click='unlock(animalItemsUnlockString("headAccessory"))') {{ $t('purchaseAll') }} - #animal-tails.row(v-if='activeSubPage === "tails"') - .section.col-12.customize-options - .option(v-for='option in animalItems("back")', - :class='{active: option.active, locked: option.locked}') - .sprite.customize-option(:class="`icon_back_special_${option.key}`", @click='option.click') - .gem-lock(v-if='option.gemLocked') - .svg-icon.gem(v-html='icons.gem') - span 2 - .gold-lock(v-if='option.goldLocked') - .svg-icon.gold(v-html='icons.gold') - span 20 - .col-12.text-center(v-if='!animalItemsOwned("back")') - .gem-lock - .svg-icon.gem(v-html='icons.gem') - span 5 - button.btn.btn-secondary.purchase-all(@click='unlock(animalItemsUnlockString("back"))') {{ $t('purchaseAll') }} - #headband.row(v-if='activeSubPage === "headband"') - .col-12.customize-options - .option(v-for='option in headbands', :class='{active: option.active}') - .sprite.customize-option(:class="`headAccessory_special_${option.key}`", @click='option.click') - #wheelchairs.row(v-if='activeSubPage === "wheelchair"') - .col-12.customize-options - .option(@click='set({"preferences.chair": "none"})', :class='{active: user.preferences.chair === "none"}') - | None - .option(v-for='option in chairKeys', - :class='{active: user.preferences.chair === option}') - .chair.sprite.customize-option(:class="`button_chair_${option}`", @click='set({"preferences.chair": option})') - #flowers.row(v-if='activeSubPage === "flower"') - .col-12.customize-options - .head_0.option(@click='set({"preferences.hair.flower":0})', :class='{active: user.preferences.hair.flower === 0}') - .option(v-for='option in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]', - :class='{active: user.preferences.hair.flower === option}') - .sprite.customize-option(:class="`hair_flower_${option}`", @click='set({"preferences.hair.flower": option})') - .row(v-if='activeSubPage === "flower"') - .col-12.customize-options - // button.customize-option(ng-repeat='item in ::getGearArray("animal")', class='{{::item.key}}', - ng-class="{locked: user.items.gear.owned[item.key] == undefined, selectableInventory: user.preferences.costume ? user.items.gear.costume.headAccessory == item.key : user.items.gear.equipped.headAccessory == item.key}", - popover='{{::item.notes()}}', popover-title='{{::item.text()}}', popover-trigger='mouseenter', - popover-placement='right', popover-append-to-body='true', - ng-click='user.items.gear.owned[item.key] ? equip(item.key) : purchase(item.type,item)') + extraSettings( + v-if='activeTopPage === "extra"', + :editing="editing" + ) + #backgrounds.section.container.customize-section(v-if='activeTopPage === "backgrounds"') .row.title-row toggle-switch.backgroundFilterToggle(:label="'Hide locked backgrounds'", v-model='filterBackgrounds') @@ -270,19 +213,6 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true } #avatar-modal { - .sub-menu:hover { - cursor: pointer; - } - - .sub-menu-item { - text-align: center; - border-bottom: 2px solid #f9f9f9; - } - - .sub-menu .sub-menu-item:hover, .sub-menu .sub-menu-item.active { - color: $purple-200; - border-bottom: 2px solid $purple-200; - } .customize-section { text-align: center; @@ -794,6 +724,7 @@ import toggleSwitch from 'client/components/ui/toggleSwitch'; import bodySettings from './avatarModal/body-settings'; import skinSettings from './avatarModal/skin-settings'; import hairSettings from './avatarModal/hair-settings'; +import extraSettings from './avatarModal/extra-settings'; import subMenu from './avatarModal/sub-menu'; import logoPurple from 'assets/svg/logo-purple.svg'; @@ -811,7 +742,6 @@ import {avatarEditorUtilies} from '../mixins/avatarEditUtilities'; import content from 'common/script/content/index'; const skinsBySet = groupBy(appearance.skin, 'set.key'); -const hairColorBySet = groupBy(appearance.hair.color, 'set.key'); export default { mixins: [guide, notifications, avatarEditorUtilies], @@ -822,6 +752,7 @@ export default { bodySettings, skinSettings, hairSettings, + extraSettings, subMenu, }, @@ -835,7 +766,6 @@ export default { }, data () { let backgroundShopSets = getBackgroundShopSets(); - const bgItems = []; return { loading: false, @@ -847,11 +777,7 @@ export default { animalSkinKeys: ['bear', 'cactus', 'fox', 'lion', 'panda', 'pig', 'tiger', 'wolf'], premiumHairColorKeys: ['rainbow', 'yellow', 'green', 'purple', 'blue', 'TRUred'], - animalItemKeys: { - back: ['bearTail', 'cactusTail', 'foxTail', 'lionTail', 'pandaTail', 'pigTail', 'tigerTail', 'wolfTail'], - headAccessory: ['bearEars', 'cactusEars', 'foxEars', 'lionEars', 'pandaEars', 'pigEars', 'tigerEars', 'wolfEars'], - }, - chairKeys: ['black', 'blue', 'green', 'pink', 'red', 'yellow', 'handleless_black', 'handleless_blue', 'handleless_green', 'handleless_pink', 'handleless_red', 'handleless_yellow'], + icons: Object.freeze({ logoPurple, bodyIcon, @@ -896,184 +822,8 @@ export default { }, computed: { ...mapState({user: 'user.data'}), - headbands () { - let keys = ['blackHeadband', 'blueHeadband', 'greenHeadband', 'pinkHeadband', 'redHeadband', 'whiteHeadband', 'yellowHeadband']; - let options = keys.map(key => { - let newKey = `headAccessory_special_${key}`; - let option = {}; - option.key = key; - option.active = this.user.preferences.costume ? this.user.items.gear.costume.headAccessory === newKey : this.user.items.gear.equipped.headAccessory === newKey; - option.click = () => { - let type = this.user.preferences.costume ? 'costume' : 'equipped'; - return this.equip(newKey, type); - }; - return option; - }); - return options; - }, - eyewear () { - let keys = [ - 'blackTopFrame', 'blueTopFrame', 'greenTopFrame', 'pinkTopFrame', 'redTopFrame', 'whiteTopFrame', 'yellowTopFrame', - 'blackHalfMoon', 'blueHalfMoon', 'greenHalfMoon', 'pinkHalfMoon', 'redHalfMoon', 'whiteHalfMoon', 'yellowHalfMoon', - ]; - let options = keys.map(key => { - let newKey = `eyewear_special_${key}`; - let option = {}; - option.key = key; - option.active = this.user.preferences.costume ? this.user.items.gear.costume.eyewear === newKey : this.user.items.gear.equipped.eyewear === newKey; - option.click = () => { - let type = this.user.preferences.costume ? 'costume' : 'equipped'; - return this.equip(newKey, type); - }; - return option; - }); - return options; - }, - specialShirts () { - // @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 keys = this.specialShirtKeys; - let options = keys.map(key => { - return this.mapKeysToOption(key, 'shirt'); - }); - return options; - }, - rainbowSkins () { - // @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 keys = this.rainbowSkinKeys; - let options = keys.map(key => { - return this.mapKeysToOption(key, 'skin'); - }); - return options; - }, - animalSkins () { - // @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 keys = this.animalSkinKeys; - let options = keys.map(key => { - return this.mapKeysToOption(key, 'skin'); - }); - return options; - }, - 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 - - let seasonalSkins = []; - for (let key in skinsBySet) { - let set = skinsBySet[key]; - - let keys = set.map(item => { - return item.key; - }); - - let options = keys.map(optionKey => { - return this.mapKeysToOption(optionKey, 'skin', '', key); - }); - - let text = this.$t(key); - if (appearanceSets[key] && appearanceSets[key].text) { - text = appearanceSets[key].text(); - } - - let compiledSet = { - key, - options, - keys, - text, - }; - seasonalSkins.push(compiledSet); - } - - return seasonalSkins; - }, - 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 - - let seasonalHairColors = []; - for (let key in hairColorBySet) { - let set = hairColorBySet[key]; - - let keys = set.map(item => { - return item.key; - }); - - let options = keys.map(optionKey => { - return this.mapKeysToOption(optionKey, 'hair', 'color', key); - }); - - let text = this.$t(key); - if (appearanceSets[key] && appearanceSets[key].text) { - text = appearanceSets[key].text(); - } - let 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 - let keys = this.premiumHairColorKeys; - let options = keys.map(key => { - return 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 - let keys = this.baseHair2Keys; - let options = keys.map(key => { - return 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 - let keys = this.baseHair3Keys; - let options = keys.map(key => { - return this.mapKeysToOption(key, 'hair', 'base'); - }); - 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 - let keys = this.baseHair4Keys; - let options = keys.map(key => { - return 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 - let keys = this.baseHair5Keys; - let options = keys.map(key => { - return 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 - let keys = this.baseHair6Keys; - let options = keys.map(key => { - return this.mapKeysToOption(key, 'hair', 'beard'); - }); - return options; - }, editing () { return this.$store.state.avatarEditorOptions.editingUser; }, @@ -1123,41 +873,6 @@ export default { }); return ownedBackgrounds; }, - extraSubMenuItems () { - const items = [ - { - id: 'glasses', - label: this.$t('glasses'), - }, - { - id: 'wheelchair', - label: this.$t('wheelchair'), - }, - { - id: 'flower', - label: this.$t('accent'), - }, - ]; - - if (this.editing) { - items.push({ - id: 'ears', - label: this.$t('animalEars'), - }); - - items.push({ - id: 'tails', - label: this.$t('animalTails'), - }); - - items.push({ - id: 'headband', - label: this.$t('headband'), - }); - } - - return items; - }, }, methods: { purchase (type, key) { @@ -1247,51 +962,6 @@ export default { backgroundPurchased () { this.backgroundUpdate = new Date(); }, - animalItemsUnlockString (category) { - const keys = this.animalItemKeys[category].map(key => { - return `items.gear.owned.${category}_special_${key}`; - }); - - 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; - }, - 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 keys = this.animalItemKeys[category]; - let options = keys.map(key => { - let newKey = `${category}_special_${key}`; - let userPurchased = this.user.items.gear.owned[newKey]; - - let 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.gemLocked = userPurchased === undefined; - option.goldLocked = userPurchased === false; - option.locked = option.gemLocked || option.goldLocked; - option.click = () => { - if (option.gemLocked) { - return this.unlock(`items.gear.owned.${newKey}`); - } else if (option.goldLocked) { - return this.buy(newKey); - } else { - let type = this.user.preferences.costume ? 'costume' : 'equipped'; - return this.equip(newKey, type); - } - }; - return option; - }); - return options; - }, }, }; diff --git a/website/client/mixins/avatarEditUtilities.js b/website/client/mixins/avatarEditUtilities.js index 9c389d426cc4..eadee775b916 100644 --- a/website/client/mixins/avatarEditUtilities.js +++ b/website/client/mixins/avatarEditUtilities.js @@ -31,8 +31,8 @@ export const avatarEditorUtilies = { option.pathKey = pathKey; option.active = userPreference === key; option.class = this.createClass(type, subType, key); - option.click = () => { - return this.locked ? this.unlock(`${this.pathKey}.${this.key}`) : this.set({[`preferences.${this.pathKey}`]: this.key}); + option.click = (optionParam) => { + return option.locked ? this.unlock(`${optionParam.pathKey}.${key}`) : this.set({[`preferences.${optionParam.pathKey}`]: optionParam.key}); }; return option; }, @@ -47,7 +47,7 @@ export const avatarEditorUtilies = { if (locked) hide = moment(appearanceSets[set].availableUntil).isBefore(moment()); } - option.locked = locked; + option.gemLocked = locked; option.hide = hide; if (locked) { option.gem = 2; From e89610038d2d7564e9b161d888f15bb3295b97a6 Mon Sep 17 00:00:00 2001 From: negue Date: Wed, 31 Jul 2019 20:14:52 +0200 Subject: [PATCH 14/18] fix sprite positions / lint --- .../avatarModal/customize-options.vue | 37 +++++++--- .../components/avatarModal/extra-settings.vue | 6 +- .../components/avatarModal/hair-settings.vue | 23 +++--- website/client/components/creatorIntro.vue | 72 ------------------- website/client/mixins/avatarEditUtilities.js | 2 +- 5 files changed, 41 insertions(+), 99 deletions(-) diff --git a/website/client/components/avatarModal/customize-options.vue b/website/client/components/avatarModal/customize-options.vue index 24771f68fd68..e975cfc41424 100644 --- a/website/client/components/avatarModal/customize-options.vue +++ b/website/client/components/avatarModal/customize-options.vue @@ -108,10 +108,9 @@ transform: rotate(-45deg); position: absolute; top: 0; - - margin-top: 33px; - margin-bottom: 20px; - margin-left: 0; + margin-top: 30px; + margin-bottom: 20px; + margin-left: -1px; } } } @@ -140,6 +139,9 @@ &:hover { cursor: pointer; } + } + + .outer-option-background:not(.none) { .sprite.customize-option { // margin: 0 auto; @@ -161,18 +163,33 @@ margin-left: -4px; } &.chair { - margin-left: -2px; - margin-top: -4px; + margin-left: -1px; + margin-top: -1px; + + &.handleless { + margin-left: -5px; + margin-top: -5px; + } } &.color, &.bangs { margin-top: 4px; margin-left: -3px; } - &.hair.base { - margin-top: 0px; - margin-left: -5px; - } + &.hair.base { + margin-top: 0px; + margin-left: -5px; + } + + &.headAccessory { + margin-top: 0; + margin-left: -4px; + } + + &.headband { + margin-top: -6px; + margin-left: -27px; + } } } diff --git a/website/client/components/avatarModal/extra-settings.vue b/website/client/components/avatarModal/extra-settings.vue index 9d8a0631cca1..b4571a3b7590 100644 --- a/website/client/components/avatarModal/extra-settings.vue +++ b/website/client/components/avatarModal/extra-settings.vue @@ -173,7 +173,7 @@ option.none = true; } option.active = this.user.preferences.chair === key; - option.class = `button_chair_${key} chair`; + option.class = `button_chair_${key} chair ${key.includes('handleless_') ? 'handleless' : ''}`; option.click = () => { return this.set({'preferences.chair': key}); }; @@ -214,9 +214,9 @@ let 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`; + option.class = `headAccessory_special_${option.key} ${category}`; if (category === 'back') { - option.class = `icon_back_special_${option.key}`; + option.class = `icon_back_special_${option.key} back`; } option.gemLocked = userPurchased === undefined; option.goldLocked = userPurchased === false; diff --git a/website/client/components/avatarModal/hair-settings.vue b/website/client/components/avatarModal/hair-settings.vue index 3bd0a22af5e6..3df4fad5e2e6 100644 --- a/website/client/components/avatarModal/hair-settings.vue +++ b/website/client/components/avatarModal/hair-settings.vue @@ -40,7 +40,7 @@ customize-options( v-if='editing', :items='beardList', - :fullSet='isPurchaseAllNeeded(\'hair\', [\'baseHair5\', \'baseHair6\'], [\'mustache\', \'beard\'])', + :fullSet='isPurchaseAllNeeded("hair", ["baseHair5", "baseHair6"], ["mustache", "beard"])', @unlock='unlock(`hair.mustache.${baseHair5Keys.join(",hair.mustache.")},hair.beard.${baseHair6Keys.join(",hair.beard.")}`)' ) @@ -200,11 +200,8 @@ return options; }, hairBangs () { - const none = { - key: 0, - none: true, - class: `hair_bangs_0_${this.user.preferences.hair.color}`, // todo add current hair bangs setting - }; + const none = this.mapKeysToFreeOption(0, 'hair', 'bangs'); + none.none = true; const options = [1, 2, 3, 4].map(s => this.mapKeysToFreeOption(s, 'hair', 'bangs')); @@ -262,13 +259,13 @@ }); 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, - ], - }); + sets.push({ + fullSet: !this.userOwnsSet('hair', this.baseHair2Keys, 'base'), + unlock: () => this.unlock(`hair.base.${this.baseHair2Keys.join(',hair.base.')}`), + options: [ + ...this.baseHair2, + ], + }); } return sets; diff --git a/website/client/components/creatorIntro.vue b/website/client/components/creatorIntro.vue index beca7fa040f2..346b3dcf08fd 100644 --- a/website/client/components/creatorIntro.vue +++ b/website/client/components/creatorIntro.vue @@ -223,64 +223,6 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true display: none !important; } - .customize-options .option { - display: inline-block; - vertical-align: bottom; - //padding: .5em; - height: 64px; - width: 64px; - // margin: 1em .5em .5em 0; - // border: 4px solid $gray-700; - border-radius: 10px; - position: relative; - - &.none { - .sprite { - opacity: 0.24; - } - - .redline-outer { - height: 60px; - width: 60px; - position: absolute; - bottom: 0; - margin: 0 auto 0 0; - - .redline { - width: 60px; - height: 4px; - display: block; - background: red; - transform: rotate(-45deg); - position: absolute; - top: 0; - - margin-top: 10px; - margin-bottom: 20px; - margin-left: 6px; - } - } - } - - &.locked { - border: none; - border-radius: 2px; - background-color: #ffffff; - box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12); - margin-top: 0; - } - - &.active { - background: white; - border: solid 4px $purple-300; - } - - &:hover { - cursor: pointer; - } - } - - #creator-background { background-color: $purple-200; } @@ -709,17 +651,13 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true