From 6c64a1cd8c48244ccbf9d971e75aedbf16c0d1c8 Mon Sep 17 00:00:00 2001 From: pengfluf Date: Sun, 13 May 2018 17:04:43 +0300 Subject: [PATCH 1/4] Beard and mustache facial hairs now can be bought as a full set for 5 gems (#10338) * Purchasing All Facial Hairs Fixed * Notifications z-index fixed * Notifications z-index fixed x2 * Z-indexes fixed, facial hairs buying corrected * isPurchaseAllNeeded refactored * isPurchaseAllNeeded is more generic now * Linting Passed --- website/client/components/creatorIntro.vue | 97 +++++++++++++++---- website/client/components/header/menu.vue | 20 +++- .../components/snackbars/notifications.vue | 33 +++++-- 3 files changed, 122 insertions(+), 28 deletions(-) diff --git a/website/client/components/creatorIntro.vue b/website/client/components/creatorIntro.vue index 4cff8ea8b2..19ca0efc5e 100644 --- a/website/client/components/creatorIntro.vue +++ b/website/client/components/creatorIntro.vue @@ -163,32 +163,27 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true :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.beard": 0})', :class="[{ active: user.preferences.hair.beard === 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_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='!userOwnsSet("hair", baseHair5Keys, "beard")') - .gem-lock - .svg-icon.gem(v-html='icons.gem') - span 5 - button.btn.btn-secondary.purchase-all(@click='unlock(`hair.beard.${baseHair5Keys.join(",hair.beard.")}`)') {{ $t('purchaseAll') }} .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 baseHair6', + .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.text-center(v-if='!userOwnsSet("hair", baseHair6Keys, "mustache")') + .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.${baseHair6Keys.join(",hair.mustache.")}`)') {{ $t('purchaseAll') }} + button.btn.btn-secondary.purchase-all(@click='unlock(`hair.mustache.${baseHair5Keys.join(",hair.mustache.")},hair.beard.${baseHair6Keys.join(",hair.beard.")}`)') {{ $t('purchaseAll') }} #extra.section.container.customize-section(v-if='activeTopPage === "extra"') .row.sub-menu .col-3.offset-1.text-center.sub-menu-item(@click='changeSubPage("glasses")', :class='{active: activeSubPage === "glasses"}') @@ -1010,8 +1005,8 @@ export default { baseHair2Keys: [2, 4, 5, 6, 7, 8], baseHair3Keys: [9, 10, 11, 12, 13, 14], baseHair4Keys: [15, 16, 17, 18, 19, 20], - baseHair5Keys: [1, 2, 3], - baseHair6Keys: [1, 2], + baseHair5Keys: [1, 2], + baseHair6Keys: [1, 2, 3], animalEarsKeys: ['bearEars', 'cactusEars', 'foxEars', 'lionEars', 'pandaEars', 'pigEars', 'tigerEars', 'wolfEars'], icons: Object.freeze({ logoPurple, @@ -1228,7 +1223,7 @@ export default { let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line let keys = this.baseHair5Keys; let options = keys.map(key => { - return this.mapKeysToOption(key, 'hair', 'beard'); + return this.mapKeysToOption(key, 'hair', 'mustache'); }); return options; }, @@ -1237,7 +1232,7 @@ export default { let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line let keys = this.baseHair6Keys; let options = keys.map(key => { - return this.mapKeysToOption(key, 'hair', 'mustache'); + return this.mapKeysToOption(key, 'hair', 'beard'); }); return options; }, @@ -1335,6 +1330,68 @@ export default { 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. + * @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/components/header/menu.vue b/website/client/components/header/menu.vue index 09400e80d4..22800b5dc9 100644 --- a/website/client/components/header/menu.vue +++ b/website/client/components/header/menu.vue @@ -3,7 +3,7 @@ div inbox-modal creator-intro profile - b-navbar.navbar.navbar-inverse.fixed-top.navbar-expand-lg(type="dark") + b-navbar.navbar.navbar-inverse.fixed-top.navbar-expand-lg(type="dark", :class="navbarZIndexClass") .navbar-header .logo.svg-icon.d-none.d-xl-block(v-html="icons.logo") .svg-icon.gryphon.d-md-block.d-none.d-xl-none @@ -145,7 +145,16 @@ div padding-right: 12.5px; height: 56px; box-shadow: 0 1px 2px 0 rgba($black, 0.24); - z-index: 1042; // To stay above snakbar notifications and modals + } + + .navbar-z-index { + &-normal { + z-index: 1080; + } + + &-modal { + z-index: 1035; + } } .navbar-header { @@ -327,7 +336,14 @@ export default { user: 'user.data', userHourglasses: 'user.data.purchased.plan.consecutive.trinkets', groupPlans: 'groupPlans', + modalStack: 'modalStack', }), + navbarZIndexClass () { + if (this.modalStack.length > 0) { + return 'navbar-z-index-modal'; + } + return 'navbar-z-index-normal'; + }, }, mounted () { this.getUserGroupPlans(); diff --git a/website/client/components/snackbars/notifications.vue b/website/client/components/snackbars/notifications.vue index 18705af494..22b0d78030 100644 --- a/website/client/components/snackbars/notifications.vue +++ b/website/client/components/snackbars/notifications.vue @@ -1,6 +1,6 @@ @@ -8,13 +8,23 @@ .notifications { position: fixed; right: 10px; - top: 65px; width: 350px; - z-index: 1041; // 1041 is above modal backgrounds + z-index: 1070; // 1070 is above modal backgrounds + + &-top-pos { + &-normal { + top: 65px; + } + + &-sleeping { + top: 105px; + } + } } From 355f0fedfb4215cdbb0e1aa89485a4dfb71851ae Mon Sep 17 00:00:00 2001 From: Brian Fenton Date: Sun, 13 May 2018 09:12:26 -0500 Subject: [PATCH 3/4] disabling checking off a subtask if not assigned to a user (#10357) --- website/client/components/tasks/task.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/client/components/tasks/task.vue b/website/client/components/tasks/task.vue index b64d7b7c75..1a2f055dcf 100644 --- a/website/client/components/tasks/task.vue +++ b/website/client/components/tasks/task.vue @@ -64,7 +64,7 @@ type="checkbox", :checked="item.completed", @change="toggleChecklistItem(item)", - :disabled="castingSpell", + :disabled="castingSpell || !isUser", :id="`checklist-${item.id}`" ) label.custom-control-label(v-markdown="item.text", :for="`checklist-${item.id}`") From 8cd695c39703c16dbeb1350b9564b31dff2fa80e Mon Sep 17 00:00:00 2001 From: aszlig Date: Sun, 13 May 2018 16:14:17 +0200 Subject: [PATCH 4/4] locales/groups: Don't wrap task text in code block (#10349) So far if a task contained Markdown, a system message like this would have been posted to the group chat: foo has claimed "Some [link](http://example.org/)" Also, if the Markdown contained backticked code fragments, the whole text would be displayed in red except the code part. The reason for this is because the system message is already in Markdown and a backticked task text would result in the following Markdown: `foo has claimed "Foo `bar`"` Here there are two code blocks, one with `foo has claimed "Foo ` and another which only has `"`. This is fixed by simply changing the userIsClamingTask translation string to not wrap the task text inside a code block, as per @Alys suggestion. Signed-off-by: aszlig --- website/common/locales/en/groups.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/common/locales/en/groups.json b/website/common/locales/en/groups.json index 4eded55be6..785eefb406 100644 --- a/website/common/locales/en/groups.json +++ b/website/common/locales/en/groups.json @@ -321,7 +321,7 @@ "approvalsTitle": "Tasks Awaiting Approval", "upgradeTitle": "Upgrade", "blankApprovalsDescription": "When your group completes tasks that need your approval, they'll appear here! Adjust approval requirement settings under task editing.", - "userIsClamingTask": "`<%= username %> has claimed \"<%= task %>\"`", + "userIsClamingTask": "`<%= username %> has claimed:` <%= task %>", "approvalRequested": "Approval Requested", "refreshApprovals": "Refresh Approvals", "refreshGroupTasks": "Refresh Group Tasks",