Merge branch 'develop' into negue/modal-notifications
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 229 KiB |
|
Before Width: | Height: | Size: 549 KiB After Width: | Height: | Size: 554 KiB |
|
Before Width: | Height: | Size: 449 KiB After Width: | Height: | Size: 483 KiB |
|
Before Width: | Height: | Size: 315 KiB After Width: | Height: | Size: 184 KiB |
|
Before Width: | Height: | Size: 343 KiB After Width: | Height: | Size: 412 KiB |
|
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 197 KiB |
|
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 154 KiB |
|
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 164 KiB |
|
Before Width: | Height: | Size: 153 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 158 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 163 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 127 KiB |
|
Before Width: | Height: | Size: 149 KiB After Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 114 KiB |
@@ -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;
|
||||
}
|
||||
@@ -34,3 +34,4 @@
|
||||
@import './progress-bar';
|
||||
@import './pin';
|
||||
@import './animals';
|
||||
@import './iconalert';
|
||||
|
||||
@@ -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',
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||