Merge branch 'develop' of github.com:HabitRPG/habitica into develop

This commit is contained in:
Matteo Pagliazzi 2018-05-13 16:30:47 +02:00
commit 2c3d268a63
6 changed files with 130 additions and 31 deletions

View file

@ -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;
},

View file

@ -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();

View file

@ -139,7 +139,7 @@
h4.popover-content-title(v-else) {{ item.text }}
.popover-content-text(v-if='item.locked && item.key === "lostMasterclasser1"') {{ `${$t('questUnlockLostMasterclasser')}` }}
.popover-content-text(v-if='item.locked && item.unlockCondition && item.unlockCondition.incentiveThreshold') {{ `${$t('loginIncentiveQuest', {count: item.unlockCondition.incentiveThreshold})}` }}
.popover-content-text(v-if='item.locked && item.previous') {{ `${$t('unlockByQuesting', {title: item.previous})}` }}
.popover-content-text(v-if='item.locked && item.previous && isBuyingDependentOnPrevious(item)') {{ `${$t('unlockByQuesting', {title: item.previous})}` }}
.popover-content-text(v-if='item.lvl > user.stats.lvl') {{ `${$t('mustLvlQuest', {level: item.lvl})}` }}
questInfo(v-if='!item.locked', :quest="item")
@ -486,6 +486,11 @@ export default {
this.$root.$emit('bv::show::modal', 'buy-quest-modal');
},
isBuyingDependentOnPrevious (item) {
let questsNotDependentToPrevious = ['moon2', 'moon3'];
if (item.key in questsNotDependentToPrevious) return false;
return true;
},
},
};
</script>

View file

@ -1,6 +1,6 @@
<template lang="pug">
.notifications
div(v-for='notification in notifications', :key='notification.uuid')
.notifications(:class="notificationsTopPos")
div(v-for='notification in notificationStore', :key='notification.uuid')
notification(:notification='notification')
</template>
@ -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;
}
}
}
</style>
<script>
import { mapState } from 'client/libs/store';
import notification from './notification';
export default {
@ -22,8 +32,19 @@ export default {
notification,
},
computed: {
notifications () {
return this.$store.state.notificationStore;
...mapState({
notificationStore: 'notificationStore',
userSleeping: 'user.data.preferences.sleep',
}),
notificationsTopPos () {
const base = 'notifications-top-pos-';
let modifier = '';
if (this.userSleeping) {
modifier = 'sleeping';
} else {
modifier = 'normal';
}
return `${base}${modifier}`;
},
},
};

View file

@ -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}`")

View file

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