Merge branch 'develop' into negue/modal-notifications

This commit is contained in:
Matteo Pagliazzi
2018-10-13 21:18:16 +02:00
882 changed files with 46962 additions and 35748 deletions
+42 -40
View File
@@ -15,21 +15,21 @@ div
router-view(v-if="!isUserLoggedIn || isStaticPage")
template(v-else)
template(v-if="isUserLoaded")
div.resting-banner(v-if="showRestingBanner")
div.resting-banner(v-show="showRestingBanner", ref="restingBanner")
span.content
span.label {{ $t('innCheckOutBanner') }}
span.separator |
span.label.d-inline.d-sm-none {{ $t('innCheckOutBannerShort') }}
span.label.d-none.d-sm-inline {{ $t('innCheckOutBanner') }}
span.separator |
span.resume(@click="resumeDamage()") {{ $t('resumeDamage') }}
div.closepadding(@click="hideBanner()")
span.svg-icon.inline.icon-10(aria-hidden="true", v-html="icons.close")
notifications-display
app-menu(:class='{"restingInn": showRestingBanner}')
app-menu(:class='{"restingInn": showRestingBanner}' :style="{ marginTop: bannerHeight + 'px' }")
.container-fluid
app-header(:class='{"restingInn": showRestingBanner}')
buyModal(
:item="selectedItemToBuy || {}",
:withPin="true",
@change="resetItemToBuy($event)",
@buyPressed="customPurchase($event)",
:genericPurchase="genericPurchase(selectedItemToBuy)",
@@ -119,20 +119,9 @@ div
z-index: 1600 !important; /* Must stay above nav bar */
}
.restingInn {
.navbar {
top: 40px;
}
#app-header {
margin-top: 40px !important;
}
}
.resting-banner {
width: 100%;
height: 40px;
min-height: 40px;
background-color: $blue-10;
position: fixed;
top: 0;
@@ -140,11 +129,10 @@ div
display: flex;
.content {
height: 24px;
line-height: 1.71;
text-align: center;
color: $white;
padding: 8px 38px 8px 8px;
margin: auto;
}
@@ -161,6 +149,13 @@ div
}
}
@media only screen and (max-width: 768px) {
.content {
font-size: 12px;
line-height: 1.4;
}
}
.separator {
color: $blue-100;
margin: 0px 15px;
@@ -169,6 +164,7 @@ div
.resume {
font-weight: bold;
cursor: pointer;
white-space:nowrap;
}
}
</style>
@@ -224,6 +220,7 @@ export default {
loading: true,
currentTipNumber: 0,
bannerHidden: false,
bannerHeight: 0,
};
},
computed: {
@@ -332,23 +329,13 @@ export default {
if (notificationNotFoundMessage.indexOf(errorMessage) !== -1) snackbarTimeout = true;
let errorsToShow = [];
let usernameCheck = false;
let emailCheck = false;
let passwordCheck = false;
// show only the first error for each param
let paramErrorsFound = {};
if (errorData.errors) {
for (let e of errorData.errors) {
if (!usernameCheck && e.param === 'username') {
if (!paramErrorsFound[e.param]) {
errorsToShow.push(e.message);
usernameCheck = true;
}
if (!emailCheck && e.param === 'email') {
errorsToShow.push(e.message);
emailCheck = true;
}
if (!passwordCheck && e.param === 'password') {
errorsToShow.push(e.message);
passwordCheck = true;
paramErrorsFound[e.param] = true;
}
}
} else {
@@ -414,7 +401,6 @@ export default {
this.$store.watch(state => state.title, (title) => {
document.title = title;
});
this.$nextTick(() => {
// Load external scripts after the app has been rendered
Analytics.load();
@@ -432,6 +418,14 @@ export default {
this.hideLoadingScreen();
window.addEventListener('resize', this.setBannerOffset);
// Adjust the positioning of the header banners
this.$watch('showRestingBanner', () => {
this.$nextTick(() => {
this.setBannerOffset();
});
}, {immediate: true});
// Adjust the timezone offset
if (this.user.preferences.timezoneOffset !== this.browserTimezoneOffset) {
this.$store.dispatch('user:set', {
@@ -458,6 +452,7 @@ export default {
this.$root.$off('bv::show::modal');
this.$root.$off('buyModal::showItem');
this.$root.$off('selectMembersModal::showItem');
window.removeEventListener('resize', this.setBannerOffset);
},
mounted () {
// Remove the index.html loading screen and now show the inapp loading
@@ -567,13 +562,6 @@ export default {
});
}
},
resetItemToBuy ($event) {
// @TODO: Do we need this? I think selecting a new item
// overwrites. @negue might know
if (!$event && this.selectedItemToBuy.purchaseType !== 'card') {
this.selectedItemToBuy = null;
}
},
itemSelected (item) {
this.selectedItemToBuy = item;
},
@@ -623,10 +611,22 @@ export default {
},
hideBanner () {
this.bannerHidden = true;
this.setBannerOffset();
},
resumeDamage () {
this.$store.dispatch('user:sleep');
},
setBannerOffset () {
let contentPlacement = 0;
if (this.showRestingBanner && this.$refs.restingBanner !== undefined) {
contentPlacement = this.$refs.restingBanner.clientHeight;
}
this.bannerHeight = contentPlacement;
let smartBanner = document.getElementsByClassName('smartbanner')[0];
if (smartBanner !== undefined) {
smartBanner.style.top = `${contentPlacement}px`;
}
},
},
};
</script>
@@ -658,4 +658,6 @@ export default {
<style src="assets/css/sprites/spritesmith-main-20.css"></style>
<style src="assets/css/sprites/spritesmith-main-21.css"></style>
<style src="assets/css/sprites/spritesmith-main-22.css"></style>
<style src="assets/css/sprites/spritesmith-main-23.css"></style>
<style src="assets/css/sprites.css"></style>
<style src="smartbanner.js/dist/smartbanner.min.css"></style>
@@ -1,42 +1,96 @@
.promo_armoire_backgrounds_201808 {
.achievement-costumeContest6x {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -817px 0px;
background-position: -1277px -296px;
width: 144px;
height: 156px;
}
.promo_alligator {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: 0px 0px;
width: 480px;
height: 360px;
}
.promo_animal_tails {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -994px 0px;
width: 141px;
height: 441px;
}
.promo_armoire_backgrounds_201810 {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: 0px -937px;
width: 423px;
height: 147px;
}
.promo_fall_avatar_customizations {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -394px -723px;
width: 336px;
height: 207px;
}
.promo_fall_festival_2017 {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -481px -495px;
width: 414px;
height: 210px;
}
.promo_fall_festival_2018 {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: 0px -723px;
width: 393px;
height: 213px;
}
.promo_forest_friends_bundle {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -1136px 0px;
width: 423px;
height: 147px;
}
.promo_ghost_potions {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -424px -937px;
width: 423px;
height: 147px;
}
.promo_ios {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -441px 0px;
background-position: 0px -361px;
width: 375px;
height: 361px;
}
.promo_kangaroo {
.promo_mystery_201809 {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: 0px -327px;
width: 420px;
height: 336px;
background-position: -1136px -148px;
width: 306px;
height: 147px;
}
.promo_mystery_201807 {
.promo_seasonal_shop {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -817px -442px;
width: 114px;
height: 120px;
background-position: -1136px -591px;
width: 162px;
height: 138px;
}
.promo_spooky_sparkles {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -1136px -296px;
width: 140px;
height: 294px;
}
.promo_take_this {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -817px -563px;
background-position: -1443px -148px;
width: 96px;
height: 69px;
}
.promo_unconventional_armor {
.scene_nametag {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: -441px -362px;
width: 180px;
height: 180px;
background-position: -481px 0px;
width: 512px;
height: 208px;
}
.scene_tavern {
.scene_tools {
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
background-position: 0px 0px;
width: 440px;
height: 326px;
background-position: -481px -209px;
width: 366px;
height: 285px;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,474 @@
.Pet-Unicorn-Red {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -82px 0px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-Shade {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -574px -400px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-Skeleton {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -164px 0px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-White {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: 0px -100px;
width: 81px;
height: 99px;
}
.Pet-Unicorn-Zombie {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -82px -100px;
width: 81px;
height: 99px;
}
.Pet-Whale-Base {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -164px -100px;
width: 81px;
height: 99px;
}
.Pet-Whale-CottonCandyBlue {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -246px 0px;
width: 81px;
height: 99px;
}
.Pet-Whale-CottonCandyPink {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -246px -100px;
width: 81px;
height: 99px;
}
.Pet-Whale-Desert {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: 0px -200px;
width: 81px;
height: 99px;
}
.Pet-Whale-Golden {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -82px -200px;
width: 81px;
height: 99px;
}
.Pet-Whale-Red {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -164px -200px;
width: 81px;
height: 99px;
}
.Pet-Whale-Shade {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -246px -200px;
width: 81px;
height: 99px;
}
.Pet-Whale-Skeleton {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -328px 0px;
width: 81px;
height: 99px;
}
.Pet-Whale-White {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -328px -100px;
width: 81px;
height: 99px;
}
.Pet-Whale-Zombie {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -328px -200px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Aquatic {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: 0px -300px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Base {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -82px -300px;
width: 81px;
height: 99px;
}
.Pet-Wolf-CottonCandyBlue {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -164px -300px;
width: 81px;
height: 99px;
}
.Pet-Wolf-CottonCandyPink {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -246px -300px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Cupid {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -328px -300px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Desert {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -410px 0px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Ember {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -410px -100px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Fairy {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -410px -200px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Floral {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -410px -300px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Ghost {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -492px 0px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Glass {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -492px -100px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Glow {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -492px -200px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Golden {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -492px -300px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Holly {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: 0px -400px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Peppermint {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -82px -400px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Rainbow {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -164px -400px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Red {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -246px -400px;
width: 81px;
height: 99px;
}
.Pet-Wolf-RoyalPurple {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -328px -400px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Shade {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -410px -400px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Shimmer {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -492px -400px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Skeleton {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -574px 0px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Spooky {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -574px -100px;
width: 81px;
height: 99px;
}
.Pet-Wolf-StarryNight {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -574px -200px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Thunderstorm {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -574px -300px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Veteran {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: 0px 0px;
width: 81px;
height: 99px;
}
.Pet-Wolf-White {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: 0px -500px;
width: 81px;
height: 99px;
}
.Pet-Wolf-Zombie {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -82px -500px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Base {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -164px -500px;
width: 81px;
height: 99px;
}
.Pet-Yarn-CottonCandyBlue {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -246px -500px;
width: 81px;
height: 99px;
}
.Pet-Yarn-CottonCandyPink {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -328px -500px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Desert {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -410px -500px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Golden {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -492px -500px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Red {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -574px -500px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Shade {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -656px 0px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Skeleton {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -656px -100px;
width: 81px;
height: 99px;
}
.Pet-Yarn-White {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -656px -200px;
width: 81px;
height: 99px;
}
.Pet-Yarn-Zombie {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -656px -300px;
width: 81px;
height: 99px;
}
.Pet_HatchingPotion_Aquatic {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -656px -469px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Base {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -69px -669px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_CottonCandyBlue {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: 0px -600px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_CottonCandyPink {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -69px -600px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Cupid {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -138px -600px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Desert {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -207px -600px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Ember {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -276px -600px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Fairy {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -345px -600px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Floral {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -414px -600px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Ghost {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -483px -600px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Glass {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -552px -600px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Glow {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -621px -600px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Golden {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: 0px -669px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Holly {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -656px -400px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Peppermint {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -138px -669px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Purple {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -207px -669px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Rainbow {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -276px -669px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Red {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -345px -669px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_RoyalPurple {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -414px -669px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Shade {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -483px -669px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Shimmer {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -552px -669px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Skeleton {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -621px -669px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Spooky {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -738px 0px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_StarryNight {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -738px -69px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Thunderstorm {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -738px -138px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_White {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -738px -207px;
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Zombie {
background-image: url('~assets/images/sprites/spritesmith-main-23.png');
background-position: -738px -276px;
width: 68px;
height: 68px;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 549 KiB

After

Width:  |  Height:  |  Size: 554 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 KiB

After

Width:  |  Height:  |  Size: 483 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 KiB

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 343 KiB

After

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 KiB

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 114 KiB

+56
View File
@@ -0,0 +1,56 @@
.iconalert {
position: relative;
padding: $alert-padding-y $alert-padding-x;
margin-bottom: $alert-margin-bottom;
border-radius: 2px;
color: white;
font-family: Roboto;
font-size: 14px;
line-height: 1.43;
padding-left: 60px
}
.iconalert::before {
height:100%;
content:' ';
position: absolute;
top: 0;
left: 0;
width: 44px;
background-repeat: no-repeat;
background-position: center center;
margin-right: $alert-padding-x;
border-top-left-radius: 2px;
border-bottom-left-radius: 2px;
}
.iconalert-success {
background-color: #24cc8f;
}
.iconalert-success::before {
background-image: url(~client/assets/svg/for-css/checkbox-white.svg);
background-size: 13px 10px;
background-color: #1ca372;
}
.iconalert-warning::before, .iconalert-error::before {
background-image: url(~client/assets/svg/for-css/alert-white.svg);
background-size: 16px 16px;
}
.iconalert-warning {
background-color: #ffa623;
}
.iconalert-warning::before {
background-color: #ee9109;
}
.iconalert-error {
background-color: #f74e52;
}
.iconalert-error::before {
background-color: #de3f3f;
}
+1
View File
@@ -34,3 +34,4 @@
@import './progress-bar';
@import './pin';
@import './animals';
@import './iconalert';
+5 -5
View File
@@ -2,8 +2,8 @@
// possible values are: normal, fall, habitoween, thanksgiving, winter, nye, birthday, valentines, spring, summer
// more to be added on future seasons
$npc_market_flavor: 'normal';
$npc_quests_flavor: 'normal';
$npc_seasonal_flavor: 'normal';
$npc_timetravelers_flavor: 'normal';
$npc_tavern_flavor: 'normal';
$npc_market_flavor: 'fall';
$npc_quests_flavor: 'fall';
$npc_seasonal_flavor: 'fall';
$npc_timetravelers_flavor: 'fall';
$npc_tavern_flavor: 'fall';
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="#FFF" fill-rule="evenodd" d="M0 1.994C0 .893.895 0 1.994 0h12.012C15.107 0 16 .895 16 1.994v12.012A1.995 1.995 0 0 1 14.006 16H1.994A1.995 1.995 0 0 1 0 14.006V1.994zM2 2v12h12V2H2zm5 2h2v5H7V4zm0 6h2v2H7v-2z"/>
</svg>

After

Width:  |  Height:  |  Size: 319 B

@@ -70,9 +70,9 @@ export default {
facebook,
}),
tweet,
achievementLink: `${BASE_URL}/social/achievement`,
twitterLink: `https://twitter.com/intent/tweet?text=${tweet}&via=habitica&url=${BASE_URL}/social/achievement&count=none`,
facebookLink: `https://www.facebook.com/sharer/sharer.php?text=${tweet}&u=${BASE_URL}/social/achievement`,
achievementLink: `${BASE_URL}`,
twitterLink: `https://twitter.com/intent/tweet?text=${tweet}&via=habitica&url=${BASE_URL}&count=none`,
facebookLink: `https://www.facebook.com/sharer/sharer.php?text=${tweet}&u=${BASE_URL}`,
};
},
};
@@ -164,30 +164,30 @@ export default {
classGear (heroClass) {
if (heroClass === 'rogue') {
return {
armor: 'armor_rogue_5',
head: 'head_rogue_5',
shield: 'shield_rogue_6',
weapon: 'weapon_rogue_6',
armor: 'armor_special_fall2018Rogue',
head: 'head_special_fall2018Rogue',
shield: 'shield_special_fall2018Rogue',
weapon: 'weapon_special_fall2018Rogue',
};
} else if (heroClass === 'wizard') {
return {
armor: 'armor_wizard_5',
head: 'head_wizard_5',
weapon: 'weapon_wizard_6',
armor: 'armor_special_fall2018Mage',
head: 'head_special_fall2018Mage',
weapon: 'weapon_special_fall2018Mage',
};
} else if (heroClass === 'healer') {
return {
armor: 'armor_healer_5',
head: 'head_healer_5',
shield: 'shield_healer_5',
weapon: 'weapon_healer_6',
armor: 'armor_special_fall2018Healer',
head: 'head_special_fall2018Healer',
shield: 'shield_special_fall2018Healer',
weapon: 'weapon_special_fall2018Healer',
};
} else {
return {
armor: 'armor_warrior_5',
head: 'head_warrior_5',
shield: 'shield_warrior_5',
weapon: 'weapon_warrior_6',
armor: 'armor_special_fall2018Warrior',
head: 'head_special_fall2018Warrior',
shield: 'shield_special_fall2018Warrior',
weapon: 'weapon_special_fall2018Warrior',
};
}
},
+54 -3
View File
@@ -11,20 +11,20 @@
span {{registering ? $t('signUpWithSocial', {social: 'Google'}) : $t('loginWithSocial', {social: 'Google'})}}
.form-group(v-if='registering')
label(for='usernameInput', v-once) {{$t('username')}}
input#usernameInput.form-control(type='text', :placeholder='$t("usernamePlaceholder")', v-model='username')
input#usernameInput.form-control(type='text', :placeholder='$t("usernamePlaceholder")', v-model='username', :class='{"input-valid": usernameValid, "input-invalid": usernameInvalid}')
.form-group(v-if='!registering')
label(for='usernameInput', v-once) {{$t('emailOrUsername')}}
input#usernameInput.form-control(type='text', :placeholder='$t("emailOrUsername")', v-model='username')
.form-group(v-if='registering')
label(for='emailInput', v-once) {{$t('email')}}
input#emailInput.form-control(type='email', :placeholder='$t("emailPlaceholder")', v-model='email')
input#emailInput.form-control(type='email', :placeholder='$t("emailPlaceholder")', v-model='email', :class='{"input-invalid": emailInvalid, "input-valid": emailValid}')
.form-group
label(for='passwordInput', v-once) {{$t('password')}}
a.float-right.forgot-password(v-once, v-if='!registering', @click='forgotPassword = true') {{$t('forgotPassword')}}
input#passwordInput.form-control(type='password', :placeholder='$t(registering ? "passwordPlaceholder" : "password")', v-model='password')
.form-group(v-if='registering')
label(for='confirmPasswordInput', v-once) {{$t('confirmPassword')}}
input#confirmPasswordInput.form-control(type='password', :placeholder='$t("confirmPasswordPlaceholder")', v-model='passwordConfirm')
input#confirmPasswordInput.form-control(type='password', :placeholder='$t("confirmPasswordPlaceholder")', v-model='passwordConfirm', :class='{"input-invalid": passwordConfirmInvalid, "input-valid": passwordConfirmValid}')
small.form-text(v-once, v-html="$t('termsAndAgreement')")
.text-center
.btn.btn-info(@click='register()', v-if='registering', v-once) {{$t('joinHabitica')}}
@@ -49,6 +49,8 @@
.social-button {
width: 100%;
height: 100%;
white-space: inherit;
text-align: center;
.text {
@@ -69,12 +71,17 @@
small.form-text {
text-align: center;
}
.input-valid {
color: #fff;
}
}
</style>
<script>
import hello from 'hellojs';
import { setUpAxios } from 'client/libs/auth';
import debounce from 'lodash/debounce';
import facebookSquareIcon from 'assets/svg/facebook-square.svg';
import googleIcon from 'assets/svg/google.svg';
@@ -88,6 +95,7 @@ export default {
email: '',
password: '',
passwordConfirm: '',
usernameIssues: [],
};
data.icons = Object.freeze({
@@ -104,7 +112,50 @@ export default {
google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line
});
},
computed: {
emailValid () {
if (this.email.length <= 3) return false;
return this.validateEmail(this.email);
},
emailInvalid () {
return !this.emailValid;
},
usernameValid () {
if (this.username.length <= 3) return false;
return this.usernameIssues.length === 0;
},
usernameInvalid () {
return !this.usernameValid;
},
passwordConfirmValid () {
if (this.passwordConfirm.length <= 3) return false;
return this.passwordConfirm === this.password;
},
passwordConfirmInvalid () {
return !this.passwordConfirmValid;
},
},
watch: {
username () {
this.validateUsername(this.username);
},
},
methods: {
// eslint-disable-next-line func-names
validateUsername: debounce(function (username) {
if (username.length <= 3) {
return;
}
this.$store.dispatch('auth:verifyUsername', {
username: this.username,
}).then(res => {
if (res.issues !== undefined) {
this.usernameIssues = res.issues;
} else {
this.usernameIssues = [];
}
});
}, 500),
// @TODO: Abstract hello in to action or lib
async socialAuth (network) {
try {
@@ -21,23 +21,24 @@
.col-12.col-md-6
.btn.btn-secondary.social-button(@click='socialAuth("google")')
.svg-icon.social-icon(v-html="icons.googleIcon")
span {{registering ? $t('signUpWithSocial', {social: 'Google'}) : $t('loginWithSocial', {social: 'Google'})}}
.text {{registering ? $t('signUpWithSocial', {social: 'Google'}) : $t('loginWithSocial', {social: 'Google'})}}
.form-group(v-if='registering')
label(for='usernameInput', v-once) {{$t('username')}}
input#usernameInput.form-control(type='text', :placeholder='$t("usernamePlaceholder")', v-model='username')
input#usernameInput.form-control(type='text', :placeholder='$t("usernamePlaceholder")', v-model='username', :class='{"input-valid": usernameValid, "input-invalid": usernameInvalid}')
.input-error(v-for="issue in usernameIssues") {{ issue }}
.form-group(v-if='!registering')
label(for='usernameInput', v-once) {{$t('emailOrUsername')}}
input#usernameInput.form-control(type='text', :placeholder='$t("emailOrUsername")', v-model='username')
.form-group(v-if='registering')
label(for='emailInput', v-once) {{$t('email')}}
input#emailInput.form-control(type='email', :placeholder='$t("emailPlaceholder")', v-model='email')
input#emailInput.form-control(type='email', :placeholder='$t("emailPlaceholder")', v-model='email', :class='{"input-invalid": emailInvalid, "input-valid": emailValid}')
.form-group
label(for='passwordInput', v-once) {{$t('password')}}
a.float-right.forgot-password(v-once, v-if='!registering', @click='forgotPassword = true') {{$t('forgotPassword')}}
input#passwordInput.form-control(type='password', :placeholder='$t(registering ? "passwordPlaceholder" : "password")', v-model='password')
.form-group(v-if='registering')
label(for='confirmPasswordInput', v-once) {{$t('confirmPassword')}}
input#confirmPasswordInput.form-control(type='password', :placeholder='$t("confirmPasswordPlaceholder")', v-model='passwordConfirm')
input#confirmPasswordInput.form-control(type='password', :placeholder='$t("confirmPasswordPlaceholder")', v-model='passwordConfirm', :class='{"input-invalid": passwordConfirmInvalid, "input-valid": passwordConfirmValid}')
small.form-text(v-once, v-html="$t('termsAndAgreement')")
.text-center
.btn.btn-info(@click='register()', v-if='registering', v-once) {{$t('joinHabitica')}}
@@ -200,6 +201,10 @@
color: $white;
}
#usernameInput.input-invalid {
margin-bottom: 0.5em;
}
.form-text {
font-size: 14px;
color: $white;
@@ -207,6 +212,8 @@
.social-button {
width: 100%;
height: 100%;
white-space: inherit;
text-align: center;
.text {
@@ -275,11 +282,19 @@
.forgot-password {
color: #bda8ff !important;
}
.input-error {
color: #fff;
font-size: 90%;
width: 100%;
text-align: center;
}
</style>
<script>
import axios from 'axios';
import hello from 'hellojs';
import debounce from 'lodash/debounce';
import gryphon from 'assets/svg/gryphon.svg';
import habiticaIcon from 'assets/svg/habitica-logo.svg';
@@ -298,6 +313,7 @@ export default {
hasError: null,
code: null,
},
usernameIssues: [],
};
data.icons = Object.freeze({
@@ -322,6 +338,30 @@ export default {
}
return false;
},
emailValid () {
if (this.email.length <= 3) return false;
return this.validateEmail(this.email);
},
emailInvalid () {
if (this.email.length <= 3) return false;
return !this.emailValid;
},
usernameValid () {
if (this.username.length <= 3) return false;
return this.usernameIssues.length === 0;
},
usernameInvalid () {
if (this.username.length <= 3) return false;
return !this.usernameValid;
},
passwordConfirmValid () {
if (this.passwordConfirm.length <= 3) return false;
return this.passwordConfirm === this.password;
},
passwordConfirmInvalid () {
if (this.passwordConfirm.length <= 3) return false;
return !this.passwordConfirmValid;
},
},
mounted () {
hello.init({
@@ -355,8 +395,26 @@ export default {
},
immediate: true,
},
username () {
this.validateUsername(this.username);
},
},
methods: {
// eslint-disable-next-line func-names
validateUsername: debounce(function (username) {
if (username.length <= 3 || !this.registering) {
return;
}
this.$store.dispatch('auth:verifyUsername', {
username: this.username,
}).then(res => {
if (res.issues !== undefined) {
this.usernameIssues = res.issues;
} else {
this.usernameIssues = [];
}
});
}, 500),
async register () {
// @TODO do not use alert
if (!this.email) {
@@ -2,7 +2,7 @@
.row
challenge-modal(v-on:updatedChallenge='updatedChallenge')
leave-challenge-modal(:challengeId='challenge._id')
close-challenge-modal(:members='members', :challengeId='challenge._id')
close-challenge-modal(:members='members', :challengeId='challenge._id', :prize='challenge.prize')
challenge-member-progress-modal(:challengeId='challenge._id')
.col-12.col-md-8.standard-page
.row
@@ -30,7 +30,7 @@
div.category-wrap(@click.prevent="toggleCategorySelect")
span.category-select(v-if='workingChallenge.categories.length === 0') {{$t('none')}}
.category-label(v-for='category in workingChallenge.categories') {{$t(categoriesHashByKey[category])}}
.category-box(v-if="showCategorySelect")
.category-box(v-if="showCategorySelect && creating")
.form-check(
v-for="group in categoryOptions",
:key="group.key",
@@ -74,7 +74,7 @@ div
import memberSearchDropdown from 'client/components/members/memberSearchDropdown';
export default {
props: ['challengeId', 'members'],
props: ['challengeId', 'members', 'prize'],
components: {
memberSearchDropdown,
},
@@ -102,7 +102,10 @@ export default {
},
async deleteChallenge () {
if (!confirm('Are you sure you want to delete this challenge?')) return;
this.challenge = await this.$store.dispatch('challenges:deleteChallenge', {challengeId: this.challengeId});
this.challenge = await this.$store.dispatch('challenges:deleteChallenge', {
challengeId: this.challengeId,
prize: this.prize,
});
this.$router.push('/challenges/myChallenges');
},
},
@@ -14,11 +14,17 @@
button.btn.btn-secondary.create-challenge-button.float-right(@click='createChallenge()')
.svg-icon.positive-icon(v-html="icons.positiveIcon")
span(v-once) {{$t('createChallenge')}}
.row
.no-challenges.text-center.col-md-6.offset-3(v-if='!loading && filteredChallenges.length === 0')
h2(v-once) {{$t('noChallengeMatchFilters')}}
.row
.col-12.col-md-6(v-for='challenge in filteredChallenges')
challenge-item(:challenge='challenge')
.row
.col-12.text-center
.col-12.text-center(v-if='!loading && filteredChallenges.length > 0')
button.btn.btn-secondary(@click='loadMore()') {{ $t('loadMore') }}
</template>
@@ -41,6 +47,15 @@
margin-right: .5em;
}
}
.no-challenges {
color: $gray-200;
margin-top: 10em;
h2 {
color: $gray-200;
}
}
</style>
<script>
@@ -7,6 +7,7 @@
.row.header-row
.col-md-8.text-left
h1(v-once) {{$t('myChallenges')}}
h2(v-if='loading && challenges.length === 0') {{ $t('loading') }}
.col-md-4
// @TODO: implement sorting span.dropdown-label {{ $t('sortBy') }}
b-dropdown(:text="$t('sort')", right=true)
@@ -16,12 +17,16 @@
span(v-once) {{$t('createChallenge')}}
.row
.no-challenges.text-center.col-md-6.offset-3(v-if='filteredChallenges.length === 0')
.no-challenges.text-center.col-md-6.offset-3(v-if='!loading && challenges.length === 0')
.svg-icon(v-html="icons.challengeIcon")
h2(v-once) {{$t('noChallengeTitle')}}
p(v-once) {{$t('challengeDescription1')}}
p(v-once) {{$t('challengeDescription2')}}
.row
.no-challenges.text-center.col-md-6.offset-3(v-if='!loading && challenges.length > 0 && filteredChallenges.length === 0')
h2(v-once) {{$t('noChallengeMatchFilters')}}
.row
.col-12.col-md-6(v-for='challenge in filteredChallenges')
challenge-item(:challenge='challenge')
@@ -48,14 +53,15 @@
}
.no-challenges {
color: $gray-300;
color: $gray-200;
margin-top: 10em;
h2 {
color: $gray-300;
color: $gray-200;
}
.svg-icon {
color: #C3C0C7;
width: 88.7px;
margin: 1em auto;
}
@@ -84,6 +90,7 @@ export default {
challengeIcon,
positiveIcon,
}),
loading: false,
challenges: [],
sort: 'none',
sortOptions: [
@@ -113,7 +120,7 @@ export default {
};
},
mounted () {
this.loadchallanges();
this.loadChallenges();
},
computed: {
filteredChallenges () {
@@ -138,10 +145,12 @@ export default {
this.$store.state.challengeOptions.workingChallenge = {};
this.$root.$emit('bv::show::modal', 'challenge-modal');
},
async loadchallanges () {
async loadChallenges () {
this.loading = true;
this.challenges = await this.$store.dispatch('challenges:getUserChallenges', {
member: true,
});
this.loading = false;
},
challengeCreated (challenge) {
this.challenges.push(challenge);
@@ -14,7 +14,7 @@ div.autocomplete-selection(v-if='searchResults.length > 0', :style='autocomplete
import groupBy from 'lodash/groupBy';
export default {
props: ['selections', 'text', 'coords', 'chat'],
props: ['selections', 'text', 'coords', 'chat', 'textbox'],
data () {
return {
currentSearch: '',
@@ -25,9 +25,15 @@ export default {
},
computed: {
autocompleteStyle () {
function heightToUse (textBox, topCoords) {
let textBoxHeight = textBox['user-entry'].clientHeight;
return topCoords < textBoxHeight ? topCoords + 30 : textBoxHeight + 10;
}
return {
top: `${this.coords.TOP + 30}px`,
top: `${heightToUse(this.textbox, this.coords.TOP)}px`,
left: `${this.coords.LEFT + 30}px`,
marginLeft: '-28px',
marginTop: '28px',
position: 'absolute',
minWidth: '100px',
minHeight: '100px',
@@ -42,6 +48,7 @@ export default {
return option.toLowerCase().indexOf(currentSearch.toLowerCase()) !== -1;
});
},
},
mounted () {
this.grabUserNames();
+26 -20
View File
@@ -7,30 +7,31 @@ div
h3.leader(
:class='userLevelStyle(msg)',
@click="showMemberModal(msg.uuid)",
v-b-tooltip.hover.top="('contributor' in msg) ? msg.contributor.text : ''",
v-b-tooltip.hover.top="tierTitle",
)
| {{msg.user}}
.svg-icon(v-html="tierIcon", v-if='showShowTierStyle')
p.time(v-b-tooltip="", :title="msg.timestamp | date") {{msg.timestamp | timeAgo}}
.text(v-markdown='msg.text')
hr
.action(@click='like()', v-if='!inbox && msg.likes', :class='{active: msg.likes[user._id]}')
.svg-icon(v-html="icons.like")
span(v-if='!msg.likes[user._id]') {{ $t('like') }}
span(v-if='msg.likes[user._id]') {{ $t('liked') }}
span.action(v-if='!inbox', @click='copyAsTodo(msg)')
.svg-icon(v-html="icons.copy")
| {{$t('copyAsTodo')}}
span.action(v-if='!inbox && user.flags.communityGuidelinesAccepted && msg.uuid !== "system"', @click='report(msg)')
.svg-icon(v-html="icons.report")
| {{$t('report')}}
// @TODO make flagging/reporting work in the inbox. NOTE: it must work even if the communityGuidelines are not accepted and it MUST work for messages that you have SENT as well as received. -- Alys
span.action(v-if='msg.uuid === user._id || inbox || user.contributor.admin', @click='remove()')
.svg-icon(v-html="icons.delete")
| {{$t('delete')}}
span.action.float-right.liked(v-if='likeCount > 0')
.svg-icon(v-html="icons.liked")
| + {{ likeCount }}
div(v-if='msg.id')
.action(@click='like()', v-if='!inbox && msg.likes', :class='{active: msg.likes[user._id]}')
.svg-icon(v-html="icons.like")
span(v-if='!msg.likes[user._id]') {{ $t('like') }}
span(v-if='msg.likes[user._id]') {{ $t('liked') }}
span.action(v-if='!inbox', @click='copyAsTodo(msg)')
.svg-icon(v-html="icons.copy")
| {{$t('copyAsTodo')}}
span.action(v-if='!inbox && user.flags.communityGuidelinesAccepted && msg.uuid !== "system"', @click='report(msg)')
.svg-icon(v-html="icons.report")
| {{$t('report')}}
// @TODO make flagging/reporting work in the inbox. NOTE: it must work even if the communityGuidelines are not accepted and it MUST work for messages that you have SENT as well as received. -- Alys
span.action(v-if='msg.uuid === user._id || inbox || user.contributor.admin', @click='remove()')
.svg-icon(v-html="icons.delete")
| {{$t('delete')}}
span.action.float-right.liked(v-if='likeCount > 0')
.svg-icon(v-html="icons.liked")
| + {{ likeCount }}
</template>
<style lang="scss" scoped>
@@ -118,6 +119,8 @@ import markdownDirective from 'client/directives/markdown';
import { mapState } from 'client/libs/store';
import styleHelper from 'client/mixins/styleHelper';
import achievementsLib from '../../../common/script/libs/achievements';
import deleteIcon from 'assets/svg/delete.svg';
import copyIcon from 'assets/svg/copy.svg';
import likeIcon from 'assets/svg/like.svg';
@@ -220,6 +223,10 @@ export default {
}
return this.icons[`tier${message.contributor.level}`];
},
tierTitle () {
const message = this.msg;
return achievementsLib.getContribText(message.contributor, message.backer) || '';
},
},
methods: {
async like () {
@@ -254,8 +261,7 @@ export default {
this.$emit('message-removed', message);
if (this.inbox) {
axios.delete(`/api/v4/user/messages/${message.id}`);
this.$delete(this.user.inbox.messages, message.id);
await axios.delete(`/api/v4/inbox/messages/${message.id}`);
return;
}
@@ -230,6 +230,11 @@ export default {
this.chat.splice(chatIndex, 1, message);
},
messageRemoved (message) {
if (this.inbox) {
this.$emit('message-removed', message);
return;
}
const chatIndex = findIndex(this.chat, chatMessage => {
return chatMessage.id === message.id;
});
@@ -96,16 +96,10 @@ export default {
};
},
created () {
this.$root.$on('habitica::report-chat', data => {
if (!data.message || !data.groupId) return;
this.abuseObject = data.message;
this.groupId = data.groupId;
this.reportComment = '';
this.$root.$emit('bv::show::modal', 'report-flag');
});
this.$root.$on('habitica::report-chat', this.handleReport);
},
destroyed () {
this.$root.$off('habitica::report-chat');
this.$root.$off('habitica::report-chat', this.handleReport);
},
methods: {
close () {
@@ -129,6 +123,13 @@ export default {
});
this.close();
},
handleReport (data) {
if (!data.message || !data.groupId) return;
this.abuseObject = data.message;
this.groupId = data.groupId;
this.reportComment = '';
this.$root.$emit('bv::show::modal', 'report-flag');
},
},
};
</script>
+115 -54
View File
@@ -193,8 +193,10 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
.col-3.text-center.sub-menu-item(@click='changeSubPage("flower")', :class='{active: activeSubPage === "flower"}')
strong(v-once) {{$t('accent')}}
.row.sub-menu(v-if='editing')
.col-4.offset-2.text-center.sub-menu-item(@click='changeSubPage("ears")' :class='{active: activeSubPage === "ears"}')
.col-4.text-center.sub-menu-item(@click='changeSubPage("ears")' :class='{active: activeSubPage === "ears"}')
strong(v-once) {{$t('animalEars')}}
.col-4.text-center.sub-menu-item(@click='changeSubPage("tails")' :class='{active: activeSubPage === "tails"}')
strong(v-once) {{$t('animalTails')}}
.col-4.text-center.sub-menu-item(@click='changeSubPage("headband")' :class='{active: activeSubPage === "headband"}')
strong(v-once) {{$t('headband')}}
#glasses.row(v-if='activeSubPage === "glasses"')
@@ -203,17 +205,36 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
.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 animalEars',
.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.locked')
.gem-lock(v-if='option.gemLocked')
.svg-icon.gem(v-html='icons.gem')
span 2
.col-12.text-center(v-if='!animalEarsOwned')
.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(animalEarsUnlockString)') {{ $t('purchaseAll') }}
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}')
@@ -570,20 +591,21 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
}
}
.text-center .gem-lock {
display: inline-block;
margin-right: 1em;
margin-bottom: 1.6em;
vertical-align: bottom;
.text-center {
.gem-lock, .gold-lock {
display: inline-block;
margin-right: 1em;
margin-bottom: 1.6em;
vertical-align: bottom;
}
}
.gem-lock {
.gem-lock, .gold-lock {
.svg-icon {
width: 16px;
}
span {
color: #24cc8f;
font-weight: bold;
margin-left: .5em;
}
@@ -594,6 +616,14 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
}
}
.gem-lock span {
color: $green-10
}
.gold-lock span {
color: $yellow-10
}
.option.active {
border-color: $purple-200;
}
@@ -710,7 +740,7 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
color: #24cc8f;
}
.gem {
.gem, .coin {
width: 16px;
}
@@ -725,13 +755,13 @@ b-modal#avatar-modal(title="", :size='editing ? "lg" : "md"', :hide-header='true
font-size: 14px;
}
.gem {
.gem, .coin {
width: 20px;
}
}
}
.gem {
.gem, .coin {
margin: 0 .5em;
display: inline-block;
vertical-align: bottom;
@@ -837,6 +867,7 @@ import { mapState } from 'client/libs/store';
import avatar from './avatar';
import { getBackgroundShopSets } from '../../common/script/libs/shops';
import unlock from '../../common/script/ops/unlock';
import buy from '../../common/script/ops/buy/buy';
import guide from 'client/mixins/guide';
import notifications from 'client/mixins/notifications';
import appearance from 'common/script/content/appearance';
@@ -850,6 +881,7 @@ import skinIcon from 'assets/svg/skin.svg';
import hairIcon from 'assets/svg/hair.svg';
import backgroundsIcon from 'assets/svg/backgrounds.svg';
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';
@@ -1013,7 +1045,10 @@ export default {
baseHair4Keys: [15, 16, 17, 18, 19, 20],
baseHair5Keys: [1, 2],
baseHair6Keys: [1, 2, 3],
animalEarsKeys: ['bearEars', 'cactusEars', 'foxEars', 'lionEars', 'pandaEars', 'pigEars', 'tigerEars', 'wolfEars'],
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,
@@ -1024,6 +1059,7 @@ export default {
backgroundsIcon,
gem,
pin,
gold,
}),
modalPage: 1,
activeTopPage: 'body',
@@ -1075,44 +1111,6 @@ export default {
});
return options;
},
animalEarsUnlockString () {
let animalItemKeys = this.animalEarsKeys.map(key => {
return `items.gear.owned.headAccessory_special_${key}`;
});
return animalItemKeys.join(',');
},
animalEarsOwned () {
// @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.animalEarsKeys.forEach(key => {
if (!this.user.items.gear.owned[`headAccessory_special_${key}`]) own = false;
});
return own;
},
animalEars () {
// @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.animalEarsKeys;
let options = keys.map(key => {
let newKey = `headAccessory_special_${key}`;
let userPurchased = this.user.items.gear.owned[newKey];
let locked = !userPurchased;
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.locked = locked;
option.click = () => {
let type = this.user.preferences.costume ? 'costume' : 'equipped';
return locked ? this.unlock(`items.gear.owned.${newKey}`) : 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
@@ -1525,6 +1523,24 @@ export default {
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;
@@ -1550,6 +1566,51 @@ 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;
},
},
};
</script>
+4 -1
View File
@@ -6,6 +6,7 @@
.row
textarea(:placeholder='placeholder',
v-model='newMessage',
ref='user-entry',
:class='{"user-entry": newMessage}',
@keydown='updateCarretPosition',
@keyup.ctrl.enter='sendMessageShortcut()',
@@ -16,6 +17,7 @@
autocomplete(
:text='newMessage',
v-on:select="selectedAutocomplete",
:textbox='textbox',
:coords='coords',
:chat='group.chat')
@@ -62,6 +64,7 @@
TOP: 0,
LEFT: 0,
},
textbox: this.$refs,
};
},
computed: {
@@ -187,7 +190,7 @@
position: relative;
textarea {
height: 150px;
min-height: 150px;
width: 100%;
background-color: $white;
border: solid 1px $gray-400;
@@ -14,6 +14,11 @@
button.btn.btn-secondary.create-group-button.float-right(@click='createGroup()')
.svg-icon.positive-icon(v-html="icons.positiveIcon")
span(v-once) {{$t('createGuild2')}}
.row
.no-guilds.text-center.col-md-6.offset-md-3(v-if='!loading && filteredGuilds.length === 0')
h2(v-once) {{$t('noGuildsMatchFilters')}}
.row
.col-md-12
public-guild-item(v-for="guild in filteredGuilds", :key='guild._id', :guild="guild", :display-leave='true')
@@ -39,6 +44,15 @@
display: inline-block;
margin-right: .5em;
}
.no-guilds {
color: $gray-200;
margin-top: 10em;
h2 {
color: $gray-200;
}
}
</style>
<script>
+1 -6
View File
@@ -62,7 +62,7 @@
p(v-markdown='group.description')
sidebar-section(
:title="$t('challenges')",
:tooltip="isParty ? $t('challengeDetails') : $t('privateDescription')"
:tooltip="$t('challengeDetails')"
)
group-challenges(:groupId='searchId')
div.text-center
@@ -478,11 +478,6 @@ export default {
return n.type === 'NEW_CHAT_MESSAGE' && n.data.group.id === groupId;
});
},
deleteAllMessages () {
if (confirm(this.$t('confirmDeleteAllMessages'))) {
// User.clearPMs();
}
},
checkForAchievements () {
// Checks if user's party has reached 2 players for the first time.
if (!this.user.achievements.partyUp && this.group.memberCount >= 2) {
@@ -33,29 +33,29 @@ div
.col-1.actions
b-dropdown(right=true)
.svg-icon.inline.dots(slot='button-content', v-html="icons.dots")
b-dropdown-item(@click='removeMember(member, index)', v-if='isLeader')
span.dropdown-icon-item
.svg-icon.inline(v-html="icons.removeIcon", v-if='isLeader')
span.text {{$t('removeMember')}}
b-dropdown-item(@click='sendMessage(member)')
span.dropdown-icon-item
.svg-icon.inline(v-html="icons.messageIcon")
span.text {{$t('sendMessage')}}
b-dropdown-item(@click='promoteToLeader(member)', v-if='shouldShowPromoteToLeader')
b-dropdown-item(@click='promoteToLeader(member)', v-if='shouldShowLeaderFunctions(member._id)')
span.dropdown-icon-item
.svg-icon.inline(v-html="icons.starIcon")
span.text {{$t('promoteToLeader')}}
b-dropdown-item(@click='addManager(member._id)', v-if='isLeader && groupIsSubscribed')
b-dropdown-item(@click='addManager(member._id)', v-if='shouldShowAddManager(member._id)')
span.dropdown-icon-item
.svg-icon.inline(v-html="icons.starIcon")
span.text {{$t('addManager')}}
b-dropdown-item(@click='removeManager(member._id)', v-if='isLeader && groupIsSubscribed')
b-dropdown-item(@click='removeManager(member._id)', v-if='shouldShowRemoveManager(member._id)')
span.dropdown-icon-item
.svg-icon.inline(v-html="icons.removeIcon")
span.text {{$t('removeManager2')}}
b-dropdown-item(@click='viewProgress(member)', v-if='challengeId')
span.dropdown-icon-item
span.text {{ $t('viewProgress') }}
b-dropdown-item(@click='removeMember(member, index)', v-if='shouldShowLeaderFunctions(member._id)')
span.dropdown-icon-item
.svg-icon.inline(v-html="icons.removeIcon")
span.text {{$t('removeMember')}}
.row(v-if='isLoadMoreAvailable')
.col-12.text-center
button.btn.btn-secondary(@click='loadMoreMembers()') {{ $t('loadMore') }}
@@ -295,9 +295,6 @@ export default {
},
computed: {
...mapState({user: 'user.data'}),
shouldShowPromoteToLeader () {
return !this.challengeId && (this.isLeader || this.isAdmin);
},
isLeader () {
if (!this.group || !this.group.leader) return false;
return this.user._id === this.group.leader || this.user._id === this.group.leader._id;
@@ -338,6 +335,9 @@ export default {
// @TOOD: We might not need this since groupId is computed now
this.getMembers();
},
challengeId () {
this.getMembers();
},
group () {
this.getMembers();
},
@@ -377,9 +377,7 @@ export default {
this.invites = invites;
}
if (this.$store.state.memberModalOptions.viewingMembers.length > 0) {
this.members = this.$store.state.memberModalOptions.viewingMembers;
}
this.members = this.$store.state.memberModalOptions.viewingMembers;
},
async clickMember (uid, forceShow) {
let user = this.$store.state.user.data;
@@ -497,6 +495,17 @@ export default {
progressMemberId: member._id,
});
},
shouldShowAddManager (memberId) {
if (memberId === this.group.leader || memberId === this.group.leader._id) return false;
return !(this.group.managers && this.group.managers[memberId]);
},
shouldShowRemoveManager (memberId) {
if (!this.isLeader && !this.isAdmin) return false;
return this.group.managers && this.group.managers[memberId];
},
shouldShowLeaderFunctions (memberId) {
return !this.challengeId && (this.isLeader || this.isAdmin) && this.user._id !== memberId;
},
},
};
</script>
+22 -30
View File
@@ -2,18 +2,11 @@
.row
sidebar(v-on:search="updateSearch", v-on:filter="updateFilters")
.no-guilds.standard-page(v-if='filteredGuilds.length === 0')
.no-guilds-wrapper
.svg-icon(v-html='icons.greyBadge')
h2 {{$t('noGuildsTitle')}}
p {{$t('noGuildsParagraph1')}}
p {{$t('noGuildsParagraph2')}}
span(v-if='loading') {{ $t('loading') }}
.standard-page(v-if='filteredGuilds.length > 0')
.standard-page
.row
.col-md-8
h1.page-header.float-left(v-once) {{ $t('myGuilds') }}
.col-md-8.text-left
h1.page-header(v-once) {{ $t('myGuilds') }}
h2(v-if='loading && guilds.length === 0') {{ $t('loading') }}
.col-4
button.btn.btn-secondary.create-group-button.float-right(@click='createGroup()')
.svg-icon.positive-icon(v-html="icons.positiveIcon")
@@ -22,6 +15,18 @@
span.dropdown-label {{ $t('sortBy') }}
b-dropdown(:text="$t('sort')", right=true)
b-dropdown-item(v-for='sortOption in sortOptions', :key="sortOption.value", @click='sort(sortOption.value)') {{sortOption.text}}
.row
.no-guilds.text-center.col-md-6.offset-md-3(v-if='!loading && guilds.length === 0')
.svg-icon(v-html='icons.greyBadge')
h2(v-once) {{$t('noGuildsTitle')}}
p(v-once) {{$t('noGuildsParagraph1')}}
p(v-once) {{$t('noGuildsParagraph2')}}
.row
.no-guilds.text-center.col-md-6.offset-md-3(v-if='!loading && guilds.length > 0 && filteredGuilds.length === 0')
h2(v-once) {{$t('noGuildsMatchFilters')}}
.row
.col-md-12
public-guild-item(v-for="guild in filteredGuilds", :key='guild._id', :guild="guild", :display-gem-bank='true')
@@ -41,29 +46,16 @@
}
.no-guilds {
text-align: center;
color: $gray-200;
margin-top: 15em;
margin-top: 10em;
p {
font-size: 14px;
line-height: 1.43;
h2 {
color: $gray-200;
}
.no-guilds-wrapper {
width: 400px;
margin: 0 auto;
.svg-icon {
width: 60px;
margin: 0 auto;
}
}
}
@media only screen and (max-width: 768px) {
.no-guilds-wrapper {
width: 100% !important;
.svg-icon {
width: 88.7px;
margin: 1em auto;
}
}
</style>
@@ -139,8 +139,6 @@ export default {
this.emitFilters();
},
searchTerm: throttle(function searchTerm (newSearch) {
if (newSearch.length <= 1) return; // @TODO: eh, should we limit based on length?
this.$emit('search', {
searchTerm: newSearch,
});
+14 -29
View File
@@ -35,26 +35,11 @@ div
span.small-text(v-html="$t('inviteFriendsParty')")
br
button.btn.btn-primary(@click='createOrInviteParty()') {{ user.party._id ? $t('inviteFriends') : $t('startAParty') }}
a.useMobileApp(v-if="isAndroidMobile()", v-once, href="https://play.google.com/store/apps/details?id=com.habitrpg.android.habitica") {{ $t('useMobileApps') }}
a.useMobileApp(v-if="isIOSMobile()", v-once, href="https://itunes.apple.com/us/app/habitica-gamified-task-manager/id994882113?mt=8") {{ $t('useMobileApps') }}
</template>
<style lang="scss" scoped>
@import '~client/assets/scss/colors.scss';
.useMobileApp {
background: red;
color: white;
z-index: 10;
width: 100%;
margin: 10px 5px 0 0;
height: 64px;
text-align: center;
display: flex;
align-items: center;
}
#app-header {
padding-left: 24px;
padding-top: 9px;
@@ -155,12 +140,6 @@ export default {
...mapActions({
getPartyMembers: 'party:getMembers',
}),
isAndroidMobile () {
return navigator.userAgent.match(/Android/i);
},
isIOSMobile () {
return navigator.userAgent.match(/iPhone|iPad|iPod/i);
},
expandMember (memberId) {
if (this.expandedMember === memberId) {
this.expandedMember = null;
@@ -175,11 +154,13 @@ export default {
this.$root.$emit('bv::show::modal', 'create-party-modal');
}
},
showPartyMembers () {
async showPartyMembers () {
const party = await this.$store.dispatch('party:getParty');
this.$root.$emit('habitica:show-member-modal', {
groupId: this.user.party._id,
groupId: party.data._id,
viewingMembers: this.partyMembers,
group: this.user.party,
group: party.data,
});
},
setPartyMembersWidth ($event) {
@@ -192,12 +173,16 @@ export default {
if (this.user.party && this.user.party._id) {
this.$store.state.memberModalOptions.groupId = this.user.party._id;
this.getPartyMembers();
this.$root.$on('inviteModal::inviteToGroup', (group) => {
this.inviteModalGroup = group;
this.$root.$emit('bv::show::modal', 'invite-modal');
});
}
},
mounted () {
this.$root.$on('inviteModal::inviteToGroup', (group) => {
this.inviteModalGroup = group;
this.$root.$emit('bv::show::modal', 'invite-modal');
});
},
destroyed () {
this.$root.off('inviteModal::inviteToGroup');
},
};
</script>
+3 -2
View File
@@ -396,12 +396,13 @@ export default {
toggleUserDropdown () {
this.isUserDropdownOpen = !this.isUserDropdownOpen;
},
sync () {
async sync () {
this.$root.$emit('habitica::resync-requested');
return Promise.all([
await Promise.all([
this.$store.dispatch('user:fetch', {forceLoad: true}),
this.$store.dispatch('tasks:fetchUserTasks', {forceLoad: true}),
]);
this.$root.$emit('habitica::resync-completed');
},
async getUserGroupPlans () {
this.$store.state.groupPlans = await this.$store.dispatch('guilds:getGroupPlans');
@@ -61,4 +61,4 @@ export default {
},
};
</script>
</script>
@@ -0,0 +1,74 @@
<template lang="pug">
base-notification(
:can-remove="false",
:has-icon="false",
:read-after-click="false",
:notification="{}",
@click="action",
)
div.text-center(slot="content")
div.username-notification-title {{ $t('setUsernameNotificationTitle') }}
div {{ $t('setUsernameNotificationBody') }}
div.current-username-container.mx-auto
label.font-weight-bold {{ $t('currentUsername') + " " }}
label @
label {{ user.auth.local.username }}
.notifications-buttons
.btn.btn-small.btn-secondary(@click.stop="changeUsername()") {{ $t('goToSettings') }}
</template>
<style lang='scss'>
@import '../../../assets/scss/colors.scss';
.username-notification-title {
font-size: 16px;
margin-bottom: 8px;
font-weight: bold;
color: $purple-300;
}
.current-username-container {
border-radius: 2px;
background-color: #f9f9f9;
border: solid 1px #e1e0e3;
padding: 8px 16px 8px 16px;
display: inline-block;
margin-top: 16px;
margin-bottom: 16px;
label {
display: inline;
}
.notification-buttons {
display: inline-block;
}
}
</style>
<script>
import BaseNotification from './base';
import { mapState } from 'client/libs/store';
import axios from 'axios';
export default {
props: ['notification'],
components: {
BaseNotification,
},
computed: {
...mapState({user: 'user.data'}),
},
methods: {
action () {
this.$router.push({ name: 'site' });
},
async confirmUsername () {
await axios.put('/api/v4/user/auth/update-username', {username: this.user.auth.local.username});
},
changeUsername () {
this.$router.push({ name: 'site' });
},
},
};
</script>
@@ -93,6 +93,7 @@ import CARD_RECEIVED from './notifications/cardReceived';
import NEW_INBOX_MESSAGE from './notifications/newInboxMessage';
import NEW_CHAT_MESSAGE from './notifications/newChatMessage';
import WORLD_BOSS from './notifications/worldBoss';
import VERIFY_USERNAME from './notifications/verifyUsername';
export default {
components: {
@@ -105,6 +106,7 @@ export default {
UNALLOCATED_STATS_POINTS, NEW_MYSTERY_ITEMS, CARD_RECEIVED,
NEW_INBOX_MESSAGE, NEW_CHAT_MESSAGE,
WorldBoss: WORLD_BOSS,
VERIFY_USERNAME,
},
data () {
return {
@@ -127,6 +129,7 @@ export default {
'QUEST_INVITATION', 'GROUP_TASK_APPROVAL', 'GROUP_TASK_APPROVED',
'NEW_MYSTERY_ITEMS', 'CARD_RECEIVED',
'NEW_INBOX_MESSAGE', 'NEW_CHAT_MESSAGE', 'UNALLOCATED_STATS_POINTS',
'VERIFY_USERNAME',
],
};
},
@@ -179,6 +182,16 @@ export default {
});
}
if (this.user.flags.verifiedUsername !== true) {
notifications.push({
type: 'VERIFY_USERNAME',
data: {
username: this.user.auth.local.username,
},
id: 'custom-change-username',
});
}
const orderMap = this.notificationsOrder;
// Push the notifications stored in user.notifications
@@ -405,6 +405,8 @@ export default {
this.currentDraggingEgg = egg;
this.eggClickMode = true;
// Wait for the div.eggInfo.mouse node to be added to the DOM before
// changing its position.
this.$nextTick(() => {
this.mouseMoved(lastMouseMoveEvent);
});
@@ -427,6 +429,8 @@ export default {
this.currentDraggingPotion = potion;
this.potionClickMode = true;
// Wait for the div.hatchingPotionInfo.mouse node to be added to the
// DOM before changing its position.
this.$nextTick(() => {
this.mouseMoved(lastMouseMoveEvent);
});
@@ -471,6 +475,11 @@ export default {
},
mouseMoved ($event) {
// Keep track of the last mouse position even in click mode so that we
// know where to position the dragged potion/egg info on item click.
lastMouseMoveEvent = $event;
// Update the potion/egg popover if we are already dragging it.
if (this.potionClickMode) {
// dragging potioninfo is 180px wide (90 would be centered)
this.$refs.clickPotionInfo.style.left = `${$event.x - 60}px`;
@@ -479,8 +488,6 @@ export default {
// dragging eggInfo is 180px wide (90 would be centered)
this.$refs.clickEggInfo.style.left = `${$event.x - 60}px`;
this.$refs.clickEggInfo.style.top = `${$event.y + 10}px`;
} else {
lastMouseMoveEvent = $event;
}
},
},
@@ -17,7 +17,7 @@ div
triggers="hover",
placement="top",
)
h4.popover-content-title {{ item.text() }}
h4.popover-content-title {{ itemName || item.text() }}
div.popover-content-text(v-html="item.notes()")
</template>
@@ -45,6 +45,9 @@ export default {
itemContentClass: {
type: String,
},
itemName: {
type: String,
},
active: {
type: Boolean,
},
@@ -145,47 +145,17 @@
.btn.btn-flat.btn-show-more(@click="setShowMore(mountGroup.key)", v-if='mountGroup.key !== "specialMounts"')
| {{ $_openedItemRows_isToggled(mountGroup.key) ? $t('showLess') : $t('showMore') }}
drawer(:title="$t('quickInventory')",
:errorMessage="(!hasDrawerTabItems(selectedDrawerTab)) ? ((selectedDrawerTab === 0) ? $t('noFoodAvailable') : $t('noSaddlesAvailable')) : null")
div(slot="drawer-header")
.drawer-tab-container
.drawer-tab.text-right
a.drawer-tab-text(
@click="selectedDrawerTab = 0",
:class="{'drawer-tab-text-active': selectedDrawerTab === 0}",
) {{ drawerTabs[0].label }}
.clearfix
.drawer-tab.float-left
a.drawer-tab-text(
@click="selectedDrawerTab = 1",
:class="{ 'drawer-tab-text-active': selectedDrawerTab === 1 }",
) {{ drawerTabs[1].label }}
#petLikeToEatStable.drawer-help-text(v-once)
| {{ $t('petLikeToEat') + ' ' }}
span.svg-icon.inline.icon-16(v-html="icons.information")
b-popover(
target="petLikeToEatStable"
placement="top"
)
.popover-content-text(v-html="$t('petLikeToEatText')", v-once)
drawer-slider(
:items="drawerTabs[selectedDrawerTab].items",
:scrollButtonsVisible="hasDrawerTabItems(selectedDrawerTab)",
slot="drawer-slider",
:itemWidth=94,
:itemMargin=24,
:itemType="selectedDrawerTab"
)
template(slot="item", slot-scope="context")
foodItem(
:item="context.item",
:itemCount="userItems.food[context.item.key]",
:active="currentDraggingFood == context.item",
@itemDragEnd="onDragEnd()",
@itemDragStart="onDragStart($event, context.item)",
@itemClick="onFoodClicked($event, context.item)"
)
inventoryDrawer
template(slot="item", slot-scope="ctx")
foodItem(
:item="ctx.item",
:itemCount="ctx.itemCount",
:itemContentClass="ctx.itemClass",
:active="currentDraggingFood === ctx.item",
@itemDragEnd="onDragEnd()",
@itemDragStart="onDragStart($event, ctx.item)",
@itemClick="onFoodClicked($event, ctx.item)"
)
hatchedPetDialog(:hideText="true")
div.foodInfo(ref="dragginFoodInfo")
div(v-if="currentDraggingFood != null")
@@ -284,9 +254,6 @@
}
}
.drawer-slider .items {
height: 114px;
}
.modal-backdrop.fade.show {
background-color: $purple-50;
@@ -386,6 +353,7 @@
import StarBadge from 'client/components/ui/starBadge';
import CountBadge from 'client/components/ui/countBadge';
import DrawerSlider from 'client/components/ui/drawerSlider';
import InventoryDrawer from 'client/components/shared/inventoryDrawer';
import ResizeDirective from 'client/directives/resize.directive';
import DragDropDirective from 'client/directives/dragdrop.directive';
@@ -399,6 +367,8 @@
import openedItemRowsMixin from 'client/mixins/openedItemRows';
import petMixin from 'client/mixins/petMixin';
import { CONSTANTS, setLocalSetting, getLocalSetting } from 'client/libs/userlocalManager';
// TODO Normalize special pets and mounts
// import Store from 'client/store';
// import deepFreeze from 'client/libs/deepFreeze';
@@ -423,6 +393,7 @@
MountRaisedModal,
WelcomeModal,
HatchingModal,
InventoryDrawer,
},
directives: {
resize: ResizeDirective,
@@ -430,13 +401,15 @@
mousePosition: MouseMoveDirective,
},
data () {
const stableSortState = getLocalSetting(CONSTANTS.keyConstants.STABLE_SORT_STATE) || 'standard';
return {
viewOptions: {},
hideMissing: false,
searchText: null,
searchTextThrottled: '',
// sort has the translation-keys as values
selectedSortBy: 'standard',
selectedSortBy: stableSortState,
sortByItems: [
'standard',
'AZ',
@@ -461,6 +434,11 @@
let search = this.searchText.toLowerCase();
this.searchTextThrottled = search;
}, 250),
selectedSortBy: {
handler () {
setLocalSetting(CONSTANTS.keyConstants.STABLE_SORT_STATE, this.selectedSortBy);
},
},
},
computed: {
...mapState({
@@ -849,11 +827,12 @@
}
},
mouseMoved ($event) {
// Keep track of the last mouse position even in click mode so that we
// know where to position the dragged food icon on click.
lastMouseMoveEvent = $event;
if (this.foodClickMode) {
this.$refs.clickFoodInfo.style.left = `${$event.x - 70}px`;
this.$refs.clickFoodInfo.style.top = `${$event.y}px`;
} else {
lastMouseMoveEvent = $event;
}
},
},
+41 -19
View File
@@ -221,10 +221,8 @@ export default {
...mapState({
user: 'user.data',
userHp: 'user.data.stats.hp',
userExp: 'user.data.stats.exp',
userGp: 'user.data.stats.gp',
userMp: 'user.data.stats.mp',
userLvl: 'user.data.stats.lvl',
userNotifications: 'user.data.notifications',
userAchievements: 'user.data.achievements', // @TODO: does this watch deeply?
armoireEmpty: 'user.data.flags.armoireEmpty',
@@ -233,9 +231,15 @@ export default {
userClassSelect () {
return !this.user.flags.classSelected && this.user.stats.lvl >= 10;
},
userHasClass () {
return this.$store.getters['members:hasClass'](this.user);
},
invitedToQuest () {
return this.user.party.quest.RSVPNeeded && !this.user.party.quest.completed;
},
userExpAndLvl () {
return [this.user.stats.exp, this.user.stats.lvl];
},
},
watch: {
userHp (after, before) {
@@ -253,16 +257,6 @@ export default {
if (after < 0) this.playSound('Minus_Habit');
},
userExp (after, before) {
if (after === before) return;
if (this.user.stats.lvl === 0) return;
let exp = after - before;
if (exp < -50) { // recalculate exp if user level up
exp = toNextLevel(this.user.stats.lvl - 1) - before + after;
}
this.exp(exp);
},
userGp (after, before) {
if (after === before) return;
if (this.user.stats.lvl === 0) return;
@@ -283,15 +277,11 @@ export default {
},
userMp (after, before) {
if (after === before) return;
if (!this.$store.getters['members:hasClass'](this.user)) return;
if (!this.userHasClass) return;
let mana = after - before;
const mana = after - before;
this.mp(mana);
},
userLvl (after, before) {
if (after <= before || this.$store.state.isRunningYesterdailies) return;
this.showLevelUpNotifications(after);
},
userClassSelect (after) {
if (this.user.needsCron) return;
if (!after) return;
@@ -315,6 +305,9 @@ export default {
if (after !== true) return;
this.$root.$emit('bv::show::modal', 'quest-invitation');
},
userExpAndLvl (after, before) {
this.displayUserExpAndLvlNotifications(after[0], before[0], after[1], before[1]);
},
},
mounted () {
Promise.all([
@@ -385,6 +378,35 @@ export default {
debounceCheckUserAchievements: debounce(function debounceCheck () {
this.checkUserAchievements();
}, 700),
displayUserExpAndLvlNotifications (afterExp, beforeExp, afterLvl, beforeLvl) {
if (afterExp === beforeExp && afterLvl === beforeLvl) return;
// XP evaluation
if (afterExp !== beforeExp) {
if (this.user.stats.lvl === 0) return;
const lvlUps = afterLvl - beforeLvl;
let exp = afterExp - beforeExp;
if (lvlUps > 0) {
let level = Math.trunc(beforeLvl);
exp += toNextLevel(level);
// loop if more than 1 lvl up
for (let i = 1; i < lvlUps; i += 1) {
level += 1;
exp += toNextLevel(level);
}
}
this.exp(exp);
}
// Lvl evaluation
if (afterLvl !== beforeLvl) {
if (afterLvl <= beforeLvl || this.$store.state.isRunningYesterdailies) return;
this.showLevelUpNotifications(afterLvl);
}
},
checkUserAchievements () {
if (this.user.needsCron) return;
@@ -560,7 +582,7 @@ export default {
case 'CRON':
if (notification.data) {
if (notification.data.hp) this.hp(notification.data.hp, 'hp');
if (notification.data.mp) this.mp(notification.data.mp);
if (notification.data.mp && this.userHasClass) this.mp(notification.data.mp);
}
break;
case 'SCORED_TASK':
@@ -41,7 +41,11 @@ b-modal#send-gems(:title="title", :hide-footer="true", size='lg', @hide='onHide(
//include ../formatting-help
.modal-footer
button.btn.btn-primary(v-if='fromBal', @click='sendGift()') {{ $t("send") }}
button.btn.btn-primary(
v-if="fromBal",
@click="sendGift()",
:disabled="sendingInProgress"
) {{ $t("send") }}
template(v-else)
button.btn.btn-primary(@click='showStripe({gift, uuid: userReceivingGems._id})') {{ $t('card') }}
button.btn.btn-warning(@click='openPaypalGift({gift: gift, giftedTo: userReceivingGems._id})') PayPal
@@ -103,6 +107,7 @@ export default {
assistanceEmailObject: {
hrefTechAssistanceEmail: `<a href="mailto:${TECH_ASSISTANCE_EMAIL}">${TECH_ASSISTANCE_EMAIL}</a>`,
},
sendingInProgress: false,
};
},
computed: {
@@ -130,6 +135,7 @@ export default {
methods: {
// @TODO move to payments mixin or action (problem is that we need notifications)
async sendGift () {
this.sendingInProgress = true;
await this.$store.dispatch('members:transferGems', {
message: this.gift.message,
toUserId: this.userReceivingGems._id,
@@ -139,7 +145,9 @@ export default {
this.close();
},
onHide () {
this.gift.gems.amount = 0;
this.gift.message = '';
this.sendingInProgress = false;
},
close () {
this.$root.$emit('bv::hide::modal', 'send-gems');
@@ -40,7 +40,7 @@ export default {
...mapState({user: 'user.data', credentials: 'credentials'}),
getCodesUrl () {
if (!this.user) return '';
return `/api/v4/coupons?_id=${this.user._id}&apiToken=${this.credentials.API_TOKEN}`;
return '/api/v4/coupons';
},
},
methods: {
+126 -36
View File
@@ -4,7 +4,7 @@
reset-modal
delete-modal
h1.col-12 {{ $t('settings') }}
.col-6
.col-sm-6
.form-horizontal
h5 {{ $t('language') }}
select.form-control(:value='user.preferences.language',
@@ -105,7 +105,7 @@
p(v-html="$t('timezoneUTC', {utc: timezoneOffsetToUtc})")
p(v-html="$t('timezoneInfo')")
.col-6
.col-sm-6
h2 {{ $t('registration') }}
.panel-body
div
@@ -115,13 +115,9 @@
button.btn.btn-primary.mb-2(disabled='disabled', v-if='!hasBackupAuthOption(network.key) && user.auth[network.key].id') {{ $t('registeredWithSocial', {network: network.name}) }}
button.btn.btn-danger(@click='deleteSocialAuth(network)', v-if='hasBackupAuthOption(network.key) && user.auth[network.key].id') {{ $t('detachSocial', {network: network.name}) }}
hr
div(v-if='!user.auth.local.username')
div(v-if='!user.auth.local.email')
p {{ $t('addLocalAuth') }}
p {{ $t('usernameLimitations') }}
.form(name='localAuth', novalidate)
//-.alert.alert-danger(ng-messages='changeUsername.$error && changeUsername.submitted') {{ $t('fillAll') }}
.form-group
input.form-control(type='text', :placeholder="$t('username')", v-model='localAuth.username', required)
.form-group
input.form-control(type='text', :placeholder="$t('email')", v-model='localAuth.email', required)
.form-group
@@ -130,37 +126,36 @@
input.form-control(type='password', :placeholder="$t('confirmPass')", v-model='localAuth.confirmPassword', required)
button.btn.btn-primary(type='submit', @click='addLocalAuth()') {{ $t('submit') }}
.usersettings(v-if='user.auth.local.username')
p {{ $t('username') }}
|: {{user.auth.local.username}}
p
small.muted
| {{ $t('loginNameDescription') }}
p {{ $t('email') }}
|: {{user.auth.local.email}}
hr
.usersettings
h5 {{ $t('changeDisplayName') }}
.form(name='changeDisplayName', novalidate)
.form-group
input#changeDisplayname.form-control(type='text', :placeholder="$t('newDisplayName')", v-model='temporaryDisplayName')
button.btn.btn-primary(type='submit', @click='changeDisplayName(temporaryDisplayName)') {{ $t('submit') }}
h5 {{ $t('changeUsername') }}
.form(v-if='user.auth.local', name='changeUsername', novalidate)
//-.alert.alert-danger(ng-messages='changeUsername.$error && changeUsername.submitted') {{ $t('fillAll') }}
.form(name='changeUsername', novalidate)
.iconalert.iconalert-success(v-if='verifiedUsername') {{ $t('usernameVerifiedConfirmation', {'username': user.auth.local.username}) }}
.iconalert.iconalert-warning(v-else)
div.align-middle
span {{ $t('usernameNotVerified') }}
.form-group
input.form-control(type='text', :placeholder="$t('newUsername')", v-model='usernameUpdates.username')
input#changeUsername.form-control(@blur='restoreEmptyUsername()',type='text', :placeholder="$t('newUsername')", v-model='usernameUpdates.username', :class='{"is-invalid input-invalid": usernameInvalid}')
.input-error(v-for="issue in usernameIssues") {{ issue }}
small.form-text.text-muted {{ $t('changeUsernameDisclaimer') }}
button.btn.btn-primary(type='submit', @click='changeUser("username", usernameUpdates)', :disabled='usernameCannotSubmit') {{ $t('saveAndConfirm') }}
h5(v-if='user.auth.local.email') {{ $t('changeEmail') }}
.form(v-if='user.auth.local.email', name='changeEmail', novalidate)
.form-group
input.form-control(type='password', :placeholder="$t('password')", v-model='usernameUpdates.password')
button.btn.btn-primary(type='submit', @click='changeUser("username", usernameUpdates)') {{ $t('submit') }}
h5 {{ $t('changeEmail') }}
.form(v-if='user.auth.local', name='changeEmail', novalidate)
.form-group
input.form-control(type='text', :placeholder="$t('newEmail')", v-model='emailUpdates.newEmail')
input#changeEmail.form-control(type='text', :placeholder="$t('newEmail')", v-model='emailUpdates.newEmail')
.form-group
input.form-control(type='password', :placeholder="$t('password')", v-model='emailUpdates.password')
button.btn.btn-primary(type='submit', @click='changeUser("email", emailUpdates)') {{ $t('submit') }}
h5 {{ $t('changePass') }}
.form(v-if='user.auth.local', name='changePassword', novalidate)
h5(v-if='user.auth.local.email') {{ $t('changePass') }}
.form(v-if='user.auth.local.email', name='changePassword', novalidate)
.form-group
input.form-control(type='password', :placeholder="$t('oldPass')", v-model='passwordUpdates.password')
input#changePassword.form-control(type='password', :placeholder="$t('oldPass')", v-model='passwordUpdates.password')
.form-group
input.form-control(type='password', :placeholder="$t('newPass')", v-model='passwordUpdates.newPassword')
.form-group
@@ -177,10 +172,33 @@
popover-trigger='mouseenter', v-b-popover.hover.auto="$t('deleteAccPop')") {{ $t('deleteAccount') }}
</template>
<style scoped>
<style lang="scss" scoped>
@import '~client/assets/scss/colors.scss';
input {
color: $gray-50;
}
.usersettings h5 {
margin-top: 1em;
}
.iconalert > div > span {
line-height: 25px;
}
.iconalert > div:after {
clear: both;
content: '';
display: table;
}
.input-error {
color: $red-50;
font-size: 90%;
width: 100%;
margin-top: 5px;
}
</style>
<script>
@@ -188,7 +206,7 @@ import hello from 'hellojs';
import moment from 'moment';
import axios from 'axios';
import { mapState } from 'client/libs/store';
import debounce from 'lodash/debounce';
import restoreModal from './restoreModal';
import resetModal from './resetModal';
import deleteModal from './deleteModal';
@@ -224,7 +242,8 @@ export default {
availableFormats: ['MM/dd/yyyy', 'dd/MM/yyyy', 'yyyy/MM/dd'],
dayStartOptions,
newDayStart: 0,
usernameUpdates: {},
temporaryDisplayName: '',
usernameUpdates: {username: ''},
emailUpdates: {},
passwordUpdates: {},
localAuth: {
@@ -233,6 +252,7 @@ export default {
password: '',
confirmPassword: '',
},
usernameIssues: [],
};
},
mounted () {
@@ -240,12 +260,26 @@ export default {
// @TODO: We may need to request the party here
this.party = this.$store.state.party;
this.newDayStart = this.user.preferences.dayStart;
this.usernameUpdates.username = this.user.auth.local.username || null;
this.temporaryDisplayName = this.user.profile.name;
this.emailUpdates.newEmail = this.user.auth.local.email || null;
this.localAuth.username = this.user.auth.local.username || null;
hello.init({
facebook: process.env.FACEBOOK_KEY, // eslint-disable-line no-process-env
google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line no-process-env
}, {
redirect_uri: '', // eslint-disable-line
});
const focusID = this.$route.query.focus;
if (focusID !== undefined && focusID !== null) {
this.$nextTick(() => {
const element = document.getElementById(focusID);
if (element !== undefined && element !== null) {
element.focus();
}
});
}
},
computed: {
...mapState({
@@ -275,8 +309,47 @@ export default {
hasClass () {
return this.$store.getters['members:hasClass'](this.user);
},
verifiedUsername () {
return this.user.flags.verifiedUsername;
},
usernameValid () {
if (this.usernameUpdates.username.length <= 1) return false;
return this.usernameIssues.length === 0;
},
usernameInvalid () {
if (this.usernameUpdates.username.length <= 1) return false;
return !this.usernameValid;
},
usernameCannotSubmit () {
if (this.usernameUpdates.username.length <= 1) return true;
return !this.usernameValid;
},
},
watch: {
usernameUpdates: {
handler () {
this.validateUsername(this.usernameUpdates.username);
},
deep: true,
},
},
methods: {
// eslint-disable-next-line func-names
validateUsername: debounce(function (username) {
if (username.length <= 1 || username === this.user.auth.local.username) {
this.usernameIssues = [];
return;
}
this.$store.dispatch('auth:verifyUsername', {
username,
}).then(res => {
if (res.issues !== undefined) {
this.usernameIssues = res.issues;
} else {
this.usernameIssues = [];
}
});
}, 500),
set (preferenceType, subtype) {
let settings = {};
if (!subtype) {
@@ -349,8 +422,19 @@ export default {
},
async changeUser (attribute, updates) {
await axios.put(`/api/v4/user/auth/update-${attribute}`, updates);
alert(this.$t(`${attribute}Success`));
this.user[attribute] = updates[attribute];
if (attribute === 'username') {
this.user.auth.local.username = updates[attribute];
this.localAuth.username = this.user.auth.local.username;
this.user.flags.verifiedUsername = true;
} else if (attribute === 'email') {
this.user.auth.local.email = updates[attribute];
}
},
async changeDisplayName (newName) {
await axios.put('/api/v4/user/', {'profile.name': newName});
alert(this.$t('displayNameSuccess'));
this.user.profile.name = newName;
this.temporaryDisplayName = newName;
},
openRestoreModal () {
this.$root.$emit('bv::show::modal', 'restore');
@@ -383,8 +467,14 @@ export default {
alert(e.message);
}
},
addLocalAuth () {
axios.post('/api/v4/user/auth/local/register', this.localAuth, 'addedLocalAuth');
async addLocalAuth () {
await axios.post('/api/v4/user/auth/local/register', this.localAuth);
alert(this.$t('addedLocalAuth'));
},
restoreEmptyUsername () {
if (this.usernameUpdates.username.length < 1) {
this.usernameUpdates.username = this.user.auth.local.username;
}
},
},
};
@@ -0,0 +1,169 @@
<template lang="pug">
drawer.inventoryDrawer(
:title="$t('quickInventory')"
:errorMessage="inventoryDrawerErrorMessage(selectedDrawerItemType)"
)
div(slot="drawer-header")
drawer-header-tabs(
:tabs="filteredTabs",
@changedPosition="tabSelected($event)"
)
div(slot="right-item")
#petLikeToEatMarket.drawer-help-text(v-once)
| {{ $t('petLikeToEat') + ' ' }}
span.svg-icon.inline.icon-16(v-html="icons.information")
b-popover(
target="petLikeToEatMarket",
:placement="'top'",
)
.popover-content-text(v-html="$t('petLikeToEatText')", v-once)
drawer-slider(
v-if="hasOwnedItemsForType(selectedDrawerItemType)"
:items="ownedItems(selectedDrawerItemType) || []",
slot="drawer-slider",
:itemWidth=94,
:itemMargin=24,
:itemType="selectedDrawerTab"
)
template(slot="item", slot-scope="ctx")
slot(
name="item",
:item="ctx.item",
:itemClass="getItemClass(selectedDrawerContentType, ctx.item.key)",
:itemCount="userItems[selectedDrawerContentType][ctx.item.key] || 0",
:itemName="getItemName(selectedDrawerItemType, ctx.item)",
:itemType="selectedDrawerItemType"
)
</template>
<script>
import {mapState} from 'client/libs/store';
import inventoryUtils from 'client/mixins/inventoryUtils';
import svgInformation from 'assets/svg/information.svg';
import _filter from 'lodash/filter';
import CountBadge from 'client/components/ui/countBadge';
import Item from 'client/components/inventory/item';
import Drawer from 'client/components/ui/drawer';
import DrawerSlider from 'client/components/ui/drawerSlider';
import DrawerHeaderTabs from 'client/components/ui/drawerHeaderTabs';
export default {
mixins: [inventoryUtils],
components: {
Item,
CountBadge,
Drawer,
DrawerSlider,
DrawerHeaderTabs,
},
props: {
defaultSelectedTab: {
type: Number,
default: 0,
},
showEggs: Boolean,
showPotions: Boolean,
},
data () {
return {
drawerTabs: [
{
key: 'eggs',
label: this.$t('eggs'),
show: () => this.showEggs,
},
{
key: 'food',
label: this.$t('foodTitle'),
show: () => true,
},
{
key: 'hatchingPotions',
label: this.$t('hatchingPotions'),
show: () => this.showPotions,
},
{
key: 'special',
contentType: 'food',
label: this.$t('special'),
show: () => true,
},
],
selectedDrawerTab: this.defaultSelectedTab,
icons: Object.freeze({
information: svgInformation,
}),
};
},
computed: {
...mapState({
content: 'content',
userItems: 'user.data.items',
}),
selectedDrawerItemType () {
return this.filteredTabs[this.selectedDrawerTab].key;
},
selectedDrawerContentType () {
return this.filteredTabs[this.selectedDrawerTab].contentType ||
this.selectedDrawerItemType;
},
filteredTabs () {
return this.drawerTabs.filter(t => t.show());
},
},
methods: {
ownedItems (type) {
let mappedItems = _filter(this.content[type], i => {
return this.userItems[type][i.key] > 0;
});
switch (type) {
case 'food':
return _filter(mappedItems, f => {
return f.key !== 'Saddle';
});
case 'special':
if (this.userItems.food.Saddle) {
return _filter(this.content.food, f => {
return f.key === 'Saddle';
});
} else {
return [];
}
default:
return mappedItems;
}
},
tabSelected ($event) {
this.selectedDrawerTab = $event;
},
hasOwnedItemsForType (type) {
return this.ownedItems(type).length > 0;
},
inventoryDrawerErrorMessage (type) {
if (!this.hasOwnedItemsForType(type)) {
switch (type) {
case 'food': return this.$t('noFoodAvailable');
case 'special': return this.$t('noSaddlesAvailable');
default:
// @TODO: Change any places using similar locales from `pets.json` and use these new locales from 'inventory.json'
return this.$t('noItemsAvailableForType', {type: this.$t(`${type}ItemType`)});
}
}
},
},
};
</script>
<style lang="scss">
.inventoryDrawer {
.drawer-slider {
height: 126px;
}
}
</style>
@@ -0,0 +1,126 @@
<template lang="pug">
div.featuredItems
.background(:class="{broken: broken}")
.background(:class="{cracked: broken, broken: broken}")
div.npc
div.featured-label
span.rectangle
span.text {{npcName}}
span.rectangle
div.content
div.featured-label.with-border
span.rectangle
span.text {{ featuredText }}
span.rectangle
div.items.margin-center
shopItem(
v-for="item in featuredItems",
:key="item.key",
:item="item",
:price="item.value",
:itemContentClass="'shop_'+item.key",
:emptyItem="false",
:popoverPosition="'top'",
@click="featuredItemSelected(item)"
)
template(slot="itemBadge", slot-scope="ctx")
span.badge.badge-pill.badge-item.badge-svg(
:class="{'item-selected-badge': ctx.item.pinned, 'hide': !ctx.item.pinned}",
@click.prevent.stop="togglePinned(ctx.item)"
)
span.svg-icon.inline.icon-12.color(v-html="icons.pin")
</template>
<script>
import ShopItem from './shopItem';
import pinUtils from 'client/mixins/pinUtils';
import svgPin from 'assets/svg/pin.svg';
export default {
mixins: [pinUtils],
props: {
broken: Boolean,
npcName: String,
featuredText: String,
featuredItems: Array,
},
components: {
ShopItem,
},
data () {
return {
icons: Object.freeze({
pin: svgPin,
}),
};
},
methods: {
featuredItemSelected (item) {
this.$emit('featuredItemSelected', item);
},
},
};
</script>
<style lang="scss" scoped>
.featuredItems {
height: 216px;
.background {
width: 100%;
height: 216px;
position: absolute;
top: 0;
left: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.content {
display: flex;
flex-direction: column;
z-index: 1; // Always cover background.
}
.npc {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 216px;
}
.background.broken {
background: url('~assets/images/npc/broken/market_broken_background.png');
background-repeat: repeat-x;
}
.background.cracked {
background: url('~assets/images/npc/broken/market_broken_layer.png');
background-repeat: repeat-x;
}
.broken .npc {
background: url('~assets/images/npc/broken/market_broken_npc.png');
background-repeat: no-repeat;
}
}
.featured-label {
margin: 24px auto;
}
@media only screen and (max-width: 768px) {
.featuredItems .content {
display: none !important;
}
}
</style>
+1 -7
View File
@@ -11,17 +11,11 @@
<script>
import SecondaryMenu from 'client/components/secondaryMenu';
import notifications from 'client/mixins/notifications';
export default {
mixins: [notifications],
components: {
SecondaryMenu,
},
methods: {
showUnpinNotification (item) {
this.text(this.$t('unpinnedItem', {item: item.text}));
},
},
methods: {},
};
</script>
@@ -0,0 +1,52 @@
<template lang="pug">
div
countBadge(
v-if="item.showCount !== false",
:show="true",
:count="count"
)
.badge.badge-pill.badge-purple.gems-left(v-if='item.key === "gem"')
| {{ gemsLeft }}
span.badge.badge-pill.badge-item.badge-svg(
:class="{'item-selected-badge': item.pinned, 'hide': !item.pinned}",
@click.prevent.stop="togglePinned(item)"
)
span.svg-icon.inline.icon-12.color(v-html="icons.pin")
</template>
<script>
import { mapState } from 'client/libs/store';
import CountBadge from 'client/components/ui/countBadge';
import svgPin from 'assets/svg/pin.svg';
import planGemLimits from 'common/script/libs/planGemLimits';
import pinUtils from '../../../mixins/pinUtils';
export default {
mixins: [pinUtils],
props: ['item'],
components: {
CountBadge,
},
data () {
return {
icons: Object.freeze({
pin: svgPin,
}),
};
},
computed: {
...mapState({
user: 'user.data',
userItems: 'user.data.items',
}),
count () {
return this.userItems[this.item.purchaseType][this.item.key];
},
gemsLeft () {
if (!this.user.purchased.plan) return 0;
return planGemLimits.convCap + this.user.purchased.plan.consecutive.gemCapExtra - this.user.purchased.plan.gemsBought;
},
},
};
</script>
@@ -0,0 +1,94 @@
<template lang="pug">
div.items
shopItem(v-for="item in sortedMarketItems",
:key="item.key",
:item="item",
:emptyItem="false",
:popoverPosition="'top'",
@click="itemSelected(item)")
span(slot="popoverContent")
strong(v-if='item.key === "gem" && gemsLeft === 0') {{ $t('maxBuyGems') }}
h4.popover-content-title {{ item.text }}
template(slot="itemBadge", slot-scope="ctx")
category-item(:item='ctx.item')
</template>
<script>
import { mapState } from 'client/libs/store';
import pinUtils from 'client/mixins/pinUtils';
import planGemLimits from 'common/script/libs/planGemLimits';
import ShopItem from '../shopItem';
import CategoryItem from './categoryItem';
import _filter from 'lodash/filter';
import _sortBy from 'lodash/sortBy';
import _map from 'lodash/map';
export default {
mixins: [pinUtils],
props: ['hideLocked', 'hidePinned', 'searchBy', 'sortBy', 'category'],
components: {
CategoryItem,
ShopItem,
},
computed: {
...mapState({
content: 'content',
user: 'user.data',
userItems: 'user.data.items',
userStats: 'user.data.stats',
}),
gemsLeft () {
if (!this.user.purchased.plan) return 0;
return planGemLimits.convCap + this.user.purchased.plan.consecutive.gemCapExtra - this.user.purchased.plan.gemsBought;
},
sortedMarketItems () {
let result = _map(this.category.items, (e) => {
return {
...e,
pinned: this.isPinned(e),
};
});
result = _filter(result, (item) => {
if (this.hidePinned && item.pinned) {
return false;
}
if (this.searchBy) {
let foundPosition = item.text.toLowerCase().indexOf(this.searchBy);
if (foundPosition === -1) {
return false;
}
}
return true;
});
switch (this.sortBy) {
case 'AZ': {
result = _sortBy(result, ['text']);
break;
}
case 'sortByNumber': {
result = _sortBy(result, item => {
if (item.showCount === false) return 0;
return this.userItems[item.purchaseType][item.key] || 0;
});
break;
}
}
return result;
},
},
methods: {
itemSelected (item) {
this.$root.$emit('buyModal::showItem', item);
},
},
};
</script>
@@ -0,0 +1,169 @@
<template lang="pug">
layout-section(:title="$t('equipment')")
div(slot="filters")
filter-dropdown(
:label="$t('class')",
:initialItem="selectedGearCategory",
:items="marketGearCategories",
:withIcon="true",
@selected="selectedGroupGearByClass = $event.id"
)
span(slot="item", slot-scope="ctx")
span.svg-icon.inline.icon-16(v-html="icons[ctx.item.id]")
span.text {{ getClassName(ctx.item.id) }}
filter-dropdown(
:label="$t('sortBy')",
:initialItem="selectedSortGearBy",
:items="sortGearBy",
@selected="selectedSortGearBy = $event"
)
span(slot="item", slot-scope="ctx")
span.text {{ $t(ctx.item.id) }}
itemRows(
:items="sortedGearItems",
:itemWidth=94,
:itemMargin=24,
:type="'gear'",
:noItemsLabel="$t('noGearItemsOfClass')",
slot="content"
)
template(slot="item", slot-scope="ctx")
shopItem(
:key="ctx.item.key",
:item="ctx.item",
:emptyItem="userItems.gear[ctx.item.key] === undefined",
:popoverPosition="'top'",
@click="gearSelected(ctx.item)"
)
template(slot="itemBadge", slot-scope="ctx")
span.badge.badge-pill.badge-item.badge-svg(
:class="{'item-selected-badge': ctx.item.pinned, 'hide': !ctx.item.pinned}",
@click.prevent.stop="togglePinned(ctx.item)"
)
span.svg-icon.inline.icon-12.color(v-html="icons.pin")
</template>
<script>
import {mapState} from 'client/libs/store';
import LayoutSection from 'client/components/ui/layoutSection';
import FilterDropdown from 'client/components/ui/filterDropdown';
import ItemRows from 'client/components/ui/itemRows';
import ShopItem from '../shopItem';
import shops from 'common/script/libs/shops';
import svgPin from 'assets/svg/pin.svg';
import svgWarrior from 'assets/svg/warrior.svg';
import svgWizard from 'assets/svg/wizard.svg';
import svgRogue from 'assets/svg/rogue.svg';
import svgHealer from 'assets/svg/healer.svg';
import _filter from 'lodash/filter';
import _sortBy from 'lodash/sortBy';
import pinUtils from '../../../mixins/pinUtils';
const sortGearTypes = ['sortByType', 'sortByPrice', 'sortByCon', 'sortByPer', 'sortByStr', 'sortByInt'].map(g => ({id: g}));
const sortGearTypeMap = {
sortByType: 'type',
sortByPrice: 'value',
sortByCon: 'con',
sortByStr: 'str',
sortByInt: 'int',
};
export default {
mixins: [pinUtils],
props: ['hideLocked', 'hidePinned', 'searchBy'],
components: {
LayoutSection,
FilterDropdown,
ItemRows,
ShopItem,
},
data () {
return {
sortGearBy: sortGearTypes,
selectedSortGearBy: sortGearTypes[0],
selectedGroupGearByClass: '',
icons: Object.freeze({
pin: svgPin,
warrior: svgWarrior,
wizard: svgWizard,
rogue: svgRogue,
healer: svgHealer,
}),
};
},
computed: {
...mapState({
content: 'content',
user: 'user.data',
userItems: 'user.data.items',
userStats: 'user.data.stats',
}),
marketGearCategories () {
return shops.getMarketGearCategories(this.user).map(c => {
c.id = c.identifier;
return c;
});
},
selectedGearCategory () {
return this.marketGearCategories.filter(c => c.id === this.selectedGroupGearByClass)[0];
},
sortedGearItems () {
let category = _filter(this.marketGearCategories, ['identifier', this.selectedGroupGearByClass]);
let result = _filter(category[0].items, (gear) => {
if (this.hideLocked && gear.locked) {
return false;
}
if (this.hidePinned && gear.pinned) {
return false;
}
if (this.searchBy) {
let foundPosition = gear.text.toLowerCase().indexOf(this.searchBy);
if (foundPosition === -1) {
return false;
}
}
// hide already owned
return !this.userItems.gear.owned[gear.key];
});
// first all unlocked
// then the selected sort
result = _sortBy(result, [(item) => item.locked, sortGearTypeMap[this.selectedSortGearBy.id]]);
return result;
},
},
methods: {
getClassName (classType) {
if (classType === 'wizard') {
return this.$t('mage');
} else {
return this.$t(classType);
}
},
gearSelected (item) {
if (!item.locked) {
this.$root.$emit('buyModal::showItem', item);
}
},
},
created () {
this.selectedGroupGearByClass = this.userStats.class;
},
};
</script>
<style scoped>
</style>
@@ -0,0 +1,46 @@
<template lang="pug">
.form
h2(v-once) {{ $t('filter') }}
.form-group
checkbox(
v-for="category in categories",
:key="category.identifier",
:id="`category-${category.identifier}`",
:checked.sync="viewOptions[category.identifier].selected",
:text="category.text"
)
div.form-group.clearfix
h3.float-left(v-once) {{ $t('hideLocked') }}
toggle-switch.float-right(
v-model="lockedChecked",
@change="$emit('update:hideLocked', $event)"
)
div.form-group.clearfix
h3.float-left(v-once) {{ $t('hidePinned') }}
toggle-switch.float-right(
v-model="pinnedChecked",
@change="$emit('update:hidePinned', $event)"
)
</template>
<script>
import Checkbox from 'client/components/ui/checkbox';
import toggleSwitch from 'client/components/ui/toggleSwitch';
export default {
props: ['hidePinned', 'hideLocked', 'categories', 'viewOptions'],
components: {
Checkbox,
toggleSwitch,
},
data () {
return {
lockedChecked: this.hideLocked,
pinnedChecked: this.hidePinned,
};
},
};
</script>
<style scoped>
</style>

Some files were not shown because too many files have changed in this diff Show More