feat(event): countdown banners for limited availability items
This commit is contained in:
@@ -161,16 +161,10 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
<countdown-banner
|
||||
v-if="item.event && item.owned == null"
|
||||
class="limitedTime"
|
||||
>
|
||||
<span
|
||||
class="svg-icon inline icon-16 clock-icon"
|
||||
v-html="icons.clock"
|
||||
></span>
|
||||
<span class="limitedString">{{ limitedString }}</span>
|
||||
</div>
|
||||
:endDate = "endDate"
|
||||
/>
|
||||
<div
|
||||
v-if="item.key === 'rebirth_orb' && item.value > 0 && user.stats.lvl >= 100"
|
||||
class="free-rebirth d-flex align-items-center"
|
||||
@@ -399,6 +393,7 @@ import svgWhiteClock from '@/assets/svg/clock-white.svg';
|
||||
|
||||
import BalanceInfo from './balanceInfo.vue';
|
||||
import PinBadge from '@/components/ui/pinBadge';
|
||||
import CountdownBanner from './countdownBanner';
|
||||
import currencyMixin from './_currencyMixin';
|
||||
import notifications from '@/mixins/notifications';
|
||||
import buyMixin from '@/mixins/buy';
|
||||
@@ -432,6 +427,7 @@ export default {
|
||||
Item,
|
||||
Avatar,
|
||||
PinBadge,
|
||||
CountdownBanner,
|
||||
},
|
||||
mixins: [buyMixin, currencyMixin, notifications, numberInvalid, spellsMixin],
|
||||
props: {
|
||||
@@ -462,6 +458,7 @@ export default {
|
||||
|
||||
selectedAmountToBuy: 1,
|
||||
isPinned: false,
|
||||
endDate: seasonalShopConfig.dateRange.end,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -494,9 +491,6 @@ export default {
|
||||
}
|
||||
return this.item.notes;
|
||||
},
|
||||
limitedString () {
|
||||
return this.$t('limitedOffer', { date: moment(seasonalShopConfig.dateRange.end).format('LL') });
|
||||
},
|
||||
gemsLeft () {
|
||||
if (!this.user.purchased.plan) return 0;
|
||||
return planGemLimits.convCap
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div class="limitedTime">
|
||||
<span
|
||||
class="svg-icon inline icon-16"
|
||||
v-html="icons.clock"
|
||||
></span>
|
||||
<span class="limitedString"> {{ limitedString }} </span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
.limitedTime {
|
||||
height: 32px;
|
||||
background-color: $purple-300;
|
||||
width: calc(100% + 30px);
|
||||
margin: 0 -15px; // the modal content has its own padding
|
||||
|
||||
font-size: 12px;
|
||||
line-height: 1.33;
|
||||
text-align: center;
|
||||
color: $white;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.limitedString {
|
||||
height: 16px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import svgClock from '@/assets/svg/clock.svg';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
endDate: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
clock: svgClock,
|
||||
}),
|
||||
timer: '',
|
||||
limitedString: '',
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.countdownString();
|
||||
this.timer = setInterval(this.countdownString, 30000);
|
||||
},
|
||||
methods: {
|
||||
countdownString () {
|
||||
const diffDuration = moment.duration(moment(this.endDate).diff(moment()));
|
||||
|
||||
if (diffDuration.days() > 0) {
|
||||
this.limitedString = this.$t('limitedAvailabilityDays', {
|
||||
days: diffDuration.days(),
|
||||
hours: diffDuration.hours(),
|
||||
minutes: diffDuration.minutes(),
|
||||
});
|
||||
} else {
|
||||
this.limitedString = this.$t('limitedAvailabilityHours', {
|
||||
hours: diffDuration.hours(),
|
||||
minutes: diffDuration.minutes(),
|
||||
});
|
||||
}
|
||||
},
|
||||
cancelAutoUpdate () {
|
||||
clearInterval(this.timer);
|
||||
},
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.cancelAutoUpdate();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -48,6 +48,12 @@ export default {
|
||||
},
|
||||
mixins: [pinUtils],
|
||||
props: ['hideLocked', 'hidePinned', 'searchBy', 'sortBy', 'category'],
|
||||
data () {
|
||||
return {
|
||||
timer: '',
|
||||
limitedString: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
content: 'content',
|
||||
@@ -60,9 +66,6 @@ export default {
|
||||
return planGemLimits.convCap
|
||||
+ this.user.purchased.plan.consecutive.gemCapExtra - this.user.purchased.plan.gemsBought;
|
||||
},
|
||||
limitedString () {
|
||||
return this.$t('limitedOffer', { date: moment(seasonalShopConfig.dateRange.end).format('LL') });
|
||||
},
|
||||
sortedMarketItems () {
|
||||
let result = _map(this.category.items, e => ({
|
||||
...e,
|
||||
@@ -103,10 +106,36 @@ export default {
|
||||
return result;
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.countdownString();
|
||||
this.timer = setInterval(this.countdownString, 30000);
|
||||
},
|
||||
methods: {
|
||||
itemSelected (item) {
|
||||
this.$root.$emit('buyModal::showItem', item);
|
||||
},
|
||||
countdownString () {
|
||||
const diffDuration = moment.duration(moment(seasonalShopConfig.dateRange.end).diff(moment()));
|
||||
|
||||
if (diffDuration.days() > 0) {
|
||||
this.limitedString = this.$t('limitedAvailabilityDays', {
|
||||
days: diffDuration.days(),
|
||||
hours: diffDuration.hours(),
|
||||
minutes: diffDuration.minutes(),
|
||||
});
|
||||
} else {
|
||||
this.limitedString = this.$t('limitedAvailabilityHours', {
|
||||
hours: diffDuration.hours(),
|
||||
minutes: diffDuration.minutes(),
|
||||
});
|
||||
}
|
||||
},
|
||||
cancelAutoUpdate () {
|
||||
clearInterval(this.timer);
|
||||
},
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.cancelAutoUpdate();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -84,16 +84,10 @@
|
||||
>
|
||||
<questDialogDrops :item="item" />
|
||||
</div>
|
||||
<div
|
||||
<countdown-banner
|
||||
v-if="item.event"
|
||||
class="limitedTime"
|
||||
>
|
||||
<span
|
||||
class="svg-icon inline icon-16 clock-icon"
|
||||
v-html="icons.clock"
|
||||
></span>
|
||||
<span class="limitedString">{{ limitedString }}</span>
|
||||
</div>
|
||||
:endDate="endDate"
|
||||
/>
|
||||
<div
|
||||
slot="modal-footer"
|
||||
class="clearfix"
|
||||
@@ -268,7 +262,6 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import { mapState } from '@/libs/store';
|
||||
import seasonalShopConfig from '@/../../common/script/libs/shops-seasonal.config';
|
||||
|
||||
@@ -285,6 +278,7 @@ import notifications from '@/mixins/notifications';
|
||||
import buyMixin from '@/mixins/buy';
|
||||
import numberInvalid from '@/mixins/numberInvalid';
|
||||
import PinBadge from '@/components/ui/pinBadge';
|
||||
import CountdownBanner from '../countdownBanner';
|
||||
|
||||
import questDialogDrops from './questDialogDrops';
|
||||
import questDialogContent from './questDialogContent';
|
||||
@@ -295,6 +289,7 @@ export default {
|
||||
PinBadge,
|
||||
questDialogDrops,
|
||||
questDialogContent,
|
||||
CountdownBanner,
|
||||
},
|
||||
mixins: [buyMixin, currencyMixin, notifications, numberInvalid],
|
||||
props: {
|
||||
@@ -321,6 +316,7 @@ export default {
|
||||
|
||||
isPinned: false,
|
||||
selectedAmountToBuy: 1,
|
||||
endDate: seasonalShopConfig.dateRange.end,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -344,9 +340,6 @@ export default {
|
||||
if (this.priceType === 'hourglasses') return this.icons.hourglass;
|
||||
return this.icons.gem;
|
||||
},
|
||||
limitedString () {
|
||||
return this.$t('limitedOffer', { date: moment(seasonalShopConfig.dateRange.end).format('LL') });
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
item: function itemChanged () {
|
||||
|
||||
@@ -143,6 +143,8 @@ export default {
|
||||
starHalf: svgStarHalf,
|
||||
starEmpty: svgStarEmpty,
|
||||
}),
|
||||
timer: '',
|
||||
limitedString: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -153,9 +155,10 @@ export default {
|
||||
|
||||
return 1;
|
||||
},
|
||||
limitedString () {
|
||||
return this.$t('limitedOffer', { date: moment(seasonalShopConfig.dateRange.end).format('LL') });
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.countdownString();
|
||||
this.timer = setInterval(this.countdownString, 30000);
|
||||
},
|
||||
methods: {
|
||||
stars () {
|
||||
@@ -182,6 +185,28 @@ export default {
|
||||
}
|
||||
return collect.text;
|
||||
},
|
||||
countdownString () {
|
||||
const diffDuration = moment.duration(moment(seasonalShopConfig.dateRange.end).diff(moment()));
|
||||
|
||||
if (diffDuration.days() > 0) {
|
||||
this.limitedString = this.$t('limitedAvailabilityDays', {
|
||||
days: diffDuration.days(),
|
||||
hours: diffDuration.hours(),
|
||||
minutes: diffDuration.minutes(),
|
||||
});
|
||||
} else {
|
||||
this.limitedString = this.$t('limitedAvailabilityHours', {
|
||||
hours: diffDuration.hours(),
|
||||
minutes: diffDuration.minutes(),
|
||||
});
|
||||
}
|
||||
},
|
||||
cancelAutoUpdate () {
|
||||
clearInterval(this.timer);
|
||||
},
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.cancelAutoUpdate();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -291,16 +291,18 @@ export default {
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return Object.freeze({
|
||||
return {
|
||||
itemId: uuid(),
|
||||
icons: {
|
||||
icons: Object.freeze({
|
||||
gems: svgGem,
|
||||
gold: svgGold,
|
||||
lock: svgLock,
|
||||
hourglasses: svgHourglasses,
|
||||
clock: svgClock,
|
||||
},
|
||||
});
|
||||
}),
|
||||
timer: '',
|
||||
limitedString: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
showNotes () {
|
||||
@@ -314,10 +316,10 @@ export default {
|
||||
}
|
||||
return 'gold';
|
||||
},
|
||||
limitedString () {
|
||||
return this.item.owned === false ? ''
|
||||
: this.$t('limitedOffer', { date: moment(seasonalShopConfig.dateRange.end).format('LL') });
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.countdownString();
|
||||
this.timer = setInterval(this.countdownString, 30000);
|
||||
},
|
||||
methods: {
|
||||
click () {
|
||||
@@ -338,6 +340,28 @@ export default {
|
||||
locked: this.item.locked,
|
||||
};
|
||||
},
|
||||
countdownString () {
|
||||
const diffDuration = moment.duration(moment(seasonalShopConfig.dateRange.end).diff(moment()));
|
||||
|
||||
if (diffDuration.days() > 0) {
|
||||
this.limitedString = this.$t('limitedAvailabilityDays', {
|
||||
days: diffDuration.days(),
|
||||
hours: diffDuration.hours(),
|
||||
minutes: diffDuration.minutes(),
|
||||
});
|
||||
} else {
|
||||
this.limitedString = this.$t('limitedAvailabilityHours', {
|
||||
hours: diffDuration.hours(),
|
||||
minutes: diffDuration.minutes(),
|
||||
});
|
||||
}
|
||||
},
|
||||
cancelAutoUpdate () {
|
||||
clearInterval(this.timer);
|
||||
},
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.cancelAutoUpdate();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -125,5 +125,7 @@
|
||||
"welcome3": "Progress in life and the game!",
|
||||
"welcome3notes": "As you improve your life, your avatar will level up and unlock pets, quests, equipment, and more!",
|
||||
"imReady": "Enter Habitica",
|
||||
"limitedOffer": "Available until <%= date %>"
|
||||
"limitedOffer": "Available until <%= date %>",
|
||||
"limitedAvailabilityDays": "Available for <%= days %>d <%= hours %>h <%= minutes %>m",
|
||||
"limitedAvailabilityHours": "Available for <%= hours %>h <%= minutes %>m"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user